Marco Burato
4 years ago
committed by
GitHub
16 changed files with 837 additions and 667 deletions
@ -1,290 +0,0 @@ |
|||
/*
|
|||
anycubic_serial.cpp --- Support for Anycubic i3 Mega TFT serial connection |
|||
Created by Christian Hopp on 09.12.17. |
|||
|
|||
Original file: |
|||
HardwareSerial.cpp - Hardware serial library for Wiring |
|||
Copyright (c) 2006 Nicholas Zambetti. All right reserved. |
|||
|
|||
This library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
This library is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with this library; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
Modified 23 November 2006 by David A. Mellis |
|||
Modified 28 September 2010 by Mark Sproul |
|||
Modified 14 August 2012 by Alarus |
|||
*/ |
|||
|
|||
#include "../../../../inc/MarlinConfig.h" |
|||
|
|||
#if ENABLED(ANYCUBIC_TFT_MODEL) |
|||
|
|||
#include <Arduino.h> |
|||
|
|||
// This next line disables the entire anycubic_serial.cpp,
|
|||
// to support AtTiny series and other chips without a UART
|
|||
#ifdef UBRR3H |
|||
|
|||
#include "anycubic_serial.h" |
|||
|
|||
#include <stdlib.h> |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#include <inttypes.h> |
|||
#include "wiring_private.h" |
|||
|
|||
// Define constants and variables for buffering incoming serial data. We're
|
|||
// using a ring buffer (I think), in which head is the index of the location
|
|||
// to which to write the next incoming character and tail is the index of the
|
|||
// location from which to read.
|
|||
#if (RAMEND < 1000) |
|||
#define SERIAL_BUFFER_SIZE 64 |
|||
#else |
|||
#define SERIAL_BUFFER_SIZE 128 |
|||
#endif |
|||
|
|||
struct ring_buffer { |
|||
unsigned char buffer[SERIAL_BUFFER_SIZE]; |
|||
volatile unsigned int head; |
|||
volatile unsigned int tail; |
|||
}; |
|||
|
|||
ring_buffer rx_buffer_ajg = { { 0 }, 0, 0 }; |
|||
ring_buffer tx_buffer_ajg = { { 0 }, 0, 0 }; |
|||
|
|||
inline void store_char(unsigned char c, ring_buffer *buffer) { |
|||
int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE; |
|||
|
|||
// if we should be storing the received character into the location
|
|||
// just before the tail (meaning that the head would advance to the
|
|||
// current location of the tail), we're about to overflow the buffer
|
|||
// and so we don't write the character or advance the head.
|
|||
if (i != buffer->tail) { |
|||
buffer->buffer[buffer->head] = c; |
|||
buffer->head = i; |
|||
} |
|||
} |
|||
|
|||
#if defined(USART3_RX_vect) && defined(UDR3) |
|||
void serialEvent3() __attribute__((weak)); |
|||
void serialEvent3() {} |
|||
#define serialEvent3_implemented |
|||
ISR(USART3_RX_vect) { |
|||
if (bit_is_clear(UCSR3A, UPE3)) { |
|||
unsigned char c = UDR3; |
|||
store_char(c, &rx_buffer_ajg); |
|||
} |
|||
else { |
|||
unsigned char c = UDR3; |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
#ifdef USART3_UDRE_vect |
|||
|
|||
ISR(USART3_UDRE_vect) { |
|||
if (tx_buffer_ajg.head == tx_buffer_ajg.tail) { |
|||
// Buffer empty, so disable interrupts
|
|||
cbi(UCSR3B, UDRIE3); |
|||
} |
|||
else { |
|||
// There is more data in the output buffer. Send the next byte
|
|||
unsigned char c = tx_buffer_ajg.buffer[tx_buffer_ajg.tail]; |
|||
tx_buffer_ajg.tail = (tx_buffer_ajg.tail + 1) % SERIAL_BUFFER_SIZE; |
|||
|
|||
UDR3 = c; |
|||
} |
|||
} |
|||
|
|||
#endif |
|||
|
|||
// Constructors ////////////////////////////////////////////////////////////////
|
|||
|
|||
AnycubicSerialClass::AnycubicSerialClass(ring_buffer *rx_buffer, ring_buffer *tx_buffer, |
|||
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, |
|||
volatile uint8_t *ucsra, volatile uint8_t *ucsrb, |
|||
volatile uint8_t *ucsrc, volatile uint8_t *udr, |
|||
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x |
|||
) { |
|||
_rx_buffer = rx_buffer; |
|||
_tx_buffer = tx_buffer; |
|||
_ubrrh = ubrrh; |
|||
_ubrrl = ubrrl; |
|||
_ucsra = ucsra; |
|||
_ucsrb = ucsrb; |
|||
_ucsrc = ucsrc; |
|||
_udr = udr; |
|||
_rxen = rxen; |
|||
_txen = txen; |
|||
_rxcie = rxcie; |
|||
_udrie = udrie; |
|||
_u2x = u2x; |
|||
} |
|||
|
|||
// Public Methods //////////////////////////////////////////////////////////////
|
|||
|
|||
void AnycubicSerialClass::begin(unsigned long baud) { |
|||
uint16_t baud_setting; |
|||
bool use_u2x = true; |
|||
|
|||
#if F_CPU == 16000000UL |
|||
// hardcoded exception for compatibility with the bootloader shipped
|
|||
// with the Duemilanove and previous boards and the firmware on the 8U2
|
|||
// on the Uno and Mega 2560.
|
|||
if (baud == 57600) use_u2x = false; |
|||
#endif |
|||
|
|||
try_again: |
|||
|
|||
if (use_u2x) { |
|||
*_ucsra = 1 << _u2x; |
|||
baud_setting = (F_CPU / 4 / baud - 1) / 2; |
|||
} else { |
|||
*_ucsra = 0; |
|||
baud_setting = (F_CPU / 8 / baud - 1) / 2; |
|||
} |
|||
|
|||
if ((baud_setting > 4095) && use_u2x) { |
|||
use_u2x = false; |
|||
goto try_again; |
|||
} |
|||
|
|||
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
|
|||
*_ubrrh = baud_setting >> 8; |
|||
*_ubrrl = baud_setting; |
|||
|
|||
transmitting = false; |
|||
|
|||
sbi(*_ucsrb, _rxen); |
|||
sbi(*_ucsrb, _txen); |
|||
sbi(*_ucsrb, _rxcie); |
|||
cbi(*_ucsrb, _udrie); |
|||
} |
|||
|
|||
void AnycubicSerialClass::begin(unsigned long baud, byte config) { |
|||
uint16_t baud_setting; |
|||
uint8_t current_config; |
|||
bool use_u2x = true; |
|||
|
|||
#if F_CPU == 16000000UL |
|||
// hardcoded exception for compatibility with the bootloader shipped
|
|||
// with the Duemilanove and previous boards and the firmware on the 8U2
|
|||
// on the Uno and Mega 2560.
|
|||
if (baud == 57600) use_u2x = false; |
|||
#endif |
|||
|
|||
try_again: |
|||
|
|||
if (use_u2x) { |
|||
*_ucsra = 1 << _u2x; |
|||
baud_setting = (F_CPU / 4 / baud - 1) / 2; |
|||
} |
|||
else { |
|||
*_ucsra = 0; |
|||
baud_setting = (F_CPU / 8 / baud - 1) / 2; |
|||
} |
|||
|
|||
if ((baud_setting > 4095) && use_u2x) { |
|||
use_u2x = false; |
|||
goto try_again; |
|||
} |
|||
|
|||
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
|
|||
*_ubrrh = baud_setting >> 8; |
|||
*_ubrrl = baud_setting; |
|||
|
|||
//set the data bits, parity, and stop bits
|
|||
#ifdef __AVR_ATmega8__ |
|||
config |= 0x80; // select UCSRC register (shared with UBRRH)
|
|||
#endif |
|||
*_ucsrc = config; |
|||
|
|||
sbi(*_ucsrb, _rxen); |
|||
sbi(*_ucsrb, _txen); |
|||
sbi(*_ucsrb, _rxcie); |
|||
cbi(*_ucsrb, _udrie); |
|||
} |
|||
|
|||
void AnycubicSerialClass::end() { |
|||
// wait for transmission of outgoing data
|
|||
while (_tx_buffer->head != _tx_buffer->tail) |
|||
; |
|||
|
|||
cbi(*_ucsrb, _rxen); |
|||
cbi(*_ucsrb, _txen); |
|||
cbi(*_ucsrb, _rxcie); |
|||
cbi(*_ucsrb, _udrie); |
|||
|
|||
// clear any received data
|
|||
_rx_buffer->head = _rx_buffer->tail; |
|||
} |
|||
|
|||
int AnycubicSerialClass::available(void) { |
|||
return (int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE; |
|||
} |
|||
|
|||
int AnycubicSerialClass::peek(void) { |
|||
if (_rx_buffer->head == _rx_buffer->tail) { |
|||
return -1; |
|||
} else { |
|||
return _rx_buffer->buffer[_rx_buffer->tail]; |
|||
} |
|||
} |
|||
|
|||
int AnycubicSerialClass::read(void) { |
|||
// if the head isn't ahead of the tail, we don't have any characters
|
|||
if (_rx_buffer->head == _rx_buffer->tail) { |
|||
return -1; |
|||
} else { |
|||
unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; |
|||
_rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE; |
|||
return c; |
|||
} |
|||
} |
|||
|
|||
void AnycubicSerialClass::flush() { |
|||
// UDR is kept full while the buffer is not empty, so TXC triggers when EMPTY && SENT
|
|||
while (transmitting && ! (*_ucsra & _BV(TXC0))); |
|||
transmitting = false; |
|||
} |
|||
|
|||
size_t AnycubicSerialClass::write(uint8_t c) { |
|||
int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE; |
|||
|
|||
// If the output buffer is full, there's nothing for it other than to
|
|||
// wait for the interrupt handler to empty it a bit
|
|||
// ???: return 0 here instead?
|
|||
while (i == _tx_buffer->tail) |
|||
; |
|||
|
|||
_tx_buffer->buffer[_tx_buffer->head] = c; |
|||
_tx_buffer->head = i; |
|||
|
|||
sbi(*_ucsrb, _udrie); |
|||
// clear the TXC bit -- "can be cleared by writing a one to its bit location"
|
|||
transmitting = true; |
|||
sbi(*_ucsra, TXC0); |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
AnycubicSerialClass::operator bool() { |
|||
return true; |
|||
} |
|||
|
|||
// Preinstantiate Objects //////////////////////////////////////////////////////
|
|||
|
|||
AnycubicSerialClass AnycubicSerial(&rx_buffer_ajg, &tx_buffer_ajg, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3); |
|||
|
|||
#endif // UBRR3H
|
|||
#endif // ANYCUBIC_TFT_MODEL
|
@ -1,145 +0,0 @@ |
|||
/*
|
|||
anycubic_serial.h --- Support for Anycubic i3 Mega TFT serial connection |
|||
Created by Christian Hopp on 09.12.17. |
|||
|
|||
Original file: |
|||
|
|||
HardwareSerial.h - Hardware serial library for Wiring |
|||
Copyright (c) 2006 Nicholas Zambetti. All right reserved. |
|||
|
|||
This library is free software; you can redistribute it and/or |
|||
modify it under the terms of the GNU Lesser General Public |
|||
License as published by the Free Software Foundation; either |
|||
version 2.1 of the License, or (at your option) any later version. |
|||
|
|||
This library is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public |
|||
License along with this library; if not, write to the Free Software |
|||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|||
|
|||
Modified 28 September 2010 by Mark Sproul |
|||
Modified 14 August 2012 by Alarus |
|||
*/ |
|||
#pragma once |
|||
|
|||
#include <inttypes.h> |
|||
#include <avr/pgmspace.h> |
|||
|
|||
#include "Stream.h" |
|||
|
|||
#define FORCE_INLINE __attribute__((always_inline)) inline |
|||
|
|||
struct ring_buffer; |
|||
|
|||
class AnycubicSerialClass : public Stream { |
|||
private: |
|||
ring_buffer *_rx_buffer; |
|||
ring_buffer *_tx_buffer; |
|||
volatile uint8_t *_ubrrh; |
|||
volatile uint8_t *_ubrrl; |
|||
volatile uint8_t *_ucsra; |
|||
volatile uint8_t *_ucsrb; |
|||
volatile uint8_t *_ucsrc; |
|||
volatile uint8_t *_udr; |
|||
uint8_t _rxen; |
|||
uint8_t _txen; |
|||
uint8_t _rxcie; |
|||
uint8_t _udrie; |
|||
uint8_t _u2x; |
|||
bool transmitting; |
|||
public: |
|||
AnycubicSerialClass(ring_buffer *rx_buffer, ring_buffer *tx_buffer, |
|||
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, |
|||
volatile uint8_t *ucsra, volatile uint8_t *ucsrb, |
|||
volatile uint8_t *ucsrc, volatile uint8_t *udr, |
|||
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x |
|||
); |
|||
void begin(unsigned long); |
|||
void begin(unsigned long, uint8_t); |
|||
void end(); |
|||
virtual int available(void); |
|||
virtual int peek(void); |
|||
virtual int read(void); |
|||
virtual void flush(void); |
|||
virtual size_t write(uint8_t); |
|||
inline size_t write(unsigned long n) { return write((uint8_t)n); } |
|||
inline size_t write(long n) { return write((uint8_t)n); } |
|||
inline size_t write(unsigned int n) { return write((uint8_t)n); } |
|||
inline size_t write(int n) { return write((uint8_t)n); } |
|||
using Print::write; // pull in write(str) and write(buf, size) from Print
|
|||
operator bool(); |
|||
}; |
|||
|
|||
// Define config for Serial.begin(baud, config);
|
|||
#define SERIAL_5N1 0x00 |
|||
#define SERIAL_6N1 0x02 |
|||
#define SERIAL_7N1 0x04 |
|||
#define SERIAL_8N1 0x06 |
|||
#define SERIAL_5N2 0x08 |
|||
#define SERIAL_6N2 0x0A |
|||
#define SERIAL_7N2 0x0C |
|||
#define SERIAL_8N2 0x0E |
|||
#define SERIAL_5E1 0x20 |
|||
#define SERIAL_6E1 0x22 |
|||
#define SERIAL_7E1 0x24 |
|||
#define SERIAL_8E1 0x26 |
|||
#define SERIAL_5E2 0x28 |
|||
#define SERIAL_6E2 0x2A |
|||
#define SERIAL_7E2 0x2C |
|||
#define SERIAL_8E2 0x2E |
|||
#define SERIAL_5O1 0x30 |
|||
#define SERIAL_6O1 0x32 |
|||
#define SERIAL_7O1 0x34 |
|||
#define SERIAL_8O1 0x36 |
|||
#define SERIAL_5O2 0x38 |
|||
#define SERIAL_6O2 0x3A |
|||
#define SERIAL_7O2 0x3C |
|||
#define SERIAL_8O2 0x3E |
|||
|
|||
extern void serialEventRun(void) __attribute__((weak)); |
|||
|
|||
#define ANYCUBIC_SERIAL_PROTOCOL(x) (AnycubicSerial.print(x)) |
|||
#define ANYCUBIC_SERIAL_PROTOCOL_F(x,y) (AnycubicSerial.print(x, y)) |
|||
#define ANYCUBIC_SERIAL_PROTOCOLPGM(x) (AnycubicSerialprintPGM(PSTR(x))) |
|||
#define ANYCUBIC_SERIAL_(x) (AnycubicSerial.print(x), AnycubicSerial.write('\n')) |
|||
#define ANYCUBIC_SERIAL_PROTOCOLLN(x) (AnycubicSerial.print(x), AnycubicSerial.write('\r'), AnycubicSerial.write('\n')) |
|||
#define ANYCUBIC_SERIAL_PROTOCOLLNPGM(x) (AnycubicSerialprintPGM(PSTR(x)), AnycubicSerial.write('\r'), AnycubicSerial.write('\n')) |
|||
|
|||
#define ANYCUBIC_SERIAL_START() (AnycubicSerial.write('\r'), AnycubicSerial.write('\n')) |
|||
#define ANYCUBIC_SERIAL_CMD_SEND(x) (AnycubicSerialprintPGM(PSTR(x)), AnycubicSerial.write('\r'), AnycubicSerial.write('\n')) |
|||
#define ANYCUBIC_SERIAL_ENTER() (AnycubicSerial.write('\r'), AnycubicSerial.write('\n')) |
|||
#define ANYCUBIC_SERIAL_SPACE() (AnycubicSerial.write(' ')) |
|||
|
|||
const char newErr[] PROGMEM = "ERR "; |
|||
const char newSucc[] PROGMEM = "OK"; |
|||
|
|||
#define ANYCUBIC_SERIAL_ERROR_START (AnycubicSerialprintPGM(newErr)) |
|||
#define ANYCUBIC_SERIAL_ERROR(x) ANYCUBIC_SERIAL_PROTOCOL(x) |
|||
#define ANYCUBIC_SERIAL_ERRORPGM(x) ANYCUBIC_SERIAL_PROTOCOLPGM(x) |
|||
#define ANYCUBIC_SERIAL_ERRORLN(x) ANYCUBIC_SERIAL_PROTOCOLLN(x) |
|||
#define ANYCUBIC_SERIAL_ERRORLNPGM(x) ANYCUBIC_SERIAL_PROTOCOLLNPGM(x) |
|||
|
|||
//##define ANYCUBIC_SERIAL_ECHO_START (AnycubicSerialprintPGM(newSucc))
|
|||
#define ANYCUBIC_SERIAL_ECHOLN(x) ANYCUBIC_SERIAL_PROTOCOLLN(x) |
|||
#define ANYCUBIC_SERIAL_SUCC_START (AnycubicSerialprintPGM(newSucc)) |
|||
#define ANYCUBIC_SERIAL_ECHOPAIR(name,value) (serial_echopair_P(PSTR(name),(value))) |
|||
#define ANYCUBIC_SERIAL_ECHOPGM(x) ANYCUBIC_SERIAL_PROTOCOLPGM(x) |
|||
#define ANYCUBIC_SERIAL_ECHO(x) ANYCUBIC_SERIAL_PROTOCOL(x) |
|||
|
|||
#ifdef UBRR3H |
|||
|
|||
extern AnycubicSerialClass AnycubicSerial; |
|||
|
|||
FORCE_INLINE void AnycubicSerialprintPGM(const char *str) { |
|||
char ch = pgm_read_byte(str); |
|||
while (ch) { |
|||
AnycubicSerial.write(ch); |
|||
ch = pgm_read_byte(++str); |
|||
} |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,536 @@ |
|||
/**
|
|||
* Marlin 3D Printer Firmware |
|||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
|||
* |
|||
* Based on Sprinter and grbl. |
|||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
/**
|
|||
* extui_anycubic_chiron_lcd.cpp |
|||
* |
|||
* Anycubic Chiron TFT support for Marlin |
|||
*/ |
|||
|
|||
#include "../inc/MarlinConfigPre.h" |
|||
|
|||
#if ENABLED(ANYCUBIC_LCD_CHIRON) |
|||
|
|||
#include "extui/ui_api.h" |
|||
|
|||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR) |
|||
#if GRID_MAX_POINTS_X != 5 || GRID_MAX_POINTS_Y != 5 |
|||
#error ANYCUBIC CHIRON LCD requires a 5x5 bed leveling grid (GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y) |
|||
#endif |
|||
#else |
|||
#error ANYCUBIC CHIRON LCD requires AUTO_BED_LEVELING_BILINEAR enabled |
|||
#endif |
|||
|
|||
#if DISABLED(FILAMENT_RUNOUT_SENSOR) |
|||
#error ANYCUBIC CHIRON LCD requires FILAMENT_RUNOUT_SENSOR enabled |
|||
#endif |
|||
|
|||
#if ENABLED(POWER_LOSS_RECOVERY) |
|||
#error ANYCUBIC CHIRON LCD does not currently support POWER_LOSS_RECOVERY |
|||
#endif |
|||
|
|||
static bool is_auto_leveling = false; |
|||
static bool is_printing_from_sd = false; |
|||
static bool is_out_of_filament = false; |
|||
|
|||
static void sendNewLine(void) { |
|||
ANYCUBIC_LCD_SERIAL.write('\r'); |
|||
ANYCUBIC_LCD_SERIAL.write('\n'); |
|||
} |
|||
|
|||
static void send(const char *str) { |
|||
ANYCUBIC_LCD_SERIAL.print(str); |
|||
} |
|||
|
|||
static void sendLine(const char *str) { |
|||
send(str); |
|||
sendNewLine(); |
|||
} |
|||
|
|||
static void send_P(PGM_P str) { |
|||
while (const char c = pgm_read_byte(str++)) |
|||
ANYCUBIC_LCD_SERIAL.write(c); |
|||
} |
|||
|
|||
static void sendLine_P(PGM_P str) { |
|||
send_P(str); |
|||
sendNewLine(); |
|||
} |
|||
|
|||
static void sendValue_P(PGM_P prefix, int value) { |
|||
send_P(prefix); |
|||
ANYCUBIC_LCD_SERIAL.print(value); |
|||
} |
|||
|
|||
static void sendValue_P(PGM_P prefix, float value) { |
|||
send_P(prefix); |
|||
ANYCUBIC_LCD_SERIAL.print(value); |
|||
} |
|||
|
|||
static void sendValueLine_P(PGM_P prefix, int value) { |
|||
send_P(prefix); |
|||
ANYCUBIC_LCD_SERIAL.print(value); |
|||
sendNewLine(); |
|||
} |
|||
|
|||
static void sendValueLine_P(PGM_P prefix, float value) { |
|||
send_P(prefix); |
|||
ANYCUBIC_LCD_SERIAL.print(value); |
|||
sendNewLine(); |
|||
} |
|||
|
|||
static int parseIntArgument(const char *buffer, char letterId) { |
|||
char *p = strchr(buffer, letterId); |
|||
if (!p) |
|||
return -1; |
|||
return atoi(p+1); |
|||
} |
|||
|
|||
static float parseFloatArgument(const char *buffer, char letterId) { |
|||
char *p = strchr(buffer, letterId); |
|||
if (!p) |
|||
return NAN; |
|||
return strtof(p+1, nullptr); |
|||
} |
|||
|
|||
static int mmToHundredths(float x) { |
|||
// Round
|
|||
if (x >= 0) |
|||
x += 0.005f; |
|||
else |
|||
x -= 0.005f; |
|||
return (int)(x * 100.0f); |
|||
} |
|||
|
|||
static float hundredthsToMm(int x) { |
|||
return x / 100.0f; |
|||
} |
|||
|
|||
#define SEND_PGM(str) send_P(PSTR(str)) |
|||
#define SENDLINE_PGM(str) sendLine_P(PSTR(str)) |
|||
#define SENDVALUE_PGM(prefix, value) sendValue_P(PSTR(prefix), value) |
|||
#define SENDVALUELINE_PGM(prefix, value) sendValueLine_P(PSTR(prefix), value) |
|||
|
|||
namespace ExtUI { |
|||
|
|||
static void moveAxis(float delta, feedRate_t feedrate, axis_t axis) { |
|||
float pos = getAxisPosition_mm(axis); |
|||
pos += delta; |
|||
setAxisPosition_mm(pos, axis, feedrate); |
|||
} |
|||
|
|||
static void handleCmd(const char *rx) { |
|||
static FileList fileList; |
|||
static char selectedFileShortName[8+1+3+1]; |
|||
|
|||
if (rx[0] != 'A') { |
|||
SERIAL_ECHOPGM("Unexpected RX: "); |
|||
SERIAL_ECHOLN(rx); |
|||
|
|||
return; |
|||
} |
|||
|
|||
const int cmd = atoi(&rx[1]); |
|||
|
|||
// Uncomment for debugging RX
|
|||
//if (cmd > 7 && cmd != 20) {
|
|||
// SERIAL_ECHOPGM("RX: ");
|
|||
// SERIAL_ECHOLN(rx);
|
|||
//}
|
|||
|
|||
switch (cmd) { |
|||
case 0: // Get Hotend Actual Temperature
|
|||
SENDVALUELINE_PGM("A0V ", (int)getActualTemp_celsius(E0)); |
|||
break; |
|||
case 1: // Get Hotend Target Temperature
|
|||
SENDVALUELINE_PGM("A1V ", (int)getTargetTemp_celsius(E0)); |
|||
break; |
|||
case 2: // Get Bed Actual Temperature
|
|||
SENDVALUELINE_PGM("A2V ", (int)getActualTemp_celsius(BED)); |
|||
break; |
|||
case 3: // Get Bed Target Temperature
|
|||
SENDVALUELINE_PGM("A3V ", (int)getTargetTemp_celsius(BED)); |
|||
break; |
|||
case 4: // Get Fan Speed
|
|||
SENDVALUELINE_PGM("A4V ", (int)getTargetFan_percent(FAN0)); |
|||
break; |
|||
case 5: // Get Current Coordinates
|
|||
SENDVALUE_PGM("A5V X: ", getAxisPosition_mm(X)); |
|||
SENDVALUE_PGM(" Y: ", getAxisPosition_mm(Y)); |
|||
SENDVALUE_PGM(" Z: ", getAxisPosition_mm(Z)); |
|||
sendNewLine(); |
|||
break; |
|||
case 6: // Get SD Card Print Status
|
|||
if (isPrintingFromMedia()) |
|||
SENDVALUELINE_PGM("A6V ", (int)getProgress_percent()); |
|||
else |
|||
SENDLINE_PGM("A6V ---"); |
|||
break; |
|||
case 7: // Get Printing Time
|
|||
if (isPrinting()) { |
|||
const int totalMinutes = getProgress_seconds_elapsed() / 60; |
|||
SENDVALUE_PGM("A7V ", (int)(totalMinutes/60)); |
|||
SENDVALUE_PGM(" H ", (int)(totalMinutes%60)); |
|||
SENDLINE_PGM(" M"); |
|||
} else { |
|||
SENDLINE_PGM("A7V 999:999"); |
|||
} |
|||
break; |
|||
case 8: // Get SD Card File List
|
|||
if (isMediaInserted()) { |
|||
const int startIndex = parseIntArgument(rx, 'S'); |
|||
SENDLINE_PGM("FN "); |
|||
for (int i = 0, fileIndex = 0, numFiles = 0; i < (int)fileList.count() && numFiles < 4; i++) { |
|||
fileList.seek(i); |
|||
if (!fileList.isDir()) { |
|||
if (fileIndex >= startIndex) { |
|||
sendLine(fileList.shortFilename()); |
|||
sendLine(fileList.longFilename()); |
|||
numFiles++; |
|||
} |
|||
fileIndex++; |
|||
} |
|||
} |
|||
SENDLINE_PGM("END"); |
|||
} else { |
|||
SENDLINE_PGM("J02"); |
|||
} |
|||
break; |
|||
case 9: // Pause SD Card Print
|
|||
if (isPrintingFromMedia()) { |
|||
pausePrint(); |
|||
is_printing_from_sd = false; |
|||
SENDLINE_PGM("J05"); |
|||
} else { |
|||
SENDLINE_PGM("J16"); // Print stopped
|
|||
} |
|||
break; |
|||
case 10: // Resume SD Card Print
|
|||
if (is_out_of_filament) { |
|||
is_out_of_filament = false; |
|||
// Filament change did eject the old filament automatically,
|
|||
// now continue and load the new one
|
|||
setUserConfirmed(); |
|||
SENDLINE_PGM("J04"); // Printing from SD card
|
|||
} else if (isPrintingFromMediaPaused()) { |
|||
resumePrint(); |
|||
SENDLINE_PGM("J04"); // Printing from SD card
|
|||
} |
|||
break; |
|||
case 11: // Stop SD Card Print
|
|||
if (isPrintingFromMedia()) { |
|||
stopPrint(); |
|||
is_printing_from_sd = false; |
|||
SENDLINE_PGM("J16"); // Print stopped
|
|||
} |
|||
break; |
|||
//case 12: // Kill
|
|||
// break;
|
|||
case 13: // Select File
|
|||
if (!isPrinting()) { |
|||
// Store selected file name
|
|||
char *p = strchr(rx, ' '); |
|||
if (p != nullptr && strlen(p+1) < sizeof(selectedFileShortName)) { |
|||
strcpy(selectedFileShortName, p+1); |
|||
SENDLINE_PGM("J20"); // Open succeeded
|
|||
} |
|||
else |
|||
SENDLINE_PGM("J21"); // Open failed
|
|||
} |
|||
break; |
|||
case 14: // Start Print
|
|||
if (!isPrinting() && strcmp(selectedFileShortName, "") != 0) { |
|||
printFile(selectedFileShortName); |
|||
is_printing_from_sd = true; |
|||
SENDLINE_PGM("J04"); // Printing from SD card
|
|||
} |
|||
break; |
|||
case 15: // Resume from power outage
|
|||
// This is not supported, just report print as completed
|
|||
SENDLINE_PGM("J16"); // Print stopped
|
|||
break; |
|||
case 16: // Set Hotend Target Temperature
|
|||
{ |
|||
int temp = parseIntArgument(rx, 'S'); |
|||
if (temp >= 0) |
|||
setTargetTemp_celsius(temp, E0); |
|||
} |
|||
break; |
|||
case 17: // Set Bed Target Temperature
|
|||
{ |
|||
int temp = parseIntArgument(rx, 'S'); |
|||
if (temp >= 0) |
|||
setTargetTemp_celsius(temp, BED); |
|||
} |
|||
break; |
|||
case 18: // Set Fan Speed
|
|||
{ |
|||
int temp = parseIntArgument(rx, 'S'); |
|||
if (temp >= 0) |
|||
setTargetFan_percent(temp, FAN0); |
|||
} |
|||
break; |
|||
case 19: // Disable Motors
|
|||
injectCommands_P(PSTR("M84")); |
|||
break; |
|||
case 20: // Get/Set Printing Speed
|
|||
{ |
|||
int newPerc = parseIntArgument(rx, 'S'); |
|||
if (newPerc >= 0) |
|||
setFeedrate_percent(newPerc); |
|||
else |
|||
SENDVALUELINE_PGM("A20V ", (int)getFeedrate_percent()); |
|||
} |
|||
break; |
|||
case 21: // Home axes
|
|||
if (!isPrinting()) { |
|||
const bool hasX = strchr(rx, 'X') != nullptr, |
|||
hasY = strchr(rx, 'Y') != nullptr, |
|||
hasZ = strchr(rx, 'Z') != nullptr, |
|||
hasC = strchr(rx, 'C') != nullptr; |
|||
if (hasX || hasY || hasZ) { |
|||
if (hasX) injectCommands_P(PSTR("G28 X")); |
|||
if (hasY) injectCommands_P(PSTR("G28 Y")); |
|||
if (hasZ) injectCommands_P(PSTR("G28 Z")); |
|||
} else if (hasC) { |
|||
injectCommands_P(PSTR("G28")); |
|||
} |
|||
} |
|||
break; |
|||
case 22: // Move axes
|
|||
if (!isPrinting()) { |
|||
const int feedrate = parseIntArgument(rx, 'F') / 60; |
|||
float delta; |
|||
if (!isnan(delta = parseFloatArgument(rx, 'X'))) |
|||
moveAxis(delta, feedrate, X); |
|||
else if (!isnan(delta = parseFloatArgument(rx, 'Y'))) |
|||
moveAxis(delta, feedrate, Y); |
|||
else if (!isnan(delta = parseFloatArgument(rx, 'Z'))) |
|||
moveAxis(delta, feedrate, Z); |
|||
} |
|||
break; |
|||
case 23: // Preheat PLA
|
|||
setTargetTemp_celsius(PREHEAT_1_TEMP_HOTEND, E0); |
|||
setTargetTemp_celsius(PREHEAT_1_TEMP_BED, BED); |
|||
SENDLINE_PGM("OK"); |
|||
break; |
|||
case 24: // Preheat ABS
|
|||
setTargetTemp_celsius(PREHEAT_2_TEMP_HOTEND, E0); |
|||
setTargetTemp_celsius(PREHEAT_2_TEMP_BED, BED); |
|||
SENDLINE_PGM("OK"); |
|||
break; |
|||
case 25: // Cool down
|
|||
setTargetTemp_celsius(0, E0); |
|||
setTargetTemp_celsius(0, BED); |
|||
SENDLINE_PGM("J12"); |
|||
break; |
|||
case 26: // Refresh SD Card
|
|||
fileList.refresh(); |
|||
break; |
|||
//case 27: // Adjust Servo Angles
|
|||
// break;
|
|||
//case 28: // Filament Test
|
|||
// break;
|
|||
case 29: // Get Bed Autolevel Grid
|
|||
{ |
|||
int x = parseIntArgument(rx, 'X'), |
|||
y = parseIntArgument(rx, 'Y'); |
|||
if (x != -1 && y != -1) { |
|||
xy_uint8_t coord; |
|||
coord.set(x, y); |
|||
const int value = mmToHundredths(getMeshPoint(coord)); |
|||
SENDVALUELINE_PGM("A29V ", value); |
|||
} |
|||
} |
|||
break; |
|||
case 30: // Autolevel
|
|||
if (strchr(rx, 'S')) { // Autoleveling started by clicking "PROBE" and then "OK"
|
|||
// Note:
|
|||
// We check for completion by monitoring the command queue.
|
|||
// Since it will become empty *while* processing the last injected command,
|
|||
// we enqueue an extra 10ms delay so we can the determine when all the others
|
|||
// have completed.
|
|||
if (isMachineHomed()) |
|||
injectCommands_P(PSTR("G29\nG4 P10")); |
|||
else |
|||
injectCommands_P(PSTR("G28\nG29\nG4 P10")); |
|||
is_auto_leveling = true; |
|||
} else { // Entering Autoleveling screen
|
|||
if (isPrinting()) |
|||
SENDLINE_PGM("J24"); // Disallow autoleveling
|
|||
else |
|||
SENDLINE_PGM("J26"); // Allow autoleveling
|
|||
} |
|||
break; |
|||
case 31: // Set Bed Autolevel Z offset
|
|||
if (strchr(rx, 'G')) { // Get
|
|||
SENDVALUELINE_PGM("A31V ", getZOffset_mm()); |
|||
} else if (strchr(rx, 'S')) { // Set
|
|||
float delta = parseFloatArgument(rx, 'S'); |
|||
delta = constrain(delta, -1.0, 1.0); |
|||
setZOffset_mm(getZOffset_mm() + delta); |
|||
|
|||
SENDVALUELINE_PGM("A31V ", getZOffset_mm()); |
|||
} else if (strchr(rx, 'D')) { // Save
|
|||
injectCommands_P(PSTR("M500")); |
|||
} |
|||
break; |
|||
//case 32: // ?
|
|||
// break;
|
|||
case 33: // Get Version Info
|
|||
SENDLINE_PGM("J33 " SHORT_BUILD_VERSION); |
|||
break; |
|||
case 34: // Set Bed Autolevel Grid
|
|||
{ |
|||
int x = parseIntArgument(rx, 'X'), |
|||
y = parseIntArgument(rx, 'Y'), |
|||
v = parseIntArgument(rx, 'V'); |
|||
if (x != -1 && y != -1 && v != -1) { // Set new value
|
|||
float value = hundredthsToMm(v); |
|||
value = constrain(value, -10, 10); |
|||
|
|||
xy_uint8_t coord; |
|||
coord.set(x, y); |
|||
setMeshPoint(coord, value); |
|||
} else if (strchr(rx, 'S')) { // Save (apply new values)
|
|||
injectCommands_P(PSTR("M500")); |
|||
} else if (strchr(rx, 'C')) { // Cancel (discard new values)
|
|||
injectCommands_P(PSTR("M501")); |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
#define RX_LEN_MAX 63 |
|||
static void parseSerialRx() { |
|||
static char rxBuffer[RX_LEN_MAX+1]; |
|||
static uint8_t rxLen = 0; |
|||
|
|||
while (ANYCUBIC_LCD_SERIAL.available()) { |
|||
const char c = ANYCUBIC_LCD_SERIAL.read(); |
|||
switch (c) { |
|||
case '\r': case '\n': |
|||
if (rxLen > 0 && rxLen <= RX_LEN_MAX) { |
|||
rxBuffer[rxLen] = '\0'; // Terminate string
|
|||
handleCmd(rxBuffer); |
|||
} |
|||
rxLen = 0; |
|||
break; |
|||
default: |
|||
if (rxLen < RX_LEN_MAX) |
|||
rxBuffer[rxLen++] = c; |
|||
else { |
|||
rxLen = 0xFF; // Overrun
|
|||
SERIAL_ECHOPGM("Warning: dropping long received line"); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void detectPrintFromSdCompletion() { |
|||
// Note: printFile() queues some commands that actually start the print, so isPrintingFromMedia()
|
|||
// initially returns false
|
|||
if (is_printing_from_sd && !commandsInQueue() && !isPrintingFromMedia()) { |
|||
is_printing_from_sd = false; |
|||
SENDLINE_PGM("J14"); // Print done
|
|||
} |
|||
} |
|||
|
|||
static void detectAutolevelingCompletion() { |
|||
if (is_auto_leveling && !commandsInQueue()) { |
|||
is_auto_leveling = false; |
|||
injectCommands_P(PSTR("M500")); |
|||
SENDLINE_PGM("J25"); // Autoleveling done
|
|||
} |
|||
} |
|||
|
|||
void onStartup() { |
|||
ANYCUBIC_LCD_SERIAL.begin(115200); |
|||
sendNewLine(); |
|||
SENDLINE_PGM("J17"); // Reset
|
|||
delay_ms(10); |
|||
SENDLINE_PGM("J12"); // Ready
|
|||
} |
|||
|
|||
void onIdle() { |
|||
parseSerialRx(); |
|||
detectAutolevelingCompletion(); |
|||
detectPrintFromSdCompletion(); |
|||
} |
|||
|
|||
void onPrinterKilled(PGM_P const error, PGM_P const component) { } |
|||
|
|||
void onMediaInserted() { |
|||
SENDLINE_PGM("J00"); // SD Inserted
|
|||
} |
|||
|
|||
void onMediaError() { } |
|||
|
|||
void onMediaRemoved() { |
|||
SENDLINE_PGM("J01"); // SD Removed
|
|||
} |
|||
|
|||
void onPlayTone(const uint16_t frequency, const uint16_t duration) { |
|||
tone(BEEPER_PIN, frequency, duration); |
|||
} |
|||
|
|||
void onPrintTimerStarted() { } |
|||
|
|||
void onPrintTimerPaused() { } |
|||
|
|||
void onPrintTimerStopped() { } |
|||
|
|||
void onFilamentRunout(const extruder_t extruder) { |
|||
is_out_of_filament = true; |
|||
SENDLINE_PGM("J23"); // Filament runout
|
|||
SENDLINE_PGM("J18"); // Print paused
|
|||
// Note: printer will unload filament automatically
|
|||
} |
|||
|
|||
void onUserConfirmRequired(const char * const msg) { } |
|||
|
|||
void onStatusChanged(const char * const msg) { } |
|||
|
|||
void onFactoryReset() { } |
|||
|
|||
void onStoreSettings(char *buff) { } |
|||
|
|||
void onLoadSettings(const char *buff) { } |
|||
|
|||
void onConfigurationStoreWritten(bool success) { } |
|||
|
|||
void onConfigurationStoreRead(bool success) { } |
|||
|
|||
void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) { } |
|||
|
|||
#if ENABLED(POWER_LOSS_RECOVERY) |
|||
void onPowerLossResume() { } |
|||
#endif |
|||
|
|||
#if HAS_PID_HEATING |
|||
void onPidTuning(const result_t rst) { } |
|||
#endif |
|||
} |
|||
|
|||
#endif // ANYCUBIC_LCD_CHIRON
|
Loading…
Reference in new issue