Configtool: major restructuring.

We distinguish between printers and boards now. Most of the
code was split into one file per tab/window and moved into
configtool/.
This commit is contained in:
jbernardis 2015-01-13 20:08:14 -05:00 committed by Markus Hitter
parent 943a8326a9
commit d7789ee217
26 changed files with 4765 additions and 2596 deletions

2446
config.py Normal file → Executable file

File diff suppressed because it is too large Load Diff

219
config/board.gen7-v1.4.h Normal file
View File

@ -0,0 +1,219 @@
/***************************************************************************\
* *
* 1. CPU *
* *
\***************************************************************************/
//PROCESSORS_START
#ifndef __AVR_ATmega644__
#ifndef __AVR_ATmega644P__
#ifndef __AVR_ATmega1284__
#ifndef __AVR_ATmega1284P__
#error Wrong CPU type.
#endif
#endif
#endif
#endif
//PROCESSORS_END
/** \def F_CPU
CPU clock rate. #ifndef required for Arduino compatibility.
*/
#ifndef F_CPU
#define F_CPU 20000000UL
#endif
/** \def MOTHERBOARD
This is the motherboard, as opposed to the extruder. See extruder/ directory
for GEN3 extruder firmware.
*/
#define MOTHERBOARD
/***************************************************************************\
* *
* 2. PINOUTS *
* *
\***************************************************************************/
#include "../arduino.h"
#define X_STEP_PIN DIO29
#define X_DIR_PIN DIO28
#define X_MIN_PIN DIO0
//#define X_MAX_PIN xxxx
//#define X_ENABLE_PIN xxxx
//#define X_INVERT_DIR
//#define X_INVERT_MIN
//#define X_INVERT_MAX
//#define X_INVERT_ENABLE
#define Y_STEP_PIN DIO27
#define Y_DIR_PIN DIO26
#define Y_MIN_PIN DIO1
//#define Y_MAX_PIN xxxx
//#define Y_ENABLE_PIN xxxx
//#define Y_INVERT_DIR
//#define Y_INVERT_MIN
//#define Y_INVERT_MAX
//#define Y_INVERT_ENABLE
#define Z_STEP_PIN DIO23
#define Z_DIR_PIN DIO22
#define Z_MIN_PIN DIO2
//#define Z_MAX_PIN xxxx
//#define Z_ENABLE_PIN xxxx
//#define Z_INVERT_DIR
//#define Z_INVERT_MIN
//#define Z_INVERT_MAX
//#define Z_INVERT_ENABLE
#define E_STEP_PIN DIO19
#define E_DIR_PIN DIO18
//#define E_ENABLE_PIN xxxx
//#define E_INVERT_DIR
//#define E_INVERT_ENABLE
#define PS_ON_PIN DIO15
//#define PS_MOSFET_PIN xxxx
#define STEPPER_ENABLE_PIN DIO25
#define STEPPER_INVERT_ENABLE
/** \def DEBUG_LED_PIN
Enable flashing of a LED during motor stepping.
Disabled by default. Uncommenting this makes the binary a few bytes larger
and adds a few cycles to the step timing interrrupt in timer.c. Also used
for precision profiling (profiling works even without actually having such
a LED in hardware), see
http://reprap.org/wiki/Teacup_Firmware#Doing_precision_profiling
*/
//#define DEBUG_LED_PIN DIO21
/***************************************************************************\
* *
* 3. TEMPERATURE SENSORS *
* *
\***************************************************************************/
#ifndef DEFINE_TEMP_SENSOR
#define DEFINE_TEMP_SENSOR(...)
#endif
/** \def TEMP_MAX6675 TEMP_THERMISTOR TEMP_AD595 TEMP_PT100 TEMP_INTERCOM
Which temperature sensor types are you using? Leave all used ones
uncommented, comment out all others to save binary size and enhance
performance.
*/
//#define TEMP_MAX6675
#define TEMP_THERMISTOR
//#define TEMP_AD595
//#define TEMP_PT100
//#define TEMP_INTERCOM
/** \def TEMP_SENSOR_PIN
Temperature sensor pins a user should be able to choose from in configtool.
All commented out.
*/
//#define TEMP_SENSOR_PIN AIO0
//#define TEMP_SENSOR_PIN AIO1
//#define TEMP_SENSOR_PIN AIO7
/** \def DEFINE_TEMP_SENSOR
Define your temperature sensors here. One line for each sensor, only
limited by the number of available ATmega pins.
Types are same as TEMP_ list above - TT_MAX6675, TT_THERMISTOR, TT_AD595,
TT_PT100, TT_INTERCOM. See list in temp.c.
The "additional" field is used for TT_THERMISTOR only. It defines the
name of the table(s) in ThermistorTable.h to use. Typically, this is
THERMISTOR_EXTRUDER for the first or only table, or THERMISTOR_BED for
the second table. See also early in ThermistorTable.{single|double}.h.
For a GEN3 set temp_type to TT_INTERCOM and temp_pin to AIO0. The pin
won't be used in this case.
*/
// name type pin additional
//DEFINE_TEMP_SENSORS_START
DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO1, THERMISTOR_EXTRUDER)
DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, AIO0, THERMISTOR_BED)
//DEFINE_TEMP_SENSORS_END
/***************************************************************************\
* *
* 4. HEATERS *
* *
\***************************************************************************/
#ifndef DEFINE_HEATER
#define DEFINE_HEATER(...)
#endif
/** \def HEATER_PIN
Heater pins a user should be able to choose from in configtool. All
commented out.
*/
//#define HEATER_PIN DIO3
//#define HEATER_PIN DIO4
//#define HEATER_PIN DIO5
/** \def DEFINE_HEATER
Define your heaters and devices here.
To attach a heater to a temp sensor above, simply use exactly the same
name - copy+paste is your friend. Some common names are 'extruder',
'bed', 'fan', 'motor', ... names with special meaning can be found
in gcode_process.c. Currently, these are:
HEATER_extruder (M104)
HEATER_bed (M140)
HEATER_fan (M106)
Devices don't neccessarily have a temperature sensor, e.g. fans or
milling spindles. Operate such devices by setting their power (M106),
instead of setting their temperature (M104).
Also note, the index of a heater (M106 P#) can differ from the index of
its attached temperature sensor (M104 P#) in case sensor-less devices
are defined or the order of the definitions differs. The first defined
device has the index 0 (zero).
Set 'pwm' to ...
1 for using PWM on a PWM-able pin and on/off on other pins.
0 for using on/off on a PWM-able pin, too.
Using PWM usually gives smoother temperature control but can conflict
with slow switches, like solid state relays. PWM frequency can be
influenced globally with FAST_PWM, see below.
*/
// name port pwm
//DEFINE_HEATERS_START
DEFINE_HEATER(extruder, DIO4, 1)
DEFINE_HEATER(bed, DIO3, 1)
#define HEATER_EXTRUDER HEATER_extruder
#define HEATER_BED HEATER_bed
//DEFINE_HEATERS_END
/***************************************************************************\
* *
* 5. COMMUNICATION OPTIONS *
* *
\***************************************************************************/
/** \def BAUD
Baud rate for the serial RS232 protocol connection to the host. Usually
115200, other common values are 19200, 38400 or 57600. Ignored when USB_SERIAL
is defined.
*/
#define BAUD 115200
/** \def USB_SERIAL
Define this for using USB instead of the serial RS232 protocol. Works on
USB-equipped ATmegas, like the ATmega32U4, only.
*/
//#define USB_SERIAL

215
config/board.ramps-v1.3.h Normal file
View File

@ -0,0 +1,215 @@
/***************************************************************************\
* *
* 1. CPU *
* *
\***************************************************************************/
//PROCESSORS_START
#ifndef __AVR_ATmega1280__
#ifndef __AVR_ATmega2560__
#error Wrong CPU type.
#endif
#endif
//PROCESSORS_END
/** \def F_CPU
CPU clock rate. #ifndef required for Arduino compatibility.
*/
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
/** \def MOTHERBOARD
This is the motherboard, as opposed to the extruder. See extruder/ directory
for GEN3 extruder firmware.
*/
#define MOTHERBOARD
/***************************************************************************\
* *
* 2. PINOUTS *
* *
\***************************************************************************/
#include "../arduino.h"
#define X_STEP_PIN DIO54
#define X_DIR_PIN DIO55
#define X_MIN_PIN DIO3
//#define X_MAX_PIN DIO2
#define X_ENABLE_PIN DIO38
//#define X_INVERT_DIR
//#define X_INVERT_MIN
//#define X_INVERT_MAX
#define X_INVERT_ENABLE
#define Y_STEP_PIN DIO60
#define Y_DIR_PIN DIO61
#define Y_MIN_PIN DIO14
//#define Y_MAX_PIN DIO15
#define Y_ENABLE_PIN DIO56
//#define Y_INVERT_DIR
//#define Y_INVERT_MIN
//#define Y_INVERT_MAX
#define Y_INVERT_ENABLE
#define Z_STEP_PIN DIO46
#define Z_DIR_PIN DIO48
#define Z_MIN_PIN DIO18
//#define Z_MAX_PIN DIO19
#define Z_ENABLE_PIN DIO62
//#define Z_INVERT_DIR
//#define Z_INVERT_MIN
//#define Z_INVERT_MAX
#define Z_INVERT_ENABLE
#define E_STEP_PIN DIO26
#define E_DIR_PIN DIO28
#define E_ENABLE_PIN DIO24
//#define E_INVERT_DIR
#define E_INVERT_ENABLE
//#define PS_ON_PIN xxxx
//#define PS_MOSFET_PIN xxxx
//#define STEPPER_ENABLE_PIN xxxx
//#define STEPPER_INVERT_ENABLE
/** \def DEBUG_LED_PIN
Enable flashing of a LED during motor stepping.
Disabled by default. Uncommenting this makes the binary a few bytes larger
and adds a few cycles to the step timing interrrupt in timer.c. Also used
for precision profiling (profiling works even without actually having such
a LED in hardware), see
http://reprap.org/wiki/Teacup_Firmware#Doing_precision_profiling
*/
//#define DEBUG_LED_PIN DIO13
/***************************************************************************\
* *
* 3. TEMPERATURE SENSORS *
* *
\***************************************************************************/
#ifndef DEFINE_TEMP_SENSOR
#define DEFINE_TEMP_SENSOR(...)
#endif
/** \def TEMP_MAX6675 TEMP_THERMISTOR TEMP_AD595 TEMP_PT100 TEMP_INTERCOM
Which temperature sensor types are you using? Leave all used ones
uncommented, comment out all others to save binary size and enhance
performance.
*/
//#define TEMP_MAX6675
#define TEMP_THERMISTOR
//#define TEMP_AD595
//#define TEMP_PT100
//#define TEMP_INTERCOM
/** \def TEMP_SENSOR_PIN
Temperature sensor pins a user should be able to choose from in configtool.
All commented out.
*/
//#define TEMP_SENSOR_PIN AIO13
//#define TEMP_SENSOR_PIN AIO14
//#define TEMP_SENSOR_PIN AIO15
/** \def DEFINE_TEMP_SENSOR
Define your temperature sensors here. One line for each sensor, only
limited by the number of available ATmega pins.
Types are same as TEMP_ list above - TT_MAX6675, TT_THERMISTOR, TT_AD595,
TT_PT100, TT_INTERCOM. See list in temp.c.
The "additional" field is used for TT_THERMISTOR only. It defines the
name of the table(s) in ThermistorTable.h to use. Typically, this is
THERMISTOR_EXTRUDER for the first or only table, or THERMISTOR_BED for
the second table. See also early in ThermistorTable.{single|double}.h.
For a GEN3 set temp_type to TT_INTERCOM and temp_pin to AIO0. The pin
won't be used in this case.
*/
// name type pin additional
//DEFINE_TEMP_SENSORS_START
DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO13, THERMISTOR_EXTRUDER)
DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, AIO14, THERMISTOR_BED)
//DEFINE_TEMP_SENSORS_END
/***************************************************************************\
* *
* 4. HEATERS *
* *
\***************************************************************************/
#ifndef DEFINE_HEATER
#define DEFINE_HEATER(...)
#endif
/** \def HEATER_PIN
Heater pins a user should be able to choose from in configtool. All
commented out.
*/
//#define HEATER_PIN DIO5
//#define HEATER_PIN DIO8
//#define HEATER_PIN DIO10
/** \def DEFINE_HEATER
Define your heaters and devices here.
To attach a heater to a temp sensor above, simply use exactly the same
name - copy+paste is your friend. Some common names are 'extruder',
'bed', 'fan', 'motor', ... names with special meaning can be found
in gcode_process.c. Currently, these are:
HEATER_extruder (M104)
HEATER_bed (M140)
HEATER_fan (M106)
Devices don't neccessarily have a temperature sensor, e.g. fans or
milling spindles. Operate such devices by setting their power (M106),
instead of setting their temperature (M104).
Also note, the index of a heater (M106 P#) can differ from the index of
its attached temperature sensor (M104 P#) in case sensor-less devices
are defined or the order of the definitions differs. The first defined
device has the index 0 (zero).
Set 'pwm' to ...
1 for using PWM on a PWM-able pin and on/off on other pins.
0 for using on/off on a PWM-able pin, too.
Using PWM usually gives smoother temperature control but can conflict
with slow switches, like solid state relays. PWM frequency can be
influenced globally with FAST_PWM, see below.
*/
// name port pwm
//DEFINE_HEATERS_START
DEFINE_HEATER(extruder, DIO10, 1)
DEFINE_HEATER(bed, DIO8, 1)
#define HEATER_EXTRUDER HEATER_extruder
#define HEATER_BED HEATER_bed
//DEFINE_HEATERS_END
/***************************************************************************\
* *
* 5. COMMUNICATION OPTIONS *
* *
\***************************************************************************/
/** \def BAUD
Baud rate for the serial RS232 protocol connection to the host. Usually
115200, other common values are 19200, 38400 or 57600. Ignored when USB_SERIAL
is defined.
*/
#define BAUD 115200
/** \def USB_SERIAL
Define this for using USB instead of the serial RS232 protocol. Works on
USB-equipped ATmegas, like the ATmega32U4, only.
*/
//#define USB_SERIAL

160
config/config-syntax.txt Normal file
View File

@ -0,0 +1,160 @@
Syntax rules for configuration files in order to be used with the graphical
configuration utility.
Variable Names - both board and printer files
---------------------------------------------
Every #define variable used in the configuration must appear in the file.
If the variable is being given a value, it must appear normally i.e.
#define NAME value
or
#define NAME
If a variable is NOT to be defined, then it must be included in a single line
comment:
//#define NAME
It is immaterial if the name as a comment has a value. This line just marks
place where the variable will be inserted if it is defined. Although there
may be arbitrary spaces before and after the "//", no other intervening
characters may be placed on this line.
The above is based on the variable NAME, not on value. For example, for
acceleration there are three different and mutually exclusive variable names:
//#define ACCELERATION_REPRAP
#define ACCELERATION_RAMPING
//#define ACCELERATION_TEMPORAL
All of these names MUST appear in the header file (with at most one of them
un-commented out).
However, the variable KINEMATICS only has a finite set of values and would
seem to be a similar field, but there is only one variable name: KINEMATICS
- so it should only appear once.
This is ok:
//#define KINEMATICS KINEMATICS_STRAIGHT
as is this:
#define KINEMATICS KINEMATICS_STRAIGHT
but this is not:
#define KINEMATICS KINEMATICS_STRAIGHT
//#define KINEMATICS KINEMATICS_COREXY
Help Text - both board and printer files
----------------------------------------
The help text that is displayed on the GUI comes directly from the
configuration file itself. In order to be parsed correctly, help text must
be formatted as follows:
The start of a help text block is as follows:
/** \def NAME1 NAME2 NAME2 NAME4 ...
All subsequent lines are copied exactly as entered into the help text until
the end delimiter is reached. The end delimiter is a line that starts with '*/'.
Note that it is possible to specify multiple names on the start line. This
allows identical help text for similar or related fields.
Processor Preamble - Board file
-------------------------------
The utility parses out the processor types from the config file when it loads
it, and it provides an interface where processors may be added or removed.
When a file is saved, the utility needs to know 1) where to insert the new
processor preamble, and 2) what to remove from the old file. To achieve this,
there is a sequence that identifies the start of the processor preamble, and
a sequence that indicates the end. As follows:
//PROCESSORS_START
#ifndef __AVR_ATmega1280__
#ifndef __AVR_ATmega2560__
#error Wrong CPU type.
#endif
#endif
//PROCESSORS_END
when a file is saved, all of the lines between these two delimiters are removed
and a new preamble is generated based on the values from the screen. The
supported processor types are defined in a python list in the data.py file. As
additional processors are supported, then can be added to this list.
If the START/END delimiters are not present, then the processor preamble will
not be generated and the content from the original file will be retained.
Temperature Sensor Considerations - Board file
----------------------------------------------
The board configuration file has a section that includes the definition of
temperature sensors. Because the number of temperature sensors may grow or
shrink, or might even be 0, the utility needs to know where to place these
definitions. For this reason, delimiters are used to mark the beginning
and end of this section:
//DEFINE_TEMP_SENSORS_START
DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, AIO13, THERMISTOR_EXTRUDER)
DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, AIO14, THERMISTOR_BED)
//DEFINE_TEMP_SENSORS_END
If the START/END delimiters are not found in the file, then the temperature
sensor definitions will not be generated and the original contents of the
file will be retained.
In addition to this, it is possible to limit the number of pins that are
available to the user to use for thermistors. To do this, enter as many
lines of the following form as are needed. Note that these lines are
commented out. If no such lines are defined, then ALL pin names will be
allowed.
//#define THERMISTOR_PIN AIO13
//#define THERMISTOR_PIN AIO14
//#define THERMISTOR_PIN AIO15
Heater Considerations - Board file
----------------------------------
The board configuration file has a section that includes the definition of
heaters. Because the number of heaters may grow or shrink, or might even be 0,
the utility needs to know where to place these definitions. For this reason,
delimiters are used to mark the beginning and end of this section:
//DEFINE_HEATERS_START
DEFINE_HEATER(extruder, DIO10, 1)
DEFINE_HEATER(bed, DIO8, 1)
#define HEATER_EXTRUDER HEATER_extruder
#define HEATER_BED HEATER_bed
//DEFINE_HEATERS_END
Note that these delimiters also enclose the defines for the heater names. This
is different than in the sensor case where these definitions are not necessary.
If the START/END delimiters are not found in the file, then the temperature
sensor definitions will not be generated and the original contents of the
file will be retained.
In addition to this, it is possible to limit the number of pins that are
available to the user to use for heaters. To do this, enter as many lines of
the following form as are needed. Note that these lines are commented out.
If no such lines are defined, then ALL pin names will be allowed.
//#define HEATER_PIN DIO10
//#define HEATER_PIN DIO8
//#define HEATER_PIN DIO5

339
config/printer.mendel.h Normal file
View File

@ -0,0 +1,339 @@
/***************************************************************************\
* *
* 6. MECHANICAL/HARDWARE *
* *
\***************************************************************************/
/** \def KINEMATICS
This defines the type of kinematics your printer uses. That's essential!
Valid values (see dda_kinematics.h):
KINEMATICS_STRAIGHT Motors move axis directions directly. This is the
traditional type, found in many printers, including
Mendel, Prusa i3, Mendel90, Ormerod, Mantis.
KINEMATICS_COREXY A bot using CoreXY kinematics. Typical for CoreXY are
long and crossing toothed belts and a print head moving
on the X-Y-plane.
*/
#define KINEMATICS KINEMATICS_STRAIGHT
/** \def STEPS_PER_M_X STEPS_PER_M_Y STEPS_PER_M_Z STEPS_PER_M_E
Steps per meter ( = steps per mm * 1000 ), calculate these values
appropriate for your machine.
All numbers are integers, so no decimal point, please :-)
Valid range: 20 to 4'0960'000 (0.02 to 40960 steps/mm)
*/
#define STEPS_PER_M_X 40000
#define STEPS_PER_M_Y 40000
#define STEPS_PER_M_Z 320000
#define STEPS_PER_M_E 96271
/** \def MAXIMUM_FEEDRATE_X MAXIMUM_FEEDRATE_Y MAXIMUM_FEEDRATE_Z MAXIMUM_FEEDRATE_E
Used for G0 rapid moves and as a cap for all other feedrates.
*/
#define MAXIMUM_FEEDRATE_X 6000
#define MAXIMUM_FEEDRATE_Y 6000
#define MAXIMUM_FEEDRATE_Z 200
#define MAXIMUM_FEEDRATE_E 6000
/** \def SEARCH_FEEDRATE_X SEARCH_FEEDRATE_Y SEARCH_FEEDRATE_Z
Used when doing precision endstop search and as default feedrate. No
SEARCH_FEEDRATE_E, as E can't be searched.
*/
#define SEARCH_FEEDRATE_X 200
#define SEARCH_FEEDRATE_Y 200
#define SEARCH_FEEDRATE_Z 50
/** \def ENDSTOP_CLEARANCE_X ENDSTOP_CLEARANCE_Y ENDSTOP_CLEARANCE_Z
When hitting an endstop, Teacup properly decelerates instead of doing an
aprupt stop to save your mechanics. Ineviteably, this means it overshoots
the endstop trigger point by some distance.
To deal with this, Teacup adapts homing movement speeds to what your
endstops can deal with. The higher the allowed acceleration ( = deceleration,
see #define ACCELERATION) and the more clearance the endstop comes with,
the faster Teacup will do homing movements.
Set here how many micrometers (mm * 1000) your endstop allows the carriage
to overshoot the trigger point. Typically 1000 or 2000 for mechanical
endstops, more for optical ones. You can set it to zero, in which case
SEARCH_FEEDRATE_{XYZ} is used, but expect very slow homing movements.
Units: micrometers
Sane values: 0 to 20000 (0 to 20 mm)
Valid range: 0 to 1000000
*/
#define ENDSTOP_CLEARANCE_X 1000
#define ENDSTOP_CLEARANCE_Y 1000
#define ENDSTOP_CLEARANCE_Z 100
/** \def X_MIN X_MAX Y_MIN Y_MAX Z_MIN Z_MAX
Soft axis limits, in mm. Define them to your machine's size relative to what
your host considers to be the origin.
*/
//#define X_MIN 0.0
//#define X_MAX 200.0
//#define Y_MIN 0.0
//#define Y_MAX 200.0
//#define Z_MIN 0.0
//#define Z_MAX 140.0
/** \def E_ABSOLUTE
Some G-code creators produce relative length commands for the extruder,
others absolute ones. G-code using absolute lengths can be recognized when
there are G92 E0 commands from time to time. If you have G92 E0 in your
G-code, define this flag.
This is the startup default and can be changed with M82/M83 while running.
*/
#define E_ABSOLUTE
/** \def ACCELERATION_REPRAP ACCELERATION_RAMPING ACCELERATION_TEMPORAL
Choose optionally one of ACCELERATION_REPRAP, ACCELERATION_RAMPING or
ACCELERATION_TEMPORAL. With none of them defined, movements are done
without acceleration. Recommended is ACCELERATION_RAMPING.
*/
//#define ACCELERATION_REPRAP
#define ACCELERATION_RAMPING
//#define ACCELERATION_TEMPORAL
/** \def ACCELERATION
How fast to accelerate when using ACCELERATION_RAMPING. Start with 10 for
milling (high precision) or 1000 for printing.
Units: mm/s^2
Useful range: 1 to 10'000
*/
#define ACCELERATION 1000 /* float */
/** \def LOOKAHEAD
Define this to enable look-ahead during *ramping* acceleration to smoothly
transition between moves instead of performing a dead stop every move.
Enabling look-ahead requires about 3600 bytes of flash memory.
*/
#define LOOKAHEAD
/** \def MAX_JERK_X MAX_JERK_Y MAX_JERK_Z MAX_JERK_E
When performing look-ahead, we need to decide what an acceptable jerk to the
mechanics is. Look-ahead attempts to instantly change direction at movement
crossings, which means instant changes in the speed of the axes participating
in the movement. Define here how big the speed bumps on each of the axes is
allowed to be.
If you want a full stop before and after moving a specific axis, define
MAX_JERK of this axis to 0. This is often wanted for the Z axis. If you want
to ignore jerk on an axis, define it to twice the maximum feedrate of this
axis.
Having these values too low results in more than neccessary slowdown at
movement crossings, but is otherwise harmless. Too high values can result
in stepper motors suddenly stalling. If angles between movements in your
G-code are small and your printer runs through entire curves full speed,
there's no point in raising the values.
Units: mm/min
Sane values: 0 to 400
Valid range: 0 to 65535
*/
#define MAX_JERK_X 200
#define MAX_JERK_Y 200
#define MAX_JERK_Z 0
#define MAX_JERK_E 200
/***************************************************************************\
* *
* 7. MISCELLANEOUS OPTIONS *
* *
\***************************************************************************/
/** \def USE_INTERNAL_PULLUPS
The ATmega has internal pullup resistors on it's input pins which are
counterproductive with the commonly used eletronic endstops, so they should
be switched off. For other endstops, like mechanical ones, you may want to
uncomment this.
*/
//#define USE_INTERNAL_PULLUPS
/** \def TEMP_HYSTERESIS
Actual temperature must be target +/- this hysteresis before target
temperature is considered to be achieved. Also, BANG_BANG tries to stay
within half of this hysteresis.
Unit: degree Celsius
*/
#define TEMP_HYSTERESIS 10
/** \def TEMP_RESIDENCY_TIME
Actual temperature must be close to target (within set temperature
+- TEMP_HYSTERESIS) for this long before target is achieved (and a M116
succeeds).
Unit: seconds
*/
#define TEMP_RESIDENCY_TIME 60
/** \def TEMP_EWMA
Smooth noisy temperature sensors. Good hardware shouldn't be noisy. Set to
1.0 for unfiltered data (and a 140 bytes smaller binary).
Instrument Engineer's Handbook, 4th ed, Vol 2 p126 says values of
0.05 to 0.1 are typical. Smaller is smoother but slower adjusting, larger is
quicker but rougher. If you need to use this, set the PID parameter to zero
(M132 S0) to make the PID loop insensitive to noise.
Valid range: 0.001 to 1.0
*/
#define TEMP_EWMA 1.0 /* float */
/** \def HEATER_SANITY_CHECK
Check if heater responds to changes in target temperature, disable and spit
errors if not largely untested, please comment in forum if this works, or
doesn't work for you!
*/
//#define HEATER_SANITY_CHECK
/** \def XONXOFF
Xon/Xoff flow control.
Redundant when using RepRap Host for sending G-code, but mandatory when
sending G-code files with a plain terminal emulator, like GtkTerm (Linux),
CoolTerm (Mac) or HyperTerminal (Windows).
*/
//#define XONXOFF
/** \def EECONFIG
Enable EEPROM configuration storage.
Enabled by default. Commenting this out makes the binary several hundred
bytes smaller, so you might want to disable EEPROM storage on small MCUs,
like the ATmega168.
*/
#define EECONFIG
/** \def DEBUG
Enables /heaps/ of extra output, and some extra M-codes.
WARNING: this WILL break most host-side talkers that expect particular
responses from firmware such as reprap host and replicatorG use with serial
terminal or other suitable talker only.
*/
//#define DEBUG
/** \def BANG_BANG
Drops PID loop from heater control, reduces code size significantly
(1300 bytes!).
*/
//#define BANG_BANG
/** \def BANG_BANG_ON
PWM value for Bang Bang 'on'.
*/
//#define BANG_BANG_ON 200
/** \def BANG_BANG_OFF
PWM value for Bang Bang 'off'.
*/
//#define BANG_BANG_OFF 45
/** \def MOVEBUFFER_SIZE
Move buffer size, in number of moves.
Note that each move takes a fair chunk of ram (107 bytes as of this writing),
so don't make the buffer too big. However, a larger movebuffer will probably
help with lots of short consecutive moves, as each move takes a bunch of
math (hence time) to set up so a longer buffer allows more of the math to
be done during preceding longer moves.
*/
#define MOVEBUFFER_SIZE 8
/** \def DC_EXTRUDER DC_EXTRUDER_PWM
If you have a DC motor extruder, configure it as a "heater" above and define
this value as the index or name. You probably also want to comment out
E_STEP_PIN and E_DIR_PIN in the Pinouts section above.
*/
//#define DC_EXTRUDER HEATER_motor
//#define DC_EXTRUDER_PWM 180
/** \def USE_WATCHDOG
Teacup implements a watchdog, which has to be reset every 250ms or it will
reboot the controller. As rebooting (and letting the GCode sending
application trying to continue the build with a then different Home point)
is probably even worse than just hanging, and there is no better restore
code in place, this is disabled for now.
*/
//#define USE_WATCHDOG
/** \def REFERENCE
Which analog reference to use. See analog.h for choices.
*/
#define REFERENCE REFERENCE_AVCC
/** \def STEP_INTERRUPT_INTERRUPTIBLE
This option makes the step interrupt interruptible (nested). This should
help immensely with dropped serial characters, but may also make debugging
infuriating due to the complexities arising from nested interrupts.
Note: disable this option if you're using a '168 or for some reason your
ram usage is above 90%. This option hugely increases likelihood of stack
smashing.
*/
#define STEP_INTERRUPT_INTERRUPTIBLE 1
/** \def TH_COUNT
Temperature history count. This is how many temperature readings to keep in
order to calculate derivative in PID loop higher values make PID derivative
term more stable at the expense of reaction time.
*/
#define TH_COUNT 8
/** \def FAST_PWM
Teacup offers two PWM frequencies, 76(61) Hz and 78000(62500) Hz on a
20(16) MHz electronics. The slower one is the default, as it's the safer
choice and reduces MOSFET heating. Drawback is, in a quiet environment you
might notice the heaters and your power supply humming.
Uncomment this option if you want to get rid of this humming and can afford
a hotter MOSFET or want faster PWM for other reasons.
See also: http://reprap.org/wiki/Gen7_Research#MOSFET_heat_and_PWM
*/
//#define FAST_PWM
/** \def PID_SCALE
This is the scaling of internally stored PID values. 1024L is a good value.
*/
#define PID_SCALE 1024L
/** \def ENDSTOP_STEPS
Number of steps to run into the endstops intentionally. As endstops trigger
false alarm sometimes, Teacup debounces them by counting a number of
consecutive positives.
Valid range: 1...255. Use 4 or less for reliable endstops, 8 or even more
for flaky ones.
*/
#define ENDSTOP_STEPS 4
/** \def CANNED_CYCLE
G-code commands in this string will be executed over and over again, without
user interaction or even a serial connection. It's purpose is e.g. for
exhibitions or when using Teacup for other purposes than printing. You can
add any G-code supported by Teacup.
Note: don't miss these newlines (\n) and backslashes (\).
*/
/*
#define CANNED_CYCLE "G1 X100 F3000\n" \
"G4 P500\n" \
"G1 X0\n" \
"G4 P500\n"
*/

339
config/printer.wolfstrap.h Normal file
View File

@ -0,0 +1,339 @@
/***************************************************************************\
* *
* 6. MECHANICAL/HARDWARE *
* *
\***************************************************************************/
/** \def KINEMATICS
This defines the type of kinematics your printer uses. That's essential!
Valid values (see dda_kinematics.h):
KINEMATICS_STRAIGHT Motors move axis directions directly. This is the
traditional type, found in many printers, including
Mendel, Prusa i3, Mendel90, Ormerod, Mantis.
KINEMATICS_COREXY A bot using CoreXY kinematics. Typical for CoreXY are
long and crossing toothed belts and a print head moving
on the X-Y-plane.
*/
#define KINEMATICS KINEMATICS_STRAIGHT
/** \def STEPS_PER_M_X STEPS_PER_M_Y STEPS_PER_M_Z STEPS_PER_M_E
Steps per meter ( = steps per mm * 1000 ), calculate these values
appropriate for your machine.
All numbers are integers, so no decimal point, please :-)
Valid range: 20 to 4'0960'000 (0.02 to 40960 steps/mm)
*/
#define STEPS_PER_M_X 1280000
#define STEPS_PER_M_Y 1280000
#define STEPS_PER_M_Z 1280000
#define STEPS_PER_M_E 96271
/** \def MAXIMUM_FEEDRATE_X MAXIMUM_FEEDRATE_Y MAXIMUM_FEEDRATE_Z MAXIMUM_FEEDRATE_E
Used for G0 rapid moves and as a cap for all other feedrates.
*/
#define MAXIMUM_FEEDRATE_X 600
#define MAXIMUM_FEEDRATE_Y 600
#define MAXIMUM_FEEDRATE_Z 600
#define MAXIMUM_FEEDRATE_E 2000
/** \def SEARCH_FEEDRATE_X SEARCH_FEEDRATE_Y SEARCH_FEEDRATE_Z
Used when doing precision endstop search and as default feedrate. No
SEARCH_FEEDRATE_E, as E can't be searched.
*/
#define SEARCH_FEEDRATE_X 50
#define SEARCH_FEEDRATE_Y 50
#define SEARCH_FEEDRATE_Z 50
/** \def ENDSTOP_CLEARANCE_X ENDSTOP_CLEARANCE_Y ENDSTOP_CLEARANCE_Z
When hitting an endstop, Teacup properly decelerates instead of doing an
aprupt stop to save your mechanics. Ineviteably, this means it overshoots
the endstop trigger point by some distance.
To deal with this, Teacup adapts homing movement speeds to what your
endstops can deal with. The higher the allowed acceleration ( = deceleration,
see #define ACCELERATION) and the more clearance the endstop comes with,
the faster Teacup will do homing movements.
Set here how many micrometers (mm * 1000) your endstop allows the carriage
to overshoot the trigger point. Typically 1000 or 2000 for mechanical
endstops, more for optical ones. You can set it to zero, in which case
SEARCH_FEEDRATE_{XYZ} is used, but expect very slow homing movements.
Units: micrometers
Sane values: 0 to 20000 (0 to 20 mm)
Valid range: 0 to 1000000
*/
#define ENDSTOP_CLEARANCE_X 1000
#define ENDSTOP_CLEARANCE_Y 1000
#define ENDSTOP_CLEARANCE_Z 100
/** \def X_MIN X_MAX Y_MIN Y_MAX Z_MIN Z_MAX
Soft axis limits, in mm. Define them to your machine's size relative to what
your host considers to be the origin.
*/
//#define X_MIN 0.0
//#define X_MAX 200.0
//#define Y_MIN 0.0
//#define Y_MAX 200.0
//#define Z_MIN 0.0
//#define Z_MAX 140.0
/** \def E_ABSOLUTE
Some G-code creators produce relative length commands for the extruder,
others absolute ones. G-code using absolute lengths can be recognized when
there are G92 E0 commands from time to time. If you have G92 E0 in your
G-code, define this flag.
This is the startup default and can be changed with M82/M83 while running.
*/
#define E_ABSOLUTE
/** \def ACCELERATION_REPRAP ACCELERATION_RAMPING ACCELERATION_TEMPORAL
Choose optionally one of ACCELERATION_REPRAP, ACCELERATION_RAMPING or
ACCELERATION_TEMPORAL. With none of them defined, movements are done
without acceleration. Recommended is ACCELERATION_RAMPING.
*/
//#define ACCELERATION_REPRAP
#define ACCELERATION_RAMPING
//#define ACCELERATION_TEMPORAL
/** \def ACCELERATION
How fast to accelerate when using ACCELERATION_RAMPING. Start with 10 for
milling (high precision) or 1000 for printing.
Units: mm/s^2
Useful range: 1 to 10'000
*/
#define ACCELERATION 100 /* float */
/** \def LOOKAHEAD
Define this to enable look-ahead during *ramping* acceleration to smoothly
transition between moves instead of performing a dead stop every move.
Enabling look-ahead requires about 3600 bytes of flash memory.
*/
#define LOOKAHEAD
/** \def MAX_JERK_X MAX_JERK_Y MAX_JERK_Z MAX_JERK_E
When performing look-ahead, we need to decide what an acceptable jerk to the
mechanics is. Look-ahead attempts to instantly change direction at movement
crossings, which means instant changes in the speed of the axes participating
in the movement. Define here how big the speed bumps on each of the axes is
allowed to be.
If you want a full stop before and after moving a specific axis, define
MAX_JERK of this axis to 0. This is often wanted for the Z axis. If you want
to ignore jerk on an axis, define it to twice the maximum feedrate of this
axis.
Having these values too low results in more than neccessary slowdown at
movement crossings, but is otherwise harmless. Too high values can result
in stepper motors suddenly stalling. If angles between movements in your
G-code are small and your printer runs through entire curves full speed,
there's no point in raising the values.
Units: mm/min
Sane values: 0 to 400
Valid range: 0 to 65535
*/
#define MAX_JERK_X 20
#define MAX_JERK_Y 20
#define MAX_JERK_Z 0
#define MAX_JERK_E 200
/***************************************************************************\
* *
* 7. MISCELLANEOUS OPTIONS *
* *
\***************************************************************************/
/** \def USE_INTERNAL_PULLUPS
The ATmega has internal pullup resistors on it's input pins which are
counterproductive with the commonly used eletronic endstops, so they should
be switched off. For other endstops, like mechanical ones, you may want to
uncomment this.
*/
//#define USE_INTERNAL_PULLUPS
/** \def TEMP_HYSTERESIS
Actual temperature must be target +/- this hysteresis before target
temperature is considered to be achieved. Also, BANG_BANG tries to stay
within half of this hysteresis.
Unit: degree Celsius
*/
#define TEMP_HYSTERESIS 10
/** \def TEMP_RESIDENCY_TIME
Actual temperature must be close to target (within set temperature
+- TEMP_HYSTERESIS) for this long before target is achieved (and a M116
succeeds).
Unit: seconds
*/
#define TEMP_RESIDENCY_TIME 60
/** \def TEMP_EWMA
Smooth noisy temperature sensors. Good hardware shouldn't be noisy. Set to
1.0 for unfiltered data (and a 140 bytes smaller binary).
Instrument Engineer's Handbook, 4th ed, Vol 2 p126 says values of
0.05 to 0.1 are typical. Smaller is smoother but slower adjusting, larger is
quicker but rougher. If you need to use this, set the PID parameter to zero
(M132 S0) to make the PID loop insensitive to noise.
Valid range: 0.001 to 1.0
*/
#define TEMP_EWMA 1.0 /* float */
/** \def HEATER_SANITY_CHECK
Check if heater responds to changes in target temperature, disable and spit
errors if not largely untested, please comment in forum if this works, or
doesn't work for you!
*/
//#define HEATER_SANITY_CHECK
/** \def XONXOFF
Xon/Xoff flow control.
Redundant when using RepRap Host for sending G-code, but mandatory when
sending G-code files with a plain terminal emulator, like GtkTerm (Linux),
CoolTerm (Mac) or HyperTerminal (Windows).
*/
//#define XONXOFF
/** \def EECONFIG
Enable EEPROM configuration storage.
Enabled by default. Commenting this out makes the binary several hundred
bytes smaller, so you might want to disable EEPROM storage on small MCUs,
like the ATmega168.
*/
#define EECONFIG
/** \def DEBUG
Enables /heaps/ of extra output, and some extra M-codes.
WARNING: this WILL break most host-side talkers that expect particular
responses from firmware such as reprap host and replicatorG use with serial
terminal or other suitable talker only.
*/
//#define DEBUG
/** \def BANG_BANG
Drops PID loop from heater control, reduces code size significantly
(1300 bytes!).
*/
//#define BANG_BANG
/** \def BANG_BANG_ON
PWM value for Bang Bang 'on'.
*/
//#define BANG_BANG_ON 200
/** \def BANG_BANG_OFF
PWM value for Bang Bang 'off'.
*/
//#define BANG_BANG_OFF 45
/** \def MOVEBUFFER_SIZE
Move buffer size, in number of moves.
Note that each move takes a fair chunk of ram (107 bytes as of this writing),
so don't make the buffer too big. However, a larger movebuffer will probably
help with lots of short consecutive moves, as each move takes a bunch of
math (hence time) to set up so a longer buffer allows more of the math to
be done during preceding longer moves.
*/
#define MOVEBUFFER_SIZE 8
/** \def DC_EXTRUDER DC_EXTRUDER_PWM
If you have a DC motor extruder, configure it as a "heater" above and define
this value as the index or name. You probably also want to comment out
E_STEP_PIN and E_DIR_PIN in the Pinouts section above.
*/
//#define DC_EXTRUDER HEATER_motor
//#define DC_EXTRUDER_PWM 180
/** \def USE_WATCHDOG
Teacup implements a watchdog, which has to be reset every 250ms or it will
reboot the controller. As rebooting (and letting the GCode sending
application trying to continue the build with a then different Home point)
is probably even worse than just hanging, and there is no better restore
code in place, this is disabled for now.
*/
//#define USE_WATCHDOG
/** \def REFERENCE
Which analog reference to use. See analog.h for choices.
*/
#define REFERENCE REFERENCE_AVCC
/** \def STEP_INTERRUPT_INTERRUPTIBLE
This option makes the step interrupt interruptible (nested). This should
help immensely with dropped serial characters, but may also make debugging
infuriating due to the complexities arising from nested interrupts.
Note: disable this option if you're using a '168 or for some reason your
ram usage is above 90%. This option hugely increases likelihood of stack
smashing.
*/
#define STEP_INTERRUPT_INTERRUPTIBLE 1
/** \def TH_COUNT
Temperature history count. This is how many temperature readings to keep in
order to calculate derivative in PID loop higher values make PID derivative
term more stable at the expense of reaction time.
*/
#define TH_COUNT 8
/** \def FAST_PWM
Teacup offers two PWM frequencies, 76(61) Hz and 78000(62500) Hz on a
20(16) MHz electronics. The slower one is the default, as it's the safer
choice and reduces MOSFET heating. Drawback is, in a quiet environment you
might notice the heaters and your power supply humming.
Uncomment this option if you want to get rid of this humming and can afford
a hotter MOSFET or want faster PWM for other reasons.
See also: http://reprap.org/wiki/Gen7_Research#MOSFET_heat_and_PWM
*/
//#define FAST_PWM
/** \def PID_SCALE
This is the scaling of internally stored PID values. 1024L is a good value.
*/
#define PID_SCALE 1024L
/** \def ENDSTOP_STEPS
Number of steps to run into the endstops intentionally. As endstops trigger
false alarm sometimes, Teacup debounces them by counting a number of
consecutive positives.
Valid range: 1...255. Use 4 or less for reliable endstops, 8 or even more
for flaky ones.
*/
#define ENDSTOP_STEPS 4
/** \def CANNED_CYCLE
G-code commands in this string will be executed over and over again, without
user interaction or even a serial connection. It's purpose is e.g. for
exhibitions or when using Teacup for other purposes than printing. You can
add any G-code supported by Teacup.
Note: don't miss these newlines (\n) and backslashes (\).
*/
/*
#define CANNED_CYCLE "G1 X100 F3000\n" \
"G4 P500\n" \
"G1 X0\n" \
"G4 P500\n"
*/

View File

@ -1,229 +0,0 @@
helpText = {
'STEPS_PER_M': "steps per meter ( = steps per mm * 1000 ) \
calculate these values appropriate for your machine.\n\
for threaded rods, this is:\n\n\
\t(steps motor per turn) / (pitch of the thread) * 1000\n\n\
for belts, this is\n\n\
\t(steps per motor turn) / (number of gear teeth) / (belt module) * 1000\n\n\
half-stepping doubles the number, quarter stepping requires * 4, etc.\n\
valid range = 20 to 4,0960,000 (0.02 to 40960 steps/mm). \
all numbers are integers, so no decimal point",
'MAXIMUM_FEEDRATE': "maximum feed rate - in mm/min - for G0 rapid moves and \
as a cap for all other feedrates",
'SEARCH_FEEDRATE': "search feed rate - in mm/min - used when doing precision \
endstop search and as a default feed rate",
'ENDSTOP_CLEARANCE': "When hitting an endstop, Teacup properly decelerates \
instead of doing an abrupt stop\nto save your mechanics. Inevitably, this \
means it overshoots the endstop trigger point by some distance.\n\n\
To deal with this, Teacup adapts homing movement speeds to what your endstops \
can deal with.\nThe higher the allowed acceleration and the more clearance \
the endstop comes with, the faster Teacup\nwill do homing movements.\n\n\
Set here how many micrometers (mm * 1000) your endstop allows the carriage to \
overshoot the\ntrigger point. Typically 1000 or 2000 for mechanical endstops, \
more for optical ones.\nYou can set it to zero, in which case \
SEARCH_FEEDRATE_{XYZ} is used, but expect very slow\nhoming movements.\n\n\
Units: micrometers\n\
Sane values: 0 to 20000 (0 to 20 mm)\n\
Valid range: 0 to 1000000",
'MINMAX': "soft axis limits, in mm.\n\ndefine them to your machine's size \
relative to what your host considers to be the origin.",
'E_ABSOLUTE': "some G-Code creators produce relative length commands for the \
extruder,\nothers absolute ones. G-Code using absolute lengths can be \
recognized when there\nare G92 E0 commands from time to time. if you have \
G92 E0 in your G-code, check this box.",
'ACCELERATION_REPRAP': "acceleration, reprap style.\n\n\
Each movement starts at the speed of the previous command and accelerates or \
decelerates\nlinearly to reach target speed at the end of the movement.",
'ACCELERATION_RAMPING': "acceleration and deceleration ramping.\n\n\
Each movement starts at (almost) no speed, linearly accelerates to target \
speed and decelerates\njust in time to smoothly stop at the target.",
'ACCELERATION_TEMPORAL': "This algorithm causes the timer to fire when any \
axis needs to step, instead of\n\synchronising to the axis with the most \
steps ala bresenham",
'ACCELERATION' : "how fast to accelerate when using acceleration ramping.\n\n\
given in mm/s^2, decimal allowed, useful range 1. to 10,000.\n\
Start with 10. for milling (high precision) or 1000. for printing",
'LOOKAHEAD': "Define this to enable look-ahead during *ramping* acceleration to \
smoothly transition\nbetween moves instead of performing a dead stop every \
move. Enabling look-ahead requires about\n3600 bytes of flash memory.",
'MAX_JERK': "When performing look-ahead, we need to decide what an acceptable \
jerk to the\nmechanics is. Look-ahead attempts to instantly change direction \
at movement\ncrossings, which means instant changes in the speed of the axes \
participating\nin the movement. Define here how big the speed bumps on each \
of the axes is\nallowed to be.\n\n\
If you want a full stop before and after moving a specific axis, define\n\
maximum jerk of this axis to 0. This is often wanted for the Z axis. If you \
want\nto ignore jerk on an axis, define it to twice the maximum feedrate of \
this axis.\n\nHaving these values too low results in more than necessary \
slowdown at\nmovement crossings, but is otherwise harmless. Too high values \
can result\nin stepper motors suddenly stalling. If angles between movements \
in your\nG-code are small and your printer runs through entire curves full \
speed,\nthere's no point in raising the values.\n\n\
Units: mm/min\n\
Sane values: 0 to 400\n\
Valid range: 0 to 65535",
'INCLUDE_ARDUINO': "Include arduino.h header file in the C++ source code. \
This allows you\nto define pins using the atmel/avr conventions (e.g. DI012 \
or AI01)",
'USE_INTERNAL_PULLUPS': "Use Internal Pullups. the ATmega has internal pullup \
resistors on it's input pins\nwhich are counterproductive with the commonly \
used electronic endstops, so this should be unchecked.\n\For other endstops, \
like mechanical ones, you may want to check this.",
'TX_ENABLE_PIN': "Tx Enable Pin. Not yet used",
'RX_ENABLE_PIN': "Rx Enable Pin. Not yet used",
'X_STEP_PIN': "the pin for the stepper motor step signal",
'X_DIR_PIN': "the pin for the stepper motor direction signal",
'X_MIN_PIN': "the pin for the endstop at the axis minimum position",
'X_MAX_PIN': "the pin for the endstop at the axis maximum position",
'X_ENABLE_PIN': "the pin for the stepper motor enable signal",
'X_INVERT_DIR': "true or false - invert the stepper motor direction",
'X_INVERT_MIN': "true or false - invert the signal received from the minimum endstop",
'X_INVERT_MAX': "true or false - invert the signal received from the maximum endstop",
'X_INVERT_ENABLE': "true or false - invert the stepper motor enable signal",
'Y_STEP_PIN': "the pin for the stepper motor step signal",
'Y_DIR_PIN': "the pin for the stepper motor direction signal",
'Y_MIN_PIN': "the pin for the endstop at the axis minimum position",
'Y_MAX_PIN': "the pin for the endstop at the axis maximum position",
'Y_ENABLE_PIN': "the pin for the stepper motor enable signal",
'Y_INVERT_DIR': "true or false - invert the stepper motor direction",
'Y_INVERT_MIN': "true or false - invert the signal received from the minimum endstop",
'Y_INVERT_MAX': "true or false - invert the signal received from the maximum endstop",
'Y_INVERT_ENABLE': "true or false - invert the stepper motor enable signal",
'Z_STEP_PIN': "the pin for the stepper motor step signal",
'Z_DIR_PIN': "the pin for the stepper motor direction signal",
'Z_MIN_PIN': "the pin for the endstop at the axis minimum position",
'Z_MAX_PIN': "the pin for the endstop at the axis maximum position",
'Z_ENABLE_PIN': "the pin for the stepper motor enable signal",
'Z_INVERT_DIR': "true or false - invert the stepper motor direction",
'Z_INVERT_MIN': "true or false - invert the signal received from the minimum endstop",
'Z_INVERT_MAX': "true or false - invert the signal received from the maximum endstop",
'Z_INVERT_ENABLE': "true or false - invert the stepper motor enable signal",
'E_STEP_PIN': "the pin for the stepper motor step signal",
'E_DIR_PIN': "the pin for the stepper motor direction signal",
'E_ENABLE_PIN': "the pin for the stepper motor enable signal",
'E_INVERT_DIR': "true or false - invert the stepper motor direction",
'E_INVERT_ENABLE': "true or false - invert the stepper motor enable signal",
'PS_ON_PIN': "Which pin is used to turn the power supply off",
'PS_MOSFET_PIN': "???",
'STEPPER_ENABLE_PIN': "???",
'STEPPER_INVERT_ENABLE': "???",
'SD_CARD_DETECT': "the pin used to detect if an SD card has been inserted.",
'SD_WRITE_PROTECT': "the pin used to determine if an SD card is write-protected",
'DEBUG_LED_PIN': "Enable flashing of a LED during motor stepping.\n\n\
Disabled by default. Turning this on this makes the binary a few bytes larger\n\
and adds a few cycles to the step timing interrupt. This is also used for\n\
for precision profiling",
'TEMP_HYSTERESIS': "Actual temperature must be target +/- this hysteresis \
before target\ntemperature is considered to be achieved. Also, BANG_BANG \
tries to stay\nwithin half of this hysteresis. Unit is degrees Celcius",
'TEMP_RESIDENCY_TIME': "actual temperature must be close to target (within\n\
set temperature +- TEMP_HYSTERESIS) for this long before target is achieved\n\
(and a M116 succeeds). Unit is seconds.",
'TEMP_EWMA': "Smooth noisy temperature sensors. Good hardware shouldn't be\n\
noisy. Set to 1.0 for unfiltered data (and a 140 bytes smaller binary)\n\n\
Valid range: 0.001 - 1.0",
'TEMP_TYPES': "which temperature sensors are you using? Check every type of \
sensor you use\nto enable the appropriate code. Intercom is the gen3-style \
separate extruder board. Note\nthat you will not be able to uncheck a sensor \
type that is in use",
'ADDSENSOR': 'Define your temperature sensors. One entry for each sensor, \
only limited\nby the number of available ATmega pins.\n\n\
Types are only those chosen on the main page.\n\n\
The "additional" field is used for TT_THERMISTOR only. It defines the\n\
name of the table(s) in ThermistorTable.h to use. Typically, this is\n\
THERMISTOR_EXTRUDER for the first or only table, or THERMISTOR_BED for\n\
the second table. See also early in ThermistorTable.{single|double}.h,\n\n\
For a GEN3 set type to TT_INTERCOM and pin to AIO0. The pin won\'t be used in \
this case.',
'DELSENSOR': 'Remove the selected temperature sensor from the configuration',
"HEATER_SANITY_CHECK": "check if heater responds to changes in target \
temperature, disable\nand spit errors if not. largely untested, please \
comment in forum if this works, or doesn't work for you!",
'ADDHEATER': "Add a heater to the configuration",
'DELHEATER': "Remove a heater from the configuration",
'BAUD': "Baud rate for the serial RS232 protocol connection to the host. \
Usually 115200,\nother common values are 19200, 38400 or 57600. Ignored when \
USB_SERIAL is checked.",
'USB_SERIAL': "Define this for using USB instead of the serial RS232 protocol. \
Works on\nUSB-equipped ATmegas, like the ATmega32U4, only",
'XONXOFF': "Xon/Xoff flow control. Redundant when using RepRap Host for \
sending GCode,\nbut mandatory when sending GCode files with a plain terminal \
emulator, like GtkTerm (Linux),\nCoolTerm (Mac) or HyperTerminal (Windows). \
Can also be set in Makefile",
'MOTHERBOARD': "This is the motherboard, as opposed to the extruder.",
'F_CPU': "The CPU clock rate",
'EECONFIG': "Enable EEPROM configuration storage.\n\n\
Enabled by default. Commenting this out makes the binary several hundred\n\
bytes smaller, so you might want to disable EEPROM storage on small MCUs,\n\
like the ATmega168.",
'DEBUG': "enables extra output, and some extra M-codes.\n\n\
WARNING: this WILL break most host-side talkers that expect particular \
responses\nfrom firmware such as reprap host and replicatorG. Use with serial \
terminal\nor other suitable talker only.",
'BANG_BANG': "Drops PID loop from heater control, reduces code size\n\
significantly (1300 bytes!)",
'BANG_BANG_ON': "PWM value for 'on'",
'BANG_BANG_OFF': "PWM value for 'off'",
'MOVEBUFFER_SIZE': "Move buffer size, in number of moves\n\nnote that each \
move takes a fair chunk of ram (~69 bytes) so don't make the buffer too big.\n\
A bigger serial readbuffer may help more than increasing this unless your \
gcodes are more than\n70 characters long on average. However, a larger \
movebuffer will probably help with lots of \nshort consecutive moves, as each \
move takes a bunch of math (hence time) to set up, so a longer\nbuffer allows \
more of the math to be done during preceding longer moves",
'DC_EXTRUDER': 'If you have a DC motor extruder, configure it as a "heater" \
on the heater page,\nand choose the name you used there in this field. You \
probably also want to comment out E_STEP_PIN\nand E_DIR_PIN on the Pinouts \
page.',
'DC_EXTRUDER_PWM': "The PWM value at which to operate the DC Extruder motor",
'USE_WATCHDOG': "Teacup implements a watchdog, which has to be reset every \
250ms or it will\nreboot the controller. As rebooting (and letting the GCode\
sending application trying to\ncontinue the build with a then different Home \
point) is probably even worse than just hanging,\nand there is no better \
restore code in place, this is disabled for now.",
'REFERENCE': "which analog reference to use. see analog.h for choices",
'STEP_INTERRUPT_INTERRUPTIBLE': "this option makes the step interrupt \
interruptible (nested).\nthis should help immensely with dropped serial \
characters, but may also make\ndebugging infuriating due to the complexities \
arising from nested interrupts.\n\nnote disable this option if you're using a \
'168 or for some reason your ram usage\nis above 90%. This option hugely \
increases likelihood of stack smashing.",
'TH_COUNT': "Temperature history count. This is how many temperature readings \
to keep in\norder to calculate derivative in PID loop. Higher values make PID \
derivative term more\nstable at the expense of reaction time",
'FAST_PWM': "Teacup offers two PWM frequencies, 76(61) Hz and 78000(62500) \
Hz on a\n20(16) MHz electronics. The slower one is the default, as it's the \
safer choice.\nThe drawback is in a quiet environment you might notice the \
heaters and power supply humming.\n\nUncomment this option if you want to get \
rid of this humming or want faster PWM for other reasons.",
'ENDSTOP_STEPS': "Number of steps to run into the endstops intentionally\n\n\
As Endstops trigger false alarm sometimes, Teacup debounces them by counting \
a number of\nconsecutive positives. Valid range is 1...255. Use 4 or less for \
reliable endstops, 8 or\neven more for flaky ones.",
'CANNED_CYCLE': "G-code commands in this string will be executed over and \
over again, without\nuser interaction or even a serial connection. It's \
purpose is e.g. for\nexhibitions or when using Teacup for other purposes than \
printing. You can\nadd any G-code supported by Teacup.",
}

0
configtool/__init__.py Normal file
View File

View File

@ -0,0 +1,124 @@
import wx
from configtool.page import Page
class AccelerationPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
self.accTypeKeys = ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING',
'ACCELERATION_TEMPORAL']
self.jerkKeys = ['MAX_JERK_X', 'MAX_JERK_Y', 'MAX_JERK_Z', 'MAX_JERK_E']
self.labels = {'ACCELERATION_REPRAP': "RepRap",
'ACCELERATION_RAMPING': "Ramping",
'ACCELERATION_TEMPORAL': "Temporal",
'ACCELERATION': "Acceleration:",
'LOOKAHEAD': "Look Ahead",
'MAX_JERK_X': "X:", 'MAX_JERK_Y': "Y:", 'MAX_JERK_Z': "Z:",
'MAX_JERK_E': "E:"}
sz = wx.GridBagSizer()
sz.AddSpacer((20, 40), pos = (0, 0))
b = wx.StaticBox(self, wx.ID_ANY, "Acceleration Type")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
style = wx.RB_GROUP
for k in self.accTypeKeys:
rb = self.addRadioButton(k, style, self.onAccTypeSelect)
style = 0
sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16)
sbox.AddSpacer((5, 5))
self.rbNone = wx.RadioButton(self, wx.ID_ANY, "None", style = style)
self.rbNone.SetValue(True)
self.Bind(wx.EVT_RADIOBUTTON, self.onAccTypeSelect, self.rbNone)
sbox.Add(self.rbNone, 1, wx.LEFT + wx.RIGHT, 16)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 1))
b = wx.StaticBox(self, wx.ID_ANY, "Ramping Parameters")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
k = 'ACCELERATION'
tc = self.addTextCtrl(k, 80, self.onTextCtrlFloat)
self.textControls[k].Enable(False)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
k = 'LOOKAHEAD'
cb = self.addCheckBox(k, self.onCheckBox)
self.checkBoxes[k].Enable(False)
sbox.Add(cb, 1, wx.ALIGN_CENTER_HORIZONTAL)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "Maximum Jerk")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.jerkKeys:
tc = self.addTextCtrl(k, 40, self.onTextCtrlInteger)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.AddSpacer((80, 20), pos = (1, 4))
sz.Add(sbox, pos = (1, 5))
self.SetSizer(sz)
self.enableAll(False)
def enableAll(self, flag = True):
self.rbNone.Enable(flag)
Page.enableAll(self, flag)
def onAccTypeSelect(self, evt):
self.assertModified(True)
rb = evt.GetEventObject()
label = rb.GetLabel()
if label == self.labels['ACCELERATION_RAMPING']:
ena = True
else:
ena = False
self.checkBoxes['LOOKAHEAD'].Enable(ena)
self.textControls['ACCELERATION'].Enable(ena)
evt.Skip()
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
self.checkBoxes['LOOKAHEAD'].Enable(False)
self.textControls['ACCELERATION'].Enable(False)
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControls[k].SetValue(cfgValues[k])
else:
self.textControls[k].SetValue("")
for tag in ['ACCELERATION_REPRAP', 'ACCELERATION_RAMPING',
'ACCELERATION_TEMPORAL']:
if tag in cfgValues.keys() and cfgValues[tag]:
self.radioButtons[tag].SetValue(True)
if tag == 'ACCELERATION_RAMPING':
self.checkBoxes['LOOKAHEAD'].Enable(True)
self.textControls['ACCELERATION'].Enable(True)
k = 'LOOKAHEAD'
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
self.assertModified(False)

115
configtool/addheaterdlg.py Normal file
View File

@ -0,0 +1,115 @@
import wx
from configtool.data import pinNames, BSIZESMALL
class AddHeaterDlg(wx.Dialog):
def __init__(self, parent, names, pins):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add heater", size = (400, 204))
self.Bind(wx.EVT_CLOSE, self.onCancel)
self.names = names
self.nameValid = False
sz = wx.BoxSizer(wx.VERTICAL)
gsz = wx.GridBagSizer()
gsz.AddSpacer((20, 20), pos = (0, 0))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Heater Name:", size = (80, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
self.tcName = wx.TextCtrl(self, wx.ID_ANY, "")
self.tcName.SetBackgroundColour("pink")
self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry)
lsz.Add(self.tcName)
self.tcName.SetToolTipString("Enter a unique name for this heater.")
gsz.Add(lsz, pos = (1, 1))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
self.chPin = wx.Choice(self, wx.ID_ANY, choices = pins)
self.chPin.Bind(wx.EVT_CHOICE, self.onChoice)
self.chPin.SetSelection(0)
lsz.Add(self.chPin)
self.chPin.SetToolTipString("Choose a pin for this heater.")
gsz.Add(lsz, pos = (3, 1))
self.cbPwm = wx.CheckBox(self, wx.ID_ANY, "PWM")
self.cbPwm.SetToolTipString("Use Pulse Width Modulation in case the "
"choosen pin allows to do so.")
gsz.AddSpacer((50, 15), pos = (1, 2))
gsz.Add(self.cbPwm, pos = (1, 3))
gsz.AddSpacer((20, 20), pos = (4, 4))
sz.Add(gsz)
sz.AddSpacer((30, 30))
bsz = wx.BoxSizer(wx.HORIZONTAL)
self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL)
self.bSave.Bind(wx.EVT_BUTTON, self.onSave)
bsz.Add(self.bSave)
self.bSave.Enable(False)
bsz.AddSpacer(30, 100)
self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL)
self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel)
bsz.Add(self.bCancel)
sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL)
self.SetSizer(sz)
def onNameEntry(self, evt):
tc = evt.GetEventObject()
w = tc.GetValue().strip()
if w == "":
self.nameValid = False
else:
if w in self.names:
self.nameValid = False
else:
self.nameValid = True
if self.nameValid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
self.checkDlgValidity()
evt.Skip()
def onChoice(self, evt):
pass
def checkDlgValidity(self):
if self.nameValid:
self.bSave.Enable(True)
else:
self.bSave.Enable(False)
def getValues(self):
nm = self.tcName.GetValue()
pin = pinNames[self.chPin.GetSelection()]
if self.cbPwm.IsChecked():
pwm = "1"
else:
pwm = "0"
return (nm, pin, pwm)
def onSave(self, evt):
self.EndModal(wx.ID_OK)
def onCancel(self, evt):
self.EndModal(wx.ID_CANCEL)

174
configtool/addsensordlg.py Normal file
View File

@ -0,0 +1,174 @@
import wx
from configtool.data import pinNames, BSIZESMALL
class AddSensorDlg(wx.Dialog):
def __init__(self, parent, names, sl, pins):
wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add temperature sensor",
size = (400, 204))
self.Bind(wx.EVT_CLOSE, self.onCancel)
self.names = names
self.nameValid = False
sz = wx.BoxSizer(wx.VERTICAL)
csz = wx.GridBagSizer()
csz.AddSpacer((10, 10), pos = (0, 0))
b = wx.StaticBox(self, wx.ID_ANY, "Sensor Type")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
style = wx.RB_GROUP
self.rbs = {}
for k in sl:
rb = wx.RadioButton(self, wx.ID_ANY, k, style = style)
self.rbs[k] = rb
self.Bind(wx.EVT_RADIOBUTTON, self.onSensorType, rb)
style = 0
sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16)
sbox.AddSpacer((5, 5))
csz.Add(sbox, pos = (1, 3))
vsz = wx.BoxSizer(wx.VERTICAL)
vsz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Sensor Name:", size = (80, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
self.tcName = wx.TextCtrl(self, wx.ID_ANY, "")
self.tcName.SetBackgroundColour("pink")
self.tcName.Bind(wx.EVT_TEXT, self.onNameEntry)
lsz.Add(self.tcName)
self.tcName.SetToolTipString("Enter a unique name for this sensor.")
vsz.Add(lsz)
vsz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Pin:", size = (80, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
self.choiceList = pinNames
self.chPin = wx.Choice(self, wx.ID_ANY, choices = pins)
self.chPin.Bind(wx.EVT_CHOICE, self.onChoice)
self.chPin.SetSelection(0)
lsz.Add(self.chPin)
self.chPin.SetToolTipString("Choose a pin name for this sensor.")
vsz.Add(lsz)
vsz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Additional:", size = (80, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
self.tcAddtl = wx.TextCtrl(self, wx.ID_ANY, "")
self.tcAddtl.Bind(wx.EVT_TEXT, self.onAddtlEntry)
self.selectSensorType(sl[0])
lsz.Add(self.tcAddtl)
self.tcAddtl.SetToolTipString("Enter additional information required "
"by the sensor type.")
vsz.Add(lsz)
csz.Add(vsz, pos = (1, 1))
csz.AddSpacer((10, 10), pos = (1, 4))
sz.Add(csz)
sz.AddSpacer((30, 30))
bsz = wx.BoxSizer(wx.HORIZONTAL)
self.bSave = wx.Button(self, wx.ID_ANY, "Save", size = BSIZESMALL)
self.bSave.Bind(wx.EVT_BUTTON, self.onSave)
bsz.Add(self.bSave)
self.bSave.Enable(False)
bsz.AddSpacer(30, 100)
self.bCancel = wx.Button(self, wx.ID_ANY, "Cancel", size = BSIZESMALL)
self.bCancel.Bind(wx.EVT_BUTTON, self.onCancel)
bsz.Add(self.bCancel)
sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL)
self.SetSizer(sz)
def onNameEntry(self, evt):
tc = evt.GetEventObject()
w = tc.GetValue().strip()
if w == "":
self.nameValid = False
else:
if w in self.names:
self.nameValid = False
else:
self.nameValid = True
if self.nameValid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
self.checkDlgValidity()
evt.Skip()
def checkDlgValidity(self):
if self.nameValid:
self.bSave.Enable(True)
else:
self.bSave.Enable(False)
def onAddtlEntry(self, evt):
evt.Skip()
def selectSensorType(self, lbl):
if lbl == 'TT_THERMISTOR':
self.tcAddtl.Enable(True);
else:
self.tcAddtl.Enable(False);
def onChoice(self, evt):
pass
def onSensorType(self, evt):
rb = evt.GetEventObject()
label = rb.GetLabel()
self.selectSensorType(label)
evt.Skip()
def getValues(self):
nm = self.tcName.GetValue()
pin = pinNames[self.chPin.GetSelection()]
addtl = self.tcAddtl.GetValue()
for k in self.rbs:
if self.rbs[k].GetValue():
stype = k
break
if stype is None:
stype = "??"
if stype in ['TT_THERMISTOR']:
return (nm, stype, pin, addtl)
else:
return (nm, stype, pin)
def onSave(self, evt):
self.EndModal(wx.ID_OK)
def onCancel(self, evt):
self.EndModal(wx.ID_CANCEL)

515
configtool/boardpanel.py Normal file
View File

@ -0,0 +1,515 @@
import os
import wx
import re
from configtool.data import (supportedCPUs, defineValueFormat,
defineBoolFormat, defineHeaterFormat, reCommDefBL,
reCommDefBoolBL, reHelpTextStart, reHelpTextEnd,
reStartSensors, reEndSensors, reStartHeaters,
reEndHeaters, reStartProcessors, reEndProcessors,
reCandHeatPins, reCandThermPins, reAVR, reDefine,
reDefineBL, reDefQS, reDefQSm, reDefQSm2,
reDefBool, reDefBoolBL, reDefHT, reDefTS,
reHeater, reSensor3, reSensor4)
from configtool.pinoutspage import PinoutsPage
from configtool.sensorpage import SensorsPage
from configtool.heaterspage import HeatersPage
from configtool.communicationspage import CommunicationsPage
from configtool.cpupage import CpuPage
class BoardPanel(wx.Panel):
def __init__(self, parent, nb, folder):
wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent
self.cfgValues = {}
self.heaters = []
self.sensors = []
self.processors = []
self.candHeatPins = []
self.candThermPins = []
self.dir = os.path.join(folder, "config")
sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21),
style = wx.BK_DEFAULT)
self.pages = []
self.titles = []
self.pageModified = []
self.pageValid = []
self.pgCpu = CpuPage(self, self.nb, len(self.pages))
text = "CPU"
self.nb.AddPage(self.pgCpu, text)
self.pages.append(self.pgCpu)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
self.pgPins = PinoutsPage(self, self.nb, len(self.pages))
text = "Pinouts"
self.nb.AddPage(self.pgPins, text)
self.pages.append(self.pgPins)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
self.pgSensors = SensorsPage(self, self.nb, len(self.pages))
text = "Temperature Sensors"
self.nb.AddPage(self.pgSensors, text)
self.pages.append(self.pgSensors)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
self.pgHeaters = HeatersPage(self, self.nb, len(self.pages))
text = "Heaters"
self.nb.AddPage(self.pgHeaters, text)
self.pages.append(self.pgHeaters)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
self.pgCommunications = CommunicationsPage(self, self.nb, len(self.pages))
text = "Communications"
self.nb.AddPage(self.pgCommunications, text)
self.pages.append(self.pgCommunications)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5)
self.SetSizer(sz)
def assertModified(self, pg, flag = True):
self.pageModified[pg] = flag
self.modifyTab(pg)
def isModified(self):
return (True in self.pageModified)
def assertValid(self, pg, flag = True):
self.pageValid[pg] = flag
self.modifyTab(pg)
if False in self.pageValid:
self.parent.enableSaveBoard(False)
else:
self.parent.enableSaveBoard(True)
def modifyTab(self, pg):
if self.pageModified[pg] and not self.pageValid[pg]:
pfx = "?* "
elif self.pageModified[pg]:
pfx = "* "
elif not self.pageValid[pg]:
pfx = "? "
else:
pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg])
def setHeaters(self, ht):
self.parent.setHeaters(ht)
def onClose(self, evt):
if not self.confirmLoseChanges("exit"):
return
self.Destroy()
def confirmLoseChanges(self, msg):
if True not in self.pageModified:
return True
dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n"
"There are changes to your board "
"configuration that will be lost.",
"Changes pending",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return False
return True
def onLoadConfig(self, evt):
if not self.confirmLoseChanges("load a new board configuration"):
return
wildcard = "Board configuration (board.*.h)|board.*.h"
dlg = wx.FileDialog(self, message = "Choose a board config file",
defaultDir = self.dir, defaultFile = "",
wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
if path is None:
return
self.dir = os.path.dirname(path)
rc = self.loadConfigFile(path)
if not rc:
dlg = wx.MessageDialog(self, "Unable to process file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return
self.parent.enableSaveBoard(True)
self.parent.setBoardTabText("Board <%s>" % os.path.basename(path))
self.pgHeaters.setCandidatePins(self.candHeatPins)
self.pgSensors.setCandidatePins(self.candThermPins)
for pg in self.pages:
pg.insertValues(self.cfgValues)
pg.setHelpText(self.helpText)
self.pgSensors.setSensors(self.sensors)
self.pgHeaters.setHeaters(self.heaters)
self.pgCpu.setProcessors(self.processors)
def loadConfigFile(self, fn):
try:
self.cfgBuffer = list(open(fn))
except:
return False
self.configFile = fn
self.processors = []
self.sensors = []
self.heaters = []
self.candHeatPins = []
self.candThermPins = []
gatheringHelpText = False
helpTextString = ""
helpKey = None
self.cfgValues = {}
self.helpText = {}
prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
m = reHelpTextStart.match(ln)
if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = ""
if ln.lstrip().startswith("//"):
m = reCandThermPins.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candThermPins.append(t[0])
continue
m = reCandHeatPins.match(ln)
if m:
t = m.groups()
if len(t) == 1:
self.candHeatPins.append(t[0])
continue
continue
if ln.lstrip().startswith("#if"):
m = re.findall(reAVR, ln)
inv = []
for p in m:
if p in supportedCPUs:
self.processors.append(p)
else:
inv.append(p)
if len(inv) > 0:
if len(inv) == 1:
a = " is"
b = "it"
else:
a = "s are"
b = "them"
dlg = wx.MessageDialog(self,
("The following processor type%s not "
"supported:\n %s\nPlease add %s to "
"\"supportedCPUs\".") %
(a, ", ".join(inv), b),
"Unsupported processor type",
wx.OK + wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
continue
if ln.lstrip().startswith("#define"):
m = reDefQS.search(ln)
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1:
self.cfgValues[t[0]] = tt[0]
continue
elif len(tt) > 1:
self.cfgValues[t[0]] = tt
continue
m = reDefine.search(ln)
if m:
t = m.groups()
if len(t) == 2:
self.cfgValues[t[0]] = t[1]
continue
m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1:
self.cfgValues[t[0]] = True
else:
m = reDefTS.search(ln)
if m:
t = m.groups()
if len(t) == 1:
s = self.parseSensor(t[0])
if s:
self.sensors.append(s)
continue
m = reDefHT.search(ln)
if m:
t = m.groups()
if len(t) == 1:
s = self.parseHeater(t[0])
if s:
self.heaters.append(s)
continue
return True
def parseSensor(self, s):
m = reSensor4.search(s)
if m:
t = m.groups()
if len(t) == 4:
return t
m = reSensor3.search(s)
if m:
t = m.groups()
if len(t) == 3:
return t
return None
def parseHeater(self, s):
m = reHeater.search(s)
if m:
t = m.groups()
if len(t) == 3:
return t
return None
def onSaveConfig(self, evt):
path = self.configFile
if self.saveConfigFile(path):
dlg = wx.MessageDialog(self, "File %s successfully written." % path,
"Save successful", wx.OK + wx.ICON_INFORMATION)
else:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
def onSaveConfigAs(self, evt):
wildcard = "Board configuration (board.*.h)|board.*.h"
dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir,
defaultFile = "", wildcard = wildcard,
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
val = dlg.ShowModal()
if val != wx.ID_OK:
dlg.Destroy()
return
path = dlg.GetPath()
dlg.Destroy()
if self.saveConfigFile(path):
dlg = wx.MessageDialog(self, "File %s successfully written." % path,
"Save successful", wx.OK + wx.ICON_INFORMATION)
self.parent.setBoardTabText("Board <%s>" % os.path.basename(path))
else:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
def saveConfigFile(self, path):
ext = os.path.splitext(os.path.basename(path))[1]
self.dir = os.path.dirname(path)
if ext == "":
path += ".h"
try:
fp = file(path, 'w')
except:
return False
self.configFile = path
values = {}
for pg in self.pages:
v1 = pg.getValues()
for k in v1.keys():
values[k] = v1[k]
self.processors = self.pgCpu.getProcessors()
skipToSensorEnd = False
skipToHeaterEnd = False
skipToProcessorEnd = False
for ln in self.cfgBuffer:
m = reStartSensors.match(ln)
if m:
fp.write(ln)
for s in self.sensors:
sstr = ", ".join(s)
fp.write("DEFINE_TEMP_SENSOR(%s)\n" % sstr)
skipToSensorEnd = True
continue
if skipToSensorEnd:
m = reEndSensors.match(ln)
if m:
fp.write(ln)
skipToSensorEnd = False
continue
m = reStartHeaters.match(ln)
if m:
fp.write(ln)
for s in self.heaters:
sstr = ", ".join(s)
fp.write("DEFINE_HEATER(%s)\n" % sstr)
fp.write("\n")
for s in self.heaters:
fp.write(defineHeaterFormat % (s[0].upper(), s[0]))
skipToHeaterEnd = True
continue
if skipToHeaterEnd:
m = reEndHeaters.match(ln)
if m:
fp.write(ln)
skipToHeaterEnd = False
continue
m = reStartProcessors.match(ln)
if m:
fp.write(ln)
for i in range(len(self.processors)):
fp.write("%s#ifndef __AVR_%s__\n" % (i * " ", self.processors[i]))
fp.write("%s#error Wrong CPU type.\n" % ((i + 1) * " "))
for s in self.processors:
fp.write("%s#endif\n" % (i * " "))
i -= 1
skipToProcessorEnd = True
continue
if skipToProcessorEnd:
m = reEndProcessors.match(ln)
if m:
fp.write(ln)
skipToProcessorEnd = False
continue
m = reDefineBL.match(ln)
if m:
t = m.groups()
if len(t) == 2:
if t[0] in values.keys() and values[t[0]] != "":
fp.write(defineValueFormat % (t[0], values[t[0]]))
else:
fp.write("//" + ln)
continue
m = reDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1:
if t[0] in values.keys() and values[t[0]]:
fp.write(defineBoolFormat % t[0])
else:
fp.write("//" + ln)
continue
m = reCommDefBL.match(ln)
if m:
t = m.groups()
if len(t) == 2:
if t[0] in values.keys() and values[t[0]] != "":
fp.write(defineValueFormat % (t[0], values[t[0]]))
else:
fp.write(ln)
continue
m = reCommDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1:
if t[0] in values.keys() and values[t[0]]:
fp.write(defineBoolFormat % t[0])
else:
fp.write(ln)
continue
fp.write(ln)
fp.close()
return True

251
configtool/calcbelt.py Normal file
View File

@ -0,0 +1,251 @@
import wx
from configtool.data import BSIZESMALL, reFloat
class CalcBelt(wx.Dialog):
def __init__(self, parent, cbUse):
wx.Dialog.__init__(self, parent, wx.ID_ANY,
"Steps calculator for belt driven axes",
size = (360, 300))
self.Bind(wx.EVT_CLOSE, self.onExit)
self.use = cbUse
labelWidth = 110
sz = wx.BoxSizer(wx.VERTICAL)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Step Angle:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
stepAngles = ["1.8 (200 per revolution)", "0.9 (400 per revolution)",
"7.5 (48 per revolution)"]
self.stepAngleValues = [200, 400, 48]
tc = wx.Choice(self, wx.ID_ANY, choices = stepAngles)
tc.SetSelection(0)
tc.Bind(wx.EVT_CHOICE, self.onChoice)
lsz.Add(tc)
tc.SetToolTipString("Step angle. Depends on your type of stepper motor.")
self.tcStep = tc
sz.Add(lsz)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Microstepping:",
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
microStepping = ["1 - full step", "1/2 - half step", "1/4 - quarter step",
"1/8", "1/16", "1/32", "1/64", "1/128"]
self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128]
tc = wx.Choice(self, wx.ID_ANY, choices = microStepping)
tc.Bind(wx.EVT_CHOICE, self.onChoice)
tc.SetSelection(4)
lsz.Add(tc)
tc.SetToolTipString("Microstepping. Most boards allow to change this by "
"setting jumpers. The value here must match the "
"setting on the board in conjunction with the type "
"of stepper driver chip.")
self.tcMicroStepping = tc
sz.Add(lsz)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Belt Pitch (in mm):",
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "2", style = wx.TE_RIGHT)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc)
tc.SetToolTipString("Belt pitch. Distance between two teeth on the belt.")
self.tcBeltPitch = tc
lsz.AddSpacer((5, 5))
beltPresets = ["-", "2mm Pitch (GT2)", "MXL Pitch (2.03mm)",
"T2.5 (2.5mm)", "3mm Pitch (GT2, HTD)",
"5mm Pitch (T5, GTD, HTD)", "0.2\" XL belt (5.08mm)"]
self.beltPresetValues = [-1, 2.0, 2.03, 2.5, 3.0, 5.0, 5.08]
tc = wx.Choice(self, wx.ID_ANY, choices = beltPresets)
tc.SetSelection(0)
tc.Bind(wx.EVT_CHOICE, self.onPresetChoice)
lsz.Add(tc)
tc.SetToolTipString("Belt pitch presets.")
self.tcPresets = tc
sz.Add(lsz)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Pulley Teeth Count:",
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "8", style = wx.TE_RIGHT)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc)
tc.SetToolTipString("Pulley teeth count. Count them!")
self.tcPulleyTeeth = tc
sz.Add(lsz)
sz.AddSpacer((30, 30))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Result:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (200, -1),
style = wx.ALIGN_LEFT)
lsz.Add(tc)
self.tcResult = tc
sz.Add(lsz)
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Resolution:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (200, -1),
style = wx.ALIGN_LEFT)
lsz.Add(tc)
self.tcResolution = tc
sz.Add(lsz)
sz.AddSpacer((20, 20))
bsz = wx.BoxSizer(wx.HORIZONTAL)
b = wx.Button(self, wx.ID_ANY, "Use for X", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForX, b)
bsz.Add(b)
self.bUseForX = b
bsz.AddSpacer((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Y", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForY, b)
bsz.Add(b)
self.bUseForY = b
bsz.AddSpacer((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Z", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForZ, b)
bsz.Add(b)
self.bUseForZ = b
bsz.AddSpacer((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for E", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForE, b)
bsz.Add(b)
self.bUseForE = b
sz.Add(bsz)
self.enableUseButtons(False)
self.SetSizer(sz)
self.Fit()
self.calculate()
def calculate(self):
self.tcResult.SetLabel("")
self.tcResolution.SetLabel("")
self.enableUseButtons(False)
s = self.tcStep.GetSelection()
sv = self.stepAngleValues[s]
try:
bp = float(self.tcBeltPitch.GetValue())
except:
return
try:
pt = int(self.tcPulleyTeeth.GetValue())
except:
return
s = self.tcMicroStepping.GetSelection()
msv = self.microSteppingValues[s]
length = pt * bp
steps = sv * msv
resultmm = steps / length
self.result = int(resultmm * 1000.0)
self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" %
(self.result, resultmm))
self.tcResolution.SetLabel("%.3f micrometers" % (length / steps * 1000.0))
self.enableUseButtons(True)
def enableUseButtons(self, flag):
self.bUseForX.Enable(flag)
self.bUseForY.Enable(flag)
self.bUseForZ.Enable(flag)
self.bUseForE.Enable(flag)
def onUseForX(self, evt):
self.use('STEPS_PER_M_X', self.result)
def onUseForY(self, evt):
self.use('STEPS_PER_M_Y', self.result)
def onUseForZ(self, evt):
self.use('STEPS_PER_M_Z', self.result)
def onUseForE(self, evt):
self.use('STEPS_PER_M_E', self.result)
def onPresetChoice(self, evt):
s = self.tcPresets.GetSelection()
sv = self.beltPresetValues[s]
if sv < 0:
return
s = "%f" % sv
s = s.rstrip("0")
if s[-1] == ".":
s += "0"
self.tcBeltPitch.SetValue(s)
def onChoice(self, evt):
self.calculate()
def onTextCtrlFloat(self, evt):
tc = evt.GetEventObject()
w = tc.GetValue().strip()
if w == "":
valid = False
else:
m = reFloat.match(w)
if m:
valid = True
else:
valid = False
if valid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
self.calculate()
evt.Skip()
def onExit(self, evt):
self.EndModal(wx.ID_OK)

269
configtool/calcscrew.py Normal file
View File

@ -0,0 +1,269 @@
import wx
from configtool.data import BSIZESMALL, reFloat
class CalcScrew(wx.Dialog):
def __init__(self, parent, cbUse):
wx.Dialog.__init__(self, parent, wx.ID_ANY,
"Steps calculator for screw driven axes",
size = (400, 204))
self.Bind(wx.EVT_CLOSE, self.onExit)
self.use = cbUse
labelWidth = 110
sz = wx.BoxSizer(wx.VERTICAL)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Step Angle:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
stepAngles = ["1.8 (200 per revolution)", "0.9 (400 per revolution)",
"7.5 (48 per revolution)"]
self.stepAngleValues = [200, 400, 48]
tc = wx.Choice(self, wx.ID_ANY, choices = stepAngles)
tc.SetSelection(0)
tc.Bind(wx.EVT_CHOICE, self.onChoice)
lsz.Add(tc)
tc.SetToolTipString("Step angle. Depends on your type of stepper motor.")
self.tcStep = tc
sz.Add(lsz)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Microstepping:",
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
microStepping = ["1 - full step", "1/2 - half step", "1/4 - quarter step",
"1/8", "1/16", "1/32", "1/64", "1/128"]
self.microSteppingValues = [1, 2, 4, 8, 16, 32, 64, 128]
tc = wx.Choice(self, wx.ID_ANY, choices = microStepping)
tc.Bind(wx.EVT_CHOICE, self.onChoice)
tc.SetSelection(4)
lsz.Add(tc)
tc.SetToolTipString("Microstepping. Most boards allow to change this by "
"setting jumpers. The value here must match the "
"setting on the board in conjunction with the type "
"of stepper driver chip.")
self.tcMicroStepping = tc
sz.Add(lsz)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Screw Pitch (mm/rev):",
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "2", style = wx.TE_RIGHT)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc)
tc.SetToolTipString("Screw pitch. Defined by the pitch of the screw.")
self.tcScrewPitch = tc
lsz.AddSpacer((5, 5))
screwPresets = ["-", "M8 - metric (1.25 mm/rev)", "M6 - metric (1 mm/rev)",
"M5 - metric (0.8 mm/rev)", "12 (12 mm/rev)",
"16 (16 mm/rev)", "25 (25 mm/rev)",
"5/15\"-18 imperial coarse (1.41111 mm/rev)",
"3/16\"-20 imperial (1.270 mm/rev)",
"1/4\"-16 ACME (1.5875 mm/rev)"]
self.screwPresetValues = [-1, 1.25, 1.00, 0.8, 12.0, 16.0, 25.0, 1.41111,
1.270, 1.5875]
tc = wx.Choice(self, wx.ID_ANY, choices = screwPresets)
tc.SetSelection(0)
tc.Bind(wx.EVT_CHOICE, self.onPresetChoice)
lsz.Add(tc)
tc.SetToolTipString("Screw pitch presets.")
self.tcPresets = tc
sz.Add(lsz)
sz.AddSpacer((10, 10))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Gear Ratio:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "1", size = (40, -1), style = wx.TE_RIGHT)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc)
tc.SetToolTipString("Gear ratio. 1:1 if there is no gear.")
self.tcRatioTop = tc
lsz.AddSpacer((5, 5))
st = wx.StaticText(self, wx.ID_ANY, ":")
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.TextCtrl(self, wx.ID_ANY, "1", size = (40, -1), style = wx.TE_RIGHT)
tc.Bind(wx.EVT_TEXT, self.onTextCtrlFloat)
lsz.Add(tc)
tc.SetToolTipString("Gear ratio. 1:1 if there is no gear.")
self.tcRatioBottom = tc
sz.Add(lsz)
sz.AddSpacer((30, 30))
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Result:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (300, -1),
style = wx.ALIGN_LEFT)
lsz.Add(tc)
self.tcResult = tc
sz.Add(lsz)
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, "Resolution:", size = (labelWidth, -1),
style = wx.ALIGN_RIGHT)
lsz.Add(st)
lsz.AddSpacer((5, 5))
tc = wx.StaticText(self, wx.ID_ANY, "", size = (300, -1),
style = wx.ALIGN_LEFT)
lsz.Add(tc)
self.tcResolution = tc
sz.Add(lsz)
sz.AddSpacer((20, 20))
bsz = wx.BoxSizer(wx.HORIZONTAL)
b = wx.Button(self, wx.ID_ANY, "Use for X", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForX, b)
bsz.Add(b)
self.bUseForX = b
bsz.AddSpacer((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Y", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForY, b)
bsz.Add(b)
self.bUseForY = b
bsz.AddSpacer((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for Z", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForZ, b)
bsz.Add(b)
self.bUseForZ = b
bsz.AddSpacer((5, 5))
b = wx.Button(self, wx.ID_ANY, "Use for E", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.onUseForE, b)
bsz.Add(b)
self.bUseForE = b
sz.Add(bsz, flag = wx.ALIGN_CENTER_HORIZONTAL)
self.enableUseButtons(False)
self.SetSizer(sz)
self.Fit()
self.calculate()
def calculate(self):
self.tcResult.SetLabel("")
self.tcResolution.SetLabel("")
self.enableUseButtons(False)
s = self.tcStep.GetSelection()
sv = self.stepAngleValues[s]
try:
sp = float(self.tcScrewPitch.GetValue())
except:
return
try:
ratioA = float(self.tcRatioTop.GetValue())
except:
return
try:
ratioB = float(self.tcRatioBottom.GetValue())
except:
return
s = self.tcMicroStepping.GetSelection()
msv = self.microSteppingValues[s]
ratio = ratioA / ratioB
steps = sv * msv
resultmm = steps / sp / ratio
self.result = int(resultmm * 1000.0)
self.tcResult.SetLabel("%d steps/m (%.3f steps/mm)" %
(self.result, resultmm))
self.tcResolution.SetLabel("%.3f micrometers" % (1.0 / resultmm * 1000.0))
self.enableUseButtons(True)
def enableUseButtons(self, flag):
self.bUseForX.Enable(flag)
self.bUseForY.Enable(flag)
self.bUseForZ.Enable(flag)
self.bUseForE.Enable(flag)
def onUseForX(self, evt):
self.use('STEPS_PER_M_X', self.result)
def onUseForY(self, evt):
self.use('STEPS_PER_M_Y', self.result)
def onUseForZ(self, evt):
self.use('STEPS_PER_M_Z', self.result)
def onUseForE(self, evt):
self.use('STEPS_PER_M_E', self.result)
def onPresetChoice(self, evt):
s = self.tcPresets.GetSelection()
sv = self.screwPresetValues[s]
if sv < 0:
return
s = "%f" % sv
s = s.rstrip("0")
if s[-1] == ".":
s += "0"
self.tcScrewPitch.SetValue(s)
def onChoice(self, evt):
self.calculate()
def onTextCtrlFloat(self, evt):
tc = evt.GetEventObject()
w = tc.GetValue().strip()
if w == "":
valid = False
else:
m = reFloat.match(w)
if m:
valid = True
else:
valid = False
if valid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
self.calculate()
evt.Skip()
def onExit(self, evt):
self.EndModal(wx.ID_OK)

View File

@ -0,0 +1,48 @@
import wx
from configtool.page import Page
class CommunicationsPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
self.defaultBaud = '115200'
self.bauds = ['19200', '38400', '57600', self.defaultBaud]
self.labels = {'BAUD': "Baud Rate:", 'USB_SERIAL': "USB Serial"}
sz = wx.GridBagSizer()
sz.AddSpacer((20, 40), pos = (0, 0))
ch = self.addChoice('BAUD', self.bauds, self.bauds.index(self.defaultBaud),
60, self.onChoice)
sz.Add(ch, pos = (1, 1))
sz.AddSpacer((100, 10), pos = (1, 2))
k = 'USB_SERIAL'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (1, 3))
self.SetSizer(sz)
self.enableAll(False)
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
self.setChoice('BAUD', cfgValues, self.defaultBaud)
self.assertModified(False)

81
configtool/cpupage.py Normal file
View File

@ -0,0 +1,81 @@
import wx
from configtool.page import Page
from configtool.data import supportedCPUs
class CpuPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
self.labels = {'MOTHERBOARD': "Motherboard", 'F_CPU': "CPU Clock Rate:"}
self.defaultClock = '16000000UL'
self.clocks = ['8000000UL', self.defaultClock, '20000000UL']
self.processors = []
sz = wx.GridBagSizer()
sz.AddSpacer((20, 40), pos = (0, 0))
k = 'MOTHERBOARD'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (1, 1))
sz.AddSpacer((100, 10), pos = (1, 2))
k = 'F_CPU'
ch = self.addChoice(k, self.clocks, self.clocks.index(self.defaultClock),
80, self.onChoice)
sz.Add(ch, pos = (1, 3))
sz.AddSpacer((100, 10), pos = (1, 4))
b = wx.StaticBox(self, wx.ID_ANY, "Processor Type(s)")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
ht = "Choose the processor(s) this configuration will work with."
for k in supportedCPUs:
cb = self.addCheckBox(k, self.onCheckBox)
cb.SetToolTipString(ht)
sbox.Add(cb)
sbox.AddSpacer((120, 5))
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 5), span = (3, 1))
self.SetSizer(sz)
self.enableAll(False)
def setProcessors(self, plist):
self.processors = plist
for p in supportedCPUs:
if p in self.processors:
self.checkBoxes[p].SetValue(True)
else:
self.checkBoxes[p].SetValue(False)
def getProcessors(self):
plist = []
for p in supportedCPUs:
if self.checkBoxes[p].IsChecked():
plist.append(p)
return plist
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
self.setChoice('F_CPU', cfgValues, self.defaultClock)
self.assertModified(False)

52
configtool/data.py Normal file
View File

@ -0,0 +1,52 @@
import re
VERSION = "0.1"
supportedCPUs = ['ATmega168', 'ATmega328P', 'ATmega644P', 'ATmega644PA',
'ATmega1280', 'ATmega1284P', 'ATmega2560', 'AT90USB1286']
pinNames = ["AIO%d" % x for x in range(16)] + ["DIO%d" % x for x in range(64)]
pinNamesWithBlank = ["-"] + pinNames
BSIZE = (90, 60)
BSIZESMALL = (90, 30)
reDefQSm = re.compile("\s*#define\s+(\S+)\s+(.*)")
reDefQSm2 = re.compile("\s*(\"[^\"]*\")")
reDefine = re.compile("\s*#define\s+(\w+)\s+(\S+)")
reDefineBL = re.compile("^\s*#define\s+(\w+)\s+(\S+)")
reCommDefBL = re.compile("^\s*//\s*#define\s+(\w+)\s+(\S+)")
reDefQS = re.compile("\s*#define\s+(\w+)\s+(\"[^\"]*\")")
reDefTS = re.compile("\s*(DEFINE_TEMP_SENSOR\\([^)]*\\))")
reDefHT = re.compile("\s*(DEFINE_HEATER\\([^)]*\\))")
reDefBool = re.compile("\s*#define\s+(\w+)\s+")
reDefBoolBL = re.compile("^\s*#define\s+(\w+)\s+")
reCommDefBoolBL = re.compile("^\s*//\s*#define\s+(\S+)\s+")
reStartSensors = re.compile("^\s*//\s*DEFINE_TEMP_SENSORS_START")
reEndSensors = re.compile("^\s*//\s*DEFINE_TEMP_SENSORS_END")
reStartHeaters = re.compile("^\s*//\s*DEFINE_HEATERS_START")
reEndHeaters = re.compile("^\s*//\s*DEFINE_HEATERS_END")
reStartProcessors = re.compile("^\s*//\s*PROCESSORS_START")
reEndProcessors = re.compile("^\s*//\s*PROCESSORS_END")
reCandHeatPins = re.compile("^\s*//\s*#define\s+HEATER_PIN\s+(\w+)")
reCandThermPins = re.compile("^\s*//\s*#define\s+TEMP_SENSOR_PIN\s+(\w+)")
reHelpTextStart = re.compile("^\s*/\*\*\s+\\\\def\s+(.*)")
reHelpTextEnd = re.compile("^\s*\*/")
reSensor3 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
reSensor4 = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
reHeater = re.compile(".*\\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*\\)")
reInteger = re.compile("^\d+U?L?$")
reFloat = re.compile("^\d+(\.\d*)?$")
reAVR = re.compile("__AVR_(\w+)__")
defineValueFormat = "#define %-24s %s\n"
defineBoolFormat = "#define %s\n"
defineHeaterFormat = "#define HEATER_%s HEATER_%s\n"
defineDCExtruderFormat = "#define %-24s HEATER_%s\n"

91
configtool/heaterlist.py Normal file
View File

@ -0,0 +1,91 @@
import wx
class HeaterList(wx.ListCtrl):
def __init__(self, parent):
self.parent = parent
self.currentItem = None
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, size = (165 + 4, 100),
style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES |
wx.LC_VRULES)
self.valid = []
self.heaterList = []
self.InsertColumn(0, "Name")
self.InsertColumn(1, "Pin")
self.InsertColumn(2, "PWM")
self.SetColumnWidth(0, 55)
self.SetColumnWidth(1, 55)
self.SetColumnWidth(2, 55)
self.SetItemCount(0)
self.attr2 = wx.ListItemAttr()
self.attr2.SetBackgroundColour("light blue")
self.attr3 = wx.ListItemAttr()
self.attr3.SetBackgroundColour("pink")
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
def updateList(self, heaterList):
self.heaterList = heaterList
self.valid = [True] * len(heaterList)
self.currentItem = None
self.parent.setItemSelected(None)
i = self.GetFirstSelected()
while i != -1:
self.Select(i, False)
i = self.GetFirstSelected()
self.SetItemCount(len(heaterList))
def setRowValidity(self, i, flag = False):
if i < 0 or i >= len(self.heaterList):
return
self.valid[i] = flag
self.Refresh()
def setTableValidity(self, flag = False):
for i in range(len(self.heaterList)):
self.setRowValidity(i, flag)
def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex
self.parent.setItemSelected(self.currentItem)
def OnItemDeselected(self, event):
self.currentItem = None
self.parent.setItemSelected(None)
def getColumnText(self, index, col):
item = self.GetItem(index, col)
return item.GetText()
def OnGetItemText(self, item, col):
if item < 0 or item >= len(self.heaterList):
return "Error - no heaters"
s = self.heaterList[item]
if col == 0:
return s[0]
elif col == 1:
return s[1]
elif col == 2:
if s[2] == "1":
return "True"
else:
return "False"
def OnGetItemAttr(self, item):
if not self.valid[item]:
return self.attr3
if item % 2 == 1:
return self.attr2
else:
return None

125
configtool/heaterspage.py Normal file
View File

@ -0,0 +1,125 @@
import wx
from configtool.page import Page
from configtool.data import pinNames, BSIZESMALL
from configtool.heaterlist import HeaterList
from configtool.addheaterdlg import AddHeaterDlg
class HeatersPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
sz = wx.GridBagSizer()
sz.AddSpacer((30, 30), pos = (0, 0))
self.heaters = []
self.validPins = pinNames
self.lb = HeaterList(self)
sz.Add(self.lb, pos = (1, 1), span = (1, 3))
bsz = wx.BoxSizer(wx.VERTICAL)
self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd)
self.bAdd.SetToolTipString("Add a heater to the configuration.")
bsz.Add(self.bAdd)
bsz.AddSpacer((10, 10))
self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL)
self.bDelete.Enable(False)
self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete)
bsz.Add(self.bDelete)
self.bDelete.SetToolTipString("Remove the selected heater from the "
"configuration.")
sz.Add(bsz, pos = (1, 4))
self.SetSizer(sz)
self.enableAll(False)
def enableAll(self, flag = True):
self.bAdd.Enable(flag)
Page.enableAll(self, flag)
def setItemSelected(self, n):
self.selection = n
if n is None:
self.bDelete.Enable(False)
else:
self.bDelete.Enable(True)
def doAdd(self, evt):
nm = []
for s in self.heaters:
nm.append(s[0])
dlg = AddHeaterDlg(self, nm, self.validPins)
rc = dlg.ShowModal()
if rc == wx.ID_OK:
ht = dlg.getValues()
dlg.Destroy()
if rc != wx.ID_OK:
return
self.heaters.append(ht)
self.lb.updateList(self.heaters)
self.validateTable()
self.parent.setHeaters(self.heaters)
def doDelete(self, evt):
if self.selection is None:
return
self.assertModified(True)
del self.heaters[self.selection]
self.lb.updateList(self.heaters)
self.validateTable()
self.parent.setHeaters(self.heaters)
def insertValues(self, cfgValues):
self.enableAll(True)
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
self.assertModified(False)
def setHeaters(self, heaters):
self.heaters = heaters
self.lb.updateList(self.heaters)
self.validateTable()
self.parent.setHeaters(self.heaters)
def setCandidatePins(self, plist):
if not plist or len(plist) == 0:
self.validPins = pinNames
else:
self.validPins = plist
self.validateTable()
def validateTable(self):
self.lb.setTableValidity(True)
self.setFieldValidity('HEATERLIST', True)
for i in range(len(self.heaters)):
if self.heaters[i][1] not in self.validPins:
self.lb.setRowValidity(i, False)
self.setFieldValidity('HEATERLIST', False)
def setHelpText(self, ht):
Page.setHelpText(self, ht)
k = 'DEFINE_HEATER'
if k in ht.keys():
self.bAdd.SetToolTipString(ht[k])

View File

@ -0,0 +1,210 @@
import wx
from configtool.data import BSIZE
from configtool.page import Page
from configtool.calcbelt import CalcBelt
from configtool.calcscrew import CalcScrew
class MechanicalPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.id = idPg
self.parent = parent
self.spmKeys = ['STEPS_PER_M_X', 'STEPS_PER_M_Y', 'STEPS_PER_M_Z',
'STEPS_PER_M_E']
self.mfrKeys = ['MAXIMUM_FEEDRATE_X', 'MAXIMUM_FEEDRATE_Y',
'MAXIMUM_FEEDRATE_Z', 'MAXIMUM_FEEDRATE_E']
self.msrKeys = ['SEARCH_FEEDRATE_X', 'SEARCH_FEEDRATE_Y',
'SEARCH_FEEDRATE_Z']
self.eclKeys = ['ENDSTOP_CLEARANCE_X', 'ENDSTOP_CLEARANCE_Y',
'ENDSTOP_CLEARANCE_Z']
self.minmaxKeys = ['X_MIN', 'X_MAX', 'Y_MIN', 'Y_MAX', 'Z_MIN', 'Z_MAX']
self.kinematicsKeys = ['KINEMATICS_STRAIGHT', 'KINEMATICS_COREXY']
self.labels = {'STEPS_PER_M_X': "X:", 'STEPS_PER_M_Y': "Y:",
'STEPS_PER_M_Z': "Z:", 'STEPS_PER_M_E' : "E:",
'MAXIMUM_FEEDRATE_X': "X:", 'MAXIMUM_FEEDRATE_Y': "Y:",
'MAXIMUM_FEEDRATE_Z': "Z:", 'MAXIMUM_FEEDRATE_E': "E:",
'SEARCH_FEEDRATE_X': "X:", 'SEARCH_FEEDRATE_Y': "Y:",
'SEARCH_FEEDRATE_Z': "Z:",
'ENDSTOP_CLEARANCE_X': "X:", 'ENDSTOP_CLEARANCE_Y': "Y:",
'ENDSTOP_CLEARANCE_Z': "Z:",
'X_MIN': "Min X:", 'X_MAX': "Max X:", 'Y_MIN': "Min Y:",
'Y_MAX': "Max Y:", 'Z_MIN': "Min Z:", 'Z_MAX': "Max Z:",
'E_ABSOLUTE': "Absolute E Coordinates",
'KINEMATICS_STRAIGHT': "Straight",
'KINEMATICS_COREXY': "CoreXY"}
labelWidth = 40;
sz = wx.GridBagSizer()
sz.AddSpacer((10, 10), pos = (0, 0))
sz.AddSpacer((90, 10), pos = (0, 4))
b = wx.StaticBox(self, wx.ID_ANY, "Steps Per Meter")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.spmKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 1))
b = wx.StaticBox(self, wx.ID_ANY, "Maximum Feedrate")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.mfrKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 5))
b = wx.StaticBox(self, wx.ID_ANY, "Search Feedrate")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.msrKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 7))
b = wx.StaticBox(self, wx.ID_ANY, "Endstop Clearance")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.eclKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (3, 5))
b = wx.StaticBox(self, wx.ID_ANY, "Travel Limits")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.minmaxKeys:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (3, 7))
vsz = wx.BoxSizer(wx.VERTICAL)
b = wx.StaticBox(self, wx.ID_ANY, "Kinematics")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
style = wx.RB_GROUP
for k in self.kinematicsKeys:
rb = self.addRadioButton(k, style, self.onKinematicsSelect)
style = 0
sbox.Add(rb, 1, wx.LEFT + wx.RIGHT, 16)
sbox.AddSpacer((5, 5))
vsz.Add(sbox, 1, wx.LEFT, 40)
cb = self.addCheckBox('E_ABSOLUTE', self.onCheckBox)
vsz.Add(cb, 1, wx.LEFT, 40)
sz.Add(vsz, pos = (3, 1))
bsz = wx.BoxSizer(wx.VERTICAL)
b = wx.Button(self, wx.ID_ANY, "Calculate\nBelt Driven", size = BSIZE)
b.SetToolTipString("Open the calculator for axes that are belt-driven.")
self.Bind(wx.EVT_BUTTON, self.onCalcBelt, b)
self.bCalcBelt = b
bsz.Add(b, 1, wx.ALL, 5)
b = wx.Button(self, wx.ID_ANY, "Calculate\nScrew Driven", size = BSIZE)
bsz.Add(b, 1, wx.ALL, 5)
b.SetToolTipString("Open the calculator for axes that are screw-driven.")
self.Bind(wx.EVT_BUTTON, self.onCalcScrew, b)
self.bCalcScrew = b
sz.Add(bsz, pos = (1, 3))
self.enableAll(False)
self.SetSizer(sz)
def enableAll(self, flag = True):
self.bCalcBelt.Enable(flag)
self.bCalcScrew.Enable(flag)
Page.enableAll(self, flag)
def onKinematicsSelect(self, evt):
self.assertModified(True)
evt.Skip()
def onCalcBelt(self, evt):
dlg = CalcBelt(self, self.cbCalcBelt)
dlg.ShowModal()
dlg.Destroy()
def cbCalcBelt(self, field, value):
s = "%d" % value
self.textControls[field].SetValue(s)
def onCalcScrew(self, evt):
dlg = CalcScrew(self, self.cbCalcScrew)
dlg.ShowModal()
dlg.Destroy()
def cbCalcScrew(self, field, value):
s = "%d" % value
self.textControls[field].SetValue(s)
def setHelpText(self, ht):
Page.setHelpText(self, ht)
if 'KINEMATICS' in ht.keys():
for k in self.kinematicsKeys:
self.radioButtons[k].SetToolTipString(ht['KINEMATICS'])
def insertValues(self, cfgValues):
self.assertValid(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControls[k].SetValue(cfgValues[k])
else:
self.textControls[k].SetValue("")
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
if 'KINEMATICS' in cfgValues.keys():
k = cfgValues['KINEMATICS']
if k in self.kinematicsKeys:
self.radioButtons[k].SetValue(True)
else:
self.radioButtons[self.kinematicsKeys[0]].SetValue(True)
else:
self.radioButtons[self.kinematicsKeys[0]].SetValue(True)
self.assertModified(False)
self.enableAll(True)
def getValues(self):
result = Page.getValues(self)
for tag in self.kinematicsKeys:
rb = self.radioButtons[tag]
if rb.GetValue():
result['KINEMATICS'] = tag
break
return result

View File

@ -0,0 +1,291 @@
import wx
from configtool.page import Page
from configtool.data import reFloat
class MiscellaneousPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
self.labels = {'USE_INTERNAL_PULLUPS': "Use Internal Pullups",
'EECONFIG': "Enable EEPROM Storage",
'DEBUG': "Turn on Debugging",
'BANG_BANG': "Enable",
'BANG_BANG_ON': "On PWM Level:",
'BANG_BANG_OFF': "Off PWM Level:",
'MOVEBUFFER_SIZE': "Movebuffer Size:",
'DC_EXTRUDER': "Heater:", 'DC_EXTRUDER_PWM': "PWM:",
'USE_WATCHDOG': "Use the Watchdog Timer",
'REFERENCE': "Analog Reference:",
'STEP_INTERRUPT_INTERRUPTIBLE': "STEP Interrupt",
'TH_COUNT': "Temperature History Size:",
'FAST_PWM': "Fast PWM",
'ENDSTOP_STEPS': "Endstop Steps:",
'PID_SCALE': "PID Scaling Factor:",
'TEMP_HYSTERESIS': "Temperature Hysteresis:",
'TEMP_RESIDENCY_TIME': "Temperature Residency Time:",
'TEMP_EWMA': "Temperature EWMA:",
'HEATER_SANITY_CHECK': "Heater Sanity Check"}
self.heaterNameNone = "<none>"
self.heaterNames = [self.heaterNameNone]
self.boardHeaters = []
self.processors = []
self.defaultRef = 'REFERENCE_AVCC'
self.references = [self.defaultRef, 'REFERENCE_AREF',
'REFERENCE_1V1', 'REFERENCE_2V56']
sz = wx.GridBagSizer()
sz.AddSpacer((20, 40), pos = (0, 0))
sz.AddSpacer((40, 40), pos = (0, 2))
sz.AddSpacer((40, 40), pos = (0, 4))
sz.AddSpacer((20, 30), pos = (1, 0))
sz.AddSpacer((20, 30), pos = (2, 0))
sz.AddSpacer((20, 30), pos = (3, 0))
sz.AddSpacer((20, 30), pos = (4, 0))
sz.AddSpacer((20, 30), pos = (5, 0))
sz.AddSpacer((20, 30), pos = (6, 0))
sz.AddSpacer((20, 30), pos = (7, 0))
sz.AddSpacer((20, 30), pos = (8, 0))
labelWidth = 140
k = 'EECONFIG'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (1, 1))
k = 'USE_INTERNAL_PULLUPS'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (2, 1))
k = 'DEBUG'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (3, 1))
k = 'USE_WATCHDOG'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (4, 1))
k = 'STEP_INTERRUPT_INTERRUPTIBLE'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (5, 1))
k = 'FAST_PWM'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (6, 1))
k = 'XONXOFF'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (7, 1))
k = 'HEATER_SANITY_CHECK'
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (8, 1))
k = 'REFERENCE'
ch = self.addChoice(k, self.references,
self.references.index(self.defaultRef),
labelWidth, self.onChoice)
sz.Add(ch, pos = (1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "BANG BANG Bed Control")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
k = 'BANG_BANG'
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 60)
sbox.AddSpacer((5, 20))
k = 'BANG_BANG_ON'
tc = self.addTextCtrl(k, 80, self.onTextCtrlInteger)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
k = 'BANG_BANG_OFF'
tc = self.addTextCtrl(k, 80, self.onTextCtrlInteger)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (3, 3), span = (4, 1), flag = wx.ALIGN_CENTER_HORIZONTAL)
b = wx.StaticBox(self, wx.ID_ANY, "DC Motor Extruder")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
k = 'DC_EXTRUDER'
ch = self.addChoice(k, self.heaterNames, 0, 60, self.onChoice)
sbox.Add(ch)
sbox.AddSpacer((5, 5))
k = 'DC_EXTRUDER_PWM'
tc = self.addTextCtrl(k, 60, self.onTextCtrlInteger)
sbox.Add(tc)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (8,3), flag = wx.ALIGN_CENTER_HORIZONTAL)
k = 'MOVEBUFFER_SIZE'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (1, 5))
k = 'TH_COUNT'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (2, 5))
k = 'ENDSTOP_STEPS'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (3, 5))
k = 'PID_SCALE'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (4, 5))
k = 'TEMP_HYSTERESIS'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlFloat)
sz.Add(tc, pos = (6, 5))
k = 'TEMP_RESIDENCY_TIME'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlInteger)
sz.Add(tc, pos = (7, 5))
k = 'TEMP_EWMA'
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlEWMA)
sz.Add(tc, pos = (8, 5))
self.SetSizer(sz)
self.enableAll(False)
def onTextCtrlEWMA(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else:
m = reFloat.match(w)
if m:
v = float(w)
if v < 0.1 or v > 1.0:
valid = False
else:
valid = True
else:
valid = False
self.setFieldValidity(name, valid)
if valid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
evt.Skip()
def setHeaters(self, hlist):
k = 'DC_EXTRUDER'
v = self.choices[k].GetSelection()
currentChoice = self.heaterNames[v]
self.boardHeaters = [s[0] for s in hlist]
self.heaterNames = [self.heaterNameNone] + self.boardHeaters
self.choices[k].Clear()
for h in self.heaterNames:
self.choices[k].Append(h)
self.choiceOptions[k] = self.heaterNames
try:
v = self.heaterNames.index(currentChoice)
except:
v = 0
dlg = wx.MessageDialog(self,
"Printer: Miscellaneous tab:\nDC Extruder heater "
"\"%s\" not defined for this board. Please check."
% currentChoice, "Warning",
wx.OK + wx.ICON_WARNING)
dlg.ShowModal()
dlg.Destroy()
self.choices[k].SetSelection(v)
def setOriginalHeater(self, h):
k = 'DC_EXTRUDER'
if h and h.startswith("HEATER_"):
hname = h[len("HEATER_"):]
else:
hname = h
if len(self.boardHeaters) != 0:
if hname not in self.boardHeaters:
dlg = wx.MessageDialog(self,
"Printer: Miscellaneous tab:\nDC Extruder "
"heater \"%s\" not defined for this board. "
"Please check."
% hname, "Warning", wx.OK + wx.ICON_WARNING)
dlg.ShowModal()
dlg.Destroy()
self.heaterNames = [self.heaterNameNone] + self.boardHeaters
else:
self.heaterNames = [self.heaterNameNone]
if h and h != self.heaterNameNone:
self.heaterNames.append(hname)
self.choices[k].Clear()
for ht in self.heaterNames:
self.choices[k].Append(ht)
self.choiceOptions[k] = self.heaterNames
if hname:
try:
v = self.heaterNames.index(hname)
except:
v = 0
else:
v = 0
self.choices[k].SetSelection(v)
def insertValues(self, cfgValues):
self.assertValid(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControls[k].SetValue(str(cfgValues[k]))
else:
self.textControls[k].SetValue("")
self.setChoice('REFERENCE', cfgValues, self.defaultRef)
self.assertModified(False)
self.enableAll(True)
def getValues(self):
result = Page.getValues(self)
k = 'STEP_INTERRUPT_INTERRUPTIBLE'
cb = self.checkBoxes[k]
if cb.IsChecked():
result[k] = "1"
else:
result[k] = "0"
k = "DC_EXTRUDER"
s = self.choices[k].GetSelection()
v = self.choiceOptions[k][s]
if v == self.heaterNameNone:
result[k] = ""
else:
result[k] = "HEATER_%s" % v
return result

235
configtool/page.py Normal file
View File

@ -0,0 +1,235 @@
import wx
from configtool.data import pinNames, pinNamesWithBlank, reInteger, reFloat
class Page:
def __init__(self):
self.modified = False
self.valid = True
self.fieldValid = {}
self.textControls = {}
self.checkBoxes = {}
self.radioButtons = {}
self.choices = {}
self.choiceOptions = {}
def enableAll(self, flag = True):
for c in self.textControls.keys():
self.textControls[c].Enable(flag)
for c in self.checkBoxes.keys():
self.checkBoxes[c].Enable(flag)
for c in self.radioButtons.keys():
self.radioButtons[c].Enable(flag)
for c in self.choices.keys():
self.choices[c].Enable(flag)
def addTextCtrl(self, name, labelWidth, validator):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
tc = wx.TextCtrl(self, wx.ID_ANY, "", style = wx.TE_RIGHT, name = name)
self.fieldValid[name] = True
tc.Bind(wx.EVT_TEXT, validator)
self.textControls[name] = tc
lsz.Add(tc)
return lsz
def addCheckBox(self, name, validator):
if name in self.labels.keys():
lbl = self.labels[name]
else:
lbl = name
cb = wx.CheckBox(self, wx.ID_ANY, lbl)
cb.Bind(wx.EVT_CHECKBOX, validator)
self.checkBoxes[name] = cb
return cb
def addRadioButton(self, name, style, validator):
rb = wx.RadioButton(self, wx.ID_ANY, self.labels[name], style = style)
self.Bind(wx.EVT_RADIOBUTTON, validator, rb)
self.radioButtons[name] = rb
return rb
def addChoice(self, name, choices, selection, labelWidth, validator):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
ch = wx.Choice(self, wx.ID_ANY, choices = choices, name = name)
ch.Bind(wx.EVT_CHOICE, validator)
ch.SetSelection(selection)
lsz.Add(ch)
self.choices[name] = ch
self.choiceOptions[name] = choices
return lsz
def addPinChoice(self, name, choiceVal, allowBlank, labelWidth):
lsz = wx.BoxSizer(wx.HORIZONTAL)
st = wx.StaticText(self, wx.ID_ANY, self.labels[name],
size = (labelWidth, -1), style = wx.ALIGN_RIGHT)
lsz.Add(st)
if allowBlank:
opts = pinNamesWithBlank
else:
opts = pinNames
ch = wx.Choice(self, wx.ID_ANY, choices = opts, name = name)
ch.Bind(wx.EVT_CHOICE, self.onChoice)
self.choices[name] = ch
self.choiceOptions[name] = opts
try:
sv = self.pinNames.index(choiceVal)
except:
sv = 0
ch.SetSelection(sv)
lsz.Add(ch)
return lsz
def setChoice(self, name, cfgValues, default):
if name in cfgValues.keys():
bv = cfgValues[name]
else:
bv = default
try:
s = self.choiceOptions[name].index(bv)
except:
try:
s = self.choiceOptions[name].index(default)
except:
s = 0
self.choices[name].SetSelection(s)
def onTextCtrlInteger(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else:
m = reInteger.match(w)
if m:
valid = True
else:
valid = False
self.setFieldValidity(name, valid)
if valid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
evt.Skip()
def onTextCtrlFloat(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
name = tc.GetName()
w = tc.GetValue().strip()
if w == "":
valid = True
else:
m = reFloat.match(w)
if m:
valid = True
else:
valid = False
self.setFieldValidity(name, valid)
if valid:
tc.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
else:
tc.SetBackgroundColour("pink")
tc.Refresh()
evt.Skip()
def onTextCtrlPin(self, evt):
self.assertModified(True)
tc = evt.GetEventObject()
self.validatePin(tc)
evt.Skip()
def onTextCtrl(self, evt):
self.assertModified(True)
evt.Skip()
def onChoice(self, evt):
self.assertModified(True)
evt.Skip()
def onCheckBox(self, evt):
self.assertModified(True)
evt.Skip()
def setHelpText(self, ht):
for k in self.textControls.keys():
if k in ht.keys():
self.textControls[k].SetToolTipString(ht[k])
for k in self.checkBoxes.keys():
if k in ht.keys():
self.checkBoxes[k].SetToolTipString(ht[k])
for k in self.radioButtons.keys():
if k in ht.keys():
self.radioButtons[k].SetToolTipString(ht[k])
for k in self.choices.keys():
if k in ht.keys():
self.choices[k].SetToolTipString(ht[k])
def getValues(self):
self.assertModified(False)
result = {}
for k in self.checkBoxes.keys():
cb = self.checkBoxes[k]
result[k] = cb.IsChecked()
for k in self.textControls.keys():
v = self.textControls[k].GetValue()
result[k] = v
for k in self.radioButtons.keys():
result[k] = self.radioButtons[k].GetValue()
for k in self.choices.keys():
v = self.choices[k].GetSelection()
result[k] = self.choiceOptions[k][v]
return result
def assertModified(self, flag):
if flag != self.modified:
self.parent.assertModified(self.id, flag)
self.modified = flag
def setFieldValidity(self, name, flag):
self.fieldValid[name] = flag
pgValid = True
for k in self.fieldValid.keys():
if not self.fieldValid[k]:
pgValid = False
break
self.assertValid(pgValid)
def assertValid(self, flag):
if flag != self.valid:
self.parent.assertValid(self.id, flag)
self.valid = flag

196
configtool/pinoutspage.py Normal file
View File

@ -0,0 +1,196 @@
import wx
from configtool.page import Page
class PinoutsPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
pinXkeys = [('X_STEP_PIN', 2), ('X_DIR_PIN', 2), ('X_MIN_PIN', 2),
('X_MAX_PIN', 2), ('X_ENABLE_PIN', 2), ('X_INVERT_DIR', 1),
('X_INVERT_MIN', 1), ('X_INVERT_MAX', 1),
('X_INVERT_ENABLE', 1)]
pinYkeys = [('Y_STEP_PIN', 2), ('Y_DIR_PIN', 2), ('Y_MIN_PIN', 2),
('Y_MAX_PIN', 2), ('Y_ENABLE_PIN', 2), ('Y_INVERT_DIR', 1),
('Y_INVERT_MIN', 1), ('Y_INVERT_MAX', 1),
('Y_INVERT_ENABLE', 1)]
pinZkeys = [('Z_STEP_PIN', 2), ('Z_DIR_PIN', 2), ('Z_MIN_PIN', 2),
('Z_MAX_PIN', 2), ('Z_ENABLE_PIN', 2), ('Z_INVERT_DIR', 1),
('Z_INVERT_MIN', 1), ('Z_INVERT_MAX', 1),
('Z_INVERT_ENABLE', 1)]
pinEkeys = [('E_STEP_PIN', 2), ('E_DIR_PIN', 2), ('E_ENABLE_PIN', 2),
('E_INVERT_DIR', 1), ('E_INVERT_ENABLE', 1)]
self.labels = {'X_STEP_PIN': "Step Pin:", 'X_DIR_PIN': "Direction Pin:",
'X_MIN_PIN': "Minimum Pin:", 'X_MAX_PIN': "Maximum Pin:",
'X_ENABLE_PIN': "Enable Pin:",
'X_INVERT_DIR': "Invert Direction",
'X_INVERT_MIN': "Invert Minimum",
'X_INVERT_MAX': "Invert Maximum",
'X_INVERT_ENABLE': "Invert Enable",
'Y_STEP_PIN': "Step Pin:", 'Y_DIR_PIN': "Direction Pin:",
'Y_MIN_PIN': "Minimum Pin:", 'Y_MAX_PIN': "Maximum Pin:",
'Y_ENABLE_PIN': "Enable Pin:",
'Y_INVERT_DIR': "Invert Direction",
'Y_INVERT_MIN': "Invert Minimum",
'Y_INVERT_MAX': "Invert Maximum",
'Y_INVERT_ENABLE': "Invert Enable",
'Z_STEP_PIN': "Step Pin:", 'Z_DIR_PIN': "Direction Pin:",
'Z_MIN_PIN': "Minimum Pin:", 'Z_MAX_PIN': "Maximum Pin:",
'Z_ENABLE_PIN': "Enable Pin:",
'Z_INVERT_DIR': "Invert Direction",
'Z_INVERT_MIN': "Invert Minimum",
'Z_INVERT_MAX': "Invert Maximum",
'Z_INVERT_ENABLE': "Invert Enable",
'E_STEP_PIN': "Step Pin:", 'E_DIR_PIN': "Direction Pin:",
'E_ENABLE_PIN': "Enable Pin:",
'E_INVERT_DIR': "Invert Direction",
'E_INVERT_ENABLE': "Invert Enable",
'PS_ON_PIN': "PSU On Pin:",
'PS_MOSFET_PIN': "PSU MOSFET Pin:",
'STEPPER_ENABLE_PIN': "Stepper Enable Pin:",
'STEPPER_INVERT_ENABLE': "Stepper Invert Enable",
'DEBUG_LED_PIN': "Debug LED Pin:"}
labelWidth = 120
sz = wx.GridBagSizer()
sz.AddSpacer((10, 10), pos = (0, 0))
b = wx.StaticBox(self, wx.ID_ANY, "X Axis")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k, ctype in pinXkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, "", True, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 1))
b = wx.StaticBox(self, wx.ID_ANY, "Y Axis")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k, ctype in pinYkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, "", True, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 3))
b = wx.StaticBox(self, wx.ID_ANY, "Z Axis")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k, ctype in pinZkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, "", True, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 5))
b = wx.StaticBox(self, wx.ID_ANY, "E Axis")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k, ctype in pinEkeys:
if ctype == 0:
tc = self.addTextCtrl(k, labelWidth, self.onTextCtrlPin)
sbox.Add(tc)
elif ctype == 2:
tc = self.addPinChoice(k, "", True, labelWidth)
sbox.Add(tc)
else:
cb = self.addCheckBox(k, self.onCheckBox)
sbox.Add(cb, 1, wx.LEFT, 30)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 7))
k = "STEPPER_ENABLE_PIN"
tc = self.addPinChoice(k, "", True, labelWidth)
sz.Add(tc, pos = (3, 1))
sz.AddSpacer((10, 10), pos = (4, 1))
k = "STEPPER_INVERT_ENABLE"
cb = self.addCheckBox(k, self.onCheckBox)
sz.Add(cb, pos = (5, 1), flag = wx.ALIGN_CENTER_HORIZONTAL)
k = "PS_ON_PIN"
tc = self.addPinChoice(k, "", True, labelWidth)
sz.Add(tc, pos = (3, 3))
k = "PS_MOSFET_PIN"
tc = self.addPinChoice(k, "", True, labelWidth)
sz.Add(tc, pos = (5, 3))
k = "DEBUG_LED_PIN"
tc = self.addPinChoice(k, "", True, labelWidth)
sz.Add(tc, pos = (3, 7))
self.SetSizer(sz)
self.enableAll(False)
def insertValues(self, cfgValues):
self.assertValid(True)
self.enableAll(True)
for k in self.fieldValid.keys():
self.fieldValid[k] = True
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControls[k].SetValue(cfgValues[k])
else:
self.textControls[k].SetValue("")
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
for k in self.choices.keys():
self.setChoice(k, cfgValues, "-")
self.assertModified(False)
def getValues(self):
result = Page.getValues(self)
for k in self.choices.keys():
if k in result.keys() and result[k] == "-":
result[k] = ""
return result

353
configtool/printerpanel.py Normal file
View File

@ -0,0 +1,353 @@
import os
import wx
import re
from configtool.data import (defineValueFormat, defineBoolFormat, reCommDefBL,
reCommDefBoolBL, reHelpTextStart, reHelpTextEnd,
reDefine, reDefineBL, reDefQS, reDefQSm,
reDefQSm2, reDefBool, reDefBoolBL)
from configtool.mechanicalpage import MechanicalPage
from configtool.accelerationpage import AccelerationPage
from configtool.miscellaneouspage import MiscellaneousPage
class PrinterPanel(wx.Panel):
def __init__(self, parent, nb, folder):
wx.Panel.__init__(self, nb, wx.ID_ANY)
self.parent = parent
self.cfgValues = {}
self.heaters = []
self.dir = os.path.join(folder, "config")
sz = wx.BoxSizer(wx.HORIZONTAL)
self.nb = wx.Notebook(self, wx.ID_ANY, size = (21, 21),
style = wx.BK_DEFAULT)
self.pages = []
self.titles = []
self.pageModified = []
self.pageValid = []
self.pgMech = MechanicalPage(self, self.nb, len(self.pages))
text = "Mechanical"
self.nb.AddPage(self.pgMech, text)
self.pages.append(self.pgMech)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
self.pgAcc = AccelerationPage(self, self.nb, len(self.pages))
text = "Acceleration"
self.nb.AddPage(self.pgAcc, text)
self.pages.append(self.pgAcc)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
self.pgMiscellaneous = MiscellaneousPage(self, self.nb, len(self.pages))
text = "Miscellaneous"
self.nb.AddPage(self.pgMiscellaneous, text)
self.pages.append(self.pgMiscellaneous)
self.titles.append(text)
self.pageModified.append(False)
self.pageValid.append(True)
sz.Add(self.nb, 1, wx.EXPAND + wx.ALL, 5)
self.SetSizer(sz)
def onPageChange(self, evt):
print "printer notebook page changed"
print evt.GetSelection()
print evt.GetEventObject()
evt.Skip()
def checkFocus(self):
print "check focus: ", self.nb.GetSelection()
def assertModified(self, pg, flag = True):
self.pageModified[pg] = flag
self.modifyTab(pg)
def isModified(self):
return (True in self.pageModified)
def assertValid(self, pg, flag = True):
self.pageValid[pg] = flag
self.modifyTab(pg)
if False in self.pageValid:
self.parent.enableSavePrinter(False)
else:
self.parent.enableSavePrinter(True)
def modifyTab(self, pg):
if self.pageModified[pg] and not self.pageValid[pg]:
pfx = "?* "
elif self.pageModified[pg]:
pfx = "* "
elif not self.pageValid[pg]:
pfx = "? "
else:
pfx = ""
self.nb.SetPageText(pg, pfx + self.titles[pg])
def setHeaters(self, ht):
return self.pgMiscellaneous.setHeaters(ht)
def onClose(self, evt):
if not self.confirmLoseChanges("exit"):
return
self.Destroy()
def confirmLoseChanges(self, msg):
if True not in self.pageModified:
return True
dlg = wx.MessageDialog(self, "Are you sure you want to " + msg + "?\n"
"There are changes to your printer "
"configuration that will be lost.",
"Changes pending",
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_INFORMATION)
rc = dlg.ShowModal()
dlg.Destroy()
if rc != wx.ID_YES:
return False
return True
def onLoadConfig(self, evt):
if not self.confirmLoseChanges("load a new printer configuration"):
return
wildcard = "Printer configuration (printer.*.h)|printer.*.h"
dlg = wx.FileDialog(self, message = "Choose a printer config file",
defaultDir = self.dir, defaultFile = "",
wildcard = wildcard, style = wx.OPEN | wx.CHANGE_DIR)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
if path is None:
return
self.dir = os.path.dirname(path)
rc = self.loadConfigFile(path)
if not rc:
dlg = wx.MessageDialog(self, "Unable to process file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return
self.parent.enableSavePrinter(True)
self.parent.setPrinterTabText("Printer <%s>" % os.path.basename(path))
for pg in self.pages:
pg.insertValues(self.cfgValues)
pg.setHelpText(self.helpText)
k = 'DC_EXTRUDER'
if k in self.cfgValues.keys():
print "calling orig with (%s)" % self.cfgValues[k]
self.pgMiscellaneous.setOriginalHeater(self.cfgValues[k])
else:
self.pgMiscellaneous.setOriginalHeater(None)
def loadConfigFile(self, fn):
try:
self.cfgBuffer = list(open(fn))
except:
return False
self.configFile = fn
self.processors = []
gatheringHelpText = False
helpTextString = ""
helpKey = None
self.cfgValues = {}
self.helpText = {}
prevLines = ""
for ln in self.cfgBuffer:
if gatheringHelpText:
if reHelpTextEnd.match(ln):
gatheringHelpText = False
hk = helpKey.split()
for k in hk:
self.helpText[k] = helpTextString
helpTextString = ""
helpKey = None
continue
else:
helpTextString += ln
continue
m = reHelpTextStart.match(ln)
if m:
t = m.groups()
gatheringHelpText = True
helpKey = t[0]
continue
if ln.rstrip().endswith("\\"):
prevLines += ln.rstrip()[:-1]
continue
if prevLines != "":
ln = prevLines + ln
prevLines = ""
if ln.lstrip().startswith("//"):
continue
if ln.lstrip().startswith("#define"):
m = reDefQS.search(ln)
if m:
t = m.groups()
if len(t) == 2:
m = reDefQSm.search(ln)
if m:
t = m.groups()
tt = re.findall(reDefQSm2, t[1])
if len(tt) == 1:
self.cfgValues[t[0]] = tt[0]
continue
elif len(tt) > 1:
self.cfgValues[t[0]] = tt
continue
m = reDefine.search(ln)
if m:
t = m.groups()
if len(t) == 2:
if t[0] == 'DC_EXTRUDER': print "raw value (%s)" % t[1]
self.cfgValues[t[0]] = t[1]
continue
m = reDefBool.search(ln)
if m:
t = m.groups()
if len(t) == 1:
self.cfgValues[t[0]] = True
return True
def onSaveConfig(self, evt):
path = self.configFile
if self.saveConfigFile(path):
dlg = wx.MessageDialog(self, "File %s successfully written." % path,
"Save successful", wx.OK + wx.ICON_INFORMATION)
else:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
def onSaveConfigAs(self, evt):
wildcard = "Printer configuration (printer.*.h)|printer.*.h"
dlg = wx.FileDialog(self, message = "Save as ...", defaultDir = self.dir,
defaultFile = "", wildcard = wildcard,
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
val = dlg.ShowModal()
if val != wx.ID_OK:
dlg.Destroy()
return
path = dlg.GetPath()
dlg.Destroy()
if self.saveConfigFile(path):
dlg = wx.MessageDialog(self, "File %s successfully written." % path,
"Save successful", wx.OK + wx.ICON_INFORMATION)
self.parent.setPrinterTabText("Printer <%s>" % os.path.basename(path))
else:
dlg = wx.MessageDialog(self, "Unable to write to file %s." % path,
"File error", wx.OK + wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
def saveConfigFile(self, path):
ext = os.path.splitext(os.path.basename(path))[1]
self.dir = os.path.dirname(path)
if ext == "":
path += ".h"
try:
fp = file(path, 'w')
except:
return False
self.configFile = path
values = {}
for pg in self.pages:
v1 = pg.getValues()
for k in v1.keys():
values[k] = v1[k]
for ln in self.cfgBuffer:
m = reDefineBL.match(ln)
if m:
t = m.groups()
if len(t) == 2:
if t[0] in values.keys() and values[t[0]] != "":
fp.write(defineValueFormat % (t[0], values[t[0]]))
else:
fp.write("//" + ln)
continue
m = reDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1:
if t[0] in values.keys() and values[t[0]]:
fp.write(defineBoolFormat % t[0])
else:
fp.write("//" + ln)
continue
m = reCommDefBL.match(ln)
if m:
t = m.groups()
if len(t) == 2:
if t[0] in values.keys() and values[t[0]] != "":
fp.write(defineValueFormat % (t[0], values[t[0]]))
else:
fp.write(ln)
continue
m = reCommDefBoolBL.match(ln)
if m:
t = m.groups()
if len(t) == 1:
if t[0] in values.keys() and values[t[0]]:
fp.write(defineBoolFormat % t[0])
else:
fp.write(ln)
continue
fp.write(ln)
fp.close()
return True

94
configtool/sensorlist.py Normal file
View File

@ -0,0 +1,94 @@
import wx
class SensorList(wx.ListCtrl):
def __init__(self, parent):
self.parent = parent
self.currentItem = None
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, size = (495 + 4, 100),
style = wx.LC_REPORT | wx.LC_VIRTUAL |
wx.LC_HRULES | wx.LC_VRULES)
self.valid = []
self.sensorList = []
self.InsertColumn(0, "Name")
self.InsertColumn(1, "Sensor Type")
self.InsertColumn(2, "Pin")
self.InsertColumn(3, "Additional")
self.SetColumnWidth(0, 55)
self.SetColumnWidth(1, 105)
self.SetColumnWidth(2, 55)
self.SetColumnWidth(3, 280)
self.SetItemCount(0)
self.attr2 = wx.ListItemAttr()
self.attr2.SetBackgroundColour("light blue")
self.attr3 = wx.ListItemAttr()
self.attr3.SetBackgroundColour("pink")
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected)
def updateList(self, sensorList):
self.sensorList = sensorList
self.valid = [True] * len(sensorList)
self.currentItem = None
self.parent.setItemSelected(None)
i = self.GetFirstSelected()
while i != -1:
self.Select(i, False)
i = self.GetFirstSelected()
self.SetItemCount(len(sensorList))
def setRowValidity(self, i, flag = False):
if i < 0 or i >= len(self.sensorList):
return
self.valid[i] = flag
self.Refresh()
def setTableValidity(self, flag = False):
for i in range(len(self.sensorList)):
self.setRowValidity(i, flag)
def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex
self.parent.setItemSelected(self.currentItem)
def OnItemDeselected(self, event):
self.currentItem = None
self.parent.setItemSelected(None)
def getColumnText(self, index, col):
item = self.GetItem(index, col)
return item.GetText()
def OnGetItemText(self, item, col):
if item < 0 or item >= len(self.sensorList):
return "Error - no sensors"
s = self.sensorList[item]
if col == 0:
return s[0]
elif col == 1:
return s[1]
elif col == 2:
return s[2]
elif len(s) == 3:
return ""
else:
return s[3]
def OnGetItemAttr(self, item):
if not self.valid[item]:
return self.attr3
if item % 2 == 1:
return self.attr2
else:
return None

190
configtool/sensorpage.py Normal file
View File

@ -0,0 +1,190 @@
import wx
from configtool.page import Page
from configtool.data import pinNames, BSIZESMALL
from sensorlist import SensorList
from addsensordlg import AddSensorDlg
class SensorsPage(wx.Panel, Page):
def __init__(self, parent, nb, idPg):
wx.Panel.__init__(self, nb, wx.ID_ANY)
Page.__init__(self)
self.parent = parent
self.id = idPg
self.sensorTypeKeys = ['TEMP_MAX6675', 'TEMP_THERMISTOR', 'TEMP_AD595',
'TEMP_PT100', 'TEMP_INTERCOM']
self.sensorType = {'TEMP_MAX6675': "TT_MAX6675",
'TEMP_THERMISTOR': "TT_THERMISTOR",
'TEMP_AD595': "TT_AD595",
'TEMP_PT100': "TT_PT100",
'TEMP_INTERCOM': "TT_INTERCOM"}
self.labels = {'TEMP_MAX6675': "MAX6675", 'TEMP_THERMISTOR': "Thermistor",
'TEMP_AD595': "AD595", 'TEMP_PT100': "PT100",
'TEMP_INTERCOM': "Intercom"}
self.validPins = pinNames
sz = wx.GridBagSizer()
sz.AddSpacer((30, 30), pos = (0, 0))
self.sensors = []
b = wx.StaticBox(self, wx.ID_ANY, "Sensor Types")
sbox = wx.StaticBoxSizer(b, wx.VERTICAL)
sbox.AddSpacer((5, 5))
for k in self.sensorTypeKeys:
cb = self.addCheckBox(k, self.onCheckBoxSensorType)
sbox.Add(cb, 1, wx.LEFT + wx.RIGHT, 10)
sbox.AddSpacer((5, 5))
sz.Add(sbox, pos = (1, 1), span = (5, 1))
self.lb = SensorList(self)
sz.Add(self.lb, pos = (7, 1), span = (1, 3))
bsz = wx.BoxSizer(wx.VERTICAL)
self.bAdd = wx.Button(self, wx.ID_ANY, "Add", size = BSIZESMALL)
self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd)
self.bAdd.Enable(False)
self.bAdd.SetToolTipString("Add a sensor to the configuration.")
bsz.Add(self.bAdd)
bsz.AddSpacer((10, 10))
self.bDelete = wx.Button(self, wx.ID_ANY, "Delete", size = BSIZESMALL)
self.bDelete.Enable(False)
self.Bind(wx.EVT_BUTTON, self.doDelete, self.bDelete)
bsz.Add(self.bDelete)
self.bDelete.SetToolTipString("Remove the selected temperature sensor "
"from the configuration.")
sz.Add(bsz, pos = (7, 4))
self.SetSizer(sz)
self.enableAll(False)
def onCheckBoxSensorType(self, evt):
self.assertModified(True)
ct = 0
for tt in self.sensorTypeKeys:
if self.checkBoxes[tt].IsChecked():
ct += 1
if ct == 0:
self.bAdd.Enable(False)
else:
self.bAdd.Enable(True)
evt.Skip()
def setItemSelected(self, n):
self.selection = n
if n is None:
self.bDelete.Enable(False)
else:
self.bDelete.Enable(True)
def doAdd(self, evt):
sl = []
for tt in self.sensorTypeKeys:
if self.checkBoxes[tt].IsChecked():
sl.append(self.sensorType[tt])
nm = []
for s in self.sensors:
nm.append(s[0])
dlg = AddSensorDlg(self, nm, sl, self.validPins)
rc = dlg.ShowModal()
if rc == wx.ID_OK:
tt = dlg.getValues()
dlg.Destroy()
if rc != wx.ID_OK:
return
self.sensors.append(tt)
self.lb.updateList(self.sensors)
self.validateTable()
self.limitSensorTypeControls()
def doDelete(self, evt):
if self.selection is None:
return
self.assertModified(True)
del self.sensors[self.selection]
self.lb.updateList(self.sensors)
self.validateTable()
self.limitSensorTypeControls()
def limitSensorTypeControls(self):
using = {}
for s in self.sensors:
using[s[1]] = True
for k in self.sensorTypeKeys:
self.checkBoxes[k].Enable(True)
for tt in using.keys():
if not tt.startswith("TT_"):
continue
k = "TEMP_" + tt[3:]
if k in self.sensorTypeKeys:
self.checkBoxes[k].Enable(False)
def insertValues(self, cfgValues):
self.enableAll(True)
for k in self.textControls.keys():
if k in cfgValues.keys():
self.textControls[k].SetValue(cfgValues[k])
else:
self.textControls[k].SetValue("")
for k in self.checkBoxes.keys():
if k in cfgValues.keys() and cfgValues[k]:
self.checkBoxes[k].SetValue(True)
else:
self.checkBoxes[k].SetValue(False)
ct = 0
for k in self.sensorTypeKeys:
if self.checkBoxes[k].IsChecked(): ct += 1
self.bAdd.Enable(ct != 0)
self.assertModified(False)
def setSensors(self, sensors):
self.sensors = sensors
self.lb.updateList(self.sensors)
self.validateTable()
self.limitSensorTypeControls()
def setCandidatePins(self, plist):
if not plist or len(plist) == 0:
self.validPins = pinNames
else:
self.validPins = plist
self.validateTable()
def validateTable(self):
self.lb.setTableValidity(True)
self.setFieldValidity('SENSORLIST', True)
for i in range(len(self.sensors)):
if self.sensors[i][2] not in self.validPins:
self.lb.setRowValidity(i, False)
self.setFieldValidity('SENSORLIST', False)
def setHelpText(self, ht):
Page.setHelpText(self, ht)
k = 'DEFINE_TEMP_SENSOR'
if k in ht.keys():
self.bAdd.SetToolTipString(ht[k])