mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-27 01:56:14 +01:00
Merge branch 'feature/config' of https://github.com/DCC-EX/CommandStation-EX into feature/config
This commit is contained in:
commit
f76fb8d6c5
|
@ -29,16 +29,23 @@
|
|||
int ramLowWatermark = 32767; // This figure gets overwritten dynamically in loop()
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Enables an I2C 2x24 or 4x24 LCD Screen
|
||||
#ifdef ENABLE_LCD
|
||||
#if ENABLE_LCD
|
||||
bool lcdEnabled = false;
|
||||
#if defined(LIB_TYPE_PCF8574)
|
||||
LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS);
|
||||
#elif defined(LIB_TYPE_I2C)
|
||||
LiquidCrystal_I2C lcdDisplay = LiquidCrystal_I2C(LCD_ADDRESS, LCD_COLUMNS, LCD_LINES);
|
||||
#endif
|
||||
#if defined(LIB_TYPE_PCF8574)
|
||||
LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS);
|
||||
#elif defined(LIB_TYPE_I2C)
|
||||
LiquidCrystal_I2C lcdDisplay = LiquidCrystal_I2C(LCD_ADDRESS, LCD_COLUMNS, LCD_LINES);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// this code is here to demonstrate use of the DCC API and other techniques
|
||||
|
@ -109,15 +116,16 @@ DCCEXParser serialParser;
|
|||
void setup()
|
||||
{
|
||||
|
||||
////////////////////////////////////////////
|
||||
//
|
||||
// More display stuff. Need to put this in a .h file and make
|
||||
// it a class
|
||||
#ifdef ENABLE_LCD
|
||||
////////////////////////////////////////////
|
||||
//
|
||||
// More display stuff. Need to put this in a .h file and make
|
||||
// it a class
|
||||
#if ENABLE_LCD
|
||||
Wire.begin();
|
||||
// Check that we can find the LCD by its address before attempting to use it.
|
||||
Wire.beginTransmission(LCD_ADDRESS);
|
||||
if(Wire.endTransmission() == 0) {
|
||||
if (Wire.endTransmission() == 0)
|
||||
{
|
||||
lcdEnabled = true;
|
||||
lcdDisplay.begin(LCD_COLUMNS, LCD_LINES);
|
||||
lcdDisplay.setBacklight(255);
|
||||
|
@ -126,15 +134,15 @@ void setup()
|
|||
lcdDisplay.print("DCC++ EX v");
|
||||
lcdDisplay.print(VERSION);
|
||||
lcdDisplay.setCursor(0, 1);
|
||||
#if COMM_INTERFACE >= 1
|
||||
#if COMM_INTERFACE >= 1
|
||||
lcdDisplay.print("IP: PENDING");
|
||||
#else
|
||||
#else
|
||||
lcdDisplay.print("SERIAL: READY");
|
||||
#endif
|
||||
#if LCD_LINES > 2
|
||||
#endif
|
||||
#if LCD_LINES > 2
|
||||
lcdDisplay.setCursor(0, 3);
|
||||
lcdDisplay.print("TRACK POWER: OFF");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
155
DCC.h
155
DCC.h
|
@ -24,89 +24,91 @@
|
|||
|
||||
typedef void (*ACK_CALLBACK)(int result);
|
||||
|
||||
enum ackOp { // Program opcodes for the ack Manager
|
||||
BASELINE, // ensure enough resets sent before starting and obtain baseline current
|
||||
W0,W1, // issue write bit (0..1) packet
|
||||
WB, // issue write byte packet
|
||||
VB, // Issue validate Byte packet
|
||||
V0, // Issue validate bit=0 packet
|
||||
V1, // issue validate bit=1 packlet
|
||||
WACK, // wait for ack (or absence of ack)
|
||||
ITC1, // If True Callback(1) (if prevous WACK got an ACK)
|
||||
ITC0, // If True callback(0);
|
||||
ITCB, // If True callback(byte)
|
||||
NAKFAIL, // if false callback(-1)
|
||||
FAIL, // callback(-1)
|
||||
STARTMERGE, // Clear bit and byte settings ready for merge pass
|
||||
MERGE, // Merge previous wack response with byte value and decrement bit number (use for readimng CV bytes)
|
||||
SETBIT, // sets bit number to next prog byte
|
||||
SETCV, // sets cv number to next prog byte
|
||||
STASHLOCOID, // keeps current byte value for later
|
||||
COMBINELOCOID, // combines current value with stashed value and returns it
|
||||
ITSKIP, // skip to SKIPTARGET if ack true
|
||||
SKIPTARGET=0xFF // jump to target
|
||||
enum ackOp
|
||||
{ // Program opcodes for the ack Manager
|
||||
BASELINE, // ensure enough resets sent before starting and obtain baseline current
|
||||
W0,
|
||||
W1, // issue write bit (0..1) packet
|
||||
WB, // issue write byte packet
|
||||
VB, // Issue validate Byte packet
|
||||
V0, // Issue validate bit=0 packet
|
||||
V1, // issue validate bit=1 packlet
|
||||
WACK, // wait for ack (or absence of ack)
|
||||
ITC1, // If True Callback(1) (if prevous WACK got an ACK)
|
||||
ITC0, // If True callback(0);
|
||||
ITCB, // If True callback(byte)
|
||||
NAKFAIL, // if false callback(-1)
|
||||
FAIL, // callback(-1)
|
||||
STARTMERGE, // Clear bit and byte settings ready for merge pass
|
||||
MERGE, // Merge previous wack response with byte value and decrement bit number (use for readimng CV bytes)
|
||||
SETBIT, // sets bit number to next prog byte
|
||||
SETCV, // sets cv number to next prog byte
|
||||
STASHLOCOID, // keeps current byte value for later
|
||||
COMBINELOCOID, // combines current value with stashed value and returns it
|
||||
ITSKIP, // skip to SKIPTARGET if ack true
|
||||
SKIPTARGET = 0xFF // jump to target
|
||||
};
|
||||
|
||||
// Allocations with memory implications..!
|
||||
// Base system takes approx 900 bytes + 8 per loco. Turnouts, Sensors etc are dynamically created
|
||||
#ifdef ARDUINO_AVR_UNO
|
||||
const byte MAX_LOCOS=20;
|
||||
#else
|
||||
const byte MAX_LOCOS=50;
|
||||
#endif
|
||||
#ifdef ARDUINO_AVR_UNO
|
||||
const byte MAX_LOCOS = 20;
|
||||
#else
|
||||
const byte MAX_LOCOS = 50;
|
||||
#endif
|
||||
|
||||
|
||||
class DCC {
|
||||
public:
|
||||
|
||||
static void begin(const __FlashStringHelper* motorShieldName, MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber=1);
|
||||
class DCC
|
||||
{
|
||||
public:
|
||||
static void begin(const __FlashStringHelper *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver, byte timerNumber = 1);
|
||||
static void loop();
|
||||
|
||||
// Public DCC API functions
|
||||
static void setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection);
|
||||
static void setThrottle(uint16_t cab, uint8_t tSpeed, bool tDirection);
|
||||
static uint8_t getThrottleSpeed(int cab);
|
||||
static bool getThrottleDirection(int cab);
|
||||
static void writeCVByteMain(int cab, int cv, byte bValue);
|
||||
static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue);
|
||||
static void setFunction( int cab, byte fByte, byte eByte);
|
||||
static void setFn( int cab, byte functionNumber, bool on);
|
||||
static int changeFn( int cab, byte functionNumber, bool pressed);
|
||||
static void updateGroupflags(byte & flags, int functionNumber);
|
||||
static void setAccessory(int aAdd, byte aNum, bool activate) ;
|
||||
static bool writeTextPacket( byte *b, int nBytes);
|
||||
static void setFunction(int cab, byte fByte, byte eByte);
|
||||
static void setFn(int cab, byte functionNumber, bool on);
|
||||
static int changeFn(int cab, byte functionNumber, bool pressed);
|
||||
static void updateGroupflags(byte &flags, int functionNumber);
|
||||
static void setAccessory(int aAdd, byte aNum, bool activate);
|
||||
static bool writeTextPacket(byte *b, int nBytes);
|
||||
static void setProgTrackSyncMain(bool on); // when true, prog track becomes driveable
|
||||
|
||||
// ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1
|
||||
static void readCV(int cv, ACK_CALLBACK callback, bool blocking=false);
|
||||
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking=false); // -1 for error
|
||||
static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking=false) ;
|
||||
static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking=false);
|
||||
static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking=false) ;
|
||||
static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking=false);
|
||||
static void readCV(int cv, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking = false); // -1 for error
|
||||
static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
|
||||
|
||||
static void getLocoId(ACK_CALLBACK callback, bool blocking=false);
|
||||
static void getLocoId(ACK_CALLBACK callback, bool blocking = false);
|
||||
|
||||
// Enhanced API functions
|
||||
static void forgetLoco(int cab); // removes any speed reminders for this loco
|
||||
static void forgetAllLocos(); // removes all speed reminders
|
||||
static void displayCabList(Print * stream);
|
||||
static void displayCabList(Print *stream);
|
||||
|
||||
static __FlashStringHelper* getMotorShieldName();
|
||||
static __FlashStringHelper *getMotorShieldName();
|
||||
|
||||
private:
|
||||
struct LOCO {
|
||||
struct LOCO
|
||||
{
|
||||
int loco;
|
||||
byte speedCode;
|
||||
byte groupFlags;
|
||||
unsigned long functions;
|
||||
};
|
||||
static byte loopStatus;
|
||||
static void setThrottle2( uint16_t cab, uint8_t speedCode);
|
||||
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
||||
static void updateLocoReminder(int loco, byte speedCode);
|
||||
static void setFunctionInternal( int cab, byte fByte, byte eByte);
|
||||
static void setFunctionInternal(int cab, byte fByte, byte eByte);
|
||||
static bool issueReminder(int reg);
|
||||
static int nextLoco;
|
||||
static __FlashStringHelper* shieldName;
|
||||
static __FlashStringHelper *shieldName;
|
||||
|
||||
static LOCO speedTable[MAX_LOCOS];
|
||||
static byte cv1(byte opcode, int cv);
|
||||
|
@ -115,8 +117,8 @@ private:
|
|||
static void issueReminders();
|
||||
static void callback(int value);
|
||||
|
||||
// ACK MANAGER
|
||||
static ackOp const * ackManagerProg;
|
||||
// ACK MANAGER
|
||||
static ackOp const *ackManagerProg;
|
||||
static byte ackManagerByte;
|
||||
static byte ackManagerBitNum;
|
||||
static int ackManagerCv;
|
||||
|
@ -126,47 +128,46 @@ private:
|
|||
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking);
|
||||
static void ackManagerLoop(bool blocking);
|
||||
static bool checkResets(bool blocking, uint8_t numResets);
|
||||
static const int PROG_REPEATS=8; // repeats of programming commands (some decoders need at least 8 to be reliable)
|
||||
|
||||
|
||||
static const int PROG_REPEATS = 8; // repeats of programming commands (some decoders need at least 8 to be reliable)
|
||||
|
||||
// NMRA codes #
|
||||
static const byte SET_SPEED=0x3f;
|
||||
static const byte SET_SPEED = 0x3f;
|
||||
static const byte WRITE_BYTE_MAIN = 0xEC;
|
||||
static const byte WRITE_BIT_MAIN = 0xE8;
|
||||
static const byte WRITE_BYTE = 0x7C;
|
||||
static const byte VERIFY_BYTE= 0x74;
|
||||
static const byte BIT_MANIPULATE=0x78;
|
||||
static const byte WRITE_BIT=0xF0;
|
||||
static const byte VERIFY_BIT=0xE0;
|
||||
static const byte BIT_ON=0x08;
|
||||
static const byte BIT_OFF=0x00;
|
||||
static const byte VERIFY_BYTE = 0x74;
|
||||
static const byte BIT_MANIPULATE = 0x78;
|
||||
static const byte WRITE_BIT = 0xF0;
|
||||
static const byte VERIFY_BIT = 0xE0;
|
||||
static const byte BIT_ON = 0x08;
|
||||
static const byte BIT_OFF = 0x00;
|
||||
};
|
||||
|
||||
#ifdef ARDUINO_AVR_MEGA // is using Mega 1280, define as Mega 2560 (pinouts and functionality are identical)
|
||||
#define ARDUINO_AVR_MEGA2560
|
||||
#define ARDUINO_AVR_MEGA2560
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_AVR_UNO)
|
||||
#define ARDUINO_TYPE "UNO"
|
||||
#define ARDUINO_TYPE "UNO"
|
||||
#elif defined(ARDUINO_AVR_NANO)
|
||||
#define ARDUINO_TYPE "NANO"
|
||||
#define ARDUINO_TYPE "NANO"
|
||||
#elif defined(ARDUINO_AVR_MEGA2560)
|
||||
#define ARDUINO_TYPE "MEGA"
|
||||
#define ARDUINO_TYPE "MEGA"
|
||||
#elif defined(ARDUINO_ARCH_MEGAAVR)
|
||||
#define ARDUINO_TYPE "UNOWIFIR2"
|
||||
#else
|
||||
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560
|
||||
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENABLE_LCD
|
||||
#if ENABLE_LCD
|
||||
#include <Wire.h>
|
||||
#if defined(LIB_TYPE_PCF8574)
|
||||
#include <LiquidCrystal_PCF8574.h>
|
||||
extern LiquidCrystal_PCF8574 lcdDisplay;
|
||||
#elif defined(LIB_TYPE_I2C)
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
extern LiquidCrystal_I2C lcdDisplay;
|
||||
#endif
|
||||
#if defined(LIB_TYPE_PCF8574)
|
||||
#include <LiquidCrystal_PCF8574.h>
|
||||
extern LiquidCrystal_PCF8574 lcdDisplay;
|
||||
#elif defined(LIB_TYPE_I2C)
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
extern LiquidCrystal_I2C lcdDisplay;
|
||||
#endif
|
||||
extern bool lcdEnabled;
|
||||
#endif
|
||||
|
||||
|
|
531
DCCEXParser.cpp
531
DCCEXParser.cpp
|
@ -20,7 +20,9 @@
|
|||
#include "DCCEXParser.h"
|
||||
#include "DCC.h"
|
||||
#include "DCCWaveform.h"
|
||||
#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
|
||||
#include "WifiInterface.h"
|
||||
#endif
|
||||
#include "Turnouts.h"
|
||||
#include "Outputs.h"
|
||||
#include "Sensors.h"
|
||||
|
@ -31,27 +33,25 @@
|
|||
#include "EEStore.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
|
||||
// These keywords are used in the <1> command. The number is what you get if you use the keyword as a parameter.
|
||||
// To discover new keyword numbers , use the <$ YOURKEYWORD> command
|
||||
const int HASH_KEYWORD_PROG=-29718;
|
||||
const int HASH_KEYWORD_MAIN=11339;
|
||||
const int HASH_KEYWORD_JOIN=-30750;
|
||||
const int HASH_KEYWORD_CABS=-11981;
|
||||
const int HASH_KEYWORD_RAM=25982;
|
||||
const int HASH_KEYWORD_CMD=9962;
|
||||
const int HASH_KEYWORD_WIT=31594;
|
||||
const int HASH_KEYWORD_WIFI=-5583;
|
||||
const int HASH_KEYWORD_ACK=3113;
|
||||
const int HASH_KEYWORD_ON=2657;
|
||||
const int HASH_KEYWORD_DCC=6436;
|
||||
const int HASH_KEYWORD_SLOW=-17209;
|
||||
|
||||
const int HASH_KEYWORD_PROG = -29718;
|
||||
const int HASH_KEYWORD_MAIN = 11339;
|
||||
const int HASH_KEYWORD_JOIN = -30750;
|
||||
const int HASH_KEYWORD_CABS = -11981;
|
||||
const int HASH_KEYWORD_RAM = 25982;
|
||||
const int HASH_KEYWORD_CMD = 9962;
|
||||
const int HASH_KEYWORD_WIT = 31594;
|
||||
const int HASH_KEYWORD_WIFI = -5583;
|
||||
const int HASH_KEYWORD_ACK = 3113;
|
||||
const int HASH_KEYWORD_ON = 2657;
|
||||
const int HASH_KEYWORD_DCC = 6436;
|
||||
const int HASH_KEYWORD_SLOW = -17209;
|
||||
|
||||
int DCCEXParser::stashP[MAX_PARAMS];
|
||||
bool DCCEXParser::stashBusy;
|
||||
|
||||
Print * DCCEXParser::stashStream=NULL;
|
||||
Print *DCCEXParser::stashStream = NULL;
|
||||
|
||||
// This is a JMRI command parser, one instance per incoming stream
|
||||
// It doesnt know how the string got here, nor how it gets back.
|
||||
|
@ -60,76 +60,94 @@ bool DCCEXParser::stashBusy;
|
|||
// Non-DCC things like turnouts, pins and sensors are handled in additional JMRI interface classes.
|
||||
|
||||
DCCEXParser::DCCEXParser() {}
|
||||
void DCCEXParser::flush() {
|
||||
if (Diag::CMD) DIAG(F("\nBuffer flush"));
|
||||
bufferLength=0;
|
||||
inCommandPayload=false;
|
||||
void DCCEXParser::flush()
|
||||
{
|
||||
if (Diag::CMD)
|
||||
DIAG(F("\nBuffer flush"));
|
||||
bufferLength = 0;
|
||||
inCommandPayload = false;
|
||||
}
|
||||
|
||||
void DCCEXParser::loop(Stream & stream) {
|
||||
while(stream.available()) {
|
||||
if (bufferLength==MAX_BUFFER) {
|
||||
void DCCEXParser::loop(Stream &stream)
|
||||
{
|
||||
while (stream.available())
|
||||
{
|
||||
if (bufferLength == MAX_BUFFER)
|
||||
{
|
||||
flush();
|
||||
}
|
||||
char ch = stream.read();
|
||||
if (ch == '<') {
|
||||
if (ch == '<')
|
||||
{
|
||||
inCommandPayload = true;
|
||||
bufferLength=0;
|
||||
buffer[0]='\0';
|
||||
bufferLength = 0;
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
else if (ch == '>') {
|
||||
buffer[bufferLength]='\0';
|
||||
parse( & stream, buffer, false); // Parse this allowing async responses
|
||||
else if (ch == '>')
|
||||
{
|
||||
buffer[bufferLength] = '\0';
|
||||
parse(&stream, buffer, false); // Parse this allowing async responses
|
||||
inCommandPayload = false;
|
||||
break;
|
||||
} else if(inCommandPayload) {
|
||||
buffer[bufferLength++]= ch;
|
||||
}
|
||||
}
|
||||
else if (inCommandPayload)
|
||||
{
|
||||
buffer[bufferLength++] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DCCEXParser::splitValues( int result[MAX_PARAMS], const byte * cmd) {
|
||||
byte state=1;
|
||||
byte parameterCount=0;
|
||||
int runningValue=0;
|
||||
const byte * remainingCmd=cmd+1; // skips the opcode
|
||||
bool signNegative=false;
|
||||
int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
||||
{
|
||||
byte state = 1;
|
||||
byte parameterCount = 0;
|
||||
int runningValue = 0;
|
||||
const byte *remainingCmd = cmd + 1; // skips the opcode
|
||||
bool signNegative = false;
|
||||
|
||||
// clear all parameters in case not enough found
|
||||
for (int i=0;i<MAX_PARAMS;i++) result[i]=0;
|
||||
for (int i = 0; i < MAX_PARAMS; i++)
|
||||
result[i] = 0;
|
||||
|
||||
while(parameterCount<MAX_PARAMS) {
|
||||
byte hot=*remainingCmd;
|
||||
while (parameterCount < MAX_PARAMS)
|
||||
{
|
||||
byte hot = *remainingCmd;
|
||||
|
||||
switch (state) {
|
||||
switch (state)
|
||||
{
|
||||
|
||||
case 1: // skipping spaces before a param
|
||||
if (hot==' ') break;
|
||||
if (hot == '\0' || hot=='>') return parameterCount;
|
||||
state=2;
|
||||
if (hot == ' ')
|
||||
break;
|
||||
if (hot == '\0' || hot == '>')
|
||||
return parameterCount;
|
||||
state = 2;
|
||||
continue;
|
||||
|
||||
case 2: // checking sign
|
||||
signNegative=false;
|
||||
runningValue=0;
|
||||
state=3;
|
||||
if (hot!='-') continue;
|
||||
signNegative=true;
|
||||
signNegative = false;
|
||||
runningValue = 0;
|
||||
state = 3;
|
||||
if (hot != '-')
|
||||
continue;
|
||||
signNegative = true;
|
||||
break;
|
||||
case 3: // building a parameter
|
||||
if (hot>='0' && hot<='9') {
|
||||
runningValue=10*runningValue+(hot-'0');
|
||||
if (hot >= '0' && hot <= '9')
|
||||
{
|
||||
runningValue = 10 * runningValue + (hot - '0');
|
||||
break;
|
||||
}
|
||||
if (hot>='A' && hot<='Z') {
|
||||
if (hot >= 'A' && hot <= 'Z')
|
||||
{
|
||||
// Since JMRI got modified to send keywords in some rare cases, we need this
|
||||
// Super Kluge to turn keywords into a hash value that can be recognised later
|
||||
runningValue = ((runningValue << 5) + runningValue) ^ hot;
|
||||
break;
|
||||
}
|
||||
result[parameterCount] = runningValue * (signNegative ?-1:1);
|
||||
result[parameterCount] = runningValue * (signNegative ? -1 : 1);
|
||||
parameterCount++;
|
||||
state=1;
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
remainingCmd++;
|
||||
|
@ -137,181 +155,217 @@ void DCCEXParser::loop(Stream & stream) {
|
|||
return parameterCount;
|
||||
}
|
||||
|
||||
FILTER_CALLBACK DCCEXParser::filterCallback=0;
|
||||
void DCCEXParser::setFilter(FILTER_CALLBACK filter) {
|
||||
filterCallback=filter;
|
||||
FILTER_CALLBACK DCCEXParser::filterCallback = 0;
|
||||
void DCCEXParser::setFilter(FILTER_CALLBACK filter)
|
||||
{
|
||||
filterCallback = filter;
|
||||
}
|
||||
|
||||
// See documentation on DCC class for info on this section
|
||||
void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
|
||||
if (Diag::CMD) DIAG(F("\nPARSING:%s\n"),com);
|
||||
(void) EEPROM; // tell compiler not to warn thi is unused
|
||||
void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
||||
{
|
||||
if (Diag::CMD)
|
||||
DIAG(F("\nPARSING:%s\n"), com);
|
||||
(void)EEPROM; // tell compiler not to warn thi is unused
|
||||
int p[MAX_PARAMS];
|
||||
while (com[0]=='<' || com[0]==' ') com++; // strip off any number of < or spaces
|
||||
byte params=splitValues(p, com);
|
||||
byte opcode=com[0];
|
||||
while (com[0] == '<' || com[0] == ' ')
|
||||
com++; // strip off any number of < or spaces
|
||||
byte params = splitValues(p, com);
|
||||
byte opcode = com[0];
|
||||
|
||||
if (filterCallback) filterCallback(stream,opcode,params,p);
|
||||
if (filterCallback)
|
||||
filterCallback(stream, opcode, params, p);
|
||||
|
||||
// Functions return from this switch if complete, break from switch implies error <X> to send
|
||||
switch(opcode) {
|
||||
case '\0': return; // filterCallback asked us to ignore
|
||||
switch (opcode)
|
||||
{
|
||||
case '\0':
|
||||
return; // filterCallback asked us to ignore
|
||||
case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION>
|
||||
{
|
||||
int cab;
|
||||
int tspeed;
|
||||
int direction;
|
||||
|
||||
if (params==4) { // <t REGISTER CAB SPEED DIRECTION>
|
||||
cab=p[1];
|
||||
tspeed=p[2];
|
||||
direction=p[3];
|
||||
if (params == 4)
|
||||
{ // <t REGISTER CAB SPEED DIRECTION>
|
||||
cab = p[1];
|
||||
tspeed = p[2];
|
||||
direction = p[3];
|
||||
}
|
||||
else if (params==3) { // <t CAB SPEED DIRECTION>
|
||||
cab=p[0];
|
||||
tspeed=p[1];
|
||||
direction=p[2];
|
||||
else if (params == 3)
|
||||
{ // <t CAB SPEED DIRECTION>
|
||||
cab = p[0];
|
||||
tspeed = p[1];
|
||||
direction = p[2];
|
||||
}
|
||||
else break;
|
||||
else
|
||||
break;
|
||||
|
||||
// Convert JMRI bizarre -1=emergency stop, 0-126 as speeds
|
||||
// to DCC 0=stop, 1= emergency stop, 2-127 speeds
|
||||
if (tspeed>126 || tspeed<-1) break; // invalid JMRI speed code
|
||||
if (tspeed<0) tspeed=1; // emergency stop DCC speed
|
||||
else if (tspeed>0) tspeed++; // map 1-126 -> 2-127
|
||||
if (cab == 0 && tspeed>1) break; // ignore broadcasts of speed>1
|
||||
if (tspeed > 126 || tspeed < -1)
|
||||
break; // invalid JMRI speed code
|
||||
if (tspeed < 0)
|
||||
tspeed = 1; // emergency stop DCC speed
|
||||
else if (tspeed > 0)
|
||||
tspeed++; // map 1-126 -> 2-127
|
||||
if (cab == 0 && tspeed > 1)
|
||||
break; // ignore broadcasts of speed>1
|
||||
|
||||
if (direction<0 || direction>1) break; // invalid direction code
|
||||
if (direction < 0 || direction > 1)
|
||||
break; // invalid direction code
|
||||
|
||||
DCC::setThrottle(cab,tspeed,direction);
|
||||
if (params==4) StringFormatter::send(stream,F("<T %d %d %d>"), p[0], p[2],p[3]);
|
||||
else StringFormatter::send(stream,F("<O>"));
|
||||
DCC::setThrottle(cab, tspeed, direction);
|
||||
if (params == 4)
|
||||
StringFormatter::send(stream, F("<T %d %d %d>"), p[0], p[2], p[3]);
|
||||
else
|
||||
StringFormatter::send(stream, F("<O>"));
|
||||
return;
|
||||
}
|
||||
case 'f': // FUNCTION <f CAB BYTE1 [BYTE2]>
|
||||
if (parsef(stream,params,p)) return;
|
||||
if (parsef(stream, params, p))
|
||||
return;
|
||||
break;
|
||||
|
||||
case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE>
|
||||
if(p[2] != (p[2] & 1)) return;
|
||||
DCC::setAccessory(p[0],p[1],p[2]==1);
|
||||
if (p[2] != (p[2] & 1))
|
||||
return;
|
||||
DCC::setAccessory(p[0], p[1], p[2] == 1);
|
||||
return;
|
||||
|
||||
case 'T': // TURNOUT <T ...>
|
||||
if (parseT(stream,params,p)) return;
|
||||
if (parseT(stream, params, p))
|
||||
return;
|
||||
break;
|
||||
|
||||
case 'Z': // OUTPUT <Z ...>
|
||||
if (parseZ(stream,params,p)) return;
|
||||
if (parseZ(stream, params, p))
|
||||
return;
|
||||
break;
|
||||
|
||||
case 'S': // SENSOR <S ...>
|
||||
if (parseS(stream,params,p)) return;
|
||||
if (parseS(stream, params, p))
|
||||
return;
|
||||
break;
|
||||
|
||||
case 'w': // WRITE CV on MAIN <w CAB CV VALUE>
|
||||
DCC::writeCVByteMain(p[0],p[1],p[2]);
|
||||
DCC::writeCVByteMain(p[0], p[1], p[2]);
|
||||
return;
|
||||
|
||||
case 'b': // WRITE CV BIT ON MAIN <b CAB CV BIT VALUE>
|
||||
DCC::writeCVBitMain(p[0],p[1],p[2],p[3]);
|
||||
DCC::writeCVBitMain(p[0], p[1], p[2], p[3]);
|
||||
return;
|
||||
|
||||
case 'W': // WRITE CV ON PROG <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream,p)) break;
|
||||
DCC::writeCVByte(p[0],p[1],callback_W,blocking);
|
||||
if (!stashCallback(stream, p))
|
||||
break;
|
||||
DCC::writeCVByte(p[0], p[1], callback_W, blocking);
|
||||
return;
|
||||
|
||||
case 'V': // VERIFY CV ON PROG <V CV VALUE> <V CV BIT 0|1>
|
||||
if (params==2) { // <V CV VALUE>
|
||||
if (!stashCallback(stream,p)) break;
|
||||
DCC::verifyCVByte(p[0],p[1],callback_Vbyte,blocking);
|
||||
if (params == 2)
|
||||
{ // <V CV VALUE>
|
||||
if (!stashCallback(stream, p))
|
||||
break;
|
||||
DCC::verifyCVByte(p[0], p[1], callback_Vbyte, blocking);
|
||||
return;
|
||||
}
|
||||
if (params==3) {
|
||||
if (!stashCallback(stream,p)) break;
|
||||
DCC::verifyCVBit(p[0],p[1],p[2],callback_Vbit,blocking);
|
||||
if (params == 3)
|
||||
{
|
||||
if (!stashCallback(stream, p))
|
||||
break;
|
||||
DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit, blocking);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'B': // WRITE CV BIT ON PROG <B CV BIT VALUE CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream,p)) break;
|
||||
DCC::writeCVBit(p[0],p[1],p[2],callback_B,blocking);
|
||||
if (!stashCallback(stream, p))
|
||||
break;
|
||||
DCC::writeCVBit(p[0], p[1], p[2], callback_B, blocking);
|
||||
return;
|
||||
|
||||
|
||||
case 'R': // READ CV ON PROG
|
||||
if (params==3) { // <R CV CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream,p)) break;
|
||||
DCC::readCV(p[0],callback_R,blocking);
|
||||
if (params == 3)
|
||||
{ // <R CV CALLBACKNUM CALLBACKSUB>
|
||||
if (!stashCallback(stream, p))
|
||||
break;
|
||||
DCC::readCV(p[0], callback_R, blocking);
|
||||
return;
|
||||
}
|
||||
if (params==0) { // <R> New read loco id
|
||||
if (!stashCallback(stream,p)) break;
|
||||
DCC::getLocoId(callback_Rloco,blocking);
|
||||
if (params == 0)
|
||||
{ // <R> New read loco id
|
||||
if (!stashCallback(stream, p))
|
||||
break;
|
||||
DCC::getLocoId(callback_Rloco, blocking);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case '1': // POWERON <1 [MAIN|PROG]>
|
||||
case '0': // POWEROFF <0 [MAIN | PROG] >
|
||||
if (params>1) break;
|
||||
if (params > 1)
|
||||
break;
|
||||
{
|
||||
POWERMODE mode= opcode=='1'?POWERMODE::ON:POWERMODE::OFF;
|
||||
POWERMODE mode = opcode == '1' ? POWERMODE::ON : POWERMODE::OFF;
|
||||
DCC::setProgTrackSyncMain(false); // Only <1 JOIN> will set this on, all others set it off
|
||||
if (params==0) {
|
||||
if (params == 0)
|
||||
{
|
||||
DCCWaveform::mainTrack.setPowerMode(mode);
|
||||
DCCWaveform::progTrack.setPowerMode(mode);
|
||||
StringFormatter::send(stream,F("<p%c>"),opcode);
|
||||
StringFormatter::send(stream, F("<p%c>"), opcode);
|
||||
return;
|
||||
}
|
||||
switch (p[0]) {
|
||||
switch (p[0])
|
||||
{
|
||||
case HASH_KEYWORD_MAIN:
|
||||
DCCWaveform::mainTrack.setPowerMode(mode);
|
||||
StringFormatter::send(stream,F("<p%c MAIN>"),opcode);
|
||||
StringFormatter::send(stream, F("<p%c MAIN>"), opcode);
|
||||
return;
|
||||
|
||||
case HASH_KEYWORD_PROG:
|
||||
DCCWaveform::progTrack.setPowerMode(mode);
|
||||
StringFormatter::send(stream,F("<p%c PROG>"),opcode);
|
||||
StringFormatter::send(stream, F("<p%c PROG>"), opcode);
|
||||
return;
|
||||
case HASH_KEYWORD_JOIN:
|
||||
DCCWaveform::mainTrack.setPowerMode(mode);
|
||||
DCCWaveform::progTrack.setPowerMode(mode);
|
||||
if (mode==POWERMODE::ON) {
|
||||
if (mode == POWERMODE::ON)
|
||||
{
|
||||
DCC::setProgTrackSyncMain(true);
|
||||
StringFormatter::send(stream,F("<p1 JOIN>"),opcode);
|
||||
StringFormatter::send(stream, F("<p1 JOIN>"), opcode);
|
||||
}
|
||||
else StringFormatter::send(stream,F("<p0>"));
|
||||
else
|
||||
StringFormatter::send(stream, F("<p0>"));
|
||||
return;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
case 'c': // READ CURRENT <c>
|
||||
StringFormatter::send(stream,F("<a %d>"), DCCWaveform::mainTrack.getLastCurrent());
|
||||
StringFormatter::send(stream, F("<a %d>"), DCCWaveform::mainTrack.getLastCurrent());
|
||||
return;
|
||||
|
||||
case 'Q': // SENSORS <Q>
|
||||
Sensor::checkAll();
|
||||
for(Sensor * tt=Sensor::firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||
StringFormatter::send(stream,F("<%c %d>"), tt->active?'Q':'q', tt->data.snum);
|
||||
for (Sensor *tt = Sensor::firstSensor; tt != NULL; tt = tt->nextSensor)
|
||||
{
|
||||
StringFormatter::send(stream, F("<%c %d>"), tt->active ? 'Q' : 'q', tt->data.snum);
|
||||
}
|
||||
return;
|
||||
|
||||
case 's': // <s>
|
||||
StringFormatter::send(stream,F("<p%d>"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON );
|
||||
StringFormatter::send(stream,F("<iDCC-EX V-%S / %S / %S G-%S>"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA));
|
||||
StringFormatter::send(stream, F("<p%d>"), DCCWaveform::mainTrack.getPowerMode() == POWERMODE::ON);
|
||||
StringFormatter::send(stream, F("<iDCC-EX V-%S / %S / %S G-%S>"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA));
|
||||
// TODO Send stats of speed reminders table
|
||||
// TODO send status of turnouts etc etc
|
||||
return;
|
||||
|
||||
case 'E': // STORE EPROM <E>
|
||||
EEStore::store();
|
||||
StringFormatter::send(stream,F("<e %d %d %d>"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
|
||||
StringFormatter::send(stream, F("<e %d %d %d>"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
|
||||
return;
|
||||
|
||||
case 'e': // CLEAR EPROM <e>
|
||||
|
@ -320,29 +374,32 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
|
|||
return;
|
||||
|
||||
case ' ': // < >
|
||||
StringFormatter::send(stream,F("\n"));
|
||||
StringFormatter::send(stream, F("\n"));
|
||||
return;
|
||||
|
||||
case 'D': // < >
|
||||
if (parseD(stream,params,p)) return;
|
||||
if (parseD(stream, params, p))
|
||||
return;
|
||||
return;
|
||||
|
||||
case '#': // NUMBER OF LOCOSLOTS <#>
|
||||
StringFormatter::send(stream,F("<# %d>"), MAX_LOCOS);
|
||||
StringFormatter::send(stream, F("<# %d>"), MAX_LOCOS);
|
||||
return;
|
||||
|
||||
case 'F': // New command to call the new Loco Function API <F cab func 1|0>
|
||||
if (Diag::CMD) DIAG(F("Setting loco %d F%d %S"),p[0],p[1],p[2]?F("ON"):F("OFF"));
|
||||
DCC::setFn(p[0],p[1],p[2]==1);
|
||||
if (Diag::CMD)
|
||||
DIAG(F("Setting loco %d F%d %S"), p[0], p[1], p[2] ? F("ON") : F("OFF"));
|
||||
DCC::setFn(p[0], p[1], p[2] == 1);
|
||||
return;
|
||||
|
||||
case '+' : // Complex Wifi interface command (not usual parse)
|
||||
#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
|
||||
case '+': // Complex Wifi interface command (not usual parse)
|
||||
WifiInterface::ATCommand(com);
|
||||
return;
|
||||
|
||||
#endif
|
||||
default: //anything else will diagnose and drop out to <X>
|
||||
DIAG(F("\nOpcode=%c params=%d\n"),opcode,params);
|
||||
for (int i=0;i<params;i++) DIAG(F("p[%d]=%d (0x%x)\n"),i,p[i],p[i]);
|
||||
DIAG(F("\nOpcode=%c params=%d\n"), opcode, params);
|
||||
for (int i = 0; i < params; i++)
|
||||
DIAG(F("p[%d]=%d (0x%x)\n"), i, p[i], p[i]);
|
||||
break;
|
||||
|
||||
} // end of opcode switch
|
||||
|
@ -351,22 +408,24 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
|
|||
StringFormatter::send(stream, F("<X>"));
|
||||
}
|
||||
|
||||
bool DCCEXParser::parseZ( Print * stream,int params, int p[]){
|
||||
bool DCCEXParser::parseZ(Print *stream, int params, int p[])
|
||||
{
|
||||
|
||||
|
||||
switch (params) {
|
||||
switch (params)
|
||||
{
|
||||
|
||||
case 2: // <Z ID ACTIVATE>
|
||||
{
|
||||
Output * o=Output::get(p[0]);
|
||||
if(o==NULL) return false;
|
||||
Output *o = Output::get(p[0]);
|
||||
if (o == NULL)
|
||||
return false;
|
||||
o->activate(p[1]);
|
||||
StringFormatter::send(stream,F("<Y %d %d>"), p[0],p[1]);
|
||||
StringFormatter::send(stream, F("<Y %d %d>"), p[0], p[1]);
|
||||
}
|
||||
return true;
|
||||
|
||||
case 3: // <Z ID PIN INVERT>
|
||||
Output::create(p[0],p[1],p[2],1);
|
||||
Output::create(p[0], p[1], p[2], 1);
|
||||
return true;
|
||||
|
||||
case 1: // <Z ID>
|
||||
|
@ -374,80 +433,97 @@ bool DCCEXParser::parseZ( Print * stream,int params, int p[]){
|
|||
|
||||
case 0: // <Z>
|
||||
{
|
||||
bool gotone=false;
|
||||
for(Output * tt=Output::firstOutput;tt!=NULL;tt=tt->nextOutput){
|
||||
gotone=true;
|
||||
StringFormatter::send(stream,F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus);
|
||||
bool gotone = false;
|
||||
for (Output *tt = Output::firstOutput; tt != NULL; tt = tt->nextOutput)
|
||||
{
|
||||
gotone = true;
|
||||
StringFormatter::send(stream, F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus);
|
||||
}
|
||||
return gotone;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===================================
|
||||
bool DCCEXParser::parsef(Print * stream, int params, int p[]) {
|
||||
bool DCCEXParser::parsef(Print *stream, int params, int p[])
|
||||
{
|
||||
// JMRI sends this info in DCC message format but it's not exactly
|
||||
// convenient for other processing
|
||||
if (params==2) {
|
||||
byte groupcode=p[1] & 0xE0;
|
||||
if (groupcode == 0x80) {
|
||||
byte normalized= (p[1]<<1 & 0x1e ) | (p[1]>>4 & 0x01);
|
||||
funcmap(p[0],normalized,0,4);
|
||||
if (params == 2)
|
||||
{
|
||||
byte groupcode = p[1] & 0xE0;
|
||||
if (groupcode == 0x80)
|
||||
{
|
||||
byte normalized = (p[1] << 1 & 0x1e) | (p[1] >> 4 & 0x01);
|
||||
funcmap(p[0], normalized, 0, 4);
|
||||
}
|
||||
else if (groupcode == 0xC0) {
|
||||
funcmap(p[0],p[1],5,8);
|
||||
else if (groupcode == 0xC0)
|
||||
{
|
||||
funcmap(p[0], p[1], 5, 8);
|
||||
}
|
||||
else if (groupcode == 0xA0) {
|
||||
funcmap(p[0],p[1],9,12);
|
||||
else if (groupcode == 0xA0)
|
||||
{
|
||||
funcmap(p[0], p[1], 9, 12);
|
||||
}
|
||||
}
|
||||
if (params==3) {
|
||||
if (p[1]==222) funcmap(p[0],p[2],13,20);
|
||||
else if (p[1]==223) funcmap(p[0],p[2],21,28);
|
||||
if (params == 3)
|
||||
{
|
||||
if (p[1] == 222)
|
||||
funcmap(p[0], p[2], 13, 20);
|
||||
else if (p[1] == 223)
|
||||
funcmap(p[0], p[2], 21, 28);
|
||||
}
|
||||
(void)stream;// NO RESPONSE
|
||||
(void)stream; // NO RESPONSE
|
||||
return true;
|
||||
}
|
||||
|
||||
void DCCEXParser::funcmap(int cab, byte value, byte fstart, byte fstop) {
|
||||
for (int i=fstart;i<=fstop;i++) {
|
||||
void DCCEXParser::funcmap(int cab, byte value, byte fstart, byte fstop)
|
||||
{
|
||||
for (int i = fstart; i <= fstop; i++)
|
||||
{
|
||||
DCC::setFn(cab, i, value & 1);
|
||||
value>>=1;
|
||||
value >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
//===================================
|
||||
bool DCCEXParser::parseT(Print * stream, int params, int p[]) {
|
||||
switch(params){
|
||||
bool DCCEXParser::parseT(Print *stream, int params, int p[])
|
||||
{
|
||||
switch (params)
|
||||
{
|
||||
case 0: // <T> show all turnouts
|
||||
{
|
||||
bool gotOne=false;
|
||||
for(Turnout *tt=Turnout::firstTurnout;tt!=NULL;tt=tt->nextTurnout){
|
||||
gotOne=true;
|
||||
StringFormatter::send(stream,F("<H %d %d>"), tt->data.id, tt->data.tStatus & STATUS_ACTIVE);
|
||||
bool gotOne = false;
|
||||
for (Turnout *tt = Turnout::firstTurnout; tt != NULL; tt = tt->nextTurnout)
|
||||
{
|
||||
gotOne = true;
|
||||
StringFormatter::send(stream, F("<H %d %d>"), tt->data.id, tt->data.tStatus & STATUS_ACTIVE);
|
||||
}
|
||||
return gotOne; // will <X> if none found
|
||||
}
|
||||
|
||||
case 1: // <T id> delete turnout
|
||||
if (!Turnout::remove(p[0])) return false;
|
||||
StringFormatter::send(stream,F("<O>"));
|
||||
if (!Turnout::remove(p[0]))
|
||||
return false;
|
||||
StringFormatter::send(stream, F("<O>"));
|
||||
return true;
|
||||
|
||||
case 2: // <T id 0|1> activate turnout
|
||||
{
|
||||
Turnout* tt=Turnout::get(p[0]);
|
||||
if (!tt) return false;
|
||||
Turnout *tt = Turnout::get(p[0]);
|
||||
if (!tt)
|
||||
return false;
|
||||
tt->activate(p[1]);
|
||||
StringFormatter::send(stream,F("<H %d %d>"), tt->data.id, tt->data.tStatus & STATUS_ACTIVE);
|
||||
StringFormatter::send(stream, F("<H %d %d>"), tt->data.id, tt->data.tStatus & STATUS_ACTIVE);
|
||||
}
|
||||
return true;
|
||||
|
||||
case 3: // <T id addr subaddr> define turnout
|
||||
if (!Turnout::create(p[0],p[1],p[2])) return false;
|
||||
StringFormatter::send(stream,F("<O>"));
|
||||
if (!Turnout::create(p[0], p[1], p[2]))
|
||||
return false;
|
||||
StringFormatter::send(stream, F("<O>"));
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -455,19 +531,23 @@ bool DCCEXParser::parseT(Print * stream, int params, int p[]) {
|
|||
}
|
||||
}
|
||||
|
||||
bool DCCEXParser::parseS( Print * stream,int params, int p[]) {
|
||||
bool DCCEXParser::parseS(Print *stream, int params, int p[])
|
||||
{
|
||||
|
||||
switch(params){
|
||||
switch (params)
|
||||
{
|
||||
case 3: // <S id pin pullup> create sensor. pullUp indicator (0=LOW/1=HIGH)
|
||||
Sensor::create(p[0],p[1],p[2]);
|
||||
Sensor::create(p[0], p[1], p[2]);
|
||||
return true;
|
||||
|
||||
case 1: // S id> remove sensor
|
||||
if (Sensor::remove(p[0])) return true;
|
||||
if (Sensor::remove(p[0]))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case 0: // <S> lit sensor states
|
||||
for(Sensor * tt=Sensor::firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||
for (Sensor *tt = Sensor::firstSensor; tt != NULL; tt = tt->nextSensor)
|
||||
{
|
||||
StringFormatter::send(stream, F("<Q %d %d %d>"), tt->data.snum, tt->data.pin, tt->data.pullUp);
|
||||
}
|
||||
return true;
|
||||
|
@ -478,36 +558,39 @@ bool DCCEXParser::parseS( Print * stream,int params, int p[]) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool DCCEXParser::parseD( Print * stream,int params, int p[]) {
|
||||
if (params==0) return false;
|
||||
bool onOff=(params>0) && (p[1]==1 || p[1]==HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off
|
||||
switch(p[0]){
|
||||
bool DCCEXParser::parseD(Print *stream, int params, int p[])
|
||||
{
|
||||
if (params == 0)
|
||||
return false;
|
||||
bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off
|
||||
switch (p[0])
|
||||
{
|
||||
case HASH_KEYWORD_CABS: // <D CABS>
|
||||
DCC::displayCabList(stream);
|
||||
return true;
|
||||
|
||||
case HASH_KEYWORD_RAM: // <D RAM>
|
||||
StringFormatter::send(stream,F("\nFree memory=%d\n"),freeMemory());
|
||||
StringFormatter::send(stream, F("\nFree memory=%d\n"), freeMemory());
|
||||
break;
|
||||
|
||||
case HASH_KEYWORD_ACK: // <D ACK ON/OFF>
|
||||
Diag::ACK=onOff;
|
||||
Diag::ACK = onOff;
|
||||
return true;
|
||||
|
||||
case HASH_KEYWORD_CMD: // <D CMD ON/OFF>
|
||||
Diag::CMD=onOff;
|
||||
Diag::CMD = onOff;
|
||||
return true;
|
||||
|
||||
case HASH_KEYWORD_WIFI: // <D WIFI ON/OFF>
|
||||
Diag::WIFI=onOff;
|
||||
Diag::WIFI = onOff;
|
||||
return true;
|
||||
|
||||
case HASH_KEYWORD_WIT: // <D WIT ON/OFF>
|
||||
Diag::WITHROTTLE=onOff;
|
||||
Diag::WITHROTTLE = onOff;
|
||||
return true;
|
||||
|
||||
case HASH_KEYWORD_DCC:
|
||||
DCCWaveform::setDiagnosticSlowWave(params>=1 && p[1]==HASH_KEYWORD_SLOW);
|
||||
DCCWaveform::setDiagnosticSlowWave(params >= 1 && p[1] == HASH_KEYWORD_SLOW);
|
||||
return true;
|
||||
default: // invalid/unknown
|
||||
break;
|
||||
|
@ -515,40 +598,46 @@ bool DCCEXParser::parseD( Print * stream,int params, int p[]) {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
// CALLBACKS must be static
|
||||
bool DCCEXParser::stashCallback(Print * stream,int p[MAX_PARAMS]) {
|
||||
if (stashBusy || asyncBanned) return false;
|
||||
stashBusy=true;
|
||||
stashStream=stream;
|
||||
memcpy(stashP,p,MAX_PARAMS*sizeof(p[0]));
|
||||
// CALLBACKS must be static
|
||||
bool DCCEXParser::stashCallback(Print *stream, int p[MAX_PARAMS])
|
||||
{
|
||||
if (stashBusy || asyncBanned)
|
||||
return false;
|
||||
stashBusy = true;
|
||||
stashStream = stream;
|
||||
memcpy(stashP, p, MAX_PARAMS * sizeof(p[0]));
|
||||
return true;
|
||||
}
|
||||
void DCCEXParser::callback_W(int result) {
|
||||
StringFormatter::send(stashStream,F("<r%d|%d|%d %d>"), stashP[2], stashP[3],stashP[0],result==1?stashP[1]:-1);
|
||||
stashBusy=false;
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_B(int result) {
|
||||
StringFormatter::send(stashStream,F("<r%d|%d|%d %d %d>"), stashP[3],stashP[4], stashP[0],stashP[1],result==1?stashP[2]:-1);
|
||||
stashBusy=false;
|
||||
}
|
||||
void DCCEXParser::callback_Vbit(int result) {
|
||||
StringFormatter::send(stashStream,F("<v %d %d %d>"), stashP[0], stashP[1],result);
|
||||
stashBusy=false;
|
||||
}
|
||||
void DCCEXParser::callback_Vbyte(int result) {
|
||||
StringFormatter::send(stashStream,F("<v %d %d>"), stashP[0],result);
|
||||
stashBusy=false;
|
||||
void DCCEXParser::callback_W(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1);
|
||||
stashBusy = false;
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_R(int result) {
|
||||
StringFormatter::send(stashStream,F("<r%d|%d|%d %d>"),stashP[1],stashP[2],stashP[0],result);
|
||||
stashBusy=false;
|
||||
void DCCEXParser::callback_B(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r%d|%d|%d %d %d>"), stashP[3], stashP[4], stashP[0], stashP[1], result == 1 ? stashP[2] : -1);
|
||||
stashBusy = false;
|
||||
}
|
||||
void DCCEXParser::callback_Vbit(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<v %d %d %d>"), stashP[0], stashP[1], result);
|
||||
stashBusy = false;
|
||||
}
|
||||
void DCCEXParser::callback_Vbyte(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<v %d %d>"), stashP[0], result);
|
||||
stashBusy = false;
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_Rloco(int result) {
|
||||
StringFormatter::send(stashStream,F("<r %d>"),result);
|
||||
stashBusy=false;
|
||||
void DCCEXParser::callback_R(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result);
|
||||
stashBusy = false;
|
||||
}
|
||||
|
||||
void DCCEXParser::callback_Rloco(int result)
|
||||
{
|
||||
StringFormatter::send(stashStream, F("<r %d>"), result);
|
||||
stashBusy = false;
|
||||
}
|
||||
|
|
19
MemStream.h
19
MemStream.h
|
@ -24,34 +24,43 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#define MemStream_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <Stream.h>
|
||||
#endif
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
class MemStream : public Stream
|
||||
{
|
||||
private:
|
||||
uint8_t * _buffer;
|
||||
uint8_t *_buffer;
|
||||
const uint16_t _len;
|
||||
bool _buffer_overflow;
|
||||
uint16_t _pos_read;
|
||||
uint16_t _pos_write;
|
||||
bool _allowWrite;
|
||||
|
||||
|
||||
public:
|
||||
// public methods
|
||||
MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len = 0, bool allowWrite=true);
|
||||
MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len = 0, bool allowWrite = true);
|
||||
~MemStream() {}
|
||||
|
||||
operator const uint8_t *() const { return _buffer; }
|
||||
operator const char *() const { return (const char*)_buffer; }
|
||||
operator const char *() const { return (const char *)_buffer; }
|
||||
|
||||
uint16_t current_length() const { return _pos_write; }
|
||||
|
||||
bool listen() { return true; }
|
||||
void end() {}
|
||||
bool isListening() { return true; }
|
||||
bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; }
|
||||
bool overflow()
|
||||
{
|
||||
bool ret = _buffer_overflow;
|
||||
_buffer_overflow = false;
|
||||
return ret;
|
||||
}
|
||||
int peek();
|
||||
|
||||
virtual size_t write(uint8_t byte);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#ifndef MotorDrivers_h
|
||||
#define MotorDrivers_h
|
||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
// *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM.
|
||||
// It will be overwritten if the library is updated.
|
||||
|
@ -9,7 +12,6 @@
|
|||
// A custom hardware setup will require your sketch to create MotorDriver instances
|
||||
// similar to those defined here, WITHOUT editing this file.
|
||||
|
||||
|
||||
const byte UNUSED_PIN = 255;
|
||||
|
||||
// MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin,
|
||||
|
@ -17,27 +19,27 @@ const byte UNUSED_PIN = 255;
|
|||
|
||||
// Arduino standard Motor Shield
|
||||
#define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \
|
||||
new MotorDriver(3 , 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \
|
||||
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 250 , UNUSED_PIN)
|
||||
new MotorDriver(3, 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \
|
||||
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, TRIP_CURRENT_PROG, UNUSED_PIN)
|
||||
|
||||
// Pololu Motor Shield
|
||||
#define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
|
||||
new MotorDriver(4, 7, UNUSED_PIN, 9 , A0, 18, 2000, 12), \
|
||||
new MotorDriver(2, 8, UNUSED_PIN, 10, A1, 18, 250 , UNUSED_PIN)
|
||||
new MotorDriver(4, 7, UNUSED_PIN, 9, A0, 18, 2000, 12), \
|
||||
new MotorDriver(2, 8, UNUSED_PIN, 10, A1, 18, TRIP_CURRENT_PROG, UNUSED_PIN)
|
||||
|
||||
// Firebox Mk1
|
||||
#define FIREBOX_MK1 F("FIREBOX_MK1"), \
|
||||
new MotorDriver(3, 6, 7, UNUSED_PIN, A5, 9.766, 5500, UNUSED_PIN), \
|
||||
new MotorDriver(4, 8, 9, UNUSED_PIN, A1, 5.00, 250 , UNUSED_PIN)
|
||||
new MotorDriver(4, 8, 9, UNUSED_PIN, A1, 5.00, TRIP_CURRENT_PROG, UNUSED_PIN)
|
||||
|
||||
// Firebox Mk1S
|
||||
#define FIREBOX_MK1S F("FIREBOX_MK1A"), \
|
||||
new MotorDriver(24, 21, 22, 25, 23, 9.766, 5500, UNUSED_PIN), \
|
||||
new MotorDriver(30, 27, 28, 31, 29, 5.00, 250 , UNUSED_PIN)
|
||||
new MotorDriver(30, 27, 28, 31, 29, 5.00, TRIP_CURRENT_PROG, UNUSED_PIN)
|
||||
|
||||
// FunduMoto Motor Shield
|
||||
#define FUNDUMOTO_SHIELD F("FUNDUMOTO_SHIELD"), \
|
||||
new MotorDriver(10 , 12, UNUSED_PIN, 9, A0, 2.99, 2000, UNUSED_PIN), \
|
||||
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 250 , UNUSED_PIN)
|
||||
new MotorDriver(10, 12, UNUSED_PIN, 9, A0, 2.99, 2000, UNUSED_PIN), \
|
||||
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, TRIP_CURRENT_PROG, UNUSED_PIN)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -85,10 +85,15 @@ ISR(TIMER2_OVF_vect)
|
|||
|
||||
#include "ATMEGA4809/Timer.h"
|
||||
|
||||
Timer TimerA(0);
|
||||
Timer TimerA(1);
|
||||
Timer TimerB(2);
|
||||
|
||||
ISR(TCA0_OVF_vect) {
|
||||
ISR(TIMER1_OVF_vect) {
|
||||
TimerA.isrCallback();
|
||||
}
|
||||
|
||||
ISR(TIMER2_OVF_vect) {
|
||||
TimerB.isrCallback();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "DIAG.h"
|
||||
#include "StringFormatter.h"
|
||||
#include "WiThrottle.h"
|
||||
|
||||
|
||||
const char PROGMEM READY_SEARCH[] = "\r\nready\r\n";
|
||||
const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n";
|
||||
const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000";
|
||||
|
|
|
@ -21,26 +21,28 @@
|
|||
#define WifiInterface_h
|
||||
#include "DCCEXParser.h"
|
||||
#include "MemStream.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
typedef void (*HTTP_CALLBACK)(Print * stream, byte * cmd);
|
||||
typedef void (*HTTP_CALLBACK)(Print *stream, byte *cmd);
|
||||
|
||||
class WifiInterface {
|
||||
class WifiInterface
|
||||
{
|
||||
|
||||
public:
|
||||
static bool setup(Stream & setupStream, const __FlashStringHelper* SSSid, const __FlashStringHelper* password,
|
||||
const __FlashStringHelper* hostname, int port);
|
||||
public:
|
||||
static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
|
||||
const __FlashStringHelper *hostname, int port);
|
||||
static void loop();
|
||||
static void ATCommand(const byte * command);
|
||||
static void ATCommand(const byte *command);
|
||||
static void setHTTPCallback(HTTP_CALLBACK callback);
|
||||
|
||||
private:
|
||||
static Stream * wifiStream;
|
||||
private:
|
||||
static Stream *wifiStream;
|
||||
static DCCEXParser parser;
|
||||
static bool setup2( const __FlashStringHelper* SSSid, const __FlashStringHelper* password,
|
||||
const __FlashStringHelper* hostname, int port);
|
||||
static bool checkForOK(const unsigned int timeout, const char* waitfor, bool echo, bool escapeEcho=true);
|
||||
static bool setup2(const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
|
||||
const __FlashStringHelper *hostname, int port);
|
||||
static bool checkForOK(const unsigned int timeout, const char *waitfor, bool echo, bool escapeEcho = true);
|
||||
static bool isHTTP();
|
||||
static HTTP_CALLBACK httpCallback;
|
||||
static bool connected;
|
||||
|
@ -49,8 +51,8 @@ class WifiInterface {
|
|||
static int datalength;
|
||||
static int connectionId;
|
||||
static unsigned long loopTimeoutStart;
|
||||
static const byte MAX_WIFI_BUFFER=250;
|
||||
static byte buffer[MAX_WIFI_BUFFER+1];
|
||||
static const byte MAX_WIFI_BUFFER = 250;
|
||||
static byte buffer[MAX_WIFI_BUFFER + 1];
|
||||
static MemStream streamer;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user