mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-04-20 20:21:18 +02:00
Merge 9a6cd8dd76856a3e8e44f1cf60e7c06c0f0fdc4d into 8ac61b88d444ff1e8d48e17b590ba383ed591560
This commit is contained in:
commit
53320dd9f4
@ -52,6 +52,10 @@
|
|||||||
#include "DCCEX.h"
|
#include "DCCEX.h"
|
||||||
#include "Display_Implementation.h"
|
#include "Display_Implementation.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#endif // ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
#ifdef CPU_TYPE_ERROR
|
#ifdef CPU_TYPE_ERROR
|
||||||
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH THE ARCHITECTURES LISTED IN defines.h
|
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH THE ARCHITECTURES LISTED IN defines.h
|
||||||
#endif
|
#endif
|
||||||
@ -85,7 +89,7 @@ void setup()
|
|||||||
delay(STARTUP_DELAY);
|
delay(STARTUP_DELAY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Initialise HAL layer before reading EEprom or setting up MotorDrivers
|
// Initialise HAL layer before reading EEprom or setting up MotorDrivers
|
||||||
IODevice::begin();
|
IODevice::begin();
|
||||||
|
|
||||||
// As the setup of a motor shield may require a read of the current sense input from the ADC,
|
// As the setup of a motor shield may require a read of the current sense input from the ADC,
|
||||||
@ -112,12 +116,15 @@ void setup()
|
|||||||
// ESP32 needs wifi on always
|
// ESP32 needs wifi on always
|
||||||
PASSWDCHECK(WIFI_PASSWORD); // compile time check
|
PASSWDCHECK(WIFI_PASSWORD); // compile time check
|
||||||
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
||||||
|
#if OTA_AUTO_INIT
|
||||||
|
Diag::OTA = true;
|
||||||
|
#endif // OTA_AUTO_INIT
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
#if ETHERNET_ON
|
#if ETHERNET_ON
|
||||||
EthernetInterface::setup();
|
EthernetInterface::setup();
|
||||||
#endif // ETHERNET_ON
|
#endif // ETHERNET_ON
|
||||||
|
|
||||||
// Responsibility 3: Start the DCC engine.
|
// Responsibility 3: Start the DCC engine.
|
||||||
DCC::begin();
|
DCC::begin();
|
||||||
|
|
||||||
@ -165,20 +172,64 @@ void loop()
|
|||||||
// Responsibility 1: Handle DCC background processes
|
// Responsibility 1: Handle DCC background processes
|
||||||
// (loco reminders and power checks)
|
// (loco reminders and power checks)
|
||||||
DCC::loop();
|
DCC::loop();
|
||||||
|
|
||||||
// Responsibility 2: handle any incoming commands on USB connection
|
// Responsibility 2: handle any incoming commands on USB connection
|
||||||
SerialManager::loop();
|
SerialManager::loop();
|
||||||
|
|
||||||
// Responsibility 3: Optionally handle any incoming WiFi traffic
|
// Responsibility 3: Optionally handle any incoming WiFi traffic
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
#if WIFI_ON
|
#if WIFI_ON
|
||||||
WifiInterface::loop();
|
WifiInterface::loop();
|
||||||
|
|
||||||
#endif //WIFI_ON
|
#endif //WIFI_ON
|
||||||
#else //ARDUINO_ARCH_ESP32
|
#else //ARDUINO_ARCH_ESP32
|
||||||
#ifndef WIFI_TASK_ON_CORE0
|
#ifndef WIFI_TASK_ON_CORE0
|
||||||
WifiESP::loop();
|
WifiESP::loop();
|
||||||
#endif
|
#endif
|
||||||
|
// Responsibility 4: Optionally handle Arduino OTA updates
|
||||||
|
if (Diag::OTA) {
|
||||||
|
static bool otaInitialised = false;
|
||||||
|
// Initialise OTA if not already done
|
||||||
|
if (!otaInitialised) {
|
||||||
|
ArduinoOTA.setHostname(WIFI_HOSTNAME);
|
||||||
|
// Prevent locos from moving during OTA
|
||||||
|
ArduinoOTA.onStart([]() {
|
||||||
|
// Emergency stop all locos
|
||||||
|
DCC::setThrottle(0,1,1);
|
||||||
|
// Disable tracks power
|
||||||
|
TrackManager::setMainPower(POWERMODE::OFF);
|
||||||
|
TrackManager::setProgPower(POWERMODE::OFF);
|
||||||
|
// Broadcast power status
|
||||||
|
CommandDistributor::broadcastPower();
|
||||||
|
DISPLAY_START (
|
||||||
|
LCD(0,F("OTA update"));
|
||||||
|
LCD(1,F("In progress..."));
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ArduinoOTA.onEnd([]() {
|
||||||
|
DISPLAY_START (
|
||||||
|
LCD(0,F("OTA update"));
|
||||||
|
LCD(1,F("Complete"));
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ArduinoOTA.onError([](ota_error_t error) {
|
||||||
|
DISPLAY_START (
|
||||||
|
LCD(0,F("OTA update"));
|
||||||
|
LCD(1,F("Error: %d"), error);
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// Set OTA password if defined
|
||||||
|
#ifdef OTA_AUTH
|
||||||
|
ArduinoOTA.setPassword(OTA_AUTH);
|
||||||
|
#endif // OTA_AUTH
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
otaInitialised = true;
|
||||||
|
}
|
||||||
|
// Handle OTA if initialised
|
||||||
|
else {
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif //ARDUINO_ARCH_ESP32
|
#endif //ARDUINO_ARCH_ESP32
|
||||||
#if ETHERNET_ON
|
#if ETHERNET_ON
|
||||||
EthernetInterface::loop();
|
EthernetInterface::loop();
|
||||||
|
161
DCCEXParser.cpp
161
DCCEXParser.cpp
@ -9,7 +9,7 @@
|
|||||||
* © 2020-2021 Chris Harlow
|
* © 2020-2021 Chris Harlow
|
||||||
* © 2022 Colin Murdoch
|
* © 2022 Colin Murdoch
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
@ -68,10 +68,10 @@ Once a new OPCODE is decided upon, update this list.
|
|||||||
K, Reserved for future use - Potentially Railcom
|
K, Reserved for future use - Potentially Railcom
|
||||||
l, Loco speedbyte/function map broadcast
|
l, Loco speedbyte/function map broadcast
|
||||||
L, Reserved for LCC interface (implemented in EXRAIL)
|
L, Reserved for LCC interface (implemented in EXRAIL)
|
||||||
m, message to throttles broadcast
|
m, message to throttles broadcast
|
||||||
M, Write DCC packet
|
M, Write DCC packet
|
||||||
n, Reserved for SensorCam
|
n, Reserved for SensorCam
|
||||||
N, Reserved for Sensorcam
|
N, Reserved for Sensorcam
|
||||||
o, Neopixel driver (see also IO_NeoPixel.h)
|
o, Neopixel driver (see also IO_NeoPixel.h)
|
||||||
O, Output broadcast
|
O, Output broadcast
|
||||||
p, Broadcast power state
|
p, Broadcast power state
|
||||||
@ -92,7 +92,7 @@ Once a new OPCODE is decided upon, update this list.
|
|||||||
W, Write CV
|
W, Write CV
|
||||||
x,
|
x,
|
||||||
X, Invalid command response
|
X, Invalid command response
|
||||||
y,
|
y,
|
||||||
Y, Output broadcast
|
Y, Output broadcast
|
||||||
z, Direct output
|
z, Direct output
|
||||||
Z, Output configuration/control
|
Z, Output configuration/control
|
||||||
@ -123,13 +123,13 @@ Once a new OPCODE is decided upon, update this list.
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This macro can't be created easily as a portable function because the
|
// This macro can't be created easily as a portable function because the
|
||||||
// flashlist requires a far pointer for high flash access.
|
// flashlist requires a far pointer for high flash access.
|
||||||
#define SENDFLASHLIST(stream,flashList) \
|
#define SENDFLASHLIST(stream,flashList) \
|
||||||
for (int16_t i=0;;i+=sizeof(flashList[0])) { \
|
for (int16_t i=0;;i+=sizeof(flashList[0])) { \
|
||||||
int16_t value=GETHIGHFLASHW(flashList,i); \
|
int16_t value=GETHIGHFLASHW(flashList,i); \
|
||||||
if (value==INT16_MAX) break; \
|
if (value==INT16_MAX) break; \
|
||||||
StringFormatter::send(stream,F(" %d"),value); \
|
StringFormatter::send(stream,F(" %d"),value); \
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
||||||
bool DCCEXParser::stashBusy;
|
bool DCCEXParser::stashBusy;
|
||||||
@ -255,10 +255,10 @@ void DCCEXParser::setAtCommandCallback(AT_COMMAND_CALLBACK callback)
|
|||||||
atCommandCallback = callback;
|
atCommandCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an F() string
|
// Parse an F() string
|
||||||
void DCCEXParser::parse(const FSH * cmd) {
|
void DCCEXParser::parse(const FSH * cmd) {
|
||||||
DIAG(F("SETUP(\"%S\")"),cmd);
|
DIAG(F("SETUP(\"%S\")"),cmd);
|
||||||
int size=STRLEN_P((char *)cmd)+1;
|
int size=STRLEN_P((char *)cmd)+1;
|
||||||
char buffer[size];
|
char buffer[size];
|
||||||
STRCPY_P(buffer,(char *)cmd);
|
STRCPY_P(buffer,(char *)cmd);
|
||||||
parse(&USB_SERIAL,(byte *)buffer,NULL);
|
parse(&USB_SERIAL,(byte *)buffer,NULL);
|
||||||
@ -376,7 +376,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE [ONOFF]> or <a LINEARADDRESS ACTIVATE>
|
case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE [ONOFF]> or <a LINEARADDRESS ACTIVATE>
|
||||||
{
|
{
|
||||||
int address;
|
int address;
|
||||||
byte subaddress;
|
byte subaddress;
|
||||||
byte activep;
|
byte activep;
|
||||||
@ -402,7 +402,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
onoff=p[3];
|
onoff=p[3];
|
||||||
}
|
}
|
||||||
else break; // invalid no of parameters
|
else break; // invalid no of parameters
|
||||||
|
|
||||||
if (
|
if (
|
||||||
((address & 0x01FF) != address) // invalid address (limit 9 bits)
|
((address & 0x01FF) != address) // invalid address (limit 9 bits)
|
||||||
|| ((subaddress & 0x03) != subaddress) // invalid subaddress (limit 2 bits)
|
|| ((subaddress & 0x03) != subaddress) // invalid subaddress (limit 2 bits)
|
||||||
@ -417,14 +417,14 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'A': // EXTENDED ACCESSORY <A address value>
|
case 'A': // EXTENDED ACCESSORY <A address value>
|
||||||
// Note: if this happens to match a defined EXRAIL
|
// Note: if this happens to match a defined EXRAIL
|
||||||
// DCCX_SIGNAL, then EXRAIL will have intercepted
|
// DCCX_SIGNAL, then EXRAIL will have intercepted
|
||||||
// this command alrerady.
|
// this command alrerady.
|
||||||
if (params==2 && DCC::setExtendedAccessory(p[0],p[1])) return;
|
if (params==2 && DCC::setExtendedAccessory(p[0],p[1])) return;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'T': // TURNOUT <T ...>
|
case 'T': // TURNOUT <T ...>
|
||||||
if (parseT(stream, params, p))
|
if (parseT(stream, params, p))
|
||||||
return;
|
return;
|
||||||
@ -433,22 +433,22 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
#ifndef IO_NO_HAL
|
#ifndef IO_NO_HAL
|
||||||
case 'o': // Neopixel pin manipulation
|
case 'o': // Neopixel pin manipulation
|
||||||
if (p[0]==0) break;
|
if (p[0]==0) break;
|
||||||
{
|
{
|
||||||
VPIN vpin=p[0]>0 ? p[0]:-p[0];
|
VPIN vpin=p[0]>0 ? p[0]:-p[0];
|
||||||
bool setON=p[0]>0;
|
bool setON=p[0]>0;
|
||||||
if (params==1) { // <o [-]vpin>
|
if (params==1) { // <o [-]vpin>
|
||||||
IODevice::write(vpin,setON);
|
IODevice::write(vpin,setON);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (params==2) { // <o [-]vpin count>
|
if (params==2) { // <o [-]vpin count>
|
||||||
IODevice::writeRange(vpin,setON,p[1]);
|
IODevice::writeRange(vpin,setON,p[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (params==4 || params==5) { // <z [-]vpin r g b [count]>
|
if (params==4 || params==5) { // <z [-]vpin r g b [count]>
|
||||||
auto count=p[4]?p[4]:1;
|
auto count=p[4]?p[4]:1;
|
||||||
if (p[1]<0 || p[1]>0xFF) break;
|
if (p[1]<0 || p[1]>0xFF) break;
|
||||||
if (p[2]<0 || p[2]>0xFF) break;
|
if (p[2]<0 || p[2]>0xFF) break;
|
||||||
if (p[3]<0 || p[3]>0xFF) break;
|
if (p[3]<0 || p[3]>0xFF) break;
|
||||||
// strange parameter mangling... see IO_NeoPixel.h NeoPixel::_writeAnalogue
|
// strange parameter mangling... see IO_NeoPixel.h NeoPixel::_writeAnalogue
|
||||||
int colour_RG=(p[1]<<8) | p[2];
|
int colour_RG=(p[1]<<8) | p[2];
|
||||||
uint16_t colour_B=p[3];
|
uint16_t colour_B=p[3];
|
||||||
@ -457,21 +457,21 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case 'z': // direct pin manipulation
|
case 'z': // direct pin manipulation
|
||||||
if (p[0]==0) break;
|
if (p[0]==0) break;
|
||||||
if (params==1) { // <z vpin | -vpin>
|
if (params==1) { // <z vpin | -vpin>
|
||||||
if (p[0]>0) IODevice::write(p[0],HIGH);
|
if (p[0]>0) IODevice::write(p[0],HIGH);
|
||||||
else IODevice::write(-p[0],LOW);
|
else IODevice::write(-p[0],LOW);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (params>=2 && params<=4) { // <z vpin analog profile duration>
|
if (params>=2 && params<=4) { // <z vpin analog profile duration>
|
||||||
// unused params default to 0
|
// unused params default to 0
|
||||||
IODevice::writeAnalogue(p[0],p[1],p[2],p[3]);
|
IODevice::writeAnalogue(p[0],p[1],p[2],p[3]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Z': // OUTPUT <Z ...>
|
case 'Z': // OUTPUT <Z ...>
|
||||||
if (parseZ(stream, params, p))
|
if (parseZ(stream, params, p))
|
||||||
@ -511,10 +511,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
packet[i]=(byte)p[i+1];
|
packet[i]=(byte)p[i+1];
|
||||||
if (Diag::CMD) DIAG(F("packet[%d]=%d (0x%x)"), i, packet[i], packet[i]);
|
if (Diag::CMD) DIAG(F("packet[%d]=%d (0x%x)"), i, packet[i], packet[i]);
|
||||||
}
|
}
|
||||||
(opcode=='M'?DCCWaveform::mainTrack:DCCWaveform::progTrack).schedulePacket(packet,params,3);
|
(opcode=='M'?DCCWaveform::mainTrack:DCCWaveform::progTrack).schedulePacket(packet,params,3);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
||||||
if (!stashCallback(stream, p, ringStream))
|
if (!stashCallback(stream, p, ringStream))
|
||||||
@ -525,7 +525,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
DCC::writeCVByte(p[0], p[1], callback_W4);
|
DCC::writeCVByte(p[0], p[1], callback_W4);
|
||||||
else if ((params==2 || params==3 ) && p[0]=="CONSIST"_hk ) {
|
else if ((params==2 || params==3 ) && p[0]=="CONSIST"_hk ) {
|
||||||
DCC::setConsistId(p[1],p[2]=="REVERSE"_hk,callback_Wconsist);
|
DCC::setConsistId(p[1],p[2]=="REVERSE"_hk,callback_Wconsist);
|
||||||
}
|
}
|
||||||
else if (params == 2) // WRITE CV ON PROG <W CV VALUE>
|
else if (params == 2) // WRITE CV ON PROG <W CV VALUE>
|
||||||
DCC::writeCVByte(p[0], p[1], callback_W);
|
DCC::writeCVByte(p[0], p[1], callback_W);
|
||||||
else
|
else
|
||||||
@ -610,10 +610,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
}
|
}
|
||||||
//TrackManager::streamTrackState(NULL,t);
|
//TrackManager::streamTrackState(NULL,t);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '0': // POWEROFF <0 [MAIN | PROG] >
|
case '0': // POWEROFF <0 [MAIN | PROG] >
|
||||||
{
|
{
|
||||||
if (params > 1) break;
|
if (params > 1) break;
|
||||||
@ -664,7 +664,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
CommandDistributor::broadcastPower(); // <s> is the only "get power status" command we have
|
CommandDistributor::broadcastPower(); // <s> is the only "get power status" command we have
|
||||||
Turnout::printAll(stream); //send all Turnout states
|
Turnout::printAll(stream); //send all Turnout states
|
||||||
Sensor::printAll(stream); //send all Sensor states
|
Sensor::printAll(stream); //send all Sensor states
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifndef DISABLE_EEPROM
|
#ifndef DISABLE_EEPROM
|
||||||
case 'E': // STORE EPROM <E>
|
case 'E': // STORE EPROM <E>
|
||||||
@ -719,12 +719,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 'F': // New command to call the new Loco Function API <F cab func 1|0>
|
case 'F': // New command to call the new Loco Function API <F cab func 1|0>
|
||||||
if(params!=3) break;
|
if(params!=3) break;
|
||||||
|
|
||||||
if (p[1]=="DCFREQ"_hk) { // <F cab DCFREQ 0..3>
|
if (p[1]=="DCFREQ"_hk) { // <F cab DCFREQ 0..3>
|
||||||
if (p[2]<0 || p[2]>3) break;
|
if (p[2]<0 || p[2]>3) break;
|
||||||
DCC::setDCFreq(p[0],p[2]);
|
DCC::setDCFreq(p[0],p[2]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Diag::CMD)
|
if (Diag::CMD)
|
||||||
@ -740,7 +740,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case 'J' : // throttle info access
|
case 'J' : // throttle info access
|
||||||
{
|
{
|
||||||
@ -756,29 +756,29 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
}
|
}
|
||||||
CommandDistributor::setClockTime(p[1], p[2], 1);
|
CommandDistributor::setClockTime(p[1], p[2], 1);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "G"_hk: // <JG> current gauge limits
|
case "G"_hk: // <JG> current gauge limits
|
||||||
if (params>1) break;
|
if (params>1) break;
|
||||||
TrackManager::reportGauges(stream); // <g limit...limit>
|
TrackManager::reportGauges(stream); // <g limit...limit>
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "I"_hk: // <JI> current values
|
case "I"_hk: // <JI> current values
|
||||||
if (params>1) break;
|
if (params>1) break;
|
||||||
TrackManager::reportCurrent(stream); // <g limit...limit>
|
TrackManager::reportCurrent(stream); // <g limit...limit>
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "A"_hk: // <JA> intercepted by EXRAIL// <JA> returns automations/routes
|
case "A"_hk: // <JA> intercepted by EXRAIL// <JA> returns automations/routes
|
||||||
if (params!=1) break; // <JA>
|
if (params!=1) break; // <JA>
|
||||||
StringFormatter::send(stream, F("<jA>\n"));
|
StringFormatter::send(stream, F("<jA>\n"));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "M"_hk: // <JM> intercepted by EXRAIL
|
case "M"_hk: // <JM> intercepted by EXRAIL
|
||||||
if (params>1) break; // invalid cant do
|
if (params>1) break; // invalid cant do
|
||||||
// <JM> requests stash size so say none.
|
// <JM> requests stash size so say none.
|
||||||
StringFormatter::send(stream,F("<jM 0>\n"));
|
StringFormatter::send(stream,F("<jM 0>\n"));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "R"_hk: // <JR> returns rosters
|
case "R"_hk: // <JR> returns rosters
|
||||||
StringFormatter::send(stream, F("<jR"));
|
StringFormatter::send(stream, F("<jR"));
|
||||||
#ifdef EXRAIL_ACTIVE
|
#ifdef EXRAIL_ACTIVE
|
||||||
if (params==1) {
|
if (params==1) {
|
||||||
@ -791,17 +791,17 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
auto functionNames= RMFT2::getRosterFunctions(id);
|
auto functionNames= RMFT2::getRosterFunctions(id);
|
||||||
if (!functionNames) functionNames=RMFT2::getRosterFunctions(0);
|
if (!functionNames) functionNames=RMFT2::getRosterFunctions(0);
|
||||||
if (!functionNames) functionNames=F("");
|
if (!functionNames) functionNames=F("");
|
||||||
StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
|
StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
|
||||||
id, rosterName, functionNames);
|
id, rosterName, functionNames);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
StringFormatter::send(stream, F(">\n"));
|
StringFormatter::send(stream, F(">\n"));
|
||||||
return;
|
return;
|
||||||
case "T"_hk: // <JT> returns turnout list
|
case "T"_hk: // <JT> returns turnout list
|
||||||
StringFormatter::send(stream, F("<jT"));
|
StringFormatter::send(stream, F("<jT"));
|
||||||
if (params==1) { // <JT>
|
if (params==1) { // <JT>
|
||||||
for ( Turnout * t=Turnout::first(); t; t=t->next()) {
|
for ( Turnout * t=Turnout::first(); t; t=t->next()) {
|
||||||
if (t->isHidden()) continue;
|
if (t->isHidden()) continue;
|
||||||
StringFormatter::send(stream, F(" %d"),t->getId());
|
StringFormatter::send(stream, F(" %d"),t->getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -827,8 +827,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
case "O"_hk: // <JO returns turntable list
|
case "O"_hk: // <JO returns turntable list
|
||||||
StringFormatter::send(stream, F("<jO"));
|
StringFormatter::send(stream, F("<jO"));
|
||||||
if (params==1) { // <JO>
|
if (params==1) { // <JO>
|
||||||
for (Turntable * tto=Turntable::first(); tto; tto=tto->next()) {
|
for (Turntable * tto=Turntable::first(); tto; tto=tto->next()) {
|
||||||
if (tto->isHidden()) continue;
|
if (tto->isHidden()) continue;
|
||||||
StringFormatter::send(stream, F(" %d"),tto->getId());
|
StringFormatter::send(stream, F(" %d"),tto->getId());
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream, F(">\n"));
|
StringFormatter::send(stream, F(">\n"));
|
||||||
@ -873,7 +873,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default: break;
|
default: break;
|
||||||
} // switch(p[1])
|
} // switch(p[1])
|
||||||
break; // case J
|
break; // case J
|
||||||
}
|
}
|
||||||
@ -892,7 +892,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
#endif
|
#endif
|
||||||
case '/': // implemented in EXRAIL parser
|
case '/': // implemented in EXRAIL parser
|
||||||
case 'L': // LCC interface implemented in EXRAIL parser
|
case 'L': // LCC interface implemented in EXRAIL parser
|
||||||
break; // Will <X> if not intercepted by EXRAIL
|
break; // Will <X> if not intercepted by EXRAIL
|
||||||
|
|
||||||
#ifndef DISABLE_VDPY
|
#ifndef DISABLE_VDPY
|
||||||
case '@': // JMRI saying "give me virtual LCD msgs"
|
case '@': // JMRI saying "give me virtual LCD msgs"
|
||||||
@ -900,7 +900,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
StringFormatter::send(stream,
|
StringFormatter::send(stream,
|
||||||
F("<@ 0 0 \"DCC-EX v" VERSION "\">\n"
|
F("<@ 0 0 \"DCC-EX v" VERSION "\">\n"
|
||||||
"<@ 0 1 \"Lic GPLv3\">\n"));
|
"<@ 0 1 \"Lic GPLv3\">\n"));
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default: //anything else will diagnose and drop out to <X>
|
default: //anything else will diagnose and drop out to <X>
|
||||||
if (opcode >= ' ' && opcode <= '~') {
|
if (opcode >= ' ' && opcode <= '~') {
|
||||||
@ -923,7 +923,7 @@ bool DCCEXParser::parseZ(Print *stream, int16_t params, int16_t p[])
|
|||||||
|
|
||||||
switch (params)
|
switch (params)
|
||||||
{
|
{
|
||||||
|
|
||||||
case 2: // <Z ID ACTIVATE>
|
case 2: // <Z ID ACTIVATE>
|
||||||
{
|
{
|
||||||
Output *o = Output::get(p[0]);
|
Output *o = Output::get(p[0]);
|
||||||
@ -979,14 +979,14 @@ bool DCCEXParser::parsef(Print *stream, int16_t params, int16_t p[])
|
|||||||
return (funcmap(p[0], p[1], 5, 8));
|
return (funcmap(p[0], p[1], 5, 8));
|
||||||
else
|
else
|
||||||
return (funcmap(p[0], p[1], 9, 12));
|
return (funcmap(p[0], p[1], 9, 12));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params == 3) {
|
if (params == 3) {
|
||||||
if (p[1] == 222) {
|
if (p[1] == 222) {
|
||||||
return (funcmap(p[0], p[2], 13, 20));
|
return (funcmap(p[0], p[2], 13, 20));
|
||||||
} else if (p[1] == 223) {
|
} else if (p[1] == 223) {
|
||||||
return (funcmap(p[0], p[2], 21, 28));
|
return (funcmap(p[0], p[2], 21, 28));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void)stream; // NO RESPONSE
|
(void)stream; // NO RESPONSE
|
||||||
return false;
|
return false;
|
||||||
@ -1015,7 +1015,7 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[])
|
|||||||
StringFormatter::send(stream, F("<O>\n"));
|
StringFormatter::send(stream, F("<O>\n"));
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case 2: // <T id 0|1|T|C>
|
case 2: // <T id 0|1|T|C>
|
||||||
{
|
{
|
||||||
bool state = false;
|
bool state = false;
|
||||||
switch (p[1]) {
|
switch (p[1]) {
|
||||||
@ -1048,10 +1048,10 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[])
|
|||||||
if (params == 6 && p[1] == "SERVO"_hk) { // <T id SERVO n n n n>
|
if (params == 6 && p[1] == "SERVO"_hk) { // <T id SERVO n n n n>
|
||||||
if (!ServoTurnout::create(p[0], (VPIN)p[2], (uint16_t)p[3], (uint16_t)p[4], (uint8_t)p[5]))
|
if (!ServoTurnout::create(p[0], (VPIN)p[2], (uint16_t)p[3], (uint16_t)p[4], (uint8_t)p[5]))
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
if (params == 3 && p[1] == "VPIN"_hk) { // <T id VPIN n>
|
if (params == 3 && p[1] == "VPIN"_hk) { // <T id VPIN n>
|
||||||
if (!VpinTurnout::create(p[0], p[2])) return false;
|
if (!VpinTurnout::create(p[0], p[2])) return false;
|
||||||
} else
|
} else
|
||||||
if (params >= 3 && p[1] == "DCC"_hk) {
|
if (params >= 3 && p[1] == "DCC"_hk) {
|
||||||
// <T id DCC addr subadd> 0<=addr<=511, 0<=subadd<=3 (like <a> command).<T>
|
// <T id DCC addr subadd> 0<=addr<=511, 0<=subadd<=3 (like <a> command).<T>
|
||||||
if (params==4 && p[2]>=0 && p[2]<512 && p[3]>=0 && p[3]<4) { // <T id DCC n m>
|
if (params==4 && p[2]>=0 && p[2]<512 && p[3]>=0 && p[3]<4) { // <T id DCC n m>
|
||||||
@ -1061,14 +1061,14 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[])
|
|||||||
if (!DCCTurnout::create(p[0], (p[2]-1)/4+1, (p[2]-1)%4)) return false;
|
if (!DCCTurnout::create(p[0], (p[2]-1)/4+1, (p[2]-1)%4)) return false;
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
if (params==3) { // legacy <T id addr subadd> for DCC accessory
|
if (params==3) { // legacy <T id addr subadd> for DCC accessory
|
||||||
if (p[1]>=0 && p[1]<512 && p[2]>=0 && p[2]<4) {
|
if (p[1]>=0 && p[1]<512 && p[2]>=0 && p[2]<4) {
|
||||||
if (!DCCTurnout::create(p[0], p[1], p[2])) return false;
|
if (!DCCTurnout::create(p[0], p[1], p[2])) return false;
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (params==4) { // legacy <T id n n n> for Servo
|
if (params==4) { // legacy <T id n n n> for Servo
|
||||||
if (!ServoTurnout::create(p[0], (VPIN)p[1], (uint16_t)p[2], (uint16_t)p[3], 1)) return false;
|
if (!ServoTurnout::create(p[0], (VPIN)p[1], (uint16_t)p[2], (uint16_t)p[3], 1)) return false;
|
||||||
} else
|
} else
|
||||||
@ -1154,10 +1154,10 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DIAG(F("Railcom %S")
|
DIAG(F("Railcom %S")
|
||||||
,DCCWaveform::setRailcom(on,debug)?F("ON"):F("OFF"));
|
,DCCWaveform::setRailcom(on,debug)?F("ON"):F("OFF"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
@ -1187,7 +1187,7 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool onOff = (params > 0) && (p[1] == 1 || p[1] == "ON"_hk); // dont care if other stuff or missing... just means off
|
bool onOff = (params > 0) && (p[1] == 1 || p[1] == "ON"_hk); // dont care if other stuff or missing... just means off
|
||||||
|
|
||||||
DIAG(F("Ack diag %S"), onOff ? F("on") : F("off"));
|
DIAG(F("Ack diag %S"), onOff ? F("on") : F("off"));
|
||||||
Diag::ACK = onOff;
|
Diag::ACK = onOff;
|
||||||
}
|
}
|
||||||
@ -1252,8 +1252,8 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
#if !defined(IO_NO_HAL)
|
#if !defined(IO_NO_HAL)
|
||||||
case "HAL"_hk:
|
case "HAL"_hk:
|
||||||
if (p[1] == "SHOW"_hk)
|
if (p[1] == "SHOW"_hk)
|
||||||
IODevice::DumpAll();
|
IODevice::DumpAll();
|
||||||
else if (p[1] == "RESET"_hk)
|
else if (p[1] == "RESET"_hk)
|
||||||
IODevice::reset();
|
IODevice::reset();
|
||||||
@ -1264,6 +1264,11 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
|
|||||||
IODevice::writeAnalogue(p[1], p[2], params>3 ? p[3] : 0);
|
IODevice::writeAnalogue(p[1], p[2], params>3 ? p[3] : 0);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case "OTA"_hk: // <D OTA ON/OFF>
|
||||||
|
Diag::OTA = onOff;
|
||||||
|
DIAG(F("OTA=%S"), onOff ? F("ON") : F("OFF"));
|
||||||
|
return true;
|
||||||
|
|
||||||
default: // invalid/unknown
|
default: // invalid/unknown
|
||||||
return parseC(stream, params, p);
|
return parseC(stream, params, p);
|
||||||
}
|
}
|
||||||
@ -1288,7 +1293,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
|
|||||||
return Turntable::printAll(stream);
|
return Turntable::printAll(stream);
|
||||||
|
|
||||||
case 1: // <I id> broadcast type and current position
|
case 1: // <I id> broadcast type and current position
|
||||||
{
|
{
|
||||||
Turntable *tto = Turntable::get(p[0]);
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
if (tto) {
|
if (tto) {
|
||||||
bool type = tto->isEXTT();
|
bool type = tto->isEXTT();
|
||||||
@ -1299,7 +1304,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case 2: // <I id position> - rotate a DCC turntable
|
case 2: // <I id position> - rotate a DCC turntable
|
||||||
{
|
{
|
||||||
Turntable *tto = Turntable::get(p[0]);
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
@ -1327,7 +1332,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case 4: // <I id EXTT vpin home> create an EXTT turntable
|
case 4: // <I id EXTT vpin home> create an EXTT turntable
|
||||||
{
|
{
|
||||||
Turntable *tto = Turntable::get(p[0]);
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
@ -1342,7 +1347,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case 5: // <I id ADD position value angle> add a position
|
case 5: // <I id ADD position value angle> add a position
|
||||||
{
|
{
|
||||||
Turntable *tto = Turntable::get(p[0]);
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
@ -1356,7 +1361,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default: // Anything else is invalid
|
default: // Anything else is invalid
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1405,7 +1410,7 @@ void DCCEXParser::callback_W4(int16_t result)
|
|||||||
|
|
||||||
void DCCEXParser::callback_B(int16_t result)
|
void DCCEXParser::callback_B(int16_t result)
|
||||||
{
|
{
|
||||||
StringFormatter::send(getAsyncReplyStream(),
|
StringFormatter::send(getAsyncReplyStream(),
|
||||||
F("<r%d|%d|%d %d %d>\n"), stashP[3], stashP[4], stashP[0], stashP[1], result == 1 ? stashP[2] : -1);
|
F("<r%d|%d|%d %d %d>\n"), stashP[3], stashP[4], stashP[0], stashP[1], result == 1 ? stashP[2] : -1);
|
||||||
commitAsyncReplyStream();
|
commitAsyncReplyStream();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* © 2020, Chris Harlow. All rights reserved.
|
* © 2020, Chris Harlow. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of Asbelos DCC API
|
* This file is part of Asbelos DCC API
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
@ -27,10 +27,11 @@ bool Diag::WIFI=false;
|
|||||||
bool Diag::WITHROTTLE=false;
|
bool Diag::WITHROTTLE=false;
|
||||||
bool Diag::ETHERNET=false;
|
bool Diag::ETHERNET=false;
|
||||||
bool Diag::LCN=false;
|
bool Diag::LCN=false;
|
||||||
|
bool Diag::OTA=false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void StringFormatter::diag( const FSH* input...) {
|
void StringFormatter::diag( const FSH* input...) {
|
||||||
USB_SERIAL.print(F("<* "));
|
USB_SERIAL.print(F("<* "));
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(&USB_SERIAL,input,args);
|
send2(&USB_SERIAL,input,args);
|
||||||
@ -52,24 +53,24 @@ void StringFormatter::lcd(byte row, const FSH* input...) {
|
|||||||
send2(&USB_SERIAL,input,args);
|
send2(&USB_SERIAL,input,args);
|
||||||
send(&USB_SERIAL,F(" *>\n"));
|
send(&USB_SERIAL,F(" *>\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DISABLE_VDPY
|
#ifndef DISABLE_VDPY
|
||||||
// send to virtual LCD collector (if any)
|
// send to virtual LCD collector (if any)
|
||||||
if (virtualLCD) {
|
if (virtualLCD) {
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(virtualLCD,input,args);
|
send2(virtualLCD,input,args);
|
||||||
CommandDistributor::commitVirtualLCDSerial();
|
CommandDistributor::commitVirtualLCDSerial();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
DisplayInterface::setRow(row);
|
DisplayInterface::setRow(row);
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(DisplayInterface::getDisplayHandler(),input,args);
|
send2(DisplayInterface::getDisplayHandler(),input,args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringFormatter::lcd2(uint8_t display, byte row, const FSH* input...) {
|
void StringFormatter::lcd2(uint8_t display, byte row, const FSH* input...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
// send to virtual LCD collector (if any)
|
// send to virtual LCD collector (if any)
|
||||||
#ifndef DISABLE_VDPY
|
#ifndef DISABLE_VDPY
|
||||||
Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(display,row);
|
Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(display,row);
|
||||||
if (virtualLCD) {
|
if (virtualLCD) {
|
||||||
@ -79,7 +80,7 @@ void StringFormatter::lcd2(uint8_t display, byte row, const FSH* input...) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DisplayInterface::setRow(display, row);
|
DisplayInterface::setRow(display, row);
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(DisplayInterface::getDisplayHandler(),input,args);
|
send2(DisplayInterface::getDisplayHandler(),input,args);
|
||||||
}
|
}
|
||||||
@ -97,7 +98,7 @@ void StringFormatter::send(Print & stream, const FSH* input...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
||||||
|
|
||||||
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
|
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
|
||||||
|
|
||||||
char* flash=(char*)format;
|
char* flash=(char*)format;
|
||||||
@ -108,9 +109,9 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
|||||||
|
|
||||||
bool formatContinues=false;
|
bool formatContinues=false;
|
||||||
byte formatWidth=0;
|
byte formatWidth=0;
|
||||||
bool formatLeft=false;
|
bool formatLeft=false;
|
||||||
do {
|
do {
|
||||||
|
|
||||||
formatContinues=false;
|
formatContinues=false;
|
||||||
i++;
|
i++;
|
||||||
c=GETFLASH(flash+i);
|
c=GETFLASH(flash+i);
|
||||||
@ -121,16 +122,16 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
|||||||
case 'e': printEscapes(stream,va_arg(args, char*)); break;
|
case 'e': printEscapes(stream,va_arg(args, char*)); break;
|
||||||
case 'E': printEscapes(stream,(const FSH*)va_arg(args, char*)); break;
|
case 'E': printEscapes(stream,(const FSH*)va_arg(args, char*)); break;
|
||||||
case 'S':
|
case 'S':
|
||||||
{
|
{
|
||||||
const FSH* flash= (const FSH*)va_arg(args, char*);
|
const FSH* flash= (const FSH*)va_arg(args, char*);
|
||||||
|
|
||||||
#if WIFI_ON | ETHERNET_ON
|
#if WIFI_ON | ETHERNET_ON
|
||||||
// RingStream has special logic to handle flash strings
|
// RingStream has special logic to handle flash strings
|
||||||
// but is not implemented unless wifi or ethernet are enabled.
|
// but is not implemented unless wifi or ethernet are enabled.
|
||||||
// The define prevents RingStream code being added unnecessariliy.
|
// The define prevents RingStream code being added unnecessariliy.
|
||||||
if (stream->availableForWrite()==RingStream::THIS_IS_A_RINGSTREAM)
|
if (stream->availableForWrite()==RingStream::THIS_IS_A_RINGSTREAM)
|
||||||
((RingStream *)stream)->printFlash(flash);
|
((RingStream *)stream)->printFlash(flash);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
stream->print(flash);
|
stream->print(flash);
|
||||||
break;
|
break;
|
||||||
@ -165,20 +166,20 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
|||||||
break;
|
break;
|
||||||
//case 'f': stream->print(va_arg(args, double), 2); break;
|
//case 'f': stream->print(va_arg(args, double), 2); break;
|
||||||
//format width prefix
|
//format width prefix
|
||||||
case '-':
|
case '-':
|
||||||
formatLeft=true;
|
formatLeft=true;
|
||||||
formatContinues=true;
|
formatContinues=true;
|
||||||
break;
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
case '3':
|
case '3':
|
||||||
case '4':
|
case '4':
|
||||||
case '5':
|
case '5':
|
||||||
case '6':
|
case '6':
|
||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
formatWidth=formatWidth * 10 + (c-'0');
|
formatWidth=formatWidth * 10 + (c-'0');
|
||||||
formatContinues=true;
|
formatContinues=true;
|
||||||
break;
|
break;
|
||||||
@ -198,7 +199,7 @@ void StringFormatter::printEscapes(Print * stream,char * input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StringFormatter::printEscapes(Print * stream, const FSH * input) {
|
void StringFormatter::printEscapes(Print * stream, const FSH * input) {
|
||||||
|
|
||||||
if (!stream) return;
|
if (!stream) return;
|
||||||
char* flash=(char*)input;
|
char* flash=(char*)input;
|
||||||
for(int i=0; ; ++i) {
|
for(int i=0; ; ++i) {
|
||||||
@ -215,35 +216,35 @@ void StringFormatter::printEscape( char c) {
|
|||||||
void StringFormatter::printEscape(Print * stream, char c) {
|
void StringFormatter::printEscape(Print * stream, char c) {
|
||||||
if (!stream) return;
|
if (!stream) return;
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case '\n': stream->print(F("\\n")); break;
|
case '\n': stream->print(F("\\n")); break;
|
||||||
case '\r': stream->print(F("\\r")); break;
|
case '\r': stream->print(F("\\r")); break;
|
||||||
case '\0': stream->print(F("\\0")); return;
|
case '\0': stream->print(F("\\0")); return;
|
||||||
case '\t': stream->print(F("\\t")); break;
|
case '\t': stream->print(F("\\t")); break;
|
||||||
case '\\': stream->print(F("\\\\")); break;
|
case '\\': stream->print(F("\\\\")); break;
|
||||||
default: stream->write(c);
|
default: stream->write(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringFormatter::printPadded(Print* stream, long value, byte width, bool formatLeft) {
|
void StringFormatter::printPadded(Print* stream, long value, byte width, bool formatLeft) {
|
||||||
if (width==0) {
|
if (width==0) {
|
||||||
stream->print(value, DEC);
|
stream->print(value, DEC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int digits=(value <= 0)? 1: 0; // zero and negative need extra digot
|
int digits=(value <= 0)? 1: 0; // zero and negative need extra digot
|
||||||
long v=value;
|
long v=value;
|
||||||
while (v) {
|
while (v) {
|
||||||
v /= 10;
|
v /= 10;
|
||||||
digits++;
|
digits++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formatLeft) stream->print(value, DEC);
|
if (formatLeft) stream->print(value, DEC);
|
||||||
while(digits<width) {
|
while(digits<width) {
|
||||||
stream->print(' ');
|
stream->print(' ');
|
||||||
digits++;
|
digits++;
|
||||||
}
|
}
|
||||||
if (!formatLeft) stream->print(value, DEC);
|
if (!formatLeft) stream->print(value, DEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// printHex prints the full 2 byte hex with leading zeros, unlike print(value,HEX)
|
// printHex prints the full 2 byte hex with leading zeros, unlike print(value,HEX)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* © 2020, Chris Harlow. All rights reserved.
|
* © 2020, Chris Harlow. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of Asbelos DCC API
|
* This file is part of Asbelos DCC API
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
@ -30,7 +30,7 @@ class Diag {
|
|||||||
static bool WITHROTTLE;
|
static bool WITHROTTLE;
|
||||||
static bool ETHERNET;
|
static bool ETHERNET;
|
||||||
static bool LCN;
|
static bool LCN;
|
||||||
|
static bool OTA;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StringFormatter
|
class StringFormatter
|
||||||
@ -38,7 +38,7 @@ class StringFormatter
|
|||||||
public:
|
public:
|
||||||
static void send(Print * serial, const FSH* input...);
|
static void send(Print * serial, const FSH* input...);
|
||||||
static void send(Print & serial, const FSH* input...);
|
static void send(Print & serial, const FSH* input...);
|
||||||
|
|
||||||
static void printEscapes(Print * serial,char * input);
|
static void printEscapes(Print * serial,char * input);
|
||||||
static void printEscapes(Print * serial,const FSH* input);
|
static void printEscapes(Print * serial,const FSH* input);
|
||||||
static void printEscape(Print * serial, char c);
|
static void printEscape(Print * serial, char c);
|
||||||
@ -51,7 +51,7 @@ class StringFormatter
|
|||||||
static void printEscape( char c);
|
static void printEscape( char c);
|
||||||
static void printHex(Print * stream,uint16_t value);
|
static void printHex(Print * stream,uint16_t value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void send2(Print * serial, const FSH* input,va_list args);
|
static void send2(Print * serial, const FSH* input,va_list args);
|
||||||
static void printPadded(Print* stream, long value, byte width, bool formatLeft);
|
static void printPadded(Print* stream, long value, byte width, bool formatLeft);
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* © 2020-2021 Fred Decker
|
* © 2020-2021 Fred Decker
|
||||||
* © 2020-2021 Chris Harlow
|
* © 2020-2021 Chris Harlow
|
||||||
* © 2023 Nathan Kellenicki
|
* © 2023 Nathan Kellenicki
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
@ -32,7 +32,7 @@ The configuration file for DCC-EX Command Station
|
|||||||
// If you want to add your own motor driver definition(s), add them here
|
// If you want to add your own motor driver definition(s), add them here
|
||||||
// For example MY_SHIELD with display name "MINE":
|
// For example MY_SHIELD with display name "MINE":
|
||||||
// (remove comment start and end marker if you want to edit and use that)
|
// (remove comment start and end marker if you want to edit and use that)
|
||||||
/*
|
/*
|
||||||
#define MY_SHIELD F("MINE"), \
|
#define MY_SHIELD F("MINE"), \
|
||||||
new MotorDriver( 3, 12, UNUSED_PIN, 9, A0, 5.08, 3000, A4), \
|
new MotorDriver( 3, 12, UNUSED_PIN, 9, A0, 5.08, 3000, A4), \
|
||||||
new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 5.08, 1500, A5)
|
new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 5.08, 1500, A5)
|
||||||
@ -95,7 +95,7 @@ The configuration file for DCC-EX Command Station
|
|||||||
//#define DONT_TOUCH_WIFI_CONF
|
//#define DONT_TOUCH_WIFI_CONF
|
||||||
//
|
//
|
||||||
// WIFI_SSID is the network name IF you want to use your existing home network.
|
// WIFI_SSID is the network name IF you want to use your existing home network.
|
||||||
// Do NOT change this if you want to use the WiFi in Access Point (AP) mode.
|
// Do NOT change this if you want to use the WiFi in Access Point (AP) mode.
|
||||||
//
|
//
|
||||||
// If you do NOT set the WIFI_SSID and do NOT set the WIFI_PASSWORD,
|
// If you do NOT set the WIFI_SSID and do NOT set the WIFI_PASSWORD,
|
||||||
// then the WiFi chip will first try to connect to the previously
|
// then the WiFi chip will first try to connect to the previously
|
||||||
@ -111,7 +111,7 @@ The configuration file for DCC-EX Command Station
|
|||||||
//
|
//
|
||||||
// WIFI_PASSWORD is the network password for your home network or if
|
// WIFI_PASSWORD is the network password for your home network or if
|
||||||
// you want to change the password from default AP mode password
|
// you want to change the password from default AP mode password
|
||||||
// to the AP password you want.
|
// to the AP password you want.
|
||||||
// Your password may not contain ``"'' (double quote, ASCII 0x22).
|
// Your password may not contain ``"'' (double quote, ASCII 0x22).
|
||||||
#define WIFI_PASSWORD "Your network passwd"
|
#define WIFI_PASSWORD "Your network passwd"
|
||||||
//
|
//
|
||||||
@ -128,6 +128,21 @@ The configuration file for DCC-EX Command Station
|
|||||||
// true. Otherwise it is assumed that you'd like to connect to an existing network
|
// true. Otherwise it is assumed that you'd like to connect to an existing network
|
||||||
// with that SSID.
|
// with that SSID.
|
||||||
#define WIFI_FORCE_AP false
|
#define WIFI_FORCE_AP false
|
||||||
|
//
|
||||||
|
// OTA_AUTO_INIT: Set this to true if you want OTA updates to be initialized
|
||||||
|
// automatically upon startup. If set to false, OTA updates will remain
|
||||||
|
// unavailable until the "<C OTA 1>" command is executed.
|
||||||
|
// Please note that this feature requires the use of ARDUINO_ARCH_ESP32 as your board.
|
||||||
|
#define OTA_AUTO_INIT false
|
||||||
|
//
|
||||||
|
// OTA_AUTH: Set this to your desired password if you wish to secure OTA updates.
|
||||||
|
// If not set, OTA updates will be password-free.
|
||||||
|
// Note: Upon modifying the OTA password, ensure to update the "upload_flags → --auth"
|
||||||
|
// in the relevant environment within the platformio.ini file.
|
||||||
|
// To deactivate OTA authorization, comment out the line below and comment out
|
||||||
|
// the "upload_flags" line in the platformio.ini file.
|
||||||
|
// #define OTA_AUTH "dccex-ota"
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -194,7 +209,7 @@ The configuration file for DCC-EX Command Station
|
|||||||
// If you do not need programming capability, you can disable all programming related
|
// If you do not need programming capability, you can disable all programming related
|
||||||
// commands. You might want to do that if you are using an Arduino UNO and still want
|
// commands. You might want to do that if you are using an Arduino UNO and still want
|
||||||
// to use EXRAIL automation, as the Uno is lacking in RAM and Flash to run both.
|
// to use EXRAIL automation, as the Uno is lacking in RAM and Flash to run both.
|
||||||
//
|
//
|
||||||
// Note this disables all programming functionality, including EXRAIL.
|
// Note this disables all programming functionality, including EXRAIL.
|
||||||
//
|
//
|
||||||
// #define DISABLE_PROG
|
// #define DISABLE_PROG
|
||||||
@ -228,9 +243,9 @@ The configuration file for DCC-EX Command Station
|
|||||||
// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address
|
// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address
|
||||||
// is 127 and the first long address is 128. There are manufacturers which have
|
// is 127 and the first long address is 128. There are manufacturers which have
|
||||||
// another view. Lenz CS for example have considered addresses long from 100. If
|
// another view. Lenz CS for example have considered addresses long from 100. If
|
||||||
// you want to change to that mode, do
|
// you want to change to that mode, do
|
||||||
//#define HIGHEST_SHORT_ADDR 99
|
//#define HIGHEST_SHORT_ADDR 99
|
||||||
// If you want to run all your locos addressed long format, you could even do a
|
// If you want to run all your locos addressed long format, you could even do a
|
||||||
//#define HIGHEST_SHORT_ADDR 0
|
//#define HIGHEST_SHORT_ADDR 0
|
||||||
// We do not support to use the same address, for example 100(long) and 100(short)
|
// We do not support to use the same address, for example 100(long) and 100(short)
|
||||||
// at the same time, there must be a border.
|
// at the same time, there must be a border.
|
||||||
@ -249,7 +264,7 @@ The configuration file for DCC-EX Command Station
|
|||||||
//
|
//
|
||||||
// According to norm RCN-213 a DCC packet with a 1 is closed/straight
|
// According to norm RCN-213 a DCC packet with a 1 is closed/straight
|
||||||
// and one with a 0 is thrown/diverging. In DCC++ Classic, and in previous
|
// and one with a 0 is thrown/diverging. In DCC++ Classic, and in previous
|
||||||
// versions of DCC++EX, a turnout throw command was implemented in the packet as
|
// versions of DCC++EX, a turnout throw command was implemented in the packet as
|
||||||
// '1' and a close command as '0'. The #define below makes the states
|
// '1' and a close command as '0'. The #define below makes the states
|
||||||
// match with the norm. But we don't want to cause havoc on existent layouts,
|
// match with the norm. But we don't want to cause havoc on existent layouts,
|
||||||
// so we define this only for new installations. If you don't want this,
|
// so we define this only for new installations. If you don't want this,
|
||||||
@ -268,7 +283,7 @@ The configuration file for DCC-EX Command Station
|
|||||||
// you can use this to reverse the sense of all accessory commmands sent
|
// you can use this to reverse the sense of all accessory commmands sent
|
||||||
// over DCC++. This #define likewise inverts the behaviour of the <a> command
|
// over DCC++. This #define likewise inverts the behaviour of the <a> command
|
||||||
// for triggering DCC Accessory Decoders, so that <a addr subaddr 0> generates a
|
// for triggering DCC Accessory Decoders, so that <a addr subaddr 0> generates a
|
||||||
// DCC packet with D=1 (close turnout) and <a addr subaddr 1> generates D=0
|
// DCC packet with D=1 (close turnout) and <a addr subaddr 1> generates D=0
|
||||||
// (throw turnout).
|
// (throw turnout).
|
||||||
//#define DCC_ACCESSORY_COMMAND_REVERSE
|
//#define DCC_ACCESSORY_COMMAND_REVERSE
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
; https://docs.platformio.org/page/projectconf.html
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
default_envs =
|
default_envs =
|
||||||
mega2560
|
mega2560
|
||||||
uno
|
uno
|
||||||
nano
|
nano
|
||||||
@ -56,7 +56,7 @@ build_flags = -std=c++17
|
|||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = megaatmega2560
|
board = megaatmega2560
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
@ -68,7 +68,7 @@ build_flags = -DDIAG_IO=2 -DDIAG_LOOPTIMES
|
|||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = megaatmega2560
|
board = megaatmega2560
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
@ -80,7 +80,7 @@ build_flags = -DIO_NO_HAL
|
|||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = megaatmega2560
|
board = megaatmega2560
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
@ -92,7 +92,7 @@ build_flags = -DI2C_USE_WIRE
|
|||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = megaatmega2560
|
board = megaatmega2560
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
@ -106,13 +106,13 @@ lib_ignore = WiFi101
|
|||||||
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_echo = yes
|
monitor_echo = yes
|
||||||
build_flags =
|
build_flags =
|
||||||
|
|
||||||
[env:mega2560-eth]
|
[env:mega2560-eth]
|
||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = megaatmega2560
|
board = megaatmega2560
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
MDNS_Generic
|
MDNS_Generic
|
||||||
@ -129,7 +129,7 @@ monitor_echo = yes
|
|||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = uno
|
board = uno
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
@ -140,7 +140,7 @@ monitor_echo = yes
|
|||||||
platform = atmelmegaavr
|
platform = atmelmegaavr
|
||||||
board = uno_wifi_rev2
|
board = uno_wifi_rev2
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
@ -152,14 +152,14 @@ build_flags = "-DF_CPU=16000000L -DARDUINO=10813 -DARDUINO_AVR_UNO_WIFI_DEV_ED -
|
|||||||
platform = atmelmegaavr
|
platform = atmelmegaavr
|
||||||
board = nano_every
|
board = nano_every
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_echo = yes
|
monitor_echo = yes
|
||||||
upload_speed = 19200
|
upload_speed = 19200
|
||||||
build_flags =
|
build_flags =
|
||||||
|
|
||||||
[env:uno]
|
[env:uno]
|
||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
@ -193,6 +193,20 @@ build_flags = -std=c++17
|
|||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_echo = yes
|
monitor_echo = yes
|
||||||
|
|
||||||
|
[env:ESP32-OTA]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32dev
|
||||||
|
framework = arduino
|
||||||
|
lib_deps = ${env.lib_deps}
|
||||||
|
build_flags = -std=c++17
|
||||||
|
monitor_speed = 115200
|
||||||
|
monitor_echo = yes
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_port = dccex
|
||||||
|
upload_flags =
|
||||||
|
--timeout=10
|
||||||
|
--auth=dccex-ota
|
||||||
|
|
||||||
[env:Nucleo-F411RE]
|
[env:Nucleo-F411RE]
|
||||||
platform = ststm32 @ 17.6.0
|
platform = ststm32 @ 17.6.0
|
||||||
board = nucleo_f411re
|
board = nucleo_f411re
|
||||||
|
Loading…
x
Reference in New Issue
Block a user