1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-23 08:06:13 +01:00

Merge branch 'feature/config' of https://github.com/DCC-EX/CommandStation-EX into feature/config

This commit is contained in:
Asbelos 2020-09-26 08:10:55 +01:00
commit f76fb8d6c5
8 changed files with 731 additions and 613 deletions

View File

@ -29,10 +29,17 @@
int ramLowWatermark = 32767; // This figure gets overwritten dynamically in loop() int ramLowWatermark = 32767; // This figure gets overwritten dynamically in loop()
#endif #endif
#if defined(ARDUINO_ARCH_MEGAAVR)
#include <Arduino.h>
#endif
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// //
// Enables an I2C 2x24 or 4x24 LCD Screen // Enables an I2C 2x24 or 4x24 LCD Screen
#ifdef ENABLE_LCD #if ENABLE_LCD
bool lcdEnabled = false; bool lcdEnabled = false;
#if defined(LIB_TYPE_PCF8574) #if defined(LIB_TYPE_PCF8574)
LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS); LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS);
@ -113,11 +120,12 @@ void setup()
// //
// More display stuff. Need to put this in a .h file and make // More display stuff. Need to put this in a .h file and make
// it a class // it a class
#ifdef ENABLE_LCD #if ENABLE_LCD
Wire.begin(); Wire.begin();
// Check that we can find the LCD by its address before attempting to use it. // Check that we can find the LCD by its address before attempting to use it.
Wire.beginTransmission(LCD_ADDRESS); Wire.beginTransmission(LCD_ADDRESS);
if(Wire.endTransmission() == 0) { if (Wire.endTransmission() == 0)
{
lcdEnabled = true; lcdEnabled = true;
lcdDisplay.begin(LCD_COLUMNS, LCD_LINES); lcdDisplay.begin(LCD_COLUMNS, LCD_LINES);
lcdDisplay.setBacklight(255); lcdDisplay.setBacklight(255);

21
DCC.h
View File

@ -24,9 +24,11 @@
typedef void (*ACK_CALLBACK)(int result); typedef void (*ACK_CALLBACK)(int result);
enum ackOp { // Program opcodes for the ack Manager enum ackOp
{ // Program opcodes for the ack Manager
BASELINE, // ensure enough resets sent before starting and obtain baseline current BASELINE, // ensure enough resets sent before starting and obtain baseline current
W0,W1, // issue write bit (0..1) packet W0,
W1, // issue write bit (0..1) packet
WB, // issue write byte packet WB, // issue write byte packet
VB, // Issue validate Byte packet VB, // Issue validate Byte packet
V0, // Issue validate bit=0 packet V0, // Issue validate bit=0 packet
@ -55,10 +57,9 @@ SKIPTARGET=0xFF // jump to target
const byte MAX_LOCOS = 50; const byte MAX_LOCOS = 50;
#endif #endif
class DCC
class DCC { {
public: public:
static void begin(const __FlashStringHelper *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver, byte timerNumber = 1); static void begin(const __FlashStringHelper *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver, byte timerNumber = 1);
static void loop(); static void loop();
@ -94,7 +95,8 @@ class DCC {
static __FlashStringHelper *getMotorShieldName(); static __FlashStringHelper *getMotorShieldName();
private: private:
struct LOCO { struct LOCO
{
int loco; int loco;
byte speedCode; byte speedCode;
byte groupFlags; byte groupFlags;
@ -128,8 +130,6 @@ private:
static bool checkResets(bool blocking, uint8_t numResets); 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 # // 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_BYTE_MAIN = 0xEC;
@ -153,12 +153,13 @@ private:
#define ARDUINO_TYPE "NANO" #define ARDUINO_TYPE "NANO"
#elif defined(ARDUINO_AVR_MEGA2560) #elif defined(ARDUINO_AVR_MEGA2560)
#define ARDUINO_TYPE "MEGA" #define ARDUINO_TYPE "MEGA"
#elif defined(ARDUINO_ARCH_MEGAAVR)
#define ARDUINO_TYPE "UNOWIFIR2"
#else #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 #endif
#if ENABLE_LCD
#ifdef ENABLE_LCD
#include <Wire.h> #include <Wire.h>
#if defined(LIB_TYPE_PCF8574) #if defined(LIB_TYPE_PCF8574)
#include <LiquidCrystal_PCF8574.h> #include <LiquidCrystal_PCF8574.h>

View File

@ -20,7 +20,9 @@
#include "DCCEXParser.h" #include "DCCEXParser.h"
#include "DCC.h" #include "DCC.h"
#include "DCCWaveform.h" #include "DCCWaveform.h"
#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
#include "WifiInterface.h" #include "WifiInterface.h"
#endif
#include "Turnouts.h" #include "Turnouts.h"
#include "Outputs.h" #include "Outputs.h"
#include "Sensors.h" #include "Sensors.h"
@ -31,7 +33,6 @@
#include "EEStore.h" #include "EEStore.h"
#include "DIAG.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. // 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 // To discover new keyword numbers , use the <$ YOURKEYWORD> command
const int HASH_KEYWORD_PROG = -29718; const int HASH_KEYWORD_PROG = -29718;
@ -47,7 +48,6 @@ const int HASH_KEYWORD_ON=2657;
const int HASH_KEYWORD_DCC = 6436; const int HASH_KEYWORD_DCC = 6436;
const int HASH_KEYWORD_SLOW = -17209; const int HASH_KEYWORD_SLOW = -17209;
int DCCEXParser::stashP[MAX_PARAMS]; int DCCEXParser::stashP[MAX_PARAMS];
bool DCCEXParser::stashBusy; bool DCCEXParser::stashBusy;
@ -60,35 +60,45 @@ bool DCCEXParser::stashBusy;
// Non-DCC things like turnouts, pins and sensors are handled in additional JMRI interface classes. // Non-DCC things like turnouts, pins and sensors are handled in additional JMRI interface classes.
DCCEXParser::DCCEXParser() {} DCCEXParser::DCCEXParser() {}
void DCCEXParser::flush() { void DCCEXParser::flush()
if (Diag::CMD) DIAG(F("\nBuffer flush")); {
if (Diag::CMD)
DIAG(F("\nBuffer flush"));
bufferLength = 0; bufferLength = 0;
inCommandPayload = false; inCommandPayload = false;
} }
void DCCEXParser::loop(Stream & stream) { void DCCEXParser::loop(Stream &stream)
while(stream.available()) { {
if (bufferLength==MAX_BUFFER) { while (stream.available())
{
if (bufferLength == MAX_BUFFER)
{
flush(); flush();
} }
char ch = stream.read(); char ch = stream.read();
if (ch == '<') { if (ch == '<')
{
inCommandPayload = true; inCommandPayload = true;
bufferLength = 0; bufferLength = 0;
buffer[0] = '\0'; buffer[0] = '\0';
} }
else if (ch == '>') { else if (ch == '>')
{
buffer[bufferLength] = '\0'; buffer[bufferLength] = '\0';
parse(&stream, buffer, false); // Parse this allowing async responses parse(&stream, buffer, false); // Parse this allowing async responses
inCommandPayload = false; inCommandPayload = false;
break; break;
} else if(inCommandPayload) { }
else if (inCommandPayload)
{
buffer[bufferLength++] = ch; buffer[bufferLength++] = ch;
} }
} }
} }
int DCCEXParser::splitValues( int result[MAX_PARAMS], const byte * cmd) { int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
{
byte state = 1; byte state = 1;
byte parameterCount = 0; byte parameterCount = 0;
int runningValue = 0; int runningValue = 0;
@ -96,16 +106,21 @@ void DCCEXParser::loop(Stream & stream) {
bool signNegative = false; bool signNegative = false;
// clear all parameters in case not enough found // 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) { while (parameterCount < MAX_PARAMS)
{
byte hot = *remainingCmd; byte hot = *remainingCmd;
switch (state) { switch (state)
{
case 1: // skipping spaces before a param case 1: // skipping spaces before a param
if (hot==' ') break; if (hot == ' ')
if (hot == '\0' || hot=='>') return parameterCount; break;
if (hot == '\0' || hot == '>')
return parameterCount;
state = 2; state = 2;
continue; continue;
@ -113,15 +128,18 @@ void DCCEXParser::loop(Stream & stream) {
signNegative = false; signNegative = false;
runningValue = 0; runningValue = 0;
state = 3; state = 3;
if (hot!='-') continue; if (hot != '-')
continue;
signNegative = true; signNegative = true;
break; break;
case 3: // building a parameter case 3: // building a parameter
if (hot>='0' && hot<='9') { if (hot >= '0' && hot <= '9')
{
runningValue = 10 * runningValue + (hot - '0'); runningValue = 10 * runningValue + (hot - '0');
break; 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 // 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 // Super Kluge to turn keywords into a hash value that can be recognised later
runningValue = ((runningValue << 5) + runningValue) ^ hot; runningValue = ((runningValue << 5) + runningValue) ^ hot;
@ -138,75 +156,97 @@ void DCCEXParser::loop(Stream & stream) {
} }
FILTER_CALLBACK DCCEXParser::filterCallback = 0; FILTER_CALLBACK DCCEXParser::filterCallback = 0;
void DCCEXParser::setFilter(FILTER_CALLBACK filter) { void DCCEXParser::setFilter(FILTER_CALLBACK filter)
{
filterCallback = filter; filterCallback = filter;
} }
// See documentation on DCC class for info on this section // See documentation on DCC class for info on this section
void DCCEXParser::parse(Print * stream, byte *com, bool blocking) { void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
if (Diag::CMD) DIAG(F("\nPARSING:%s\n"),com); {
if (Diag::CMD)
DIAG(F("\nPARSING:%s\n"), com);
(void)EEPROM; // tell compiler not to warn thi is unused (void)EEPROM; // tell compiler not to warn thi is unused
int p[MAX_PARAMS]; int p[MAX_PARAMS];
while (com[0]=='<' || com[0]==' ') com++; // strip off any number of < or spaces while (com[0] == '<' || com[0] == ' ')
com++; // strip off any number of < or spaces
byte params = splitValues(p, com); byte params = splitValues(p, com);
byte opcode = com[0]; 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 // Functions return from this switch if complete, break from switch implies error <X> to send
switch(opcode) { switch (opcode)
case '\0': return; // filterCallback asked us to ignore {
case '\0':
return; // filterCallback asked us to ignore
case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION> case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION>
{ {
int cab; int cab;
int tspeed; int tspeed;
int direction; int direction;
if (params==4) { // <t REGISTER CAB SPEED DIRECTION> if (params == 4)
{ // <t REGISTER CAB SPEED DIRECTION>
cab = p[1]; cab = p[1];
tspeed = p[2]; tspeed = p[2];
direction = p[3]; direction = p[3];
} }
else if (params==3) { // <t CAB SPEED DIRECTION> else if (params == 3)
{ // <t CAB SPEED DIRECTION>
cab = p[0]; cab = p[0];
tspeed = p[1]; tspeed = p[1];
direction = p[2]; direction = p[2];
} }
else break; else
break;
// Convert JMRI bizarre -1=emergency stop, 0-126 as speeds // Convert JMRI bizarre -1=emergency stop, 0-126 as speeds
// to DCC 0=stop, 1= emergency stop, 2-127 speeds // to DCC 0=stop, 1= emergency stop, 2-127 speeds
if (tspeed>126 || tspeed<-1) break; // invalid JMRI speed code if (tspeed > 126 || tspeed < -1)
if (tspeed<0) tspeed=1; // emergency stop DCC speed break; // invalid JMRI speed code
else if (tspeed>0) tspeed++; // map 1-126 -> 2-127 if (tspeed < 0)
if (cab == 0 && tspeed>1) break; // ignore broadcasts of speed>1 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); DCC::setThrottle(cab, tspeed, direction);
if (params==4) StringFormatter::send(stream,F("<T %d %d %d>"), p[0], p[2],p[3]); if (params == 4)
else StringFormatter::send(stream,F("<O>")); StringFormatter::send(stream, F("<T %d %d %d>"), p[0], p[2], p[3]);
else
StringFormatter::send(stream, F("<O>"));
return; return;
} }
case 'f': // FUNCTION <f CAB BYTE1 [BYTE2]> case 'f': // FUNCTION <f CAB BYTE1 [BYTE2]>
if (parsef(stream,params,p)) return; if (parsef(stream, params, p))
return;
break; break;
case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE> case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE>
if(p[2] != (p[2] & 1)) return; if (p[2] != (p[2] & 1))
return;
DCC::setAccessory(p[0], p[1], p[2] == 1); DCC::setAccessory(p[0], p[1], p[2] == 1);
return; return;
case 'T': // TURNOUT <T ...> case 'T': // TURNOUT <T ...>
if (parseT(stream,params,p)) return; if (parseT(stream, params, p))
return;
break; break;
case 'Z': // OUTPUT <Z ...> case 'Z': // OUTPUT <Z ...>
if (parseZ(stream,params,p)) return; if (parseZ(stream, params, p))
return;
break; break;
case 'S': // SENSOR <S ...> case 'S': // SENSOR <S ...>
if (parseS(stream,params,p)) return; if (parseS(stream, params, p))
return;
break; break;
case 'w': // WRITE CV on MAIN <w CAB CV VALUE> case 'w': // WRITE CV on MAIN <w CAB CV VALUE>
@ -218,37 +258,46 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
return; return;
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)) break; if (!stashCallback(stream, p))
break;
DCC::writeCVByte(p[0], p[1], callback_W, blocking); DCC::writeCVByte(p[0], p[1], callback_W, blocking);
return; return;
case 'V': // VERIFY CV ON PROG <V CV VALUE> <V CV BIT 0|1> case 'V': // VERIFY CV ON PROG <V CV VALUE> <V CV BIT 0|1>
if (params==2) { // <V CV VALUE> if (params == 2)
if (!stashCallback(stream,p)) break; { // <V CV VALUE>
if (!stashCallback(stream, p))
break;
DCC::verifyCVByte(p[0], p[1], callback_Vbyte, blocking); DCC::verifyCVByte(p[0], p[1], callback_Vbyte, blocking);
return; return;
} }
if (params==3) { if (params == 3)
if (!stashCallback(stream,p)) break; {
if (!stashCallback(stream, p))
break;
DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit, blocking); DCC::verifyCVBit(p[0], p[1], p[2], callback_Vbit, blocking);
return; return;
} }
break; break;
case 'B': // WRITE CV BIT ON PROG <B CV BIT VALUE CALLBACKNUM CALLBACKSUB> case 'B': // WRITE CV BIT ON PROG <B CV BIT VALUE CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream,p)) break; if (!stashCallback(stream, p))
break;
DCC::writeCVBit(p[0], p[1], p[2], callback_B, blocking); DCC::writeCVBit(p[0], p[1], p[2], callback_B, blocking);
return; return;
case 'R': // READ CV ON PROG case 'R': // READ CV ON PROG
if (params==3) { // <R CV CALLBACKNUM CALLBACKSUB> if (params == 3)
if (!stashCallback(stream,p)) break; { // <R CV CALLBACKNUM CALLBACKSUB>
if (!stashCallback(stream, p))
break;
DCC::readCV(p[0], callback_R, blocking); DCC::readCV(p[0], callback_R, blocking);
return; return;
} }
if (params==0) { // <R> New read loco id if (params == 0)
if (!stashCallback(stream,p)) break; { // <R> New read loco id
if (!stashCallback(stream, p))
break;
DCC::getLocoId(callback_Rloco, blocking); DCC::getLocoId(callback_Rloco, blocking);
return; return;
} }
@ -256,17 +305,20 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
case '1': // POWERON <1 [MAIN|PROG]> case '1': // POWERON <1 [MAIN|PROG]>
case '0': // POWEROFF <0 [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 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::mainTrack.setPowerMode(mode);
DCCWaveform::progTrack.setPowerMode(mode); DCCWaveform::progTrack.setPowerMode(mode);
StringFormatter::send(stream, F("<p%c>"), opcode); StringFormatter::send(stream, F("<p%c>"), opcode);
return; return;
} }
switch (p[0]) { switch (p[0])
{
case HASH_KEYWORD_MAIN: case HASH_KEYWORD_MAIN:
DCCWaveform::mainTrack.setPowerMode(mode); DCCWaveform::mainTrack.setPowerMode(mode);
StringFormatter::send(stream, F("<p%c MAIN>"), opcode); StringFormatter::send(stream, F("<p%c MAIN>"), opcode);
@ -279,13 +331,14 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
case HASH_KEYWORD_JOIN: case HASH_KEYWORD_JOIN:
DCCWaveform::mainTrack.setPowerMode(mode); DCCWaveform::mainTrack.setPowerMode(mode);
DCCWaveform::progTrack.setPowerMode(mode); DCCWaveform::progTrack.setPowerMode(mode);
if (mode==POWERMODE::ON) { if (mode == POWERMODE::ON)
{
DCC::setProgTrackSyncMain(true); 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; return;
} }
break; break;
} }
@ -297,7 +350,8 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
case 'Q': // SENSORS <Q> case 'Q': // SENSORS <Q>
Sensor::checkAll(); Sensor::checkAll();
for(Sensor * tt=Sensor::firstSensor;tt!=NULL;tt=tt->nextSensor){ for (Sensor *tt = Sensor::firstSensor; tt != NULL; tt = tt->nextSensor)
{
StringFormatter::send(stream, F("<%c %d>"), tt->active ? 'Q' : 'q', tt->data.snum); StringFormatter::send(stream, F("<%c %d>"), tt->active ? 'Q' : 'q', tt->data.snum);
} }
return; return;
@ -324,7 +378,8 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
return; return;
case 'D': // < > case 'D': // < >
if (parseD(stream,params,p)) return; if (parseD(stream, params, p))
return;
return; return;
case '#': // NUMBER OF LOCOSLOTS <#> case '#': // NUMBER OF LOCOSLOTS <#>
@ -332,17 +387,19 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
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 (Diag::CMD) DIAG(F("Setting loco %d F%d %S"),p[0],p[1],p[2]?F("ON"):F("OFF")); 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); DCC::setFn(p[0], p[1], p[2] == 1);
return; return;
#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
case '+': // Complex Wifi interface command (not usual parse) case '+': // Complex Wifi interface command (not usual parse)
WifiInterface::ATCommand(com); WifiInterface::ATCommand(com);
return; return;
#endif
default: //anything else will diagnose and drop out to <X> default: //anything else will diagnose and drop out to <X>
DIAG(F("\nOpcode=%c params=%d\n"), opcode, params); 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]); for (int i = 0; i < params; i++)
DIAG(F("p[%d]=%d (0x%x)\n"), i, p[i], p[i]);
break; break;
} // end of opcode switch } // end of opcode switch
@ -351,15 +408,17 @@ void DCCEXParser::parse(Print * stream, byte *com, bool blocking) {
StringFormatter::send(stream, F("<X>")); 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> case 2: // <Z ID ACTIVATE>
{ {
Output *o = Output::get(p[0]); Output *o = Output::get(p[0]);
if(o==NULL) return false; if (o == NULL)
return false;
o->activate(p[1]); 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]);
} }
@ -375,7 +434,8 @@ bool DCCEXParser::parseZ( Print * stream,int params, int p[]){
case 0: // <Z> case 0: // <Z>
{ {
bool gotone = false; bool gotone = false;
for(Output * tt=Output::firstOutput;tt!=NULL;tt=tt->nextOutput){ for (Output *tt = Output::firstOutput; tt != NULL; tt = tt->nextOutput)
{
gotone = true; gotone = true;
StringFormatter::send(stream, F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus); StringFormatter::send(stream, F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus);
} }
@ -387,44 +447,57 @@ bool DCCEXParser::parseZ( Print * stream,int params, int p[]){
} }
//=================================== //===================================
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 // JMRI sends this info in DCC message format but it's not exactly
// convenient for other processing // convenient for other processing
if (params==2) { if (params == 2)
{
byte groupcode = p[1] & 0xE0; byte groupcode = p[1] & 0xE0;
if (groupcode == 0x80) { if (groupcode == 0x80)
{
byte normalized = (p[1] << 1 & 0x1e) | (p[1] >> 4 & 0x01); byte normalized = (p[1] << 1 & 0x1e) | (p[1] >> 4 & 0x01);
funcmap(p[0], normalized, 0, 4); funcmap(p[0], normalized, 0, 4);
} }
else if (groupcode == 0xC0) { else if (groupcode == 0xC0)
{
funcmap(p[0], p[1], 5, 8); funcmap(p[0], p[1], 5, 8);
} }
else if (groupcode == 0xA0) { else if (groupcode == 0xA0)
{
funcmap(p[0], p[1], 9, 12); funcmap(p[0], p[1], 9, 12);
} }
} }
if (params==3) { 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 (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; return true;
} }
void DCCEXParser::funcmap(int cab, byte value, byte fstart, byte fstop) { void DCCEXParser::funcmap(int cab, byte value, byte fstart, byte fstop)
for (int i=fstart;i<=fstop;i++) { {
for (int i = fstart; i <= fstop; i++)
{
DCC::setFn(cab, i, value & 1); DCC::setFn(cab, i, value & 1);
value >>= 1; value >>= 1;
} }
} }
//=================================== //===================================
bool DCCEXParser::parseT(Print * stream, int params, int p[]) { bool DCCEXParser::parseT(Print *stream, int params, int p[])
switch(params){ {
switch (params)
{
case 0: // <T> show all turnouts case 0: // <T> show all turnouts
{ {
bool gotOne = false; bool gotOne = false;
for(Turnout *tt=Turnout::firstTurnout;tt!=NULL;tt=tt->nextTurnout){ for (Turnout *tt = Turnout::firstTurnout; tt != NULL; tt = tt->nextTurnout)
{
gotOne = true; gotOne = true;
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);
} }
@ -432,21 +505,24 @@ bool DCCEXParser::parseT(Print * stream, int params, int p[]) {
} }
case 1: // <T id> delete turnout case 1: // <T id> delete turnout
if (!Turnout::remove(p[0])) return false; if (!Turnout::remove(p[0]))
return false;
StringFormatter::send(stream, F("<O>")); StringFormatter::send(stream, F("<O>"));
return true; return true;
case 2: // <T id 0|1> activate turnout case 2: // <T id 0|1> activate turnout
{ {
Turnout *tt = Turnout::get(p[0]); Turnout *tt = Turnout::get(p[0]);
if (!tt) return false; if (!tt)
return false;
tt->activate(p[1]); 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; return true;
case 3: // <T id addr subaddr> define turnout case 3: // <T id addr subaddr> define turnout
if (!Turnout::create(p[0],p[1],p[2])) return false; if (!Turnout::create(p[0], p[1], p[2]))
return false;
StringFormatter::send(stream, F("<O>")); StringFormatter::send(stream, F("<O>"));
return true; return true;
@ -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) 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; return true;
case 1: // S id> remove sensor case 1: // S id> remove sensor
if (Sensor::remove(p[0])) return true; if (Sensor::remove(p[0]))
return true;
break; break;
case 0: // <S> lit sensor states 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); StringFormatter::send(stream, F("<Q %d %d %d>"), tt->data.snum, tt->data.pin, tt->data.pullUp);
} }
return true; return true;
@ -478,10 +558,13 @@ bool DCCEXParser::parseS( Print * stream,int params, int p[]) {
return false; return false;
} }
bool DCCEXParser::parseD( Print * stream,int params, int p[]) { bool DCCEXParser::parseD(Print *stream, int params, int p[])
if (params==0) return false; {
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 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]){ switch (p[0])
{
case HASH_KEYWORD_CABS: // <D CABS> case HASH_KEYWORD_CABS: // <D CABS>
DCC::displayCabList(stream); DCC::displayCabList(stream);
return true; return true;
@ -515,40 +598,46 @@ bool DCCEXParser::parseD( Print * stream,int params, int p[]) {
return false; return false;
} }
// CALLBACKS must be static // CALLBACKS must be static
bool DCCEXParser::stashCallback(Print * stream,int p[MAX_PARAMS]) { bool DCCEXParser::stashCallback(Print *stream, int p[MAX_PARAMS])
if (stashBusy || asyncBanned) return false; {
if (stashBusy || asyncBanned)
return false;
stashBusy = true; stashBusy = true;
stashStream = stream; stashStream = stream;
memcpy(stashP, p, MAX_PARAMS * sizeof(p[0])); memcpy(stashP, p, MAX_PARAMS * sizeof(p[0]));
return true; return true;
} }
void DCCEXParser::callback_W(int result) { 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); StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[2], stashP[3], stashP[0], result == 1 ? stashP[1] : -1);
stashBusy = false; stashBusy = false;
} }
void DCCEXParser::callback_B(int result) { 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); 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; stashBusy = false;
} }
void DCCEXParser::callback_Vbit(int result) { void DCCEXParser::callback_Vbit(int result)
{
StringFormatter::send(stashStream, F("<v %d %d %d>"), stashP[0], stashP[1], result); StringFormatter::send(stashStream, F("<v %d %d %d>"), stashP[0], stashP[1], result);
stashBusy = false; stashBusy = false;
} }
void DCCEXParser::callback_Vbyte(int result) { void DCCEXParser::callback_Vbyte(int result)
{
StringFormatter::send(stashStream, F("<v %d %d>"), stashP[0], result); StringFormatter::send(stashStream, F("<v %d %d>"), stashP[0], result);
stashBusy = false; stashBusy = false;
} }
void DCCEXParser::callback_R(int result) { void DCCEXParser::callback_R(int result)
{
StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result); StringFormatter::send(stashStream, F("<r%d|%d|%d %d>"), stashP[1], stashP[2], stashP[0], result);
stashBusy = false; stashBusy = false;
} }
void DCCEXParser::callback_Rloco(int result) { void DCCEXParser::callback_Rloco(int result)
{
StringFormatter::send(stashStream, F("<r %d>"), result); StringFormatter::send(stashStream, F("<r %d>"), result);
stashBusy = false; stashBusy = false;
} }

View File

@ -24,7 +24,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MemStream_h #define MemStream_h
#include <inttypes.h> #include <inttypes.h>
#if defined(ARDUINO_ARCH_MEGAAVR)
#include <Arduino.h>
#else
#include <Stream.h> #include <Stream.h>
#endif
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
class MemStream : public Stream class MemStream : public Stream
@ -37,7 +42,6 @@ private:
uint16_t _pos_write; uint16_t _pos_write;
bool _allowWrite; bool _allowWrite;
public: public:
// public methods // 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);
@ -51,7 +55,12 @@ public:
bool listen() { return true; } bool listen() { return true; }
void end() {} void end() {}
bool isListening() { return true; } 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(); int peek();
virtual size_t write(uint8_t byte); virtual size_t write(uint8_t byte);

View File

@ -1,5 +1,8 @@
#ifndef MotorDrivers_h #ifndef MotorDrivers_h
#define 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. // *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM.
// It will be overwritten if the library is updated. // 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 // A custom hardware setup will require your sketch to create MotorDriver instances
// similar to those defined here, WITHOUT editing this file. // similar to those defined here, WITHOUT editing this file.
const byte UNUSED_PIN = 255; const byte UNUSED_PIN = 255;
// MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin, // MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin,
@ -18,26 +20,26 @@ const byte UNUSED_PIN = 255;
// Arduino standard Motor Shield // Arduino standard Motor Shield
#define STANDARD_MOTOR_SHIELD F("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(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(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, TRIP_CURRENT_PROG, UNUSED_PIN)
// Pololu Motor Shield // Pololu Motor Shield
#define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \ #define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
new MotorDriver(4, 7, UNUSED_PIN, 9, A0, 18, 2000, 12), \ 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(2, 8, UNUSED_PIN, 10, A1, 18, TRIP_CURRENT_PROG, UNUSED_PIN)
// Firebox Mk1 // Firebox Mk1
#define FIREBOX_MK1 F("FIREBOX_MK1"), \ #define FIREBOX_MK1 F("FIREBOX_MK1"), \
new MotorDriver(3, 6, 7, UNUSED_PIN, A5, 9.766, 5500, UNUSED_PIN), \ 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 // Firebox Mk1S
#define FIREBOX_MK1S F("FIREBOX_MK1A"), \ #define FIREBOX_MK1S F("FIREBOX_MK1A"), \
new MotorDriver(24, 21, 22, 25, 23, 9.766, 5500, UNUSED_PIN), \ 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 // FunduMoto Motor Shield
#define FUNDUMOTO_SHIELD F("FUNDUMOTO_SHIELD"), \ #define FUNDUMOTO_SHIELD F("FUNDUMOTO_SHIELD"), \
new MotorDriver(10, 12, UNUSED_PIN, 9, A0, 2.99, 2000, 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, 250 , UNUSED_PIN) new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, TRIP_CURRENT_PROG, UNUSED_PIN)
#endif #endif

View File

@ -85,10 +85,15 @@ ISR(TIMER2_OVF_vect)
#include "ATMEGA4809/Timer.h" #include "ATMEGA4809/Timer.h"
Timer TimerA(0); Timer TimerA(1);
Timer TimerB(2);
ISR(TCA0_OVF_vect) { ISR(TIMER1_OVF_vect) {
TimerA.isrCallback(); TimerA.isrCallback();
} }
ISR(TIMER2_OVF_vect) {
TimerB.isrCallback();
}
#endif #endif

View File

@ -22,6 +22,8 @@
#include "DIAG.h" #include "DIAG.h"
#include "StringFormatter.h" #include "StringFormatter.h"
#include "WiThrottle.h" #include "WiThrottle.h"
const char PROGMEM READY_SEARCH[] = "\r\nready\r\n"; const char PROGMEM READY_SEARCH[] = "\r\nready\r\n";
const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n"; const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n";
const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000"; const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000";

View File

@ -21,12 +21,14 @@
#define WifiInterface_h #define WifiInterface_h
#include "DCCEXParser.h" #include "DCCEXParser.h"
#include "MemStream.h" #include "MemStream.h"
#include <Arduino.h> #include <Arduino.h>
#include <avr/pgmspace.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: public:
static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password, static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password,