More Anycubic + Trigorilla mappings, ExtUI (#18903)
parent
7a5d408bc0
commit
6bcfb58cd4
@ -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