void TemperatureControl::on_gcode_received(void *argument) { Gcode *gcode = static_cast<Gcode *>(argument); if (gcode->has_m) { 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 <= 0) ? 0.0 : target_temperature), this->o); gcode->txt_after_ok.append(buf, n); return; } if (gcode->m == 305) { // set or get sensor settings if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index)) { TempSensor::sensor_options_t args= gcode->get_args(); args.erase('S'); // don't include the S if(args.size() > 0) { // set the new options if(sensor->set_optional(args)) { this->sensor_settings= true; }else{ gcode->stream->printf("Unable to properly set sensor settings, make sure you specify all required values\n"); } }else{ // don't override this->sensor_settings= false; } }else if(!gcode->has_letter('S')) { gcode->stream->printf("%s(S%d): using %s\n", this->designator.c_str(), this->pool_index, this->readonly?"Readonly" : this->use_bangbang?"Bangbang":"PID"); sensor->get_raw(); TempSensor::sensor_options_t options; if(sensor->get_optional(options)) { for(auto &i : options) { // foreach optional value gcode->stream->printf("%s(S%d): %c %1.18f\n", this->designator.c_str(), this->pool_index, i.first, i.second); } } } return; } // readonly sensors don't handle the rest if(this->readonly) return; if (gcode->m == 143) { if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index)) { if(gcode->has_letter('P')) { max_temp= gcode->get_value('P'); } else { gcode->stream->printf("Nothing set NOTE Usage is M143 S0 P300 where <S> is the hotend index and <P> is the maximum temp to set\n"); } }else if(gcode->get_num_args() == 0) { gcode->stream->printf("Maximum temperature for %s(%d) is %f°C\n", this->designator.c_str(), this->pool_index, max_temp); } } else if (gcode->m == 301) { if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index)) { if (gcode->has_letter('P')) setPIDp( gcode->get_value('P') ); if (gcode->has_letter('I')) setPIDi( gcode->get_value('I') ); if (gcode->has_letter('D')) setPIDd( gcode->get_value('D') ); if (gcode->has_letter('X')) this->i_max = gcode->get_value('X'); if (gcode->has_letter('Y')) this->heater_pin.max_pwm(gcode->get_value('Y')); }else if(!gcode->has_letter('S')) { gcode->stream->printf("%s(S%d): Pf:%g If:%g Df:%g X(I_max):%g max pwm: %d 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->heater_pin.max_pwm(), o); } } 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 X%1.4f Y%d\n", this->pool_index, this->p_factor, this->i_factor / this->PIDdt, this->d_factor * this->PIDdt, this->i_max, this->heater_pin.max_pwm()); gcode->stream->printf(";Max temperature setting:\nM143 S%d P%1.4f\n", this->pool_index, this->max_temp); if(this->sensor_settings) { // get or save any sensor specific optional values TempSensor::sensor_options_t options; if(sensor->get_optional(options) && !options.empty()) { gcode->stream->printf(";Optional temp sensor specific settings:\nM305 S%d", this->pool_index); for(auto &i : options) { gcode->stream->printf(" %c%1.18f", i.first, i.second); } gcode->stream->printf("\n"); } } } 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 controlled by 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) { // required so temp change happens in order THEKERNEL->conveyor->wait_for_empty_queue(); float v = gcode->get_value('S'); if (v == 0.0) { this->target_temperature = UNDEFINED; this->heater_pin.set((this->o = 0)); } else { this->set_desired_temperature(v); // wait for temp to be reached, no more gcodes will be fetched until this is complete if( gcode->m == this->set_and_wait_m_code) { if(isinf(get_temperature()) && isinf(sensor->get_temperature())) { THEKERNEL->streams->printf("Temperature reading is unreliable on %s HALT asserted - reset or M999 required\n", designator.c_str()); THEKERNEL->call_event(ON_HALT, nullptr); return; } this->waiting = true; // on_second_tick will announce temps while ( get_temperature() < target_temperature ) { THEKERNEL->call_event(ON_IDLE, this); // check if ON_HALT was called (usually by kill button) if(THEKERNEL->is_halted() || this->target_temperature == UNDEFINED) { THEKERNEL->streams->printf("Wait on temperature aborted by kill\n"); break; } } this->waiting = false; } } } } } }
//A GCode has been received //See if the current Gcode line has some orders for us void Robot::on_gcode_received(void *argument) { Gcode *gcode = static_cast<Gcode *>(argument); this->motion_mode = -1; //G-letter Gcodes are mostly what the Robot module is interrested in, other modules also catch the gcode event and do stuff accordingly if( gcode->has_g) { switch( gcode->g ) { case 0: this->motion_mode = MOTION_MODE_SEEK; gcode->mark_as_taken(); break; case 1: this->motion_mode = MOTION_MODE_LINEAR; gcode->mark_as_taken(); break; case 2: this->motion_mode = MOTION_MODE_CW_ARC; gcode->mark_as_taken(); break; case 3: this->motion_mode = MOTION_MODE_CCW_ARC; gcode->mark_as_taken(); break; case 4: { uint32_t delay_ms= 0; if (gcode->has_letter('P')) { delay_ms= gcode->get_int('P'); } if (gcode->has_letter('S')) { delay_ms += gcode->get_int('S') * 1000; } if (delay_ms > 0){ // drain queue THEKERNEL->conveyor->wait_for_empty_queue(); // wait for specified time uint32_t start= us_ticker_read(); // mbed call while ((us_ticker_read() - start) < delay_ms*1000) { THEKERNEL->call_event(ON_IDLE, this); } } gcode->mark_as_taken(); } break; case 17: this->select_plane(X_AXIS, Y_AXIS, Z_AXIS); gcode->mark_as_taken(); break; case 18: this->select_plane(X_AXIS, Z_AXIS, Y_AXIS); gcode->mark_as_taken(); break; case 19: this->select_plane(Y_AXIS, Z_AXIS, X_AXIS); gcode->mark_as_taken(); break; case 20: this->inch_mode = true; gcode->mark_as_taken(); break; case 21: this->inch_mode = false; gcode->mark_as_taken(); break; case 90: this->absolute_mode = true; gcode->mark_as_taken(); break; case 91: this->absolute_mode = false; gcode->mark_as_taken(); break; case 92: { if(gcode->get_num_args() == 0) { for (int i = X_AXIS; i <= Z_AXIS; ++i) { reset_axis_position(0, i); } } else { for (char letter = 'X'; letter <= 'Z'; letter++) { if ( gcode->has_letter(letter) ) { reset_axis_position(this->to_millimeters(gcode->get_value(letter)), letter - 'X'); } } } gcode->mark_as_taken(); return; } } } else if( gcode->has_m) { switch( gcode->m ) { case 0: // M0 - Pause until pause button pressed again THEKERNEL->pauser->take(); return; case 92: // M92 - set steps per mm if (gcode->has_letter('X')) actuators[0]->change_steps_per_mm(this->to_millimeters(gcode->get_value('X'))); if (gcode->has_letter('Y')) actuators[1]->change_steps_per_mm(this->to_millimeters(gcode->get_value('Y'))); if (gcode->has_letter('Z')) actuators[2]->change_steps_per_mm(this->to_millimeters(gcode->get_value('Z'))); if (gcode->has_letter('F')) seconds_per_minute = gcode->get_value('F'); gcode->stream->printf("X:%g Y:%g Z:%g F:%g ", actuators[0]->steps_per_mm, actuators[1]->steps_per_mm, actuators[2]->steps_per_mm, seconds_per_minute); gcode->add_nl = true; gcode->mark_as_taken(); check_max_actuator_speeds(); return; case 114: { char buf[64]; int n = snprintf(buf, sizeof(buf), "C: X:%1.3f Y:%1.3f Z:%1.3f A:%1.3f B:%1.3f C:%1.3f ", from_millimeters(this->last_milestone[0]), from_millimeters(this->last_milestone[1]), from_millimeters(this->last_milestone[2]), actuators[X_AXIS]->get_current_position(), actuators[Y_AXIS]->get_current_position(), actuators[Z_AXIS]->get_current_position() ); gcode->txt_after_ok.append(buf, n); gcode->mark_as_taken(); } return; case 120: { // push state gcode->mark_as_taken(); bool b= this->absolute_mode; saved_state_t s(this->feed_rate, this->seek_rate, b); state_stack.push(s); } break; case 121: // pop state gcode->mark_as_taken(); if(!state_stack.empty()) { auto s= state_stack.top(); state_stack.pop(); this->feed_rate= std::get<0>(s); this->seek_rate= std::get<1>(s); this->absolute_mode= std::get<2>(s); } break; case 203: // M203 Set maximum feedrates in mm/sec if (gcode->has_letter('X')) this->max_speeds[X_AXIS] = gcode->get_value('X'); if (gcode->has_letter('Y')) this->max_speeds[Y_AXIS] = gcode->get_value('Y'); if (gcode->has_letter('Z')) this->max_speeds[Z_AXIS] = gcode->get_value('Z'); if (gcode->has_letter('A')) alpha_stepper_motor->set_max_rate(gcode->get_value('A')); if (gcode->has_letter('B')) beta_stepper_motor->set_max_rate(gcode->get_value('B')); if (gcode->has_letter('C')) gamma_stepper_motor->set_max_rate(gcode->get_value('C')); check_max_actuator_speeds(); gcode->stream->printf("X:%g Y:%g Z:%g A:%g B:%g C:%g ", this->max_speeds[X_AXIS], this->max_speeds[Y_AXIS], this->max_speeds[Z_AXIS], alpha_stepper_motor->get_max_rate(), beta_stepper_motor->get_max_rate(), gamma_stepper_motor->get_max_rate()); gcode->add_nl = true; gcode->mark_as_taken(); break; case 204: // M204 Snnn - set acceleration to nnn, Znnn sets z acceleration gcode->mark_as_taken(); if (gcode->has_letter('S')) { float acc = gcode->get_value('S'); // mm/s^2 // enforce minimum if (acc < 1.0F) acc = 1.0F; THEKERNEL->planner->acceleration = acc; } if (gcode->has_letter('Z')) { float acc = gcode->get_value('Z'); // mm/s^2 // enforce positive if (acc < 0.0F) acc = 0.0F; THEKERNEL->planner->z_acceleration = acc; } break; case 205: // M205 Xnnn - set junction deviation, Z - set Z junction deviation, Snnn - Set minimum planner speed, Ynnn - set minimum step rate gcode->mark_as_taken(); if (gcode->has_letter('X')) { float jd = gcode->get_value('X'); // enforce minimum if (jd < 0.0F) jd = 0.0F; THEKERNEL->planner->junction_deviation = jd; } if (gcode->has_letter('Z')) { float jd = gcode->get_value('Z'); // enforce minimum, -1 disables it and uses regular junction deviation if (jd < -1.0F) jd = -1.0F; THEKERNEL->planner->z_junction_deviation = jd; } if (gcode->has_letter('S')) { float mps = gcode->get_value('S'); // enforce minimum if (mps < 0.0F) mps = 0.0F; THEKERNEL->planner->minimum_planner_speed = mps; } if (gcode->has_letter('Y')) { alpha_stepper_motor->default_minimum_actuator_rate = gcode->get_value('Y'); } break; case 220: // M220 - speed override percentage gcode->mark_as_taken(); if (gcode->has_letter('S')) { float factor = gcode->get_value('S'); // enforce minimum 10% speed if (factor < 10.0F) factor = 10.0F; // enforce maximum 10x speed if (factor > 1000.0F) factor = 1000.0F; seconds_per_minute = 6000.0F / factor; } break; case 400: // wait until all moves are done up to this point gcode->mark_as_taken(); THEKERNEL->conveyor->wait_for_empty_queue(); break; case 500: // M500 saves some volatile settings to config override file case 503: { // M503 just prints the settings gcode->stream->printf(";Steps per unit:\nM92 X%1.5f Y%1.5f Z%1.5f\n", actuators[0]->steps_per_mm, actuators[1]->steps_per_mm, actuators[2]->steps_per_mm); gcode->stream->printf(";Acceleration mm/sec^2:\nM204 S%1.5f Z%1.5f\n", THEKERNEL->planner->acceleration, THEKERNEL->planner->z_acceleration); gcode->stream->printf(";X- Junction Deviation, Z- Z junction deviation, S - Minimum Planner speed mm/sec:\nM205 X%1.5f Z%1.5f S%1.5f\n", THEKERNEL->planner->junction_deviation, THEKERNEL->planner->z_junction_deviation, THEKERNEL->planner->minimum_planner_speed); gcode->stream->printf(";Max feedrates in mm/sec, XYZ cartesian, ABC actuator:\nM203 X%1.5f Y%1.5f Z%1.5f A%1.5f B%1.5f C%1.5f\n", this->max_speeds[X_AXIS], this->max_speeds[Y_AXIS], this->max_speeds[Z_AXIS], alpha_stepper_motor->get_max_rate(), beta_stepper_motor->get_max_rate(), gamma_stepper_motor->get_max_rate()); // get or save any arm solution specific optional values BaseSolution::arm_options_t options; if(arm_solution->get_optional(options) && !options.empty()) { gcode->stream->printf(";Optional arm solution specific settings:\nM665"); for(auto &i : options) { gcode->stream->printf(" %c%1.4f", i.first, i.second); } gcode->stream->printf("\n"); } gcode->mark_as_taken(); break; } case 665: { // M665 set optional arm solution variables based on arm solution. gcode->mark_as_taken(); // the parameter args could be any letter each arm solution only accepts certain ones BaseSolution::arm_options_t options= gcode->get_args(); options.erase('S'); // don't include the S options.erase('U'); // don't include the U if(options.size() > 0) { // set the specified options arm_solution->set_optional(options); } options.clear(); if(arm_solution->get_optional(options)) { // foreach optional value for(auto &i : options) { // print all current values of supported options gcode->stream->printf("%c: %8.4f ", i.first, i.second); gcode->add_nl = true; } } if(gcode->has_letter('S')) { // set delta segments per second, not saved by M500 this->delta_segments_per_second = gcode->get_value('S'); gcode->stream->printf("Delta segments set to %8.4f segs/sec\n", this->delta_segments_per_second); }else if(gcode->has_letter('U')) { // or set mm_per_line_segment, not saved by M500 this->mm_per_line_segment = gcode->get_value('U'); this->delta_segments_per_second = 0; gcode->stream->printf("mm per line segment set to %8.4f\n", this->mm_per_line_segment); } break; } } } if( this->motion_mode < 0) return; //Get parameters float target[3], offset[3]; clear_vector(offset); memcpy(target, this->last_milestone, sizeof(target)); //default to last target for(char letter = 'I'; letter <= 'K'; letter++) { if( gcode->has_letter(letter) ) { offset[letter - 'I'] = this->to_millimeters(gcode->get_value(letter)); } } for(char letter = 'X'; letter <= 'Z'; letter++) { if( gcode->has_letter(letter) ) { target[letter - 'X'] = this->to_millimeters(gcode->get_value(letter)) + (this->absolute_mode ? this->toolOffset[letter - 'X'] : target[letter - 'X']); } } if( gcode->has_letter('F') ) { if( this->motion_mode == MOTION_MODE_SEEK ) this->seek_rate = this->to_millimeters( gcode->get_value('F') ); else this->feed_rate = this->to_millimeters( gcode->get_value('F') ); } //Perform any physical actions switch(this->motion_mode) { case MOTION_MODE_CANCEL: break; case MOTION_MODE_SEEK : this->append_line(gcode, target, this->seek_rate / seconds_per_minute ); break; case MOTION_MODE_LINEAR: this->append_line(gcode, target, this->feed_rate / seconds_per_minute ); break; case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: this->compute_arc(gcode, offset, target ); break; } // last_milestone was set to target in append_milestone, no need to do it again }