Exemplo n.º 1
0
/// Character Received - add it to our command
/// \param c the next character to process
void gcode_parse_char(uint8_t c) {
	uint8_t checksum_char = c;

	// uppercase
	if (c >= 'a' && c <= 'z')
		c &= ~32;

	// process previous field
	if (last_field) {
		// check if we're seeing a new field or end of line
		// any character will start a new field, even invalid/unknown ones
		if ((c >= 'A' && c <= 'Z') || c == '*' || (c == 10) || (c == 13)) {
			switch (last_field) {
				case 'G':
					next_target.G = read_digit.mantissa;
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.G);
					break;
				case 'M':
					next_target.M = read_digit.mantissa;
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.M);
					break;
				case 'X':
					if (next_target.option_inches)
						next_target.target.X = decfloat_to_int(&read_digit, 25400);
					else
						next_target.target.X = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_int32(next_target.target.X);
					break;
				case 'Y':
					if (next_target.option_inches)
						next_target.target.Y = decfloat_to_int(&read_digit, 25400);
					else
						next_target.target.Y = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_int32(next_target.target.Y);
					break;
				case 'Z':
					if (next_target.option_inches)
						next_target.target.Z = decfloat_to_int(&read_digit, 25400);
					else
						next_target.target.Z = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_int32(next_target.target.Z);
					break;
				case 'E':
					if (next_target.option_inches)
						next_target.target.E = decfloat_to_int(&read_digit, 25400);
					else
						next_target.target.E = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint32(next_target.target.E);
					break;
				case 'F':
					// just use raw integer, we need move distance and n_steps to convert it to a useful value, so wait until we have those to convert it
					if (next_target.option_inches)
						next_target.target.F = decfloat_to_int(&read_digit, 25400);
					else
						next_target.target.F = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint32(next_target.target.F);
					break;
				case 'S':
					// if this is temperature, multiply by 4 to convert to quarter-degree units
					// cosmetically this should be done in the temperature section,
					// but it takes less code, less memory and loses no precision if we do it here instead
					if ((next_target.M == 104) || (next_target.M == 109) || (next_target.M == 140))
						next_target.S = decfloat_to_int(&read_digit, 4);
					// if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on
					else if ((next_target.M >= 130) && (next_target.M <= 132))
						next_target.S = decfloat_to_int(&read_digit, PID_SCALE);
					else
						next_target.S = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint16(next_target.S);
					break;
				case 'P':
					next_target.P = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint16(next_target.P);
					break;
				case 'T':
					next_target.T = read_digit.mantissa;
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.T);
					break;
				case 'N':
					next_target.N = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint32(next_target.N);
					break;
				case '*':
					next_target.checksum_read = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.checksum_read);
					break;
			}
			// reset for next field
			last_field = 0;
			read_digit.sign = read_digit.mantissa = read_digit.exponent = 0;
		}
	}

	// skip comments
	if (next_target.seen_semi_comment == 0 && next_target.seen_parens_comment == 0) {
		// new field?
		if ((c >= 'A' && c <= 'Z') || c == '*') {
			last_field = c;
			if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
				serial_writechar(c);
		}

		// process character
		switch (c) {
			// each currently known command is either G or M, so preserve previous G/M unless a new one has appeared
			// FIXME: same for T command
			case 'G':
				next_target.seen_G = 1;
				next_target.seen_M = 0;
				next_target.M = 0;
				break;
			case 'M':
				next_target.seen_M = 1;
				next_target.seen_G = 0;
				next_target.G = 0;
				break;
			case 'X':
				next_target.seen_X = 1;
				break;
			case 'Y':
				next_target.seen_Y = 1;
				break;
			case 'Z':
				next_target.seen_Z = 1;
				break;
			case 'E':
				next_target.seen_E = 1;
				break;
			case 'F':
				next_target.seen_F = 1;
				break;
			case 'S':
				next_target.seen_S = 1;
				break;
			case 'P':
				next_target.seen_P = 1;
				break;
			case 'T':
				next_target.seen_T = 1;
				break;
			case 'N':
				next_target.seen_N = 1;
				break;
			case '*':
				next_target.seen_checksum = 1;
				break;

			// comments
			case ';':
				next_target.seen_semi_comment = 1;
				break;
			case '(':
				next_target.seen_parens_comment = 1;
				break;

			// now for some numeracy
			case '-':
				read_digit.sign = 1;
				// force sign to be at start of number, so 1-2 = -2 instead of -12
				read_digit.exponent = 0;
				read_digit.mantissa = 0;
				break;
			case '.':
				if (read_digit.exponent == 0)
					read_digit.exponent = 1;
				break;
			#ifdef	DEBUG
			case ' ':
			case '\t':
			case 10:
			case 13:
				// ignore
				break;
			#endif

			default:
				// can't do ranges in switch..case, so process actual digits here.
				if (c >= '0' && c <= '9') {
					if (read_digit.exponent < DECFLOAT_EXP_MAX + 1 &&
							((next_target.option_inches == 0 &&
							read_digit.mantissa < DECFLOAT_MANT_MM_MAX) ||
							(next_target.option_inches &&
							read_digit.mantissa < DECFLOAT_MANT_IN_MAX)))
					{
						// this is simply mantissa = (mantissa * 10) + atoi(c) in different clothes
						read_digit.mantissa = (read_digit.mantissa << 3) + (read_digit.mantissa << 1) + (c - '0');
						if (read_digit.exponent)
							read_digit.exponent++;
					}
				}
				#ifdef	DEBUG
				else {
					// invalid
					serial_writechar('?');
					serial_writechar(c);
					serial_writechar('?');
				}
				#endif
		}
	} else if ( next_target.seen_parens_comment == 1 && c == ')')
		next_target.seen_parens_comment = 0; // recognize stuff after a (comment)

	if (next_target.seen_checksum == 0)
		next_target.checksum_calculated =
			crc(next_target.checksum_calculated, checksum_char);

	// end of line
	if ((c == 10) || (c == 13)) {
		if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
			serial_writechar(c);

		if (
		#ifdef	REQUIRE_LINENUMBER
			((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1)) ||
			(next_target.seen_M && (next_target.M == 110))
		#else
			1
		#endif
			) {
			if (
				#ifdef	REQUIRE_CHECKSUM
				((next_target.checksum_calculated == next_target.checksum_read) && (next_target.seen_checksum == 1))
				#else
				((next_target.checksum_calculated == next_target.checksum_read) || (next_target.seen_checksum == 0))
				#endif
				) {
				// process
				serial_writestr_P(PSTR("ok "));
				process_gcode_command();
				serial_writechar('\n');

				// expect next line number
				if (next_target.seen_N == 1)
					next_target.N_expected = next_target.N + 1;
			}
			else {
				sersendf_P(PSTR("rs N%ld Expected checksum %d\n"), next_target.N_expected, next_target.checksum_calculated);
// 				request_resend();
			}
		}
		else {
			sersendf_P(PSTR("rs N%ld Expected line number %ld\n"), next_target.N_expected, next_target.N_expected);
// 			request_resend();
		}

		// reset variables
		next_target.seen_X = next_target.seen_Y = next_target.seen_Z = \
			next_target.seen_E = next_target.seen_F = next_target.seen_S = \
			next_target.seen_P = next_target.seen_T = next_target.seen_N = \
			next_target.seen_M = next_target.seen_checksum = next_target.seen_semi_comment = \
			next_target.seen_parens_comment = next_target.checksum_read = \
			next_target.checksum_calculated = 0;
		// last_field and read_digit are reset above already

		// assume a G1 by default
		next_target.seen_G = 1;
		next_target.G = 1;

		if (next_target.option_all_relative) {
			next_target.target.X = next_target.target.Y = next_target.target.Z = 0;
		}
		if (next_target.option_all_relative || next_target.option_e_relative) {
			next_target.target.E = 0;
		}
	}
}
Exemplo n.º 2
0
void heater_tick(heater_t h, temp_sensor_t t, uint16_t current_temp, uint16_t target_temp) {
	uint8_t		pid_output;

	#ifndef	BANG_BANG
		int16_t		heater_p;
		int16_t		heater_d;
		int16_t		t_error = target_temp - current_temp;
	#endif	/* BANG_BANG */

	if (h >= NUM_HEATERS || t >= NUM_TEMP_SENSORS)
		return;

	#ifndef	BANG_BANG
		heaters_runtime[h].temp_history[heaters_runtime[h].temp_history_pointer++] = current_temp;
		heaters_runtime[h].temp_history_pointer &= (TH_COUNT - 1);

		// PID stuff
		// proportional
		heater_p = t_error;

		// integral
		heaters_runtime[h].heater_i += t_error;
		// prevent integrator wind-up
		if (heaters_runtime[h].heater_i > heaters_pid[h].i_limit)
			heaters_runtime[h].heater_i = heaters_pid[h].i_limit;
		else if (heaters_runtime[h].heater_i < -heaters_pid[h].i_limit)
			heaters_runtime[h].heater_i = -heaters_pid[h].i_limit;

		// derivative
		// note: D follows temp rather than error so there's no large derivative when the target changes
		heater_d = heaters_runtime[h].temp_history[heaters_runtime[h].temp_history_pointer] - current_temp;

		// combine factors
		int32_t pid_output_intermed = (
			(
				(((int32_t) heater_p) * heaters_pid[h].p_factor) +
				(((int32_t) heaters_runtime[h].heater_i) * heaters_pid[h].i_factor) +
				(((int32_t) heater_d) * heaters_pid[h].d_factor)
			) / PID_SCALE
		);

		// rebase and limit factors
		if (pid_output_intermed > 255)
			pid_output = 255;
		else if (pid_output_intermed < 0)
			pid_output = 0;
		else
			pid_output = pid_output_intermed & 0xFF;

		#ifdef	DEBUG
		if (debug_flags & DEBUG_PID)
			sersendf_P(PSTR("T{E:%d, P:%d * %ld = %ld / I:%d * %ld = %ld / D:%d * %ld = %ld # O: %ld = %u}\n"), t_error, heater_p, heaters_pid[h].p_factor, (int32_t) heater_p * heaters_pid[h].p_factor / PID_SCALE, heaters_runtime[h].heater_i, heaters_pid[h].i_factor, (int32_t) heaters_runtime[h].heater_i * heaters_pid[h].i_factor / PID_SCALE, heater_d, heaters_pid[h].d_factor, (int32_t) heater_d * heaters_pid[h].d_factor / PID_SCALE, pid_output_intermed, pid_output);
		#endif
	#else
		if (current_temp >= target_temp)
			pid_output = BANG_BANG_ON;
		else
			pid_output = BANG_BANG_OFF;
	#endif
	
	#ifdef	HEATER_SANITY_CHECK
	// check heater sanity
	// implementation is a moving window with some slow-down to compensate for thermal mass
	if (target_temp > (current_temp + TEMP_HYSTERESIS)) {
		// heating
		if (current_temp > heaters_runtime[h].sane_temperature)
			// hotter than sane- good since we're heating unless too hot
			heaters_runtime[h].sane_temperature = current_temp;
		else {
			if (heaters_runtime[h].sanity_counter < 40)
				heaters_runtime[h].sanity_counter++;
			else {
				heaters_runtime[h].sanity_counter = 0;
				// ratchet up expected temp
				heaters_runtime[h].sane_temperature++;
			}
		}
		// limit to target, so if we overshoot by too much for too long an error is flagged
		if (heaters_runtime[h].sane_temperature > target_temp)
			heaters_runtime[h].sane_temperature = target_temp;
	}
	else if (target_temp < (current_temp - TEMP_HYSTERESIS)) {
		// cooling
		if (current_temp < heaters_runtime[h].sane_temperature)
			// cooler than sane- good since we're cooling
			heaters_runtime[h].sane_temperature = current_temp;
		else {
			if (heaters_runtime[h].sanity_counter < 125)
				heaters_runtime[h].sanity_counter++;
			else {
				heaters_runtime[h].sanity_counter = 0;
				// ratchet down expected temp
				heaters_runtime[h].sane_temperature--;
			}
		}
		// if we're at or below 60 celsius, don't freak out if we can't drop any more.
		if (current_temp <= 240)
			heaters_runtime[h].sane_temperature = current_temp;
		// limit to target, so if we don't cool down for too long an error is flagged
		else if (heaters_runtime[h].sane_temperature < target_temp)
			heaters_runtime[h].sane_temperature = target_temp;
	}
	// we're within HYSTERESIS of our target
	else {
		heaters_runtime[h].sane_temperature = current_temp;
		heaters_runtime[h].sanity_counter = 0;
	}

	// compare where we're at to where we should be
	if (labs(current_temp - heaters_runtime[h].sane_temperature) > TEMP_HYSTERESIS) {
		// no change, or change in wrong direction for a long time- heater is broken!
		pid_output = 0;
		sersendf_P(PSTR("!! heater %d or temp sensor %d broken- temp is %d.%dC, target is %d.%dC, didn't reach %d.%dC in %d0 milliseconds\n"), h, t, current_temp >> 2, (current_temp & 3) * 25, target_temp >> 2, (target_temp & 3) * 25, heaters_runtime[h].sane_temperature >> 2, (heaters_runtime[h].sane_temperature & 3) * 25, heaters_runtime[h].sanity_counter);
	}
Exemplo n.º 3
0
/// DEBUG - print queue.
/// Qt/hs format, t is tail, h is head, s is F/full, E/empty or neither
void print_queue() {
	sersendf_P(PSTR("Q%d/%d%c"), mb_tail, mb_head, (queue_full()?'F':(queue_empty()?'E':' ')));
}
Exemplo n.º 4
0
/// called every 10ms from clock.c - check all temp sensors that are ready for checking
void temp_sensor_tick() {
	temp_sensor_t i = 0;
	for (; i < NUM_TEMP_SENSORS; i++) {
		if (temp_sensors_runtime[i].next_read_time) {
			temp_sensors_runtime[i].next_read_time--;
		}
		else {
			uint16_t	temp = 0;
			//time to deal with this temp sensor
			switch(temp_sensors[i].temp_type) {
				#ifdef	TEMP_MAX6675
				case TT_MAX6675:
					#ifdef	PRR
						PRR &= ~MASK(PRSPI);
					#elif defined PRR0
						PRR0 &= ~MASK(PRSPI);
					#endif

					SPCR = MASK(MSTR) | MASK(SPE) | MASK(SPR0);

					// enable TT_MAX6675
					WRITE(SS, 0);

					// No delay required, see
					// https://github.com/triffid/Teacup_Firmware/issues/22

					// read MSB
					SPDR = 0;
					for (;(SPSR & MASK(SPIF)) == 0;);
					temp = SPDR;
					temp <<= 8;

					// read LSB
					SPDR = 0;
					for (;(SPSR & MASK(SPIF)) == 0;);
					temp |= SPDR;

					// disable TT_MAX6675
					WRITE(SS, 1);

					temp_sensors_runtime[i].temp_flags = 0;
					if ((temp & 0x8002) == 0) {
						// got "device id"
						temp_sensors_runtime[i].temp_flags |= PRESENT;
						if (temp & 4) {
							// thermocouple open
							temp_sensors_runtime[i].temp_flags |= TCOPEN;
						}
						else {
							temp = temp >> 3;
						}
					}

					// this number depends on how frequently temp_sensor_tick is called. the MAX6675 can give a reading every 0.22s, so set this to about 250ms
					temp_sensors_runtime[i].next_read_time = 25;

					break;
				#endif	/* TEMP_MAX6675	*/

				#ifdef	TEMP_THERMISTOR
				case TT_THERMISTOR:
					do {
						uint8_t j, table_num;
						//Read current temperature
						temp = analog_read(i);
						// for thermistors the thermistor table number is in the additional field
						table_num = temp_sensors[i].additional;

						//Calculate real temperature based on lookup table
						for (j = 1; j < NUMTEMPS; j++) {
							if (pgm_read_word(&(temptable[table_num][j][0])) > temp) {
								// Thermistor table is already in 14.2 fixed point
								#ifndef	EXTRUDER
								if (DEBUG_PID && (debug_flags & DEBUG_PID))
									sersendf_P(PSTR("pin:%d Raw ADC:%d table entry: %d"),temp_sensors[i].temp_pin,temp,j);
								#endif
								// Linear interpolating temperature value
								// y = ((x - x₀)y₁ + (x₁-x)y₀ ) / (x₁ - x₀)
								// y = temp
								// x = ADC reading
								// x₀= temptable[j-1][0]
								// x₁= temptable[j][0]
								// y₀= temptable[j-1][1]
								// y₁= temptable[j][1]
								// y =
								// Wikipedia's example linear interpolation formula.
								temp = (
								//     ((x - x₀)y₁
									((uint32_t)temp - pgm_read_word(&(temptable[table_num][j-1][0]))) * pgm_read_word(&(temptable[table_num][j][1]))
								//                 +
									+
								//                   (x₁-x)
									(pgm_read_word(&(temptable[table_num][j][0])) - (uint32_t)temp)
								//                         y₀ )
									* pgm_read_word(&(temptable[table_num][j-1][1])))
								//                              /
									/
								//                                (x₁ - x₀)
									(pgm_read_word(&(temptable[table_num][j][0])) - pgm_read_word(&(temptable[table_num][j-1][0])));
								#ifndef	EXTRUDER
								if (DEBUG_PID && (debug_flags & DEBUG_PID))
									sersendf_P(PSTR(" temp:%d.%d"),temp/4,(temp%4)*25);
								#endif
								break;
							}
						}
						#ifndef	EXTRUDER
						if (DEBUG_PID && (debug_flags & DEBUG_PID))
							sersendf_P(PSTR(" Sensor:%d\n"),i);
						#endif


						//Clamp for overflows
						if (j == NUMTEMPS)
							temp = temptable[table_num][NUMTEMPS-1][1];

						temp_sensors_runtime[i].next_read_time = 0;
					} while (0);
					break;
				#endif	/* TEMP_THERMISTOR */

				#ifdef	TEMP_AD595
				case TT_AD595:
					temp = analog_read(i);

					// convert
					// >>8 instead of >>10 because internal temp is stored as 14.2 fixed point
					temp = (temp * 500L) >> 8;

					temp_sensors_runtime[i].next_read_time = 0;

					break;
				#endif	/* TEMP_AD595 */

				#ifdef	TEMP_PT100
				case TT_PT100:
					#warning TODO: PT100 code
					break
				#endif	/* TEMP_PT100 */

				#ifdef	TEMP_INTERCOM
				case TT_INTERCOM:
					temp = read_temperature(temp_sensors[i].temp_pin);

					temp_sensors_runtime[i].next_read_time = 25;

					break;
				#endif	/* TEMP_INTERCOM */

				#ifdef	TEMP_DUMMY
				case TT_DUMMY:
					temp = temp_sensors_runtime[i].last_read_temp;

					if (temp_sensors_runtime[i].target_temp > temp)
						temp++;
					else if (temp_sensors_runtime[i].target_temp < temp)
						temp--;

					temp_sensors_runtime[i].next_read_time = 0;

					break;
				#endif	/* TEMP_DUMMY */

				default: /* prevent compiler warning */
					break;
			}
			/* Exponentially Weighted Moving Average alpha constant for smoothing
			   noisy sensors. Instrument Engineer's Handbook, 4th ed, Vol 2 p126
			   says values of 0.05 to 0.1 for TEMP_EWMA are typical. */
			#ifndef TEMP_EWMA
				#define TEMP_EWMA 1.0
			#endif
			#define EWMA_SCALE  1024L
			#define EWMA_ALPHA  ((long) (TEMP_EWMA * EWMA_SCALE))
			temp_sensors_runtime[i].last_read_temp = (uint16_t) ((EWMA_ALPHA * temp +
			  (EWMA_SCALE-EWMA_ALPHA) * temp_sensors_runtime[i].last_read_temp
			                                         ) / EWMA_SCALE);
		}
		if (labs((int16_t)(temp_sensors_runtime[i].last_read_temp - temp_sensors_runtime[i].target_temp)) < (TEMP_HYSTERESIS*4)) {
			if (temp_sensors_runtime[i].temp_residency < (TEMP_RESIDENCY_TIME*120))
				temp_sensors_runtime[i].temp_residency++;
		}
		else {
			// Deal with flakey sensors which occasionally report a wrong value
			// by setting residency back, but not entirely to zero.
			if (temp_sensors_runtime[i].temp_residency > 10)
				temp_sensors_runtime[i].temp_residency -= 10;
			else
				temp_sensors_runtime[i].temp_residency = 0;
		}

		if (temp_sensors[i].heater < NUM_HEATERS) {
			heater_tick(temp_sensors[i].heater, temp_sensors[i].temp_type, temp_sensors_runtime[i].last_read_temp, temp_sensors_runtime[i].target_temp);
		}

    if (DEBUG_PID && (debug_flags & DEBUG_PID))
      sersendf_P(PSTR("DU temp: {%d %d %d.%d}"), i,
                 temp_sensors_runtime[i].last_read_temp,
                 temp_sensors_runtime[i].last_read_temp / 4,
                 (temp_sensors_runtime[i].last_read_temp & 0x03) * 25);
	}
Exemplo n.º 5
0
/**
 * Join 2 moves by removing the full stop between them, where possible.
 * To join the moves, the expected jerk - or force - of the change in direction is calculated.
 * The jerk is used to scale the common feed rate between both moves to obtain an acceptable speed
 * to transition between 'prev' and 'current'.
 *
 * Premise: we currently join the last move in the queue and the one before it (if any).
 * This means the feed rate at the end of the 'current' move is 0.
 *
 * Premise: the 'current' move is not dispatched in the queue: it should remain constant while this
 * function is running.
 *
 * Note: the planner always makes sure the movement can be stopped within the
 * last move (= 'current'); as a result a lot of small moves will still limit the speed.
 */
void dda_join_moves(DDA *prev, DDA *current) {

  // Calculating the look-ahead settings can take a while; before modifying
  // the previous move, we need to locally store any values and write them
  // when we are done (and the previous move is not already active).
  uint32_t prev_F, prev_F_start, prev_F_end, prev_end;
  uint32_t prev_rampup, prev_rampdown, prev_total_steps;
  uint8_t prev_id;
  // Similarly, we only want to modify the current move if we have the results of the calculations;
  // until then, we do not want to touch the current move settings.
  // Note: we assume 'current' will not be dispatched while this function runs, so we do not to
  // back up the move settings: they will remain constant.
  uint32_t this_F_start, this_start, this_rampup, this_rampdown;
  int32_t jerk, jerk_e;       // Expresses the forces if we would change directions at full speed
  static uint32_t la_cnt = 0;     // Counter: how many moves did we join?
  #ifdef LOOKAHEAD_DEBUG
  static uint32_t moveno = 0;     // Debug counter to number the moves - helps while debugging
  moveno++;
  #endif

  // Bail out if there's nothing to join (e.g. G1 F1500).
  if ( ! prev || prev->nullmove)
    return;

  serprintf(PSTR("Current Delta: %ld,%ld,%ld E:%ld Live:%d\r\n"),
            current->delta_um.X, current->delta_um.Y, current->delta_um.Z,
            current->delta_um.E, current->live);
  serprintf(PSTR("Prev    Delta: %ld,%ld,%ld E:%ld Live:%d\r\n"),
            prev->delta_um.X, prev->delta_um.Y, prev->delta_um.Z,
            prev->delta_um.E, prev->live);

  // Look-ahead: attempt to join moves into smooth movements
  // Note: moves are only modified after the calculations are complete.
  // Only prepare for look-ahead if we have 2 available moves to
  // join and the Z axis is unused (for now, Z axis moves are NOT joined).
  if (prev->live == 0 && prev->delta_um.Z == current->delta_um.Z) {
    // Calculate the jerk if the previous move and this move would be joined
    // together at full speed.
    jerk = dda_jerk_size_2d(prev->delta_um.X, prev->delta_um.Y, prev->endpoint.F,
                  current->delta_um.X, current->delta_um.Y, current->endpoint.F);
    serprintf(PSTR("Jerk: %lu\r\n"), jerk);
    jerk_e = dda_jerk_size_1d(prev->delta_um.E, prev->endpoint.F,
                              current->delta_um.E, current->endpoint.F);
    serprintf(PSTR("Jerk_e: %lu\r\n"), jerk_e);
  } else {
    // Move already executing or Z moved: abort the join
    return;
  }

  // Make sure we have 2 moves and the previous move is not already active
  if (prev->live == 0) {
    // Perform an atomic copy to preserve volatile parameters during the calculations
    ATOMIC_START
      prev_id = prev->id;
      prev_F = prev->endpoint.F;
      prev_F_start = prev->F_start;
      prev_F_end = prev->F_end;
      prev_rampup = prev->rampup_steps;
      prev_rampdown = prev->rampdown_steps;
      prev_total_steps = prev->total_steps;
    ATOMIC_END

    // The initial crossing speed is the minimum between both target speeds
    // Note: this is a given: the start speed and end speed can NEVER be
    // higher than the target speed in a move!
    // Note 2: this provides an upper limit, if needed, the speed is lowered.
    uint32_t crossF = prev_F;
    if(crossF > current->endpoint.F) crossF = current->endpoint.F;

    //sersendf_P(PSTR("j:%lu - XF:%lu"), jerk, crossF);

    // If the XY jerk is too big, scale the proposed cross speed
    if(jerk > LOOKAHEAD_MAX_JERK_XY) {
      serprintf(PSTR("Jerk too big: scale cross speed between moves\r\n"));
      // Get the highest speed between both moves
      if(crossF < prev_F)
        crossF = prev_F;

      // Perform an exponential scaling
      uint32_t ujerk = (uint32_t)jerk;  // Use unsigned to double the range before overflowing
      crossF = (crossF*LOOKAHEAD_MAX_JERK_XY*LOOKAHEAD_MAX_JERK_XY)/(ujerk*ujerk);

      // Optimize: if the crossing speed is zero, there is no join possible between these
      // two (fast) moves. Stop calculating and leave the full stop that is currently between
      // them.
      if(crossF == 0)
        return;

      // Safety: make sure we never exceed the maximum speed of a move
      if(crossF > current->endpoint.F) crossF = current->endpoint.F;
      if(crossF > prev_F) crossF = prev_F;
      sersendf_P(PSTR("=>F:%lu"), crossF);
    }
    // Same to the extruder jerk: make sure we do not yank it
    if(jerk_e > LOOKAHEAD_MAX_JERK_E) {
      sersendf_P(PSTR("Jerk_e too big: scale cross speed between moves\r\n"));
      uint32_t crossF2 = MAX(current->endpoint.F, prev_F);

      // Perform an exponential scaling
      uint32_t ujerk = (uint32_t)jerk_e;  // Use unsigned to double the range before overflowing
      crossF2 = (crossF2*LOOKAHEAD_MAX_JERK_E*LOOKAHEAD_MAX_JERK_E)/(ujerk*ujerk);

      // Only continue with joining if there is a feasible crossing speed
      if(crossF2 == 0) return;

      // Safety: make sure the proposed speed is not higher than the target speeds of each move
      crossF2 = MIN(crossF2, current->endpoint.F);
      crossF2 = MIN(crossF2, prev_F);

      if(crossF2 > crossF) {
        sersendf_P(PSTR("Jerk_e: %lu => crossF: %lu (original: %lu)\r\n"), jerk_e, crossF2, crossF);
      }

      // Pick the crossing speed for these 2 move to be within the jerk limits
      crossF = MIN(crossF, crossF2);
    }

    // Show the proposed crossing speed - this might get adjusted below
    serprintf(PSTR("Initial crossing speed: %lu\r\n"), crossF);

    // Forward check: test if we can actually reach the target speed in the previous move
    // If not: we need to determine the obtainable speed and adjust crossF accordingly.
    // Note: these ramps can be longer than the move: if so we can not reach top speed.
    uint32_t up = ACCELERATE_RAMP_LEN(prev_F) - ACCELERATE_RAMP_LEN(prev_F_start);
    uint32_t down = ACCELERATE_RAMP_LEN(prev_F) - ACCELERATE_RAMP_LEN(crossF);
    // Test if both the ramp up and ramp down fit within the move
    if(up+down > prev_total_steps) {
      // Test if we can reach the crossF rate: if the difference between both ramps is larger
      // than the move itself, there is no ramp up or down from F_start to crossF...
      uint32_t diff = (up>down) ? up-down : down-up;
      if(diff > prev_total_steps) {
        // Cannot reach crossF from F_start, lower crossF and adjust both ramp-up and down
        down = 0;
        // Before we can determine how fast we can go in this move, we need the number of
        // steps needed to reach the entry speed.
        uint32_t prestep = ACCELERATE_RAMP_LEN(prev_F_start);
        // Calculate what feed rate we can reach during this move
        crossF = dda_steps_to_velocity(prestep+prev_total_steps);
        // Make sure we do not exceed the target speeds
        if(crossF > prev_F) crossF = prev_F;
        if(crossF > current->endpoint.F) crossF = current->endpoint.F;
        // The problem with the 'dda_steps_to_velocity' is that it will produce a
        // rounded result. Use it to obtain an exact amount of steps needed to reach
        // that speed and set that as the ramp up; we might stop accelerating for a
        // couple of steps but that is better than introducing errors in the moves.
        up = ACCELERATE_RAMP_LEN(crossF) - prestep;

        #ifdef LOOKAHEAD_DEBUG
        // Sanity check: the ramp up should never exceed the move length
        if(up > prev_total_steps) {
          sersendf_P(PSTR("FATAL ERROR during prev ramp scale, ramp is too long: up:%lu ; len:%lu ; target speed: %lu\r\n"),
            up, prev_total_steps, crossF);
          sersendf_P(PSTR("F_start:%lu ; F:%lu ; crossF:%lu\r\n"),
            prev_F_start, prev_F, crossF);
          dda_emergency_shutdown(PSTR("LA prev ramp scale, ramp is too long"));
        }
        #endif
        // Show the result on the speed on the clipping of the ramp
        serprintf(PSTR("Prev speed & crossing speed truncated to: %lu\r\n"), crossF);
      } else {
        // Can reach crossF; determine the apex between ramp up and ramp down
        // In other words: calculate how long we can accelerate before decelerating to exit at crossF
        // Note: while the number of steps is exponentially proportional to the velocity,
        // the acceleration is linear: we can simply remove the same number of steps of both ramps.
        uint32_t diff = (up + down - prev_total_steps) / 2;
        up -= diff;
        down -= diff;
      }

      #ifdef LOOKAHEAD_DEBUG
      // Sanity check: make sure the speed limits are maintained
      if(prev_F_start > prev_F || crossF > prev_F) {
        serprintf(PSTR("Prev target speed exceeded!: prev_F_start:%lu ; prev_F:%lu ; prev_F_end:%lu\r\n"), prev_F_start, prev_F, crossF);
        dda_emergency_shutdown(PSTR("Prev target speed exceeded"));
      }
      #endif
    }
    // Save the results
    prev_rampup = up;
    prev_rampdown = prev_total_steps - down;
    prev_F_end = crossF;
    prev_end = ACCELERATE_RAMP_LEN(prev_F_end);

    #ifdef LOOKAHEAD_DEBUG
    // Sanity check: make sure the speed limits are maintained
    if(crossF > current->endpoint.F) {
      serprintf(PSTR("This target speed exceeded!: F_start:%lu ; F:%lu ; prev_F_end:%lu\r\n"), crossF, current->endpoint.F);
      dda_emergency_shutdown(PSTR("This target speed exceeded"));
    }
    #endif

    // Forward check 2: test if we can actually reach the target speed in this move.
    // If not: determine obtainable speed and adjust crossF accordingly. If that
    // happens, a third (reverse) pass is needed to lower the speeds in the previous move...
    //ramp_scaler = ACCELERATE_SCALER(current->lead); // Use scaler for current leading axis
    up = ACCELERATE_RAMP_LEN(current->endpoint.F) - ACCELERATE_RAMP_LEN(crossF);
    down = ACCELERATE_RAMP_LEN(current->endpoint.F);
    // Test if both the ramp up and ramp down fit within the move
    if(up+down > current->total_steps) {
      // Test if we can reach the crossF rate
      // Note: this is the inverse of the previous move: we need to exit at 0 speed as
      // this is the last move in the queue. Implies that down >= up
      if(down-up > current->total_steps) {
        serprintf(PSTR("This move can not reach crossF - lower it\r\n"));
        // Cannot reach crossF, lower it and adjust ramps
        // Note: after this, the previous move needs to be modified to match crossF.
        up = 0;
        // Calculate what crossing rate we can reach: total/down * F
        crossF = dda_steps_to_velocity(current->total_steps);
        // Speed limit: never exceed the target rate
        if(crossF > current->endpoint.F) crossF = current->endpoint.F;
        // crossF will be conservative: calculate the actual ramp down length
        down = ACCELERATE_RAMP_LEN(crossF);

        #ifdef LOOKAHEAD_DEBUG
        // Make sure we can break to a full stop before the move ends
        if(down > current->total_steps) {
          sersendf_P(PSTR("FATAL ERROR during ramp scale, ramp is too long: down:%lu ; len:%lu ; target speed: %lu\r\n"),
            down, current->total_steps, crossF);
          dda_emergency_shutdown(PSTR("LA current ramp scale, ramp is too long"));
        }
        #endif
      } else {
        serprintf(PSTR("This: crossF is usable but we will not reach Fmax\r\n"));
        // Can reach crossF; determine the apex between ramp up and ramp down
        // In other words: calculate how long we can accelerate before decelerating to start at crossF
        // and end at F = 0
        uint32_t diff = (down + up - current->total_steps) / 2;
        up -= diff;
        down -= diff;
        serprintf(PSTR("Apex: %lu - new up: %lu - new down: %lu\r\n"), diff, up, down);

        // sanity stuff: calculate the speeds for these ramps
        serprintf(PSTR("Ramp up speed: %lu mm/s\r\n"), dda_steps_to_velocity(up+prev->rampup_steps));
        serprintf(PSTR("Ramp down speed: %lu mm/s\r\n"), dda_steps_to_velocity(down));
      }
    }
    // Save the results
    this_rampup = up;
    this_rampdown = current->total_steps - down;
    this_F_start = crossF;
    this_start = ACCELERATE_RAMP_LEN(this_F_start);
    serprintf(PSTR("Actual crossing speed: %lu\r\n"), crossF);

    // Potential reverse processing:
    // Make sure the crossing speed is the same, if its not, we need to slow the previous move to
    // the current crossing speed (note: the crossing speed could only be lowered).
    // This can happen when this move is a short move and the previous move was a larger or faster move:
    // since we need to be able to stop if this is the last move, we lowered the crossing speed
    // between this move and the previous move...
    if(prev_F_end != crossF) {
      // Third reverse pass: slow the previous move to end at the target crossing speed.
      //ramp_scaler = ACCELERATE_SCALER(current->lead); //todo: prev_lead // Use scaler for previous leading axis (again)
      // Note: use signed values so we  can check if results go below zero
      // Note 2: when up2 and/or down2 are below zero from the start, you found a bug in the logic above.
      int32_t up2 = ACCELERATE_RAMP_LEN(prev_F) - ACCELERATE_RAMP_LEN(prev_F_start);
      int32_t down2 = ACCELERATE_RAMP_LEN(prev_F) - ACCELERATE_RAMP_LEN(crossF);

      // Test if both the ramp up and ramp down fit within the move
      if(up2+down2 > prev_total_steps) {
        int32_t diff = (up2 + down2 - (int32_t)prev_total_steps) / 2;
        up2 -= diff;
        down2 -= diff;

        #ifdef LOOKAHEAD_DEBUG
        if(up2 < 0 || down2 < 0) {
          // Cannot reach crossF from prev_F_start - this should not happen!
          sersendf_P(PSTR("FATAL ERROR during reverse pass ramp scale, ramps are too long: up:%ld ; down:%ld; len:%lu ; F_start: %lu ; crossF: %lu\r\n"),
                    up2, down2, prev_total_steps, prev_F_start, crossF);
          sersendf_P(PSTR("Original up: %ld - down %ld (diff=%ld)\r\n"),up2+diff,down2+diff,diff);
          dda_emergency_shutdown(PSTR("reverse pass ramp scale, can not reach F_end from F_start"));
        }
        #endif
      }
      // Assign the results
      prev_rampup = up2;
      prev_rampdown = prev_total_steps - down2;
      prev_F_end = crossF;
      prev_end = ACCELERATE_RAMP_LEN(prev_F_end);
    }

    #ifdef LOOKAHEAD_DEBUG
    if(crossF > current->endpoint.F || crossF > prev_F)
      dda_emergency_shutdown(PSTR("Lookahead exceeded speed limits in crossing!"));

    // When debugging, print the 2 moves we joined
    // Legenda: Fs=F_start, len=# of steps, up/down=# steps in ramping, Fe=F_end
    serprintf(PSTR("LA: (%lu) Fs=%lu, len=%lu, up=%lu, down=%lu, Fe=%lu <=> (%lu) Fs=%lu, len=%lu, up=%lu, down=%lu, Fe=0\r\n\r\n"),
      moveno-1, prev->F_start, prev->total_steps, prev->rampup_steps,
      prev->total_steps-prev->rampdown_steps, prev->F_end,
      moveno, current->F_start, current->total_steps, current->rampup_steps,
      current->total_steps - this_rampdown);
    #endif

    uint8_t timeout = 0;

    ATOMIC_START
      // Evaluation: determine how we did...
      lookahead_joined++;

      // Determine if we are fast enough - if not, just leave the moves
      // Note: to test if the previous move was already executed and replaced by a new
      // move, we compare the DDA id.
      if(prev->live == 0 && prev->id == prev_id) {
        prev->F_end = prev_F_end;
        prev->end_steps = prev_end;
        prev->rampup_steps = prev_rampup;
        prev->rampdown_steps = prev_rampdown;
        current->rampup_steps = this_rampup;
        current->rampdown_steps = this_rampdown;
        current->F_end = 0;
        current->end_steps = 0;
        current->F_start = this_F_start;
        current->start_steps = this_start;
        la_cnt++;
      } else
        timeout = 1;
    ATOMIC_END

    // If we were not fast enough, any feedback will happen outside the atomic block:
    if(timeout) {
      sersendf_P(PSTR("Error: look ahead not fast enough\r\n"));
      lookahead_timeout++;
    }
  }
Exemplo n.º 6
0
/// Character Received - add it to our command
/// \param c the next character to process
void gcode_parse_char(uint8_t c) 
{
	// uppercase
	if (c >= 'a' && c <= 'z')
		c &= ~32;

	// process previous field
	if (last_field) {
		// check if we're seeing a new field or end of line
		// any character will start a new field, even invalid/unknown ones
		if ((c >= 'A' && c <= 'Z') || c == '*' || (c == 10) || (c == 13)) {
			switch (last_field) {
				case 'G':
					next_target.G = parse_number.AsInt( false );
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint8(next_target.G);
					break;
				case 'M':
					next_target.M = parse_number.AsInt( false );
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint8(next_target.M);
					break;
				case 'X':
					next_target.target.X = parse_number.AsPosition( next_target.option_inches );
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_int32(next_target.target.X);
					break;
				case 'Y':
					next_target.target.Y = parse_number.AsPosition( next_target.option_inches );
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_int32(next_target.target.Y);
					break;
				case 'Z':
					next_target.target.Z = parse_number.AsPosition( next_target.option_inches );
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_int32(next_target.target.Z);
					break;
				case 'E':
					next_target.target.E = parse_number.AsPosition( next_target.option_inches );
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint32(next_target.target.E);
					break;
				case 'F':
					// just use raw integer, we need move distance and n_steps to convert it to a useful value, so wait until we have those to convert it
					next_target.target.F = parse_number.AsInt( next_target.option_inches );
					//if (next_target.option_inches)
					//	next_target.target.F = decfloat_to_int(&read_digit, 25400, 1);
					//else
					//	next_target.target.F = decfloat_to_int(&read_digit, 1, 0);
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint32(next_target.target.F);
					break;
				case 'S':
					// if this is temperature, multiply by 4 to convert to quarter-degree units
					// cosmetically this should be done in the temperature section,
					// but it takes less code, less memory and loses no precision if we do it here instead
					if ((next_target.M == 104) || (next_target.M == 109) || (next_target.M == 140))
						next_target.S = parse_number.AsScaledInt( 4 ); //decfloat_to_int(&read_digit, 4, 0);
					// if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on
					else if ((next_target.M >= 130) && (next_target.M <= 132))
						next_target.S = parse_number.AsScaledInt( PID_SCALE );// decfloat_to_int(&read_digit, PID_SCALE, 0);
					else
						next_target.S = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0);
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint16(next_target.S);
					break;
				case 'P':
					next_target.P = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0);
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint16(next_target.P);
					break;
				case 'T':
					next_target.T = parse_number.AsInt( false ); //read_digit.mantissa;
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint8(next_target.T);
					break;
				case 'N':
					next_target.N = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0);
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint32(next_target.N);
					break;
				case '*':
					next_target.checksum_read = parse_number.AsInt( false ); //decfloat_to_int(&read_digit, 1, 0);
					//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
					//	serwrite_uint8(next_target.checksum_read);
					break;
			}
			// reset for next field
			last_field = 0;
			parse_number.Clear();
		}
	}

	// skip comments
	if (next_target.seen_semi_comment == 0 && next_target.seen_parens_comment == 0) {
		// new field?
		if ((c >= 'A' && c <= 'Z') || c == '*') {
			last_field = c;
			//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
			//	serial_writechar(c);
		}

		// process character
		switch (c) {
			// each currently known command is either G or M, so preserve previous G/M unless a new one has appeared
			// FIXME: same for T command
			case 'G':
				next_target.seen_G = 1;
				next_target.seen_M = 0;
				next_target.M = 0;
				break;
			case 'M':
				next_target.seen_M = 1;
				next_target.seen_G = 0;
				next_target.G = 0;
				break;
			case 'X':
				next_target.seen_X = 1;
				break;
			case 'Y':
				next_target.seen_Y = 1;
				break;
			case 'Z':
				next_target.seen_Z = 1;
				break;
			case 'E':
				next_target.seen_E = 1;
				break;
			case 'F':
				next_target.seen_F = 1;
				break;
			case 'S':
				next_target.seen_S = 1;
				break;
			case 'P':
				next_target.seen_P = 1;
				break;
			case 'T':
				next_target.seen_T = 1;
				break;
			case 'N':
				next_target.seen_N = 1;
				break;
			case '*':
				next_target.seen_checksum = 1;
				break;

			// comments
			case ';':
				next_target.seen_semi_comment = 1;
				break;
			case '(':
				next_target.seen_parens_comment = 1;
				break;

			// now for some numeracy
			case '-':
				parse_number.AddChar(c);
				break;
			case '.':
				parse_number.AddChar(c);
				break;
			#ifdef	DEBUG
			case ' ':
			case '\t':
			case 10:
			case 13:
				// ignore
				break;
			#endif

			default:
				parse_number.AddChar(c);
		}
	} else if ( next_target.seen_parens_comment == 1 && c == ')')
		next_target.seen_parens_comment = 0; // recognize stuff after a (comment)

	if (next_target.seen_checksum == 0)
		next_target.checksum_calculated = crc(next_target.checksum_calculated, c);

	// end of line
	if ((c == 10) || (c == 13)) {
		//if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
		//	serial_writechar(c);

		if (
		#ifdef	REQUIRE_LINENUMBER
			((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1)) ||
			(next_target.seen_M && (next_target.M == 110))
		#else
			1
		#endif
			) {
			if (
				#ifdef	REQUIRE_CHECKSUM
				((next_target.checksum_calculated == next_target.checksum_read) && (next_target.seen_checksum == 1))
				#else
				((next_target.checksum_calculated == next_target.checksum_read) || (next_target.seen_checksum == 0))
				#endif
				) {
				// process
                                // teacup waits until after the processing of the gcode to send the eol...
                                // FIXME - what to do here...
				serial_writestr_P(PSTR("ok "));
				process_gcode_command();
				serial_writechar('\n');

				// expect next line number
				if (next_target.seen_N == 1)
					next_target.N_expected = next_target.N + 1;
			}
			else {
				sersendf_P(PSTR("rs N%ld Expected checksum %d\n"), next_target.N_expected, next_target.checksum_calculated);
// 				request_resend();
			}
		}
		else {
			sersendf_P(PSTR("rs N%ld Expected line number %ld\n"), next_target.N_expected, next_target.N_expected);
// 			request_resend();
		}

		// reset variables
		next_target.seen_X = next_target.seen_Y = next_target.seen_Z = \
			next_target.seen_E = next_target.seen_F = next_target.seen_S = \
			next_target.seen_P = next_target.seen_T = next_target.seen_N = \
			next_target.seen_M = next_target.seen_checksum = next_target.seen_semi_comment = \
			next_target.seen_parens_comment = next_target.checksum_read = \
			next_target.checksum_calculated = 0;
		// last_field and read_digit are reset above already

		// assume a G1 by default
		next_target.seen_G = 1;
		next_target.G = 1;

		if (next_target.option_relative) {
			next_target.target.X = next_target.target.Y = next_target.target.Z = 0;
			#ifdef	E_ABSOLUTE
				next_target.target.E = 0;
			#endif
		}
		#ifndef	E_ABSOLUTE
			// E always relative
			next_target.target.E = 0;
		#endif
	}
}
Exemplo n.º 7
0
/** Character received - add it to our command.

  \param c The next character to process.

  \return Whether end of line was reached.

  This parser operates character by character, so there's no need for a
  buffer holding the entire line of G-code.
*/
uint8_t gcode_parse_char(uint8_t c) {
	uint8_t checksum_char = c;

	// uppercase
	if (c >= 'a' && c <= 'z')
		c &= ~32;
#ifdef SIMULATOR
  sim_gcode_ch(c);
#endif

  // An asterisk is a quasi-EOL and always ends all fields.
  if (c == '*') {
    next_target.seen_semi_comment = next_target.seen_parens_comment =
    next_target.read_string = 0;
  }

  // Skip comments and strings.
  if (next_target.seen_semi_comment == 0 &&
      next_target.seen_parens_comment == 0 &&
      next_target.read_string == 0
     ) {
    // Check if the field has ended. Either by a new field, space or EOL.
    if (last_field && (c < '0' || c > '9') && c != '.') {
			switch (last_field) {
				case 'G':
					next_target.G = read_digit.mantissa;
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.G);
					break;
				case 'M':
					next_target.M = read_digit.mantissa;
          #ifdef SD
            if (next_target.M == 23) {
              // SD card command with a filename.
              next_target.read_string = 1;  // Reset by string handler or EOL.
              str_buf_ptr = 0;
              last_field = 0;
            }
          #endif
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.M);
					break;
				case 'X':
					if (next_target.option_inches)
            next_target.target.axis[X] = decfloat_to_int(&read_digit, 25400);
					else
            next_target.target.axis[X] = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
            serwrite_int32(next_target.target.axis[X]);
					break;
				case 'Y':
					if (next_target.option_inches)
            next_target.target.axis[Y] = decfloat_to_int(&read_digit, 25400);
					else
            next_target.target.axis[Y] = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
            serwrite_int32(next_target.target.axis[Y]);
					break;
				case 'Z':
					if (next_target.option_inches)
            next_target.target.axis[Z] = decfloat_to_int(&read_digit, 25400);
					else
            next_target.target.axis[Z] = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
            serwrite_int32(next_target.target.axis[Z]);
					break;
				case 'E':
					if (next_target.option_inches)
            next_target.target.axis[E] = decfloat_to_int(&read_digit, 25400);
					else
            next_target.target.axis[E] = decfloat_to_int(&read_digit, 1000);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
            serwrite_int32(next_target.target.axis[E]);
					break;
				case 'F':
					// just use raw integer, we need move distance and n_steps to convert it to a useful value, so wait until we have those to convert it
					if (next_target.option_inches)
						next_target.target.F = decfloat_to_int(&read_digit, 25400);
					else
						next_target.target.F = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint32(next_target.target.F);
					break;
				case 'S':
					// if this is temperature, multiply by 4 to convert to quarter-degree units
					// cosmetically this should be done in the temperature section,
					// but it takes less code, less memory and loses no precision if we do it here instead
					if ((next_target.M == 104) || (next_target.M == 109) || (next_target.M == 140))
						next_target.S = decfloat_to_int(&read_digit, 4);
					// if this is heater PID stuff, multiply by PID_SCALE because we divide by PID_SCALE later on
					else if ((next_target.M >= 130) && (next_target.M <= 132))
						next_target.S = decfloat_to_int(&read_digit, PID_SCALE);
					else
						next_target.S = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_int32(next_target.S);
					break;
				case 'P':
					next_target.P = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint16(next_target.P);
					break;
				case 'T':
					next_target.T = read_digit.mantissa;
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.T);
					break;
				case 'N':
					next_target.N = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint32(next_target.N);
					break;
				case '*':
					next_target.checksum_read = decfloat_to_int(&read_digit, 1);
					if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
						serwrite_uint8(next_target.checksum_read);
					break;
			}
		}

		// new field?
		if ((c >= 'A' && c <= 'Z') || c == '*') {
			last_field = c;
      read_digit.sign = read_digit.mantissa = read_digit.exponent = 0;
			if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
				serial_writechar(c);
		}

		// process character
    // Can't do ranges in switch..case, so process actual digits here.
    // Do it early, as there are many more digits than characters expected.
    if (c >= '0' && c <= '9') {
      if (read_digit.exponent < DECFLOAT_EXP_MAX + 1 &&
          ((next_target.option_inches == 0 &&
          read_digit.mantissa < DECFLOAT_MANT_MM_MAX) ||
          (next_target.option_inches &&
          read_digit.mantissa < DECFLOAT_MANT_IN_MAX))) {
        // this is simply mantissa = (mantissa * 10) + atoi(c) in different clothes
        read_digit.mantissa = (read_digit.mantissa << 3) +
                              (read_digit.mantissa << 1) + (c - '0');
        if (read_digit.exponent)
          read_digit.exponent++;
      }
    }
    else {
      switch (c) {
        // Each currently known command is either G or M, so preserve
        // previous G/M unless a new one has appeared.
        // FIXME: same for T command
        case 'G':
          next_target.seen_G = 1;
          next_target.seen_M = 0;
          next_target.M = 0;
          break;
        case 'M':
          next_target.seen_M = 1;
          next_target.seen_G = 0;
          next_target.G = 0;
          break;
        case 'X':
          next_target.seen_X = 1;
          break;
        case 'Y':
          next_target.seen_Y = 1;
          break;
        case 'Z':
          next_target.seen_Z = 1;
          break;
        case 'E':
          next_target.seen_E = 1;
          break;
        case 'F':
          next_target.seen_F = 1;
          break;
        case 'S':
          next_target.seen_S = 1;
          break;
        case 'P':
          next_target.seen_P = 1;
          break;
        case 'T':
          next_target.seen_T = 1;
          break;
        case 'N':
          next_target.seen_N = 1;
          break;
        case '*':
          next_target.seen_checksum = 1;
          break;

        // comments
        case ';':
          next_target.seen_semi_comment = 1;    // Reset by EOL.
          break;
        case '(':
          next_target.seen_parens_comment = 1;  // Reset by ')' or EOL.
          break;

        // now for some numeracy
        case '-':
          read_digit.sign = 1;
          // force sign to be at start of number, so 1-2 = -2 instead of -12
          read_digit.exponent = 0;
          read_digit.mantissa = 0;
          break;
        case '.':
          if (read_digit.exponent == 0)
            read_digit.exponent = 1;
          break;
        #ifdef	DEBUG
          case ' ':
          case '\t':
          case 10:
          case 13:
            // ignore
            break;
        #endif

        default:
          #ifdef	DEBUG
            // invalid
            serial_writechar('?');
            serial_writechar(c);
            serial_writechar('?');
          #endif
          break;
      }
		}
	} else if ( next_target.seen_parens_comment == 1 && c == ')')
		next_target.seen_parens_comment = 0; // recognize stuff after a (comment)

	if (next_target.seen_checksum == 0)
		next_target.checksum_calculated =
			crc(next_target.checksum_calculated, checksum_char);

	// end of line
	if ((c == 10) || (c == 13)) {
		if (DEBUG_ECHO && (debug_flags & DEBUG_ECHO))
			serial_writechar(c);

    // Assume G1 for unspecified movements.
    if ( ! next_target.seen_G && ! next_target.seen_M && ! next_target.seen_T &&
        (next_target.seen_X || next_target.seen_Y || next_target.seen_Z ||
         next_target.seen_E || next_target.seen_F)) {
      next_target.seen_G = 1;
      next_target.G = 1;
    }

		if (
		#ifdef	REQUIRE_LINENUMBER
			((next_target.N >= next_target.N_expected) && (next_target.seen_N == 1)) ||
			(next_target.seen_M && (next_target.M == 110))
		#else
			1
		#endif
			) {
			if (
				#ifdef	REQUIRE_CHECKSUM
				((next_target.checksum_calculated == next_target.checksum_read) && (next_target.seen_checksum == 1))
				#else
				((next_target.checksum_calculated == next_target.checksum_read) || (next_target.seen_checksum == 0))
				#endif
				) {
				// process
				process_gcode_command();

        // Acknowledgement ("ok") is sent in the main loop, in mendel.c.

				// expect next line number
				if (next_target.seen_N == 1)
					next_target.N_expected = next_target.N + 1;
			}
			else {
				sersendf_P(PSTR("rs N%ld Expected checksum %d\n"), next_target.N_expected, next_target.checksum_calculated);
// 				request_resend();
			}
		}
		else {
			sersendf_P(PSTR("rs N%ld Expected line number %ld\n"), next_target.N_expected, next_target.N_expected);
// 			request_resend();
		}

		// reset variables
		next_target.seen_X = next_target.seen_Y = next_target.seen_Z = \
			next_target.seen_E = next_target.seen_F = next_target.seen_S = \
			next_target.seen_P = next_target.seen_T = next_target.seen_N = \
      next_target.seen_G = next_target.seen_M = next_target.seen_checksum = \
      next_target.seen_semi_comment = next_target.seen_parens_comment = \
      next_target.read_string = next_target.checksum_read = \
      next_target.checksum_calculated = 0;
      last_field = 0;
      read_digit.sign = read_digit.mantissa = read_digit.exponent = 0;

		if (next_target.option_all_relative) {
      next_target.target.axis[X] = next_target.target.axis[Y] = next_target.target.axis[Z] = 0;
		}
		if (next_target.option_all_relative || next_target.option_e_relative) {
      next_target.target.axis[E] = 0;
		}

    return 1;
	}

  #ifdef SD
  // Handle string reading. After checking for EOL.
  if (next_target.read_string) {
    if (c == ' ') {
      if (str_buf_ptr)
        next_target.read_string = 0;
    }
    else if (str_buf_ptr < STR_BUF_LEN) {
      gcode_str_buf[str_buf_ptr] = c;
      str_buf_ptr++;
      gcode_str_buf[str_buf_ptr] = '\0';
    }
  }
  #endif /* SD */

  return 0;
}
Exemplo n.º 8
0
void process_gcode_command() {
	uint32_t	backup_f;

	// convert relative to absolute
	if (next_target.option_all_relative) {
		next_target.target.X += startpoint.X;
		next_target.target.Y += startpoint.Y;
		next_target.target.Z += startpoint.Z;
	}

	// E relative movement.
	// Matches Sprinter's behaviour as of March 2012.
	if (next_target.option_all_relative || next_target.option_e_relative)
		next_target.target.e_relative = 1;
	else
		next_target.target.e_relative = 0;

	// implement axis limits
	#ifdef	X_MIN
		if (next_target.target.X < X_MIN * 1000.)
			next_target.target.X = X_MIN * 1000.;
	#endif
	#ifdef	X_MAX
		if (next_target.target.X > X_MAX * 1000.)
			next_target.target.X = X_MAX * 1000.;
	#endif
	#ifdef	Y_MIN
		if (next_target.target.Y < Y_MIN * 1000.)
			next_target.target.Y = Y_MIN * 1000.;
	#endif
	#ifdef	Y_MAX
		if (next_target.target.Y > Y_MAX * 1000.)
			next_target.target.Y = Y_MAX * 1000.;
	#endif
	#ifdef	Z_MIN
		if (next_target.target.Z < Z_MIN * 1000.)
			next_target.target.Z = Z_MIN * 1000.;
	#endif
	#ifdef	Z_MAX
		if (next_target.target.Z > Z_MAX * 1000.)
			next_target.target.Z = Z_MAX * 1000.;
	#endif


	// The GCode documentation was taken from http://reprap.org/wiki/Gcode .

	if (next_target.seen_T) {
	    //? --- T: Select Tool ---
	    //?
	    //? Example: T1
	    //?
	    //? Select extruder number 1 to build with.  Extruder numbering starts at 0.

	    next_tool = next_target.T;
	}

	if (next_target.seen_G) {
		uint8_t axisSelected = 0;
		switch (next_target.G) {
			case 0:
				//? G0: Rapid Linear Motion
				//?
				//? Example: G0 X12
				//?
				//? In this case move rapidly to X = 12 mm.  In fact, the RepRap firmware uses exactly the same code for rapid as it uses for controlled moves (see G1 below), as - for the RepRap machine - this is just as efficient as not doing so.  (The distinction comes from some old machine tools that used to move faster if the axes were not driven in a straight line.  For them G0 allowed any movement in space to get to the destination as fast as possible.)
				//?
				backup_f = next_target.target.F;
				next_target.target.F = MAXIMUM_FEEDRATE_X * 2L;
				enqueue(&next_target.target);
				next_target.target.F = backup_f;
				break;

			case 1:
				//? --- G1: Linear Motion at Feed Rate ---
				//?
				//? Example: G1 X90.6 Y13.8 E22.4
				//?
				//? Go in a straight line from the current (X, Y) point to the point (90.6, 13.8), extruding material as the move happens from the current extruded length to a length of 22.4 mm.
				//?
				enqueue(&next_target.target);
				break;

				//	G2 - Arc Clockwise
				// unimplemented

				//	G3 - Arc Counter-clockwise
				// unimplemented

			case 4:
				//? --- G4: Dwell ---
				//?
				//? Example: G4 P200
				//?
				//? In this case sit still doing nothing for 200 milliseconds.  During delays the state of the machine (for example the temperatures of its extruders) will still be preserved and controlled.
				//?
				queue_wait();
				// delay
				if (next_target.seen_P) {
					for (;next_target.P > 0;next_target.P--) {
						clock();
						delay_ms(1);
					}
				}
				break;

			case 20:
				//? --- G20: Set Units to Inches ---
				//?
				//? Example: G20
				//?
				//? Units from now on are in inches.
				//?
				next_target.option_inches = 1;
				break;

			case 21:
				//? --- G21: Set Units to Millimeters ---
				//?
				//? Example: G21
				//?
				//? Units from now on are in millimeters.  (This is the RepRap default.)
				//?
				next_target.option_inches = 0;
				break;

			case 30:
				//? --- G30: Go home via point ---
				//?
				//? Undocumented.
				enqueue(&next_target.target);
				// no break here, G30 is move and then go home

			case 28:
				//? --- G28: Home ---
				//?
				//? Example: G28
				//?
				//? This causes the RepRap machine to move back to its X, Y and Z zero endstops.  It does so accelerating, so as to get there fast.  But when it arrives it backs off by 1 mm in each direction slowly, then moves back slowly to the stop.  This ensures more accurate positioning.
				//?
				//? If you add coordinates, then just the axes with coordinates specified will be zeroed.  Thus
				//?
				//? G28 X0 Y72.3
				//?
				//? will zero the X and Y axes, but not Z.  The actual coordinate values are ignored.
				//?

				queue_wait();

				if (next_target.seen_X) {
					#if defined	X_MIN_PIN
						home_x_negative();
					#elif defined X_MAX_PIN
						home_x_positive();
					#endif
					axisSelected = 1;
				}
				if (next_target.seen_Y) {
					#if defined	Y_MIN_PIN
						home_y_negative();
					#elif defined Y_MAX_PIN
						home_y_positive();
					#endif
					axisSelected = 1;
				}
				if (next_target.seen_Z) {
					#if defined Z_MAX_PIN
						home_z_positive();
					#elif defined	Z_MIN_PIN
						home_z_negative();
					#endif
					axisSelected = 1;
				}
				// there's no point in moving E, as E has no endstops

				if (!axisSelected) {
					home();
				}
				break;

			case 90:
				//? --- G90: Set to Absolute Positioning ---
				//?
				//? Example: G90
				//?
				//? All coordinates from now on are absolute relative to the origin
				//? of the machine. This is the RepRap default.
				//?
				//? If you ever want to switch back and forth between relative and
				//? absolute movement keep in mind, X, Y and Z follow the machine's
				//? coordinate system while E doesn't change it's position in the
				//? coordinate system on relative movements.
				//?

				// No wait_queue() needed.
				next_target.option_all_relative = 0;
				break;

			case 91:
				//? --- G91: Set to Relative Positioning ---
				//?
				//? Example: G91
				//?
				//? All coordinates from now on are relative to the last position.
				//?

				// No wait_queue() needed.
				next_target.option_all_relative = 1;
				break;

			case 92:
				//? --- G92: Set Position ---
				//?
				//? Example: G92 X10 E90
				//?
				//? Allows programming of absolute zero point, by reseting the current position to the values specified.  This would set the machine's X coordinate to 10, and the extrude coordinate to 90. No physical motion will occur.
				//?

				queue_wait();

				if (next_target.seen_X) {
					startpoint.X = next_target.target.X;
					axisSelected = 1;
				}
				if (next_target.seen_Y) {
					startpoint.Y = next_target.target.Y;
					axisSelected = 1;
				}
				if (next_target.seen_Z) {
					startpoint.Z = next_target.target.Z;
					axisSelected = 1;
				}
				if (next_target.seen_E) {
					startpoint.E = next_target.target.E;
					axisSelected = 1;
				}

				if (axisSelected == 0) {
					startpoint.X = next_target.target.X =
					startpoint.Y = next_target.target.Y =
					startpoint.Z = next_target.target.Z =
					startpoint.E = next_target.target.E = 0;
				}

				dda_new_startpoint();
				break;

			case 161:
				//? --- G161: Home negative ---
				//?
				//? Find the minimum limit of the specified axes by searching for the limit switch.
				//?
				if (next_target.seen_X)
					home_x_negative();
				if (next_target.seen_Y)
					home_y_negative();
				if (next_target.seen_Z)
					home_z_negative();
				break;

			case 162:
				//? --- G162: Home positive ---
				//?
				//? Find the maximum limit of the specified axes by searching for the limit switch.
				//?
				if (next_target.seen_X)
					home_x_positive();
				if (next_target.seen_Y)
					home_y_positive();
				if (next_target.seen_Z)
					home_z_positive();
				break;

				// unknown gcode: spit an error
			default:
				sersendf_P(PSTR("E: Bad G-code %d"), next_target.G);
				// newline is sent from gcode_parse after we return
				return;
		}
	}
	else if (next_target.seen_M) {
		uint8_t i;

		switch (next_target.M) {
			case 0:
				//? --- M0: machine stop ---
				//?
				//? Example: M0
				//?
				//? http://linuxcnc.org/handbook/RS274NGC_3/RS274NGC_33a.html#1002379
				//? Unimplemented, especially the restart after the stop. Fall trough to M2.
				//?

			case 2:
				//? --- M2: program end ---
				//?
				//? Example: M2
				//?
				//? http://linuxcnc.org/handbook/RS274NGC_3/RS274NGC_33a.html#1002379
				//?
				queue_wait();
				for (i = 0; i < NUM_HEATERS; i++)
					temp_set(i, 0);
				power_off();
				break;

			case 112:
				//? --- M112: Emergency Stop ---
				//?
				//? Example: M112
				//?
				//? Any moves in progress are immediately terminated, then RepRap shuts down.  All motors and heaters are turned off.
				//? It can be started again by pressing the reset button on the master microcontroller.  See also M0.
				//?

				timer_stop();
				queue_flush();
				power_off();
				cli();
				for (;;)
					wd_reset();
				break;

			case 6:
				//? --- M6: tool change ---
				//?
				//? Undocumented.
				tool = next_tool;
				break;

			case 82:
				//? --- M82 - Set E codes absolute ---
				//?
				//? This is the default and overrides G90/G91.
				//? M82/M83 is not documented in the RepRap wiki, behaviour
				//? was taken from Sprinter as of March 2012.
				//?
				//? While E does relative movements, it doesn't change its
				//? position in the coordinate system. See also comment on G90.
				//?

				// No wait_queue() needed.
				next_target.option_e_relative = 0;
				break;

			case 83:
				//? --- M83 - Set E codes relative ---
				//?
				//? Counterpart to M82.
				//?

				// No wait_queue() needed.
				next_target.option_e_relative = 1;
				break;

			// M84- stop idle hold
			case 84:
				stepper_disable();
				x_disable();
				y_disable();
				z_disable();
				e_disable();
				break;

			// M3/M101- extruder on
			case 3:
			case 101:
				//? --- M101: extruder on ---
				//?
				//? Undocumented.
				if (temp_achieved() == 0) {
					enqueue(NULL);
				}
				#ifdef DC_EXTRUDER
					heater_set(DC_EXTRUDER, DC_EXTRUDER_PWM);
				#elif E_STARTSTOP_STEPS > 0
					do {
						// backup feedrate, move E very quickly then restore feedrate
						backup_f = startpoint.F;
						startpoint.F = MAXIMUM_FEEDRATE_E;
						SpecialMoveE(E_STARTSTOP_STEPS, MAXIMUM_FEEDRATE_E);
						startpoint.F = backup_f;
					} while (0);
				#endif
				break;

			// M102- extruder reverse

			// M5/M103- extruder off
			case 5:
			case 103:
				//? --- M103: extruder off ---
				//?
				//? Undocumented.
				#ifdef DC_EXTRUDER
					heater_set(DC_EXTRUDER, 0);
				#elif E_STARTSTOP_STEPS > 0
					do {
						// backup feedrate, move E very quickly then restore feedrate
						backup_f = startpoint.F;
						startpoint.F = MAXIMUM_FEEDRATE_E;
						SpecialMoveE(-E_STARTSTOP_STEPS, MAXIMUM_FEEDRATE_E);
						startpoint.F = backup_f;
					} while (0);
				#endif
				break;

			case 104:
				//? --- M104: Set Extruder Temperature (Fast) ---
				//?
				//? Example: M104 S190
				//?
				//? Set the temperature of the current extruder to 190<sup>o</sup>C and return control to the host immediately (''i.e.'' before that temperature has been reached by the extruder).  See also M116.
				//? Teacup supports an optional P parameter as a sensor index to address (eg M104 P1 S100 will set the bed temperature rather than the extruder temperature).
				//?
				if ( ! next_target.seen_S)
					break;
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				temp_set(next_target.P, next_target.S);
				if (next_target.S)
					power_on();
				break;

			case 105:
				//? --- M105: Get Extruder Temperature ---
				//?
				//? Example: M105
				//?
				//? Request the temperature of the current extruder and the build base in degrees Celsius.  The temperatures are returned to the host computer.  For example, the line sent to the host in response to this command looks like
				//?
				//? <tt>ok T:201 B:117</tt>
				//?
				//? Teacup supports an optional P parameter as a sensor index to address.
				//?
				#ifdef ENFORCE_ORDER
					queue_wait();
				#endif
				if ( ! next_target.seen_P)
					next_target.P = TEMP_SENSOR_none;
				temp_print(next_target.P);
				break;

			case 7:
			case 106:
				//? --- M106: Set Fan Speed ---
				//?
				//? Example: M106 S120
				//?
				//? Control the cooling fan (if any).
				//?

				#ifdef ENFORCE_ORDER
					// wait for all moves to complete
					queue_wait();
				#endif
				#ifdef HEATER_FAN
					if ( ! next_target.seen_S)
						break;
					temp_set(HEATER_FAN, next_target.S);
					if (next_target.S)
						power_on();
				#endif
				break;

			case 110:
				//? --- M110: Set Current Line Number ---
				//?
				//? Example: N123 M110
				//?
				//? Set the current line number to 123.  Thus the expected next line after this command will be 124.
				//? This is a no-op in Teacup.
				//?
				break;

			#ifdef	DEBUG
			case 111:
				//? --- M111: Set Debug Level ---
				//?
				//? Example: M111 S6
				//?
				//? Set the level of debugging information transmitted back to the host to level 6.  The level is the OR of three bits:
				//?
				//? <Pre>
				//? #define         DEBUG_PID       1
				//? #define         DEBUG_DDA       2
				//? #define         DEBUG_POSITION  4
				//? </pre>
				//?
				//? This command is only available in DEBUG builds of Teacup.

				if ( ! next_target.seen_S)
					break;
				debug_flags = next_target.S;
				break;
			#endif

			// M113- extruder PWM

			case 114:
				//? --- M114: Get Current Position ---
				//?
				//? Example: M114
				//?
				//? This causes the RepRap machine to report its current X, Y, Z and E coordinates to the host.
				//?
				//? For example, the machine returns a string such as:
				//?
				//? <tt>ok C: X:0.00 Y:0.00 Z:0.00 E:0.00</tt>
				//?
				#ifdef ENFORCE_ORDER
					// wait for all moves to complete
					queue_wait();
				#endif
				update_current_position();
				sersendf_P(PSTR("X:%lq,Y:%lq,Z:%lq,E:%lq,F:%lu"),
				                current_position.X, current_position.Y,
				                current_position.Z, current_position.E,
				                current_position.F);

				#ifdef	DEBUG
					if (DEBUG_POSITION && (debug_flags & DEBUG_POSITION)) {
						sersendf_P(PSTR(",c:%lu}\nEndpoint: X:%ld,Y:%ld,Z:%ld,E:%ld,F:%lu,c:%lu}"),
						                movebuffer[mb_tail].c, movebuffer[mb_tail].endpoint.X,
						                movebuffer[mb_tail].endpoint.Y, movebuffer[mb_tail].endpoint.Z,
						                movebuffer[mb_tail].endpoint.E, movebuffer[mb_tail].endpoint.F,
						#ifdef ACCELERATION_REPRAP
							movebuffer[mb_tail].end_c
						#else
							movebuffer[mb_tail].c
						#endif
						);
						print_queue();
					}
				#endif /* DEBUG */

				// newline is sent from gcode_parse after we return
				break;

			case 115:
				//? --- M115: Get Firmware Version and Capabilities ---
				//?
				//? Example: M115
				//?
				//? Request the Firmware Version and Capabilities of the current microcontroller
				//? The details are returned to the host computer as key:value pairs separated by spaces and terminated with a linefeed.
				//?
				//? sample data from firmware:
				//?  FIRMWARE_NAME:Teacup FIRMWARE_URL:http://github.com/triffid/Teacup_Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:1 TEMP_SENSOR_COUNT:1 HEATER_COUNT:1
				//?

				sersendf_P(PSTR("FIRMWARE_NAME:Teacup FIRMWARE_URL:http://github.com/triffid/Teacup_Firmware/ PROTOCOL_VERSION:1.0 MACHINE_TYPE:Mendel EXTRUDER_COUNT:%d TEMP_SENSOR_COUNT:%d HEATER_COUNT:%d"), 1, NUM_TEMP_SENSORS, NUM_HEATERS);
				// newline is sent from gcode_parse after we return
				break;

			case 116:
				//? --- M116: Wait ---
				//?
				//? Example: M116
				//?
				//? Wait for temperatures and other slowly-changing variables to arrive at their set values.

				enqueue(NULL);
				break;

			case 130:
				//? --- M130: heater P factor ---
				//? Undocumented.
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				if (next_target.seen_S)
					pid_set_p(next_target.P, next_target.S);
				break;

			case 131:
				//? --- M131: heater I factor ---
				//? Undocumented.
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				if (next_target.seen_S)
					pid_set_i(next_target.P, next_target.S);
				break;

			case 132:
				//? --- M132: heater D factor ---
				//? Undocumented.
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				if (next_target.seen_S)
					pid_set_d(next_target.P, next_target.S);
				break;

			case 133:
				//? --- M133: heater I limit ---
				//? Undocumented.
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				if (next_target.seen_S)
					pid_set_i_limit(next_target.P, next_target.S);
				break;

			case 134:
				//? --- M134: save PID settings to eeprom ---
				//? Undocumented.
				heater_save_settings();
				break;

			case 135:
				//? --- M135: set heater output ---
				//? Undocumented.
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				if (next_target.seen_S) {
					heater_set(next_target.P, next_target.S);
					power_on();
				}
				break;

			#ifdef	DEBUG
			case 136:
				//? --- M136: PRINT PID settings to host ---
				//? Undocumented.
				//? This comand is only available in DEBUG builds.
				if ( ! next_target.seen_P)
					next_target.P = HEATER_EXTRUDER;
				heater_print(next_target.P);
				break;
			#endif

			case 140:
				//? --- M140: Set heated bed temperature ---
				//? Undocumented.
				#ifdef	HEATER_BED
					if ( ! next_target.seen_S)
						break;
					temp_set(HEATER_BED, next_target.S);
					if (next_target.S)
						power_on();
				#endif
				break;

			case 190:
				//? --- M190: Power On ---
				//? Undocumented.
				//? This one is pointless in Teacup. Implemented to calm the RepRap gurus.
				//?
				power_on();
				stepper_enable();
				x_enable();
				y_enable();
				z_enable();
				e_enable();
				break;

			case 191:
				//? --- M191: Power Off ---
				//? Undocumented.
				//? Same as M2. RepRap obviously prefers to invent new numbers instead of looking into standards. 
				#ifdef ENFORCE_ORDER
					// wait for all moves to complete
					queue_wait();
				#endif
				power_off();
				break;

			case 200:
				//? --- M200: report endstop status ---
				//? Report the current status of the endstops configured in the firmware to the host.
				power_on();
				#if defined(X_MIN_PIN)
					sersendf_P(PSTR("x_min:%d "), x_min());
				#endif
				#if defined(X_MAX_PIN)
					sersendf_P(PSTR("x_max:%d "), x_max());
				#endif
				#if defined(Y_MIN_PIN)
					sersendf_P(PSTR("y_min:%d "), y_min());
				#endif
				#if defined(Y_MAX_PIN)
					sersendf_P(PSTR("y_max:%d "), y_max());
				#endif
				#if defined(Z_MIN_PIN)
					sersendf_P(PSTR("z_min:%d "), z_min());
				#endif
				#if defined(Z_MAX_PIN)
					sersendf_P(PSTR("z_max:%d "), z_max());
				#endif
				#if !(defined(X_MIN_PIN) || defined(X_MAX_PIN) || defined(Y_MIN_PIN) || defined(Y_MAX_PIN) || defined(Z_MIN_PIN) || defined(Z_MAX_PIN))
					sersendf_P(PSTR("no endstops defined"));
				#endif
				break;

			#ifdef	DEBUG
			case 240:
				//? --- M240: echo off ---
				//? Disable echo.
				//? This command is only available in DEBUG builds.
				debug_flags &= ~DEBUG_ECHO;
				serial_writestr_P(PSTR("Echo off"));
				// newline is sent from gcode_parse after we return
				break;

			case 241:
				//? --- M241: echo on ---
				//? Enable echo.
				//? This command is only available in DEBUG builds.
				debug_flags |= DEBUG_ECHO;
				serial_writestr_P(PSTR("Echo on"));
				// newline is sent from gcode_parse after we return
				break;

			#endif /* DEBUG */

				// unknown mcode: spit an error
			default:
				sersendf_P(PSTR("E: Bad M-code %d"), next_target.M);
				// newline is sent from gcode_parse after we return
		} // switch (next_target.M)
	} // else if (next_target.seen_M)
} // process_gcode_command()
Exemplo n.º 9
0
/** Open a file for reading.

  \param filename Name of the file to open and to read G-code from.

  Before too long this will cause the printer to read G-code from this file
  until done or until stopped by G-code coming in over the serial line.
*/
void sd_open(const char* filename) {
  result = pf_open(filename);
  if (result != FR_OK) {
    sersendf_P(PSTR("E: failed to open file. (%su)\n"), result);
  }
}
Exemplo n.º 10
0
/** Mount the SD card.
*/
void sd_mount(void) {
  result = pf_mount(&sdfile);
  if (result != FR_OK)
    sersendf_P(PSTR("E: SD init failed. (%su)\n"), result);
}