#include "SlowTicker.h"
#include "Pauser.h"
#include "ConfigValue.h"
-#include "TemperatureControl.h"
#include "PID_Autotuner.h"
// Temp sensor implementations:
#define preset1_checksum CHECKSUM("preset1")
#define preset2_checksum CHECKSUM("preset2")
-TemperatureControl::TemperatureControl(uint16_t name) :
- sensor(nullptr), name_checksum(name), waiting(false), min_temp_violated(false)
+TemperatureControl::TemperatureControl(uint16_t name, int index)
{
+ name_checksum= name;
+ pool_index= index;
+ waiting= false;
+ min_temp_violated= false;
+ sensor= nullptr;
+ readonly= false;
}
TemperatureControl::~TemperatureControl()
this->target_temperature = UNDEFINED;
// Settings
- this->on_config_reload(this);
+ this->load_config();
// Register for events
- register_for_event(ON_CONFIG_RELOAD);
- this->register_for_event(ON_GCODE_EXECUTE);
this->register_for_event(ON_GCODE_RECEIVED);
- this->register_for_event(ON_MAIN_LOOP);
- this->register_for_event(ON_SECOND_TICK);
this->register_for_event(ON_GET_PUBLIC_DATA);
- this->register_for_event(ON_SET_PUBLIC_DATA);
+
+ if(!this->readonly) {
+ this->register_for_event(ON_GCODE_EXECUTE);
+ this->register_for_event(ON_SECOND_TICK);
+ this->register_for_event(ON_MAIN_LOOP);
+ this->register_for_event(ON_SET_PUBLIC_DATA);
+ this->register_for_event(ON_HALT);
+ }
+}
+
+void TemperatureControl::on_halt(void *arg)
+{
+ // turn off heater
+ this->o = 0;
+ this->heater_pin.set(0);
+ this->target_temperature = UNDEFINED;
}
void TemperatureControl::on_main_loop(void *argument)
}
// Get configuration from the config file
-void TemperatureControl::on_config_reload(void *argument)
+void TemperatureControl::load_config()
{
// General config
this->designator = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, designator_checksum)->by_default(string("T"))->as_string();
+ // Heater pin
+ this->heater_pin.from_string( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->by_default("nc")->as_string());
+ if(this->heater_pin.connected()){
+ this->readonly= false;
+ this->heater_pin.as_output();
+
+ } else {
+ this->readonly= true;
+ }
+
// For backward compatibility, default to a thermistor sensor.
std::string sensor_type = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, sensor_checksum)->by_default("thermistor")->as_string();
}
sensor->UpdateConfig(temperature_control_checksum, this->name_checksum);
- this->preset1 = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, preset1_checksum)->by_default(0)->as_number();
- this->preset2 = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, preset2_checksum)->by_default(0)->as_number();
+ this->preset1 = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, preset1_checksum)->by_default(0)->as_number();
+ this->preset2 = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, preset2_checksum)->by_default(0)->as_number();
// sigma-delta output modulation
this->o = 0;
- // Heater pin
- this->heater_pin.from_string( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_string())->as_output();
- this->heater_pin.max_pwm( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, max_pwm_checksum)->by_default(255)->as_number() );
-
- this->heater_pin.set(0);
-
- // used to enable bang bang control of heater
- this->use_bangbang = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, bang_bang_checksum)->by_default(false)->as_bool();
- this->hysteresis = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, hysteresis_checksum)->by_default(2)->as_number();
-
- set_low_on_debug(heater_pin.port_number, heater_pin.pin);
+ if(!this->readonly) {
+ // used to enable bang bang control of heater
+ this->use_bangbang = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, bang_bang_checksum)->by_default(false)->as_bool();
+ this->hysteresis = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, hysteresis_checksum)->by_default(2)->as_number();
+ this->heater_pin.max_pwm( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, max_pwm_checksum)->by_default(255)->as_number() );
+ this->heater_pin.set(0);
+ set_low_on_debug(heater_pin.port_number, heater_pin.pin);
+ // activate SD-DAC timer
+ THEKERNEL->slow_ticker->attach( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, pwm_frequency_checksum)->by_default(2000)->as_number(), &heater_pin, &Pwm::on_tick);
+ }
- // activate SD-DAC timer
- THEKERNEL->slow_ticker->attach( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, pwm_frequency_checksum)->by_default(2000)->as_number() , &heater_pin, &Pwm::on_tick);
// reading tick
THEKERNEL->slow_ticker->attach( this->readings_per_second, this, &TemperatureControl::thermistor_read_tick );
setPIDp( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, p_factor_checksum)->by_default(10 )->as_number() );
setPIDi( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, i_factor_checksum)->by_default(0.3f)->as_number() );
setPIDd( THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, d_factor_checksum)->by_default(200)->as_number() );
- // set to the same as max_pwm by default
- this->i_max = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, i_max_checksum )->by_default(this->heater_pin.max_pwm())->as_number();
+
+ if(!this->readonly) {
+ // set to the same as max_pwm by default
+ this->i_max = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, i_max_checksum )->by_default(this->heater_pin.max_pwm())->as_number();
+ }
+
this->iTerm = 0.0;
this->lastInput = -1.0;
this->last_reading = 0.0;
{
Gcode *gcode = static_cast<Gcode *>(argument);
if (gcode->has_m) {
- // Get temperature
- this->active = true;
-
- // this is safe as old configs the toolmanager will not be running anyway as well as in single extruder configs
- void *returned_data;
- bool ok = THEKERNEL->public_data->get_value( tool_manager_checksum, &returned_data );
- if (ok) {
- struct pad_toolmanager toolmanager = *static_cast<struct pad_toolmanager *>(returned_data);
- this->active = toolmanager.current_tool_name == this->name_checksum;
- }
if( gcode->m == this->get_m_code ) {
char buf[32]; // should be big enough for any status
int n = snprintf(buf, sizeof(buf), "%s:%3.1f /%3.1f @%d ", this->designator.c_str(), this->get_temperature(), ((target_temperature == UNDEFINED) ? 0.0 : target_temperature), this->o);
gcode->txt_after_ok.append(buf, n);
gcode->mark_as_taken();
+ return;
+ }
- } else if (gcode->m == 301) {
+ // readonly sensors don't handle the rest
+ if(this->readonly) return;
+
+ if (gcode->m == 301) {
gcode->mark_as_taken();
if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index)) {
if (gcode->has_letter('P'))
//gcode->stream->printf("%s(S%d): Pf:%g If:%g Df:%g X(I_max):%g Pv:%g Iv:%g Dv:%g O:%d\n", this->designator.c_str(), this->pool_index, this->p_factor, this->i_factor/this->PIDdt, this->d_factor*this->PIDdt, this->i_max, this->p, this->i, this->d, o);
gcode->stream->printf("%s(S%d): Pf:%g If:%g Df:%g X(I_max):%g O:%d\n", this->designator.c_str(), this->pool_index, this->p_factor, this->i_factor / this->PIDdt, this->d_factor * this->PIDdt, this->i_max, o);
- } else if (gcode->m == 303) {
- if (gcode->has_letter('E') && (gcode->get_value('E') == this->pool_index)) {
- gcode->mark_as_taken();
- float target = 150.0;
- if (gcode->has_letter('S')) {
- target = gcode->get_value('S');
- gcode->stream->printf("Target: %5.1f\n", target);
- }
- int ncycles = 8;
- if (gcode->has_letter('C')) {
- ncycles = gcode->get_value('C');
- }
- gcode->stream->printf("Start PID tune, command is %s\n", gcode->command.c_str());
- this->pool->PIDtuner->begin(this, target, gcode->stream, ncycles);
- }
-
} else if (gcode->m == 500 || gcode->m == 503) { // M500 saves some volatile settings to config override file, M503 just prints the settings
gcode->stream->printf(";PID settings:\nM301 S%d P%1.4f I%1.4f D%1.4f\n", this->pool_index, this->p_factor, this->i_factor / this->PIDdt, this->d_factor * this->PIDdt);
gcode->mark_as_taken();
- } else if( ( gcode->m == this->set_m_code || gcode->m == this->set_and_wait_m_code ) && gcode->has_letter('S') && this->active ) {
- // Attach gcodes to the last block for on_gcode_execute
- THEKERNEL->conveyor->append_gcode(gcode);
+ } else if( ( gcode->m == this->set_m_code || gcode->m == this->set_and_wait_m_code ) && gcode->has_letter('S')) {
+ // this only gets handled if it is not controlle dby the tool manager or is active in the toolmanager
+ this->active = true;
+
+ // this is safe as old configs as well as single extruder configs the toolmanager will not be running so will return false
+ // this will also ignore anything that the tool manager is not controlling and return false, otherwise it returns the active tool
+ void *returned_data;
+ bool ok = PublicData::get_value( tool_manager_checksum, is_active_tool_checksum, this->name_checksum, &returned_data );
+ if (ok) {
+ uint16_t active_tool_name = *static_cast<uint16_t *>(returned_data);
+ this->active = (active_tool_name == this->name_checksum);
+ }
+
+ if(this->active) {
+ // Attach gcodes to the last block for on_gcode_execute
+ THEKERNEL->conveyor->append_gcode(gcode);
- // push an empty block if we have to wait, so the Planner can get things right, and we can prevent subsequent non-move gcodes from executing
- if (gcode->m == this->set_and_wait_m_code)
- // ensure that no subsequent gcodes get executed with our M109 or similar
- THEKERNEL->conveyor->queue_head_block();
+ // push an empty block if we have to wait, so the Planner can get things right, and we can prevent subsequent non-move gcodes from executing
+ if (gcode->m == this->set_and_wait_m_code) {
+ // ensure that no subsequent gcodes get executed with our M109 or similar
+ THEKERNEL->conveyor->queue_head_block();
+ }
+ }
}
}
}
} else {
this->set_desired_temperature(v);
- if( gcode->m == this->set_and_wait_m_code) {
+ if( gcode->m == this->set_and_wait_m_code && !this->waiting) {
THEKERNEL->pauser->take();
this->waiting = true;
}
if(!pdr->starts_with(temperature_control_checksum)) return;
- if(!pdr->second_element_is(this->name_checksum)) return; // will be bed or hotend
+ if(pdr->second_element_is(pool_index_checksum)) {
+ // asking for our instance pointer if we have this pool_index
+ if(pdr->third_element_is(this->pool_index)) {
+ static void *return_data;
+ return_data = this;
+ pdr->set_data_ptr(&return_data);
+ pdr->set_taken();
+ }
+ return;
+
+ }else if(!pdr->second_element_is(this->name_checksum)) return;
// ok this is targeted at us, so send back the requested data
if(pdr->third_element_is(current_temperature_checksum)) {
pdr->set_data_ptr(&this->public_data_return);
pdr->set_taken();
}
+
}
void TemperatureControl::on_set_public_data(void *argument)
if(!pdr->starts_with(temperature_control_checksum)) return;
- if(!pdr->second_element_is(this->name_checksum)) return; // will be bed or hotend
+ if(!pdr->second_element_is(this->name_checksum)) return;
// ok this is targeted at us, so set the temp
float t = *static_cast<float *>(pdr->get_data_ptr());
uint32_t TemperatureControl::thermistor_read_tick(uint32_t dummy)
{
float temperature = sensor->get_temperature();
+ if(this->readonly) {
+ last_reading = temperature;
+ return 0;
+ }
if (target_temperature > 0) {
if (isinf(temperature)) {