349 lines
15 KiB
C++
349 lines
15 KiB
C++
![]() |
/*
|
||
|
serial.cpp - Header for system level commands and real-time processes
|
||
|
Part of Grbl
|
||
|
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
|
||
|
|
||
|
2018 - Bart Dring This file was modified for use on the ESP32
|
||
|
CPU. Do not use this with Grbl for atMega328P
|
||
|
|
||
|
Grbl 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.
|
||
|
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "grbl.h"
|
||
|
#include "commands.h"
|
||
|
|
||
|
#define RX_RING_BUFFER (RX_BUFFER_SIZE+1)
|
||
|
#define TX_RING_BUFFER (TX_BUFFER_SIZE+1)
|
||
|
|
||
|
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
|
||
|
|
||
|
uint8_t serial_rx_buffer[CLIENT_COUNT][RX_RING_BUFFER];
|
||
|
uint8_t serial_rx_buffer_head[CLIENT_COUNT] = {0};
|
||
|
volatile uint8_t serial_rx_buffer_tail[CLIENT_COUNT] = {0};
|
||
|
static TaskHandle_t serialCheckTaskHandle = 0;
|
||
|
|
||
|
// Returns the number of bytes available in the RX serial buffer.
|
||
|
uint8_t serial_get_rx_buffer_available(uint8_t client)
|
||
|
{
|
||
|
uint8_t client_idx = client - 1;
|
||
|
|
||
|
uint8_t rtail = serial_rx_buffer_tail[client_idx]; // Copy to limit multiple calls to volatile
|
||
|
if (serial_rx_buffer_head[client_idx] >= rtail) { return(RX_BUFFER_SIZE - (serial_rx_buffer_head[client_idx]-rtail)); }
|
||
|
return((rtail-serial_rx_buffer_head[client_idx]-1));
|
||
|
}
|
||
|
|
||
|
void serial_init()
|
||
|
{
|
||
|
Serial.begin(BAUD_RATE);
|
||
|
grbl_send(CLIENT_SERIAL,"\r\n"); // create some white space after ESP32 boot info
|
||
|
serialCheckTaskHandle = 0;
|
||
|
// create a task to check for incoming data
|
||
|
xTaskCreatePinnedToCore( serialCheckTask, // task
|
||
|
"serialCheckTask", // name for task
|
||
|
8192, // size of task stack
|
||
|
NULL, // parameters
|
||
|
1, // priority
|
||
|
&serialCheckTaskHandle,
|
||
|
0 // core
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// this task runs and checks for data on all interfaces
|
||
|
// REaltime stuff is acted upon, then characters are added to the appropriate buffer
|
||
|
void serialCheckTask(void *pvParameters)
|
||
|
{
|
||
|
uint8_t data = 0;
|
||
|
uint8_t next_head;
|
||
|
uint8_t client = CLIENT_ALL; // who send the data
|
||
|
|
||
|
uint8_t client_idx = 0; // index of data buffer
|
||
|
|
||
|
while(true) // run continuously
|
||
|
{
|
||
|
while (Serial.available() || inputBuffer.available()
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
|| (SerialBT.hasClient() && SerialBT.available())
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|
||
|
|| Serial2Socket.available()
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
|
||
|
|| telnet_server.available()
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
if (Serial.available())
|
||
|
{
|
||
|
client = CLIENT_SERIAL;
|
||
|
data = Serial.read();
|
||
|
}
|
||
|
else if (inputBuffer.available()){
|
||
|
client = CLIENT_INPUT;
|
||
|
data = inputBuffer.read();
|
||
|
}
|
||
|
else
|
||
|
{ //currently is wifi or BT but better to prepare both can be live
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
if(SerialBT.hasClient() && SerialBT.available()){
|
||
|
client = CLIENT_BT;
|
||
|
data = SerialBT.read();
|
||
|
//Serial.write(data); // echo all data to serial
|
||
|
} else {
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|
||
|
if (Serial2Socket.available()) {
|
||
|
client = CLIENT_WEBUI;
|
||
|
data = Serial2Socket.read();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
|
||
|
if(telnet_server.available()){
|
||
|
client = CLIENT_TELNET;
|
||
|
data = telnet_server.read();
|
||
|
}
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
client_idx = client - 1; // for zero based array
|
||
|
|
||
|
// Pick off realtime command characters directly from the serial stream. These characters are
|
||
|
// not passed into the main buffer, but these set system state flag bits for realtime execution.
|
||
|
switch (data) {
|
||
|
case CMD_RESET:
|
||
|
mc_reset(); // Call motion control reset routine.
|
||
|
//report_init_message(client); // fool senders into thinking a reset happened.
|
||
|
break;
|
||
|
case CMD_STATUS_REPORT:
|
||
|
report_realtime_status(client);
|
||
|
break; // direct call instead of setting flag
|
||
|
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
|
||
|
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
|
||
|
default :
|
||
|
if (data > 0x7F) { // Real-time control characters are extended ACSII only.
|
||
|
switch(data) {
|
||
|
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
|
||
|
case CMD_JOG_CANCEL:
|
||
|
if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
|
||
|
system_set_exec_state_flag(EXEC_MOTION_CANCEL);
|
||
|
}
|
||
|
break;
|
||
|
#ifdef DEBUG
|
||
|
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
|
||
|
#endif
|
||
|
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
|
||
|
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
|
||
|
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
|
||
|
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
|
||
|
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
|
||
|
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
|
||
|
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
|
||
|
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
|
||
|
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
|
||
|
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
|
||
|
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
|
||
|
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
|
||
|
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
|
||
|
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
|
||
|
#ifdef COOLANT_FLOOD_PIN
|
||
|
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
|
||
|
#endif
|
||
|
#ifdef COOLANT_MIST_PIN
|
||
|
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
|
||
|
#endif
|
||
|
}
|
||
|
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
|
||
|
} else { // Write character to buffer
|
||
|
|
||
|
vTaskEnterCritical(&myMutex);
|
||
|
next_head = serial_rx_buffer_head[client_idx] + 1;
|
||
|
if (next_head == RX_RING_BUFFER) { next_head = 0; }
|
||
|
|
||
|
// Write data to buffer unless it is full.
|
||
|
if (next_head != serial_rx_buffer_tail[client_idx]) {
|
||
|
serial_rx_buffer[client_idx][serial_rx_buffer_head[client_idx]] = data;
|
||
|
serial_rx_buffer_head[client_idx] = next_head;
|
||
|
}
|
||
|
vTaskExitCritical(&myMutex);
|
||
|
}
|
||
|
} // switch data
|
||
|
} // if something available
|
||
|
COMMANDS::handle();
|
||
|
#ifdef ENABLE_WIFI
|
||
|
wifi_config.handle();
|
||
|
#endif
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
bt_config.handle();
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|
||
|
Serial2Socket.handle_flush();
|
||
|
#endif
|
||
|
vTaskDelay(1 / portTICK_RATE_MS); // Yield to other tasks
|
||
|
} // while(true)
|
||
|
}
|
||
|
|
||
|
// ==================== call this in main protocol loop if you want it in the main task =========
|
||
|
// be sure to stop task.
|
||
|
// Realtime stuff is acted upon, then characters are added to the appropriate buffer
|
||
|
void serialCheck()
|
||
|
{
|
||
|
uint8_t data = 0;
|
||
|
uint8_t next_head;
|
||
|
uint8_t client = CLIENT_SERIAL; // who send the data
|
||
|
|
||
|
uint8_t client_idx = 0; // index of data buffer
|
||
|
|
||
|
|
||
|
while (Serial.available() || inputBuffer.available()
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
|| (SerialBT.hasClient() && SerialBT.available())
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|
||
|
|| Serial2Socket.available()
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
if (Serial.available())
|
||
|
{
|
||
|
client = CLIENT_SERIAL;
|
||
|
data = Serial.read();
|
||
|
}
|
||
|
else if (inputBuffer.available())
|
||
|
{
|
||
|
client = CLIENT_INPUT;
|
||
|
data = inputBuffer.read();
|
||
|
}
|
||
|
#if defined (ENABLE_BLUETOOTH) || (defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN))
|
||
|
else
|
||
|
{ //currently is wifi or BT but better to prepare both can be live
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
if(SerialBT.hasClient() && SerialBT.available()){
|
||
|
client = CLIENT_BT;
|
||
|
data = SerialBT.read();
|
||
|
} else {
|
||
|
#endif
|
||
|
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|
||
|
client = CLIENT_WEBUI;
|
||
|
data = Serial2Socket.read();
|
||
|
#endif
|
||
|
#ifdef ENABLE_BLUETOOTH
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
client_idx = client - 1; // for zero based array
|
||
|
|
||
|
// Pick off realtime command characters directly from the serial stream. These characters are
|
||
|
// not passed into the main buffer, but these set system state flag bits for realtime execution.
|
||
|
switch (data) {
|
||
|
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
|
||
|
case CMD_STATUS_REPORT:
|
||
|
report_realtime_status(client);
|
||
|
break; // direct call instead of setting flag
|
||
|
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
|
||
|
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
|
||
|
default :
|
||
|
if (data > 0x7F) { // Real-time control characters are extended ACSII only.
|
||
|
switch(data) {
|
||
|
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
|
||
|
case CMD_JOG_CANCEL:
|
||
|
if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
|
||
|
system_set_exec_state_flag(EXEC_MOTION_CANCEL);
|
||
|
}
|
||
|
break;
|
||
|
#ifdef DEBUG
|
||
|
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
|
||
|
#endif
|
||
|
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
|
||
|
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
|
||
|
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
|
||
|
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
|
||
|
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
|
||
|
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
|
||
|
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
|
||
|
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
|
||
|
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
|
||
|
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
|
||
|
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
|
||
|
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
|
||
|
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
|
||
|
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
|
||
|
#ifdef COOLANT_FLOOD_PIN
|
||
|
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
|
||
|
#endif
|
||
|
#ifdef COOLANT_MIST_PIN
|
||
|
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
|
||
|
#endif
|
||
|
}
|
||
|
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
|
||
|
} else { // Write character to buffer
|
||
|
|
||
|
|
||
|
next_head = serial_rx_buffer_head[client_idx] + 1;
|
||
|
if (next_head == RX_RING_BUFFER) { next_head = 0; }
|
||
|
|
||
|
// Write data to buffer unless it is full.
|
||
|
if (next_head != serial_rx_buffer_tail[client_idx]) {
|
||
|
serial_rx_buffer[client_idx][serial_rx_buffer_head[client_idx]] = data;
|
||
|
serial_rx_buffer_head[client_idx] = next_head;
|
||
|
}
|
||
|
}
|
||
|
} // switch data
|
||
|
} // if something available
|
||
|
}
|
||
|
|
||
|
void serial_reset_read_buffer(uint8_t client)
|
||
|
{
|
||
|
for (uint8_t client_num = 0; client_num <= CLIENT_COUNT; client_num++)
|
||
|
{
|
||
|
if (client == client_num || client == CLIENT_ALL)
|
||
|
{
|
||
|
serial_rx_buffer_tail[client_num-1] = serial_rx_buffer_head[client_num-1];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Writes one byte to the TX serial buffer. Called by main program.
|
||
|
void serial_write(uint8_t data) {
|
||
|
Serial.write((char)data);
|
||
|
}
|
||
|
// Fetches the first byte in the serial read buffer. Called by main program.
|
||
|
uint8_t serial_read(uint8_t client)
|
||
|
{
|
||
|
uint8_t client_idx = client - 1;
|
||
|
|
||
|
uint8_t tail = serial_rx_buffer_tail[client_idx]; // Temporary serial_rx_buffer_tail (to optimize for volatile)
|
||
|
if (serial_rx_buffer_head[client_idx] == tail) {
|
||
|
return SERIAL_NO_DATA;
|
||
|
} else {
|
||
|
vTaskEnterCritical(&myMutex); // make sure buffer is not modified while reading by newly read chars from the serial when we are here
|
||
|
uint8_t data = serial_rx_buffer[client_idx][tail];
|
||
|
|
||
|
tail++;
|
||
|
if (tail == RX_RING_BUFFER) { tail = 0; }
|
||
|
serial_rx_buffer_tail[client_idx] = tail;
|
||
|
vTaskExitCritical(&myMutex);
|
||
|
return data;
|
||
|
}
|
||
|
}
|
||
|
|