Commit caf65063 authored by MagoKimbra's avatar MagoKimbra

Update 4.1.3

parent 7d3d03a2
......@@ -20,7 +20,7 @@
// User-specified version info of this build to display in [Pronterface, etc] terminal window during
// startup. Implementation of an idea by Prof Braino to inform user that any changes made to this
// build by the user have been successfully uploaded into firmware.
#define STRING_VERSION "4.1.2"
#define STRING_VERSION "4.1.3"
#define STRING_URL "reprap.org"
#define STRING_VERSION_CONFIG_H __DATE__ " " __TIME__ // build date and time
#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes.
......@@ -33,7 +33,7 @@
#define SERIAL_PORT 0
// This determines the communication speed of the printer
// 115200 - 250000
// 2400,9600,19200,38400,57600,115200,250000
#define BAUDRATE 115200
// This enables the serial port associated to the Bluetooth interface on AT90USB devices
......@@ -278,7 +278,6 @@
#define EXTRUDE_MINTEMP 170 // degC
#define EXTRUDE_MAXLENGTH (X_MAX_LENGTH+Y_MAX_LENGTH) //prevent extrusion of very large distances.
//===========================================================================
//======================== Thermal Runaway Protection =======================
//===========================================================================
......@@ -294,30 +293,16 @@
* The solution: Once the temperature reaches the target, start observing.
* If the temperature stays too far below the target (hysteresis) for too long,
* the firmware will halt as a safety precaution.
*
* Note that because the countdown starts only AFTER the temperature reaches
* the target, this will not catch a thermistor that is already disconnected
* when the print starts!
*
* To enable for all extruder heaters, uncomment the two defines below:
*/
// Parameters for all extruder heaters
#define THERMAL_RUNAWAY_PROTECTION_PERIOD 40 // in seconds
#define THERMAL_RUNAWAY_PROTECTION_HYSTERESIS 4 // in degree Celsius
// To enable for the bed heater, uncomment the two defines below:
// Parameters for the bed heater
#define THERMAL_RUNAWAY_PROTECTION_BED_PERIOD 20 // in seconds
#define THERMAL_RUNAWAY_PROTECTION_BED_HYSTERESIS 2 // in degree Celsius
//#define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders
//#define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed
//===========================================================================
//============================ User Interfaces ==============================
//===========================================================================
//==============================LCD and SD support=============================
//============================ LCD and SD support ===========================
// Choose ONE of these 3 charsets. This has to match your hardware. Ignored for full graphic display.
// To find out what type you have - compile with (test) - upload - click to get the menu. You'll see two typical lines from the upper half of the charset.
......@@ -402,13 +387,13 @@
// #define LCD_SCREEN_ROT_270
// SPLASH SCREEN duration in millisecond
#define SPLASH_SCREEN_DURATION 2000 // Millisecond
#define SPLASH_SCREEN_DURATION 5000 // Millisecond
/** Display Voltage Logic Selector on Alligator Board
0 = Voltage level 3.3V
1 = Voltage level 5V
*/
#define UI_VOLTAGE_LEVEL 0 // Set 5 o 3.3 V
#define UI_VOLTAGE_LEVEL 1 // Set 5 o 3.3 V
//============================== Languages UI =========================
......@@ -441,7 +426,7 @@
// M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to.
//define this to enable EEPROM support
//#define EEPROM_SETTINGS
//#define EEPROM_CHITCHAT
#define EEPROM_CHITCHAT
// to disable EEPROM Serial responses and decrease program space by ~1700 byte: comment this out:
// please keep turned on if you can.
//#define DISABLE_M503
......
......@@ -25,10 +25,12 @@
#define X_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Y_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z2_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define E_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define X_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Y_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z2_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z_PROBE_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
// ENDSTOP SETTINGS:
......
......@@ -25,10 +25,12 @@
#define X_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Y_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z2_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define E_MIN_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define X_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Y_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z2_MAX_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
#define Z_PROBE_ENDSTOP_LOGIC false // set to true to invert the logic of the endstop.
// ENDSTOP SETTINGS:
......
......@@ -7,7 +7,7 @@
// QHARLEYS Autobedlevelling has not been ported, because Marlin has now Bed-levelling
// You might need Z-Min endstop on SCARA-Printer to use this feature. Actually untested!
// Uncomment to use Morgan scara mode
#define scara_segments_per_second 200 //careful, two much will decrease performance...
#define SCARA_SEGMENTS_PER_SECOND 200 // If movement is choppy try lowering this value
// Length of inner support arm
#define Linkage_1 150 //mm Preprocessor cannot handle decimal point...
// Length of outer support arm Measure arm lengths precisely and enter
......
......@@ -23,24 +23,38 @@
#define BED_CHECK_INTERVAL 5000 //ms between checks in bang-bang control
/**
* Heating Sanity Check
*
* Whenever an M104 or M109 increases the target temperature this will wait for WATCH_TEMP_PERIOD milliseconds,
* and if the temperature hasn't increased by WATCH_TEMP_INCREASE degrees, the machine is halted, requiring a
* hard reset. This test restarts with any M104/M109, but only if the current temperature is below the target
* by at least 2 * WATCH_TEMP_INCREASE degrees celsius.
* Thermal Protection parameters
*/
#ifdef THERMAL_PROTECTION_HOTENDS
#define THERMAL_PROTECTION_PERIOD 40 // Seconds
#define THERMAL_PROTECTION_HYSTERESIS 4 // Degrees Celsius
/**
* Whenever an M104 or M109 increases the target temperature the firmware will wait for the
* WATCH_TEMP_PERIOD to transpire, and if the temperature hasn't increased by WATCH_TEMP_INCREASE
* degrees, the machine is halted, requiring a hard reset. This test restarts with any M104/M109,
* but only if the current temperature is far enough below the target for a reliable test.
*/
//#define WATCH_TEMP_PERIOD 16000 // 16 seconds
//#define WATCH_TEMP_INCREASE 4 // Heat up at least 4 degrees in 16 seconds
#define WATCH_TEMP_PERIOD 16 // Seconds
#define WATCH_TEMP_INCREASE 4 // Degrees Celsius
#endif
#ifdef THERMAL_PROTECTION_BED
#define THERMAL_PROTECTION_BED_PERIOD 20 // Seconds
#define THERMAL_PROTECTION_BED_HYSTERESIS 2 // Degrees Celsius
#endif
//automatic temperature: The hot end target temperature is calculated by all the buffered lines of gcode.
//The maximum buffered steps/sec of the extruder motor are called "se".
//You enter the autotemp mode by a M109 S<mintemp> B<maxtemp> F<factor>
// the target temperature is set to mintemp+factor*se[steps/sec] and limited by mintemp and maxtemp
// you exit the value by any M109 without F*
// Also, if the temperature is set to a value <mintemp, it is not changed by autotemp.
// on an Ultimaker, some initial testing worked with M109 S215 B260 F1 in the start.gcode
/**
* Automatic Temperature:
* The hotend target temperature is calculated by all the buffered lines of gcode.
* The maximum buffered steps/sec of the extruder motor is called "se".
* Start autotemp mode with M109 S<mintemp> B<maxtemp> F<factor>
* The target temperature is set to mintemp+factor*se[steps/sec] and is limited by
* mintemp and maxtemp. Turn this off by excuting M109 without F*
* Also, if the temperature is set to a value below mintemp, it will not be changed by autotemp.
* On an Ultimaker, some initial testing worked with M109 S215 B260 F1 in the start.gcode
*/
#define AUTOTEMP
#ifdef AUTOTEMP
#define AUTOTEMP_OLDWEIGHT 0.98
......@@ -50,11 +64,13 @@
//The M105 command return, besides traditional information, the ADC value read from temperature sensors.
//#define SHOW_TEMP_ADC_VALUES
//extruder idle oozing prevention
//if the extruder motor is idle for more than SECONDS, and the temperature over MINTEMP,
//some filament is retracted. The filament retracted is re-added before the next extrusion
//or when the target temperature is less than EXTRUDE_MINTEMP and the actual temperature
//is greater than IDLE_OOZING_MINTEMP and less than IDLE_OOZING_FEEDRATE
/**
* extruder idle oozing prevention
* if the extruder motor is idle for more than SECONDS, and the temperature over MINTEMP,
* some filament is retracted. The filament retracted is re-added before the next extrusion
* or when the target temperature is less than EXTRUDE_MINTEMP and the actual temperature
* is greater than IDLE_OOZING_MINTEMP and less than IDLE_OOZING_FEEDRATE
*/
//#define IDLE_OOZING_PREVENT
#define IDLE_OOZING_MINTEMP EXTRUDE_MINTEMP + 5
#define IDLE_OOZING_MAXTEMP IDLE_OOZING_MINTEMP + 5
......@@ -275,8 +291,11 @@
// be commented out otherwise
#define SDCARDDETECTINVERTED
#define SD_FINISHED_STEPPERRELEASE true //if sd support and the file is finished: disable steppers?
#define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place.
// Disable steppers when the file is done printing
#define SD_FINISHED_STEPPERRELEASE true
// Command to send. You may want to keep Z enabled so your bed stays in place.
#define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E"
#define SDCARD_RATHERRECENTFIRST //reverse file order of sd card menu display. Its sorted practically after the file system block order.
// if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that.
......@@ -284,7 +303,7 @@
//#define MENU_ADDAUTOSTART
// Show a progress bar on HD44780 LCDs for SD printing
#define LCD_PROGRESS_BAR
//#define LCD_PROGRESS_BAR
#ifdef LCD_PROGRESS_BAR
// Amount of time (ms) to show the bar
......
......@@ -266,8 +266,8 @@ VPATH += $(ARDUINO_INSTALL_DIR)/hardware/teensy/cores/teensy
endif
CXXSRC = WMath.cpp WString.cpp Print.cpp Marlin_main.cpp \
MarlinSerial.cpp Sd2Card.cpp SdBaseFile.cpp SdFatUtil.cpp \
SdFile.cpp SdVolume.cpp motion_control.cpp planner.cpp \
stepper.cpp temperature.cpp cardreader.cpp configuration_store.cpp \
SdFile.cpp SdVolume.cpp planner.cpp stepper.cpp \
temperature.cpp cardreader.cpp configuration_store.cpp \
watchdog.cpp SPI.cpp servo.cpp Tone.cpp ultralcd.cpp digipot_mcp4451.cpp \
vector_3.cpp qr_solve.cpp
ifeq ($(LIQUID_TWI2), 0)
......
......@@ -24,6 +24,10 @@
#include <avr/interrupt.h>
#include "Configuration.h"
#ifndef SANITYCHECK_H
#error Your Configuration.h and Configuration_adv.h files are outdated!
#endif
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
......@@ -134,14 +138,14 @@ void manage_inactivity(bool ignore_stepper_queue=false);
* X_HEAD and Y_HEAD is used for systems that don't have a 1:1 relationship between X_AXIS and X Head movement, like CoreXY bots.
*/
enum AxisEnum {X_AXIS=0, Y_AXIS=1, A_AXIS=0, B_AXIS=1, Z_AXIS=2, E_AXIS=3, X_HEAD=4, Y_HEAD=5};
enum EndstopEnum {X_MIN=0, Y_MIN=1, Z_MIN=2, Z_PROBE=3, X_MAX=4, Y_MAX=5, Z_MAX=6};
void enable_all_steppers();
void disable_all_steppers();
void FlushSerialRequestResend();
void ClearToSend();
void ok_to_send();
void get_coordinates();
#ifdef DELTA
float probe_bed(float x, float y);
void set_delta_constants();
......
This diff is collapsed.
......@@ -256,19 +256,23 @@
#define X_MIN_ENDSTOP_INVERTING !X_MIN_ENDSTOP_LOGIC
#define Y_MIN_ENDSTOP_INVERTING !Y_MIN_ENDSTOP_LOGIC
#define Z_MIN_ENDSTOP_INVERTING !Z_MIN_ENDSTOP_LOGIC
#define Z2_MIN_ENDSTOP_INVERTING !Z2_MIN_ENDSTOP_LOGIC
#define E_MIN_ENDSTOP_INVERTING !E_MIN_ENDSTOP_LOGIC
#define X_MAX_ENDSTOP_INVERTING !X_MAX_ENDSTOP_LOGIC
#define Y_MAX_ENDSTOP_INVERTING !Y_MAX_ENDSTOP_LOGIC
#define Z_MAX_ENDSTOP_INVERTING !Z_MAX_ENDSTOP_LOGIC
#define Z2_MAX_ENDSTOP_INVERTING !Z2_MAX_ENDSTOP_LOGIC
#define Z_PROBE_ENDSTOP_INVERTING !Z_PROBE_ENDSTOP_LOGIC
#else
#define X_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_LOGIC
#define Y_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_LOGIC
#define Z_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_LOGIC
#define Z2_MIN_ENDSTOP_INVERTING Z2_MIN_ENDSTOP_LOGIC
#define E_MIN_ENDSTOP_INVERTING E_MIN_ENDSTOP_LOGIC
#define X_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_LOGIC
#define Y_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_LOGIC
#define Z_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_LOGIC
#define Z2_MAX_ENDSTOP_INVERTING Z2_MAX_ENDSTOP_LOGIC
#define Z_PROBE_ENDSTOP_INVERTING Z_PROBE_ENDSTOP_LOGIC
#endif
......@@ -354,6 +358,13 @@
#define MAX_PROBE_Y (min(Y_MAX_POS, Y_MAX_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
#endif
/**
* Sled Options
*/
#ifdef Z_PROBE_SLED
#define Z_SAFE_HOMING
#endif
/**
* Servo Leveling
*/
......@@ -368,8 +379,8 @@
#define MAX_STEP_FREQUENCY 120000 // Max step frequency for Toshiba Stepper Controllers
#define DOUBLE_STEP_FREQUENCY MAX_STEP_FREQUENCY
#else
#define MAX_STEP_FREQUENCY 320000 // Max step frequency for the Due is approx. 330kHz
#define DOUBLE_STEP_FREQUENCY 100000 //96kHz is close to maximum for an Arduino Due
#define MAX_STEP_FREQUENCY 500000 // Max step frequency for the Due is approx. 330kHz
#define DOUBLE_STEP_FREQUENCY 120000 //96kHz is close to maximum for an Arduino Due
#endif
#else
#ifdef CONFIG_STEPPERS_TOSHIBA
......
......@@ -692,36 +692,32 @@ void Config_PrintSettings(bool forReplay) {
#endif //HOTENDS > 1
#ifdef DELTA
if (!forReplay) {
ECHO_LM(DB, "Endstop adjustement (mm):");
}
ECHO_SMV(DB, " M666 X", endstop_adj[X_AXIS] );
ECHO_MV(" Y", endstop_adj[Y_AXIS] );
ECHO_EMV(" Z", endstop_adj[Z_AXIS] );
if (!forReplay) {
ECHO_LM(DB, "Delta Geometry adjustment:");
}
ECHO_SMV(DB, " M666 A", tower_adj[0]);
ECHO_MV(" B", tower_adj[1]);
ECHO_MV(" C", tower_adj[2]);
ECHO_MV(" E", tower_adj[3]);
ECHO_MV(" F", tower_adj[4]);
ECHO_MV(" G", tower_adj[5]);
ECHO_SMV(DB, " M666 A", tower_adj[0], 3);
ECHO_MV(" B", tower_adj[1], 3);
ECHO_MV(" C", tower_adj[2], 3);
ECHO_MV(" I", tower_adj[3], 3);
ECHO_MV(" J", tower_adj[4], 3);
ECHO_MV(" K", tower_adj[5], 3);
ECHO_MV(" R", delta_radius);
ECHO_MV(" D", delta_diagonal_rod);
ECHO_MV(" H", max_pos[2]);
ECHO_EMV(" P", z_probe_offset[3]);
ECHO_EMV(" H", max_pos[2]);
if (!forReplay) {
ECHO_LM(DB, "Endstop Offsets:");
}
ECHO_SMV(DB, " M666 X", endstop_adj[X_AXIS]);
ECHO_MV(" Y", endstop_adj[Y_AXIS]);
ECHO_EMV(" Z", endstop_adj[Z_AXIS]);
if (!forReplay) {
ECHO_LM(DB, "Tower Positions:");
ECHO_LM(DB, "Z-Probe Offset:");
}
ECHO_SMV(DB, " Tower1 X:", delta_tower1_x);
ECHO_MV(" Y:", delta_tower1_y);
ECHO_MV(" Tower2 X:", delta_tower2_x);
ECHO_MV(" Y:", delta_tower2_y);
ECHO_MV(" Tower3 X:", delta_tower3_x);
ECHO_EMV(" Y:", delta_tower3_y);
ECHO_SMV(DB, " M666 P X", z_probe_offset[0]);
ECHO_MV(" Y", z_probe_offset[1]);
ECHO_EMV(" Z", z_probe_offset[2]);
#elif defined(Z_DUAL_ENDSTOPS)
if (!forReplay) {
......@@ -830,57 +826,7 @@ void Config_PrintSettings(bool forReplay) {
}
ECHO_LVM(DB, power_consumption_hour," W/h");
#endif
if (!forReplay) {
ECHO_LM(DB, "Power on time:");
}
char time[30];
int hours = printer_usage_seconds / 60 / 60, minutes = (printer_usage_seconds / 60) % 60;
sprintf_P(time, PSTR("%i " MSG_END_HOUR " %i " MSG_END_MINUTE), hours, minutes);
ECHO_LV(DB, time);
}
#endif //!DISABLE_M503
/**
* Lifetime on EEPROM
*
*/
void load_lifetime_stats() {
int i = LIFETIME_EEPROM_OFFSET;
char stored_magic[4];
char magic[4] = LIFETIME_MAGIC;
EEPROM_READ_VAR(i, stored_magic); // read magic
if (strncmp(magic, stored_magic, 3) != 0) {
#ifdef POWER_CONSUMPTION
power_consumption_hour = 0;
#endif
printer_usage_seconds = 0;
}
else {
EEPROM_READ_VAR(i, printer_usage_seconds);
#ifdef POWER_CONSUMPTION
EEPROM_READ_VAR(i, power_consumption_hour);
#endif
}
}
void save_lifetime_stats() {
int i = LIFETIME_EEPROM_OFFSET;
char magic[4] = "000";
EEPROM_WRITE_VAR(i, magic); // invalidate data first
EEPROM_WRITE_VAR(i, printer_usage_seconds);
#ifdef POWER_CONSUMPTION
EEPROM_WRITE_VAR(i, power_consumption_hour);
#endif
char magic2[4] = LIFETIME_MAGIC;
int j = LIFETIME_EEPROM_OFFSET;
EEPROM_WRITE_VAR(j, magic2); // validate data
config_last_update = millis();
}
......@@ -273,7 +273,6 @@ static void lcd_implementation_status_screen() {
u8g.drawFrame(42, 49 - TALL_FONT_CORRECTION, 10, 4);
u8g.drawPixel(50, 43 - TALL_FONT_CORRECTION);
// Progress bar frame
u8g.drawFrame(54, 49, 73, 4 - TALL_FONT_CORRECTION);
......@@ -346,19 +345,28 @@ static void lcd_implementation_status_screen() {
u8g.drawPixel(8,XYZ_BASELINE - 5);
u8g.drawPixel(8,XYZ_BASELINE - 3);
u8g.setPrintPos(10,XYZ_BASELINE);
if (axis_known_position[X_AXIS])
lcd_print(ftostr31ns(current_position[X_AXIS]));
else
lcd_printPGM(PSTR("---"));
u8g.setPrintPos(43,XYZ_BASELINE);
lcd_print('Y');
u8g.drawPixel(49,XYZ_BASELINE - 5);
u8g.drawPixel(49,XYZ_BASELINE - 3);
u8g.setPrintPos(51,XYZ_BASELINE);
if (axis_known_position[Y_AXIS])
lcd_print(ftostr31ns(current_position[Y_AXIS]));
else
lcd_printPGM(PSTR("---"));
u8g.setPrintPos(83,XYZ_BASELINE);
lcd_print('Z');
u8g.drawPixel(89,XYZ_BASELINE - 5);
u8g.drawPixel(89,XYZ_BASELINE - 3);
u8g.setPrintPos(91,XYZ_BASELINE);
lcd_print(ftostr31(current_position[Z_AXIS]));
if (axis_known_position[Z_AXIS])
lcd_print(ftostr32sp(current_position[Z_AXIS]));
else
lcd_printPGM(PSTR("---.--"));
u8g.setColorIndex(1); // black on white
// Feedrate
......
......@@ -82,6 +82,8 @@
#define E1_MS2_PIN -1
#define DIGIPOTSS_PIN -1
#define LCD_CONTRAST -1
#define Z2_MIN_PIN -1
#define Z2_MAX_PIN -1
/******************************************************************************
......
......@@ -44,6 +44,7 @@
//FAN pin
#define FAN_PIN ORIG_FAN_PIN
//============================================================================
......
......@@ -478,7 +478,7 @@ float junction_deviation = 0.1;
void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate, const uint8_t &extruder, const uint8_t &driver)
#else
void plan_buffer_line(const float &x, const float &y, const float &z, const float &e, float feed_rate, const uint8_t &extruder, const uint8_t &driver)
#endif //ENABLE_AUTO_BED_LEVELING
#endif // ENABLE_AUTO_BED_LEVELING
{
// Calculate the buffer head after we push this byte
int next_buffer_head = next_block_index(block_buffer_head);
......@@ -518,8 +518,7 @@ float junction_deviation = 0.1;
if (degHotend(extruder) < extrude_min_temp && !(debugLevel & DEBUG_DRYRUN)) {
position[E_AXIS] = target[E_AXIS]; //behave as if the move really took place, but ignore E part
de = 0; // no difference
ECHO_S(OK);
ECHO_EM(MSG_ERR_COLD_EXTRUDE_STOP);
ECHO_LM(ER, MSG_ERR_COLD_EXTRUDE_STOP);
}
}
......@@ -530,8 +529,7 @@ float junction_deviation = 0.1;
#endif
position[E_AXIS] = target[E_AXIS]; // Behave as if the move really took place, but ignore E part
de = 0; // no difference
ECHO_S(OK);
ECHO_EM(MSG_ERR_LONG_EXTRUDE_STOP);
ECHO_LM(ER, MSG_ERR_LONG_EXTRUDE_STOP);
#ifdef EASY_LOAD
}
allow_lengthy_extrude_once = false;
......
......@@ -114,6 +114,10 @@ FORCE_INLINE uint8_t movesplanned() { return BLOCK_MOD(block_buffer_head - block
void plan_set_e_position(const float &e);
//===========================================================================
//============================= public variables ============================
//===========================================================================
extern millis_t minsegmenttime;
extern float max_feedrate[3 + EXTRUDERS]; // set the max speeds
extern float max_retraction_feedrate[EXTRUDERS]; // set the max speeds for retraction
......
......@@ -304,4 +304,23 @@
#error HEATER_0_PIN not defined for this board
#endif
/**
* Warnings for old configurations
*/
#ifdef X_HOME_RETRACT_MM
#error [XYZ]_HOME_RETRACT_MM settings have been renamed [XYZ]_HOME_BUMP_MM
#endif
#if WATCH_TEMP_PERIOD > 500
#error WATCH_TEMP_PERIOD now uses seconds instead of milliseconds
#endif
#if !defined(THERMAL_PROTECTION_HOTENDS) && (defined(WATCH_TEMP_PERIOD) || defined(THERMAL_PROTECTION_PERIOD))
#error Thermal Runaway Protection for hotends must now be enabled with THERMAL_PROTECTION_HOTENDS
#endif
#if !defined(THERMAL_PROTECTION_BED) && defined(THERMAL_PROTECTION_BED_PERIOD)
#error Thermal Runaway Protection for the bed must now be enabled with THERMAL_PROTECTION_BED
#endif
#endif //SANITYCHECK_H
This diff is collapsed.
......@@ -70,10 +70,8 @@ void st_set_e_position(const long &e);
// Get current position in steps
long st_get_position(uint8_t axis);
#ifdef ENABLE_AUTO_BED_LEVELING
// Get current position in mm
float st_get_position_mm(uint8_t axis);
#endif //ENABLE_AUTO_BED_LEVELING
// The stepper subsystem goes to sleep when it runs out of things to execute. Call this
// to notify the subsystem that it is time to go to work.
......
......@@ -36,6 +36,7 @@
#if defined(PIDTEMPBED) || defined(PIDTEMP)
#define PID_dT ((OVERSAMPLENR * 14.0)/(F_CPU / 64.0 / 256.0))
#define RECI_PID_dT ( 1 / PID_dT )
#endif
//===========================================================================
......@@ -73,16 +74,14 @@ unsigned char soft_pwm_bed;
int current_raw_filwidth = 0; //Holds measured filament diameter - one extruder only
#endif
#define HAS_HEATER_THERMAL_PROTECTION (defined(THERMAL_RUNAWAY_PROTECTION_PERIOD) && THERMAL_RUNAWAY_PROTECTION_PERIOD > 0)
#define HAS_BED_THERMAL_PROTECTION (defined(THERMAL_RUNAWAY_PROTECTION_BED_PERIOD) && THERMAL_RUNAWAY_PROTECTION_BED_PERIOD > 0 && TEMP_SENSOR_BED != 0)
#if HAS_HEATER_THERMAL_PROTECTION || HAS_BED_THERMAL_PROTECTION
#if defined(THERMAL_PROTECTION_HOTENDS) || defined(THERMAL_PROTECTION_BED)
enum TRState { TRReset, TRInactive, TRFirstHeating, TRStable, TRRunaway };
void thermal_runaway_protection(TRState *state, millis_t *timer, float temperature, float target_temperature, int heater_id, int period_seconds, int hysteresis_degc);
#if HAS_HEATER_THERMAL_PROTECTION
#ifdef THERMAL_PROTECTION_HOTENDS
static TRState thermal_runaway_state_machine[4] = { TRReset, TRReset, TRReset, TRReset };
static millis_t thermal_runaway_timer[4]; // = {0,0,0,0};
#endif
#if HAS_BED_THERMAL_PROTECTION
#ifdef THERMAL_PROTECTION_BED
static TRState thermal_runaway_bed_state_machine = TRReset;
static millis_t thermal_runaway_bed_timer;
#endif
......@@ -95,6 +94,7 @@ unsigned char soft_pwm_bed;
//===========================================================================
//============================ private variables ============================
//===========================================================================
static volatile bool temp_meas_ready = false;
#ifdef PIDTEMP
......@@ -160,7 +160,7 @@ static float analog2temp(int raw, uint8_t e);
static float analog2tempBed(int raw);
static void updateTemperaturesFromRawValues();
#ifdef WATCH_TEMP_PERIOD
#ifdef THERMAL_PROTECTION_HOTENDS
int watch_target_temp[HOTENDS] = { 0 };
millis_t watch_heater_next_ms[HOTENDS] = { 0 };
#endif
......@@ -245,8 +245,8 @@ void PID_autotune(float temp, int hotend, int ncycles)
}
#endif
if (heating == true && input > temp) {
if (ms - t2 > 5000) {
if (heating && input > temp) {
if (ms > t2 + 5000) {
heating = false;
if (hotend < 0)
soft_pwm_bed = (bias - d) >> 1;
......@@ -257,8 +257,9 @@ void PID_autotune(float temp, int hotend, int ncycles)
max = temp;
}
}
if (heating == false && input < temp) {
if (ms - t1 > 5000) {
if (!heating && input < temp) {
if (ms > t1 + 5000) {
heating = true;
t2 = ms;
t_low = t2 - t1;
......@@ -588,15 +589,15 @@ void manage_heater() {
if (ct < max(HEATER_0_MINTEMP, 0.01)) min_temp_error(0);
#endif
#if defined(WATCH_TEMP_PERIOD) || !defined(PIDTEMPBED) || HAS_AUTO_FAN
#if defined(THERMAL_PROTECTION_HOTENDS) || !defined(PIDTEMPBED) || HAS_AUTO_FAN
millis_t ms = millis();
#endif
// Loop through all hotends
for (int e = 0; e < HOTENDS; e++) {
#if HAS_HEATER_THERMAL_PROTECTION
thermal_runaway_protection(&thermal_runaway_state_machine[e], &thermal_runaway_timer[e], current_temperature[e], target_temperature[e], e, THERMAL_RUNAWAY_PROTECTION_PERIOD, THERMAL_RUNAWAY_PROTECTION_HYSTERESIS);
#ifdef THERMAL_PROTECTION_HOTENDS
thermal_runaway_protection(&thermal_runaway_state_machine[e], &thermal_runaway_timer[e], current_temperature[e], target_temperature[e], e, THERMAL_PROTECTION_PERIOD, THERMAL_PROTECTION_HYSTERESIS);
#endif
float pid_output = get_pid_output(e);
......@@ -605,21 +606,23 @@ void manage_heater() {
soft_pwm[e] = current_temperature[e] > minttemp[e] && current_temperature[e] < maxttemp[e] ? (int)pid_output >> 1 : 0;
// Check if the temperature is failing to increase
#ifdef WATCH_TEMP_PERIOD
#ifdef THERMAL_PROTECTION_HOTENDS
// Is it time to check this extruder's heater?
if (watch_heater_next_ms[e] && ms > watch_heater_next_ms[e]) {
// Has it failed to increase enough?
if (degHotend(e) < watch_target_temp[e]) {
// Stop!
disable_all_heaters();
_temp_error(e, MSG_HEATING_FAILED, MSG_HEATING_FAILED_LCD);
_temp_error(e, PSTR(MSG_HEATING_FAILED), PSTR(MSG_HEATING_FAILED_LCD));
}
else {
// Only check once per M104/M109
watch_heater_next_ms[e] = 0;
// Start again if the target is still far off
start_watching_heater(e);
}
}
#endif // WATCH_TEMP_PERIOD
#endif // THERMAL_PROTECTION_HOTENDS
#ifdef TEMP_SENSOR_1_AS_REDUNDANT
if (fabs(current_temperature[0] - redundant_temperature) > MAX_REDUNDANT_TEMP_SENSOR_DIFF) {
......@@ -659,8 +662,8 @@ void manage_heater() {
#if TEMP_SENSOR_BED != 0
#if HAS_BED_THERMAL_PROTECTION
thermal_runaway_protection(&thermal_runaway_bed_state_machine, &thermal_runaway_bed_timer, current_temperature_bed, target_temperature_bed, -1, THERMAL_RUNAWAY_PROTECTION_BED_PERIOD, THERMAL_RUNAWAY_PROTECTION_BED_HYSTERESIS);
#ifdef THERMAL_PROTECTION_BED
thermal_runaway_protection(&thermal_runaway_bed_state_machine, &thermal_runaway_bed_timer, current_temperature_bed, target_temperature_bed, -1, THERMAL_PROTECTION_BED_PERIOD, THERMAL_PROTECTION_BED_HYSTERESIS);
#endif
#ifdef PIDTEMPBED
......@@ -777,7 +780,6 @@ static float analog2tempBed(int raw) {
static void updateTemperaturesFromRawValues() {
static millis_t last_update = millis();
millis_t temp_last_update = millis();
millis_t from_last_update = temp_last_update - last_update;
#ifdef HEATER_0_USES_MAX6675
current_temperature_raw[0] = read_max6675();
#endif
......@@ -792,6 +794,7 @@ static void updateTemperaturesFromRawValues() {
filament_width_meas = analog2widthFil();
#endif
#if HAS_POWER_CONSUMPTION_SENSOR
millis_t from_last_update = temp_last_update - last_update;
static float watt_overflow = 0.0;
power_consumption_meas = analog2power();
//MYSERIAL.println(analog2current(),3);
......@@ -802,13 +805,6 @@ static void updateTemperaturesFromRawValues() {
}
#endif
static unsigned int second_overflow = 0;
second_overflow += from_last_update;
if(second_overflow >= 1000) {
printer_usage_seconds++;
second_overflow -= 1000;
}
last_update = temp_last_update;
//Reset the watchdog after we know we have a temperature measurement.
watchdog_reset();
......@@ -1033,15 +1029,15 @@ void tp_init() {
#endif //BED_MAXTEMP
}
#ifdef WATCH_TEMP_PERIOD
#ifdef THERMAL_PROTECTION_HOTENDS
/**
* Start Heating Sanity Check for hotends that are below
* their target temperature by a configurable margin.
* This is called when the temperature is set. (M104, M109)
*/
void start_watching_heater(int e) {
millis_t ms = millis() + WATCH_TEMP_PERIOD;
if (degHotend(e) < degTargetHotend(e) - (WATCH_TEMP_INCREASE * 2)) {
millis_t ms = millis() + WATCH_TEMP_PERIOD * 1000;
if (degHotend(e) < degTargetHotend(e) - (WATCH_TEMP_INCREASE + TEMP_HYSTERESIS + 1)) {
watch_target_temp[e] = degHotend(e) + WATCH_TEMP_INCREASE;
watch_heater_next_ms[e] = ms;
}
......@@ -1050,7 +1046,7 @@ void tp_init() {
}
#endif
#if HAS_HEATER_THERMAL_PROTECTION || HAS_BED_THERMAL_PROTECTION
#if defined(THERMAL_PROTECTION_HOTENDS) || defined(THERMAL_PROTECTION_BED)
void thermal_runaway_protection(TRState *state, millis_t *timer, float temperature, float target_temperature, int heater_id, int period_seconds, int hysteresis_degc) {
......@@ -1109,7 +1105,7 @@ void tp_init() {
}
}
#endif // HAS_HEATER_THERMAL_PROTECTION || HAS_BED_THERMAL_PROTECTION
#endif // THERMAL_PROTECTION_HOTENDS || THERMAL_PROTECTION_BED
void disable_all_heaters() {
for (int i = 0; i < HOTENDS; i++) setTargetHotend(0, i);
......
......@@ -7,7 +7,7 @@
#include "stepper.h"
#include "configuration_store.h"
int8_t encoderDiff; /* encoderDiff is updated from interrupt context and added to encoderPosition every LCD update */
int8_t encoderDiff; // updated from interrupt context and added to encoderPosition every LCD update
bool encoderRateMultiplierEnabled;
int32_t lastEncoderMovementMillis;
......@@ -40,7 +40,7 @@ int gumPreheatFanSpeed;
/* !Configuration settings */
//Function pointer to menu functions.
// Function pointer to menu functions.
typedef void (*menuFunc_t)();
uint8_t lcd_status_message_level;
......@@ -239,11 +239,11 @@ static void lcd_status_screen();
} } while(0)
/** Used variables to keep track of the menu */
#ifndef REPRAPWORLD_KEYPAD
volatile uint8_t buttons; // Bits of the pressed buttons.
#else
volatile uint8_t buttons_reprapworld_keypad; // The reprapworld_keypad shift register values
volatile uint8_t buttons; //the last checked buttons in a bit array.
#ifdef REPRAPWORLD_KEYPAD
volatile uint8_t buttons_reprapworld_keypad; // to store the keypad shift register values
#endif
#ifdef LCD_HAS_SLOW_BUTTONS
volatile uint8_t slow_buttons; // Bits of the pressed buttons.
#endif
......@@ -1073,10 +1073,17 @@ static void lcd_control_menu() {
* "Control" > "Temperature" submenu
*
*/
static void lcd_control_temperature_menu() {
START_MENU(lcd_control_menu);
//
// ^ Control
//
MENU_ITEM(back, MSG_CONTROL, lcd_control_menu);
//
// Nozzle, Nozzle 2, Nozzle 3, Nozzle 4
//
#if TEMP_SENSOR_0 != 0
MENU_MULTIPLIER_ITEM_EDIT(int3, MSG_NOZZLE, &target_temperature[0], 0, HEATER_0_MAXTEMP + LCD_MAX_TEMP_OFFSET);
#endif
......@@ -1095,19 +1102,35 @@ static void lcd_control_temperature_menu() {
#endif //HOTENDS > 3
#endif //HOTENDS > 2
#endif //HOTENDS > 1
//
// Bed
//
#if TEMP_SENSOR_BED != 0
MENU_MULTIPLIER_ITEM_EDIT(int3, MSG_BED, &target_temperature_bed, 0, BED_MAXTEMP + LCD_MAX_TEMP_OFFSET);
#endif
//
// Fan Speed
//
MENU_MULTIPLIER_ITEM_EDIT(int3, MSG_FAN_SPEED, &fanSpeed, 0, 255);
#ifdef IDLE_OOZING_PREVENT
MENU_ITEM_EDIT(bool, MSG_IDLEOOZING, &idleoozing_enabled);
#endif
//
// Autotemp, Min, Max, Fact
//
#if defined(AUTOTEMP) && (TEMP_SENSOR_0 != 0)
MENU_ITEM_EDIT(bool, MSG_AUTOTEMP, &autotemp_enabled);
MENU_ITEM_EDIT(float3, MSG_MIN, &autotemp_min, 0, HEATER_0_MAXTEMP + LCD_MAX_TEMP_OFFSET);
MENU_ITEM_EDIT(float3, MSG_MAX, &autotemp_max, 0, HEATER_0_MAXTEMP + LCD_MAX_TEMP_OFFSET);
MENU_ITEM_EDIT(float32, MSG_FACTOR, &autotemp_factor, 0.0, 1.0);
#endif
//
// PID-P, PID-I, PID-D
//
#ifdef PIDTEMP
// set up temp variables - undo the default scaling
raw_Ki = unscalePID_i(PID_PARAM(Ki,0));
......@@ -1144,8 +1167,20 @@ static void lcd_control_temperature_menu() {
#endif //HOTENDS > 2
#endif //HOTENDS > 1
#endif //PIDTEMP
//
// Preheat PLA conf
//
MENU_ITEM(submenu, MSG_PREHEAT_PLA_SETTINGS, lcd_control_temperature_preheat_pla_settings_menu);
//
// Preheat ABS conf
//
MENU_ITEM(submenu, MSG_PREHEAT_ABS_SETTINGS, lcd_control_temperature_preheat_abs_settings_menu);
//
// Preheat GUM conf
//
MENU_ITEM(submenu, MSG_PREHEAT_GUM_SETTINGS, lcd_control_temperature_preheat_gum_settings_menu);
END_MENU();
}
......@@ -1155,7 +1190,6 @@ static void lcd_control_temperature_menu() {
* "Temperature" > "Preheat PLA conf" submenu
*
*/
static void lcd_control_temperature_preheat_pla_settings_menu() {
START_MENU(lcd_control_temperature_menu);
MENU_ITEM(back, MSG_TEMPERATURE, lcd_control_temperature_menu);
......@@ -1177,7 +1211,6 @@ static void lcd_control_temperature_preheat_pla_settings_menu() {
* "Temperature" > "Preheat ABS conf" submenu
*
*/
static void lcd_control_temperature_preheat_abs_settings_menu() {
START_MENU(lcd_control_temperature_menu);
MENU_ITEM(back, MSG_TEMPERATURE, lcd_control_temperature_menu);
......@@ -1220,7 +1253,6 @@ static void lcd_control_temperature_preheat_gum_settings_menu() {
* "Control" > "Motion" submenu
*
*/
static void lcd_control_motion_menu() {
START_MENU(lcd_control_menu);
MENU_ITEM(back, MSG_CONTROL, lcd_control_menu);
......@@ -1271,7 +1303,6 @@ static void lcd_control_motion_menu() {
* "Control" > "Filament" submenu
*
*/
static void lcd_control_volumetric_menu() {
START_MENU(lcd_control_menu);
MENU_ITEM(back, MSG_CONTROL, lcd_control_menu);
......@@ -1299,7 +1330,6 @@ static void lcd_control_volumetric_menu() {
* "Control" > "Contrast" submenu
*
*/
#ifdef HAS_LCD_CONTRAST
static void lcd_set_contrast() {
if (encoderPosition != 0) {
......@@ -1319,7 +1349,6 @@ static void lcd_control_volumetric_menu() {
* "Control" > "Retract" submenu
*
*/
#ifdef FWRETRACT
static void lcd_control_retract_menu() {
START_MENU(lcd_control_menu);
......@@ -1357,7 +1386,6 @@ static void lcd_sd_updir() {
* "Print from SD" submenu
*
*/
void lcd_sdcard_menu() {
if (lcdDrawUpdate == 0 && LCD_CLICKED == 0) return; // nothing to do (so don't thrash the SD card)
uint16_t fileCnt = card.getnrfilenames();
......@@ -1483,7 +1511,7 @@ menu_edit_type(unsigned long, long5, ftostr5, 0.01)
static void reprapworld_keypad_move_home() {
enqueuecommands_P((PSTR("G28"))); // move all axis home
}
#endif //REPRAPWORLD_KEYPAD
#endif // REPRAPWORLD_KEYPAD
/**
......@@ -1503,7 +1531,7 @@ void lcd_quick_feedback() {
#define LCD_FEEDBACK_FREQUENCY_DURATION_MS (1000/6)
#endif
lcd_buzz(LCD_FEEDBACK_FREQUENCY_DURATION_MS, LCD_FEEDBACK_FREQUENCY_HZ);
#elif defined(BEEPER) && BEEPER > -1
#elif defined(BEEPER) && BEEPER >= 0
#ifndef LCD_FEEDBACK_FREQUENCY_HZ
#define LCD_FEEDBACK_FREQUENCY_HZ 5000
#endif
......@@ -1753,7 +1781,7 @@ void lcd_update() {
lcd_return_to_status();
lcdDrawUpdate = 2;
}
#endif //ULTIPANEL
#endif // ULTIPANEL
if (lcdDrawUpdate == 2) lcd_implementation_clear();
if (lcdDrawUpdate) lcdDrawUpdate--;
......@@ -1931,13 +1959,12 @@ void lcd_reset_alert_level() { lcd_status_message_level = 0; }
void lcd_buzz(long duration, uint16_t freq) {
if (freq > 0) {
#if BEEPER > 0
#ifdef LCD_USE_I2C_BUZZER
lcd.buzz(duration, freq);
#elif defined(BEEPER) && BEEPER >= 0
SET_OUTPUT(BEEPER);
tone(BEEPER, freq);
tone(BEEPER, freq, duration);
delay(duration);
noTone(BEEPER);
#elif defined(LCD_USE_I2C_BUZZER)
lcd.buzz(duration, freq);
#else
delay(duration);
#endif
......
......@@ -118,7 +118,7 @@
FORCE_INLINE void lcd_setstatuspgm(const char* message, const uint8_t level=0) {}
FORCE_INLINE void lcd_buttons_update() {}
FORCE_INLINE void lcd_reset_alert_level() {}
FORCE_INLINE void lcd_buzz(long duration,uint16_t freq) {}
FORCE_INLINE void lcd_buzz(long duration, uint16_t freq) {}
FORCE_INLINE bool lcd_detected(void) { return true; }
#define LCD_MESSAGEPGM(x) do{}while(0)
......
......@@ -16,8 +16,8 @@
//#define PAGE_HEIGHT 16 //256 byte framebuffer
#define PAGE_HEIGHT 32 //512 byte framebuffer
#define WIDTH 128
#define HEIGHT 64
#define LCD_PIXEL_WIDTH 128
#define LCD_PIXEL_HEIGHT 64
#include <U8glib.h>
......@@ -64,12 +64,12 @@ uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, vo
ST7920_WRITE_BYTE(0x01); //clear CGRAM ram
u8g_Delay(15); //delay for CGRAM clear
ST7920_WRITE_BYTE(0x3E); //extended mode + GDRAM active
for(y=0;y<HEIGHT/2;y++) //clear GDRAM
for(y = 0; y < LCD_PIXEL_HEIGHT / 2; y++) //clear GDRAM
{
ST7920_WRITE_BYTE(0x80|y); //set y
ST7920_WRITE_BYTE(0x80); //set x = 0
ST7920_SET_DAT();
for(i=0;i<2*WIDTH/8;i++) //2x width clears both segments
for(i = 0; i < 2 * LCD_PIXEL_WIDTH / 8; i++) //2x width clears both segments
ST7920_WRITE_BYTE(0);
ST7920_SET_CMD();
}
......@@ -103,7 +103,7 @@ uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, vo
}
ST7920_SET_DAT();
ST7920_WRITE_BYTES(ptr,WIDTH/8); //ptr is incremented inside of macro
ST7920_WRITE_BYTES(ptr,LCD_PIXEL_WIDTH/8); //ptr is incremented inside of macro
y++;
}
ST7920_NCS();
......@@ -119,8 +119,8 @@ uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, vo
#endif
}
uint8_t u8g_dev_st7920_128x64_rrd_buf[WIDTH*(PAGE_HEIGHT/8)] U8G_NOCOMMON;
u8g_pb_t u8g_dev_st7920_128x64_rrd_pb = {{PAGE_HEIGHT,HEIGHT,0,0,0},WIDTH,u8g_dev_st7920_128x64_rrd_buf};
uint8_t u8g_dev_st7920_128x64_rrd_buf[LCD_PIXEL_WIDTH*(PAGE_HEIGHT/8)] U8G_NOCOMMON;
u8g_pb_t u8g_dev_st7920_128x64_rrd_pb = {{PAGE_HEIGHT,LCD_PIXEL_HEIGHT,0,0,0},LCD_PIXEL_WIDTH,u8g_dev_st7920_128x64_rrd_buf};
u8g_dev_t u8g_dev_st7920_128x64_rrd_sw_spi = {u8g_dev_rrd_st7920_128x64_fn,&u8g_dev_st7920_128x64_rrd_pb,&u8g_com_null_fn};
class U8GLIB_ST7920_128X64_RRD : public U8GLIB
......
......@@ -7,11 +7,11 @@
#include "ultralcd.h"
//===========================================================================
//=============================private variables ============================
//============================ private variables ============================
//===========================================================================
//===========================================================================
//=============================functinos ============================
//================================ functions ================================
//===========================================================================
......@@ -36,7 +36,7 @@ void watchdog_reset()
}
//===========================================================================
//=============================ISR ============================
//=================================== ISR ===================================
//===========================================================================
//Watchdog timer interrupt, called if main program blocks >1sec and manual reset is enabled.
......@@ -46,7 +46,7 @@ ISR(WDT_vect)
//TODO: This message gets overwritten by the kill() call
LCD_ALERTMESSAGEPGM("ERR:Please Reset");//16 characters so it fits on a 16x2 display
lcd_update();
ECHO_LM(MSG_WATCHDOG_RESET);
ECHO_LM(ER, MSG_WATCHDOG_RESET);
kill(); //kill blocks
while(1); //wait for user or serial reset
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment