mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
Compileable JMRIParser
Has Turnout, Output Sensor support. Needs further cleaning and cross checking against spec. May implement Daves Comms manager etc.
This commit is contained in:
parent
d42589aff5
commit
b89c9068ff
76
EEStore.cpp
Normal file
76
EEStore.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#include "EEStore.h"
|
||||||
|
#include "Turnouts.h"
|
||||||
|
#include "Sensors.h"
|
||||||
|
#include "Outputs.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
ExternalEEPROM EEPROM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void EEStore::init(){
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
EEPROM.begin(0x50); // Address for Microchip 24-series EEPROM with all three A pins grounded (0b1010000 = 0x50)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
eeStore=(EEStore *)calloc(1,sizeof(EEStore));
|
||||||
|
|
||||||
|
EEPROM.get(0,eeStore->data); // get eeStore data
|
||||||
|
|
||||||
|
if(strncmp(eeStore->data.id,EESTORE_ID,sizeof(EESTORE_ID))!=0){ // check to see that eeStore contains valid DCC++ ID
|
||||||
|
sprintf(eeStore->data.id,EESTORE_ID); // if not, create blank eeStore structure (no turnouts, no sensors) and save it back to EEPROM
|
||||||
|
eeStore->data.nTurnouts=0;
|
||||||
|
eeStore->data.nSensors=0;
|
||||||
|
eeStore->data.nOutputs=0;
|
||||||
|
EEPROM.put(0,eeStore->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(); // set memory pointer to first free EEPROM space
|
||||||
|
Turnout::load(); // load turnout definitions
|
||||||
|
Sensor::load(); // load sensor definitions
|
||||||
|
Output::load(); // load output definitions
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void EEStore::clear(){
|
||||||
|
|
||||||
|
sprintf(eeStore->data.id,EESTORE_ID); // create blank eeStore structure (no turnouts, no sensors) and save it back to EEPROM
|
||||||
|
eeStore->data.nTurnouts=0;
|
||||||
|
eeStore->data.nSensors=0;
|
||||||
|
eeStore->data.nOutputs=0;
|
||||||
|
EEPROM.put(0,eeStore->data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void EEStore::store(){
|
||||||
|
reset();
|
||||||
|
Turnout::store();
|
||||||
|
Sensor::store();
|
||||||
|
Output::store();
|
||||||
|
EEPROM.put(0,eeStore->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void EEStore::advance(int n){
|
||||||
|
eeAddress+=n;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void EEStore::reset(){
|
||||||
|
eeAddress=sizeof(EEStore);
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int EEStore::pointer(){
|
||||||
|
return(eeAddress);
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
EEStore *EEStore::eeStore=NULL;
|
||||||
|
int EEStore::eeAddress=0;
|
34
EEStore.h
Normal file
34
EEStore.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef EEStore_h
|
||||||
|
#define EEStore_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
|
#include <SparkFun_External_EEPROM.h>
|
||||||
|
extern ExternalEEPROM EEPROM;
|
||||||
|
#else
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EESTORE_ID "DCC++"
|
||||||
|
|
||||||
|
struct EEStoreData{
|
||||||
|
char id[sizeof(EESTORE_ID)];
|
||||||
|
int nTurnouts;
|
||||||
|
int nSensors;
|
||||||
|
int nOutputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EEStore{
|
||||||
|
static EEStore *eeStore;
|
||||||
|
EEStoreData data;
|
||||||
|
static int eeAddress;
|
||||||
|
static void init();
|
||||||
|
static void reset();
|
||||||
|
static int pointer();
|
||||||
|
static void advance(int);
|
||||||
|
static void store();
|
||||||
|
static void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
268
JMRIParser.cpp
268
JMRIParser.cpp
|
@ -1,24 +1,31 @@
|
||||||
#include "StringParser.h"
|
#include "StringParser.h"
|
||||||
#include "JMRIParser.h"
|
#include "JMRIParser.h"
|
||||||
#include "JMRITurnout.h"
|
|
||||||
#include "DCC.h"
|
#include "DCC.h"
|
||||||
#include "DCCWaveform.h"
|
#include "DCCWaveform.h"
|
||||||
|
#include "Turnouts.h"
|
||||||
|
#include "Outputs.h"
|
||||||
|
#include "Sensors.h"
|
||||||
|
|
||||||
|
#include "EEStore.h"
|
||||||
|
|
||||||
|
const char VERSION[]="99.666";
|
||||||
|
|
||||||
// This is a JMRI command parser
|
// This is a JMRI command parser
|
||||||
// It doesnt know how the string got here, nor how it gets back.
|
// It doesnt know how the string got here, nor how it gets back.
|
||||||
// It knows nothing about hardware or tracks... it just parses strings and
|
// It knows nothing about hardware or tracks... it just parses strings and
|
||||||
// calls the corresponding DCC api.
|
// calls the corresponding DCC api.
|
||||||
//
|
// Non-DCC things like turnouts, pins and sensors are handled in additional JMRI interface classes.
|
||||||
|
|
||||||
|
|
||||||
int JMRIParser::p[MAX_PARAMS];
|
|
||||||
|
|
||||||
// See documentation on DCC class for info on this section
|
// See documentation on DCC class for info on this section
|
||||||
void JMRIParser::parse(Stream & stream,const char *com) {
|
void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
StringParser::send(stream,F("\nParsing %s\n"),com);
|
int p[MAX_PARAMS];
|
||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
int params=StringParser::parse(com+1,p,MAX_PARAMS);
|
int params=StringParser::parse(com+1,p,MAX_PARAMS);
|
||||||
|
|
||||||
|
|
||||||
|
// Functions return from this switch if complete, break from switch implies error <X> to send
|
||||||
switch(com[0]) {
|
switch(com[0]) {
|
||||||
|
|
||||||
/***** SET ENGINE THROTTLES USING 128-STEP SPEED CONTROL ****/
|
/***** SET ENGINE THROTTLES USING 128-STEP SPEED CONTROL ****/
|
||||||
|
@ -26,7 +33,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
case 't': // <t REGISTER CAB SPEED DIRECTION>
|
case 't': // <t REGISTER CAB SPEED DIRECTION>
|
||||||
DCC::setThrottle(p[1],p[2],p[3]);
|
DCC::setThrottle(p[1],p[2],p[3]);
|
||||||
StringParser::send(stream,F("<T %d %d %d>"), p[0], p[2],p[3]);
|
StringParser::send(stream,F("<T %d %d %d>"), p[0], p[2],p[3]);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** OPERATE ENGINE DECODER FUNCTIONS F0-F28 ****/
|
/***** OPERATE ENGINE DECODER FUNCTIONS F0-F28 ****/
|
||||||
|
|
||||||
|
@ -35,7 +42,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
else DCC::setFunction(p[0],p[1]);
|
else DCC::setFunction(p[0],p[1]);
|
||||||
|
|
||||||
// TODO response?
|
// TODO response?
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** OPERATE STATIONARY ACCESSORY DECODERS ****/
|
/***** OPERATE STATIONARY ACCESSORY DECODERS ****/
|
||||||
|
|
||||||
|
@ -60,7 +67,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
* returns: NONE
|
* returns: NONE
|
||||||
*/
|
*/
|
||||||
DCC::setAccessory(p[0],p[1],p[2]);
|
DCC::setAccessory(p[0],p[1],p[2]);
|
||||||
break;
|
return;
|
||||||
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE A TURN-OUT ****/
|
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE A TURN-OUT ****/
|
||||||
|
|
||||||
case 'T': // <T ID THROW>
|
case 'T': // <T ID THROW>
|
||||||
|
@ -75,92 +82,22 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
* *** SEE ACCESSORIES.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "T" COMMAND
|
* *** SEE ACCESSORIES.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "T" COMMAND
|
||||||
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
||||||
*/
|
*/
|
||||||
JMRITurnout::parse(stream,params,p);
|
if (parseT(stream,params,p)) return;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
#ifdef THIS_IS_NOT_YET_COMPLETE
|
|
||||||
|
|
||||||
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE AN OUTPUT PIN ****/
|
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE AN OUTPUT PIN ****/
|
||||||
|
|
||||||
case 'Z': // <Z ID ACTIVATE>
|
case 'Z': // <Z ID ACTIVATE>
|
||||||
/*
|
if (parseZ(stream,params,p)) return;
|
||||||
* <Z ID ACTIVATE>: sets output ID to either the "active" or "inactive" state
|
break;
|
||||||
*
|
|
||||||
* ID: the numeric ID (0-32767) of the output to control
|
|
||||||
* ACTIVATE: 0 (active) or 1 (inactive)
|
|
||||||
*
|
|
||||||
* returns: <Y ID ACTIVATE> or <X> if output ID does not exist
|
|
||||||
*
|
|
||||||
* *** SEE OUTPUTS.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "O" COMMAND
|
|
||||||
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
|
||||||
*/
|
|
||||||
|
|
||||||
int on,os,om;
|
|
||||||
Output* o;
|
|
||||||
|
|
||||||
switch(sscanf(com+1,"%d %d %d",&on,&os,&om)){
|
|
||||||
|
|
||||||
case 2: // argument is string with id number of output followed by zero (LOW) or one (HIGH)
|
|
||||||
o=Output::get(on);
|
|
||||||
if(o!=NULL)
|
|
||||||
o->activate(os);
|
|
||||||
else
|
|
||||||
CommManager::printf("<X>");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // argument is string with id number of output followed by a pin number and invert flag
|
|
||||||
Output::create(on,os,om,1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // argument is a string with id number only
|
|
||||||
Output::remove(on);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -1: // no arguments
|
|
||||||
Output::show(1); // verbose show
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
/***** CREATE/EDIT/REMOVE/SHOW A SENSOR ****/
|
/***** CREATE/EDIT/REMOVE/SHOW A SENSOR ****/
|
||||||
|
|
||||||
case 'S':
|
case 'S':
|
||||||
|
if (parseS(stream,params,p)) return;
|
||||||
int sn,ss,sm;
|
break;
|
||||||
|
|
||||||
switch(sscanf(com+1,"%d %d %d",&sn,&ss,&sm)){
|
|
||||||
|
|
||||||
case 3: // argument is string with id number of sensor followed by a pin number and pullUp indicator (0=LOW/1=HIGH)
|
|
||||||
Sensor::create(sn,ss,sm,1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // argument is a string with id number only
|
|
||||||
Sensor::remove(sn);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -1: // no arguments
|
|
||||||
Sensor::show();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // invalid number of arguments
|
|
||||||
CommManager::printf("<X>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
/***** SHOW STATUS OF ALL SENSORS ****/
|
|
||||||
|
|
||||||
case 'Q': // <Q>
|
|
||||||
/*
|
|
||||||
* returns: the status of each sensor ID in the form <Q ID> (active) or <q ID> (not active)
|
|
||||||
*/
|
|
||||||
Sensor::status();
|
|
||||||
break;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON MAIN OPERATIONS TRACK ****/
|
/***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON MAIN OPERATIONS TRACK ****/
|
||||||
|
|
||||||
case 'w': // <w CAB CV VALUE>
|
case 'w': // <w CAB CV VALUE>
|
||||||
|
@ -175,7 +112,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DCC::writeCVByteMain(p[0],p[1],p[2]);
|
DCC::writeCVByteMain(p[0],p[1],p[2]);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON MAIN OPERATIONS TRACK ****/
|
/***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON MAIN OPERATIONS TRACK ****/
|
||||||
|
|
||||||
|
@ -192,7 +129,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DCC::writeCVBitMain(p[0],p[1],p[2],p[3]);
|
DCC::writeCVBitMain(p[0],p[1],p[2],p[3]);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON PROGRAMMING TRACK ****/
|
/***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON PROGRAMMING TRACK ****/
|
||||||
|
|
||||||
|
@ -211,7 +148,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
|
|
||||||
result=DCC::writeCVByte(p[0],p[1]);
|
result=DCC::writeCVByte(p[0],p[1]);
|
||||||
StringParser::send(stream,F("<r%d|%d|%d %d>"), p[2], p[3],p[0],result?p[1]:-1);
|
StringParser::send(stream,F("<r%d|%d|%d %d>"), p[2], p[3],p[0],result?p[1]:-1);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON PROGRAMMING TRACK ****/
|
/***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON PROGRAMMING TRACK ****/
|
||||||
|
|
||||||
|
@ -231,7 +168,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
|
|
||||||
result=DCC::writeCVBit(p[0],p[1],p[2]);
|
result=DCC::writeCVBit(p[0],p[1],p[2]);
|
||||||
StringParser::send(stream,F("<r%d|%d|%d %d %d>"), p[3],p[4], p[0],p[1],result?p[2]:-1);
|
StringParser::send(stream,F("<r%d|%d|%d %d %d>"), p[3],p[4], p[0],p[1],result?p[2]:-1);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** READ CONFIGURATION VARIABLE BYTE FROM ENGINE DECODER ON PROGRAMMING TRACK ****/
|
/***** READ CONFIGURATION VARIABLE BYTE FROM ENGINE DECODER ON PROGRAMMING TRACK ****/
|
||||||
|
|
||||||
|
@ -248,7 +185,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
StringParser::send(stream,F("<r%d|%d|%d %d>"),p[1],p[2],p[0],DCC::readCV(p[0]));
|
StringParser::send(stream,F("<r%d|%d|%d %d>"),p[1],p[2],p[0],DCC::readCV(p[0]));
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** TURN ON POWER FROM MOTOR SHIELD TO TRACKS ****/
|
/***** TURN ON POWER FROM MOTOR SHIELD TO TRACKS ****/
|
||||||
|
|
||||||
|
@ -261,7 +198,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
||||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||||
StringParser::send(stream,F("<p1>"));
|
StringParser::send(stream,F("<p1>"));
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** TURN OFF POWER FROM MOTOR SHIELD TO TRACKS ****/
|
/***** TURN OFF POWER FROM MOTOR SHIELD TO TRACKS ****/
|
||||||
|
|
||||||
|
@ -274,9 +211,8 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
|
DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
|
||||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
|
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
|
||||||
StringParser::send(stream,F("<p0>"));
|
StringParser::send(stream,F("<p0>"));
|
||||||
break;
|
return;
|
||||||
|
|
||||||
#ifdef THIS_IS_NOT_YET_COMPLETE
|
|
||||||
/***** READ MAIN OPERATIONS TRACK CURRENT ****/
|
/***** READ MAIN OPERATIONS TRACK CURRENT ****/
|
||||||
|
|
||||||
case 'c': // <c>
|
case 'c': // <c>
|
||||||
|
@ -286,11 +222,19 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
* returns: <a CURRENT>
|
* returns: <a CURRENT>
|
||||||
* where CURRENT = 0-1024, based on exponentially-smoothed weighting scheme
|
* where CURRENT = 0-1024, based on exponentially-smoothed weighting scheme
|
||||||
*/
|
*/
|
||||||
StringParser::send(stream,F("<a %d>"), DCCWaveform:mainTrack->getLastRead());
|
StringParser::send(stream,F("<a %d>"), DCCWaveform::mainTrack.getLastCurrent());
|
||||||
break;
|
return;
|
||||||
|
|
||||||
|
/***** SHOW STATUS OF ALL SENSORS ****/
|
||||||
|
|
||||||
|
case 'Q': // <Q>
|
||||||
|
/*
|
||||||
|
* returns: the status of each sensor ID in the form <Q ID> (active) or <q ID> (not active)
|
||||||
|
*/
|
||||||
|
Sensor::status(stream);
|
||||||
|
return;
|
||||||
|
|
||||||
/***** READ STATUS OF DCC++ BASE STATION ****/
|
/***** READ STATUS OF DCC++ BASE STATION ****/
|
||||||
|
|
||||||
case 's': // <s>
|
case 's': // <s>
|
||||||
/*
|
/*
|
||||||
* returns status messages containing track power status, throttle status, turn-out status, and a version number
|
* returns status messages containing track power status, throttle status, turn-out status, and a version number
|
||||||
|
@ -298,18 +242,12 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
*
|
*
|
||||||
* returns: series of status messages that can be read by an interface to determine status of DCC++ Base Station and important settings
|
* returns: series of status messages that can be read by an interface to determine status of DCC++ Base Station and important settings
|
||||||
*/
|
*/
|
||||||
// mainTrack->showStatus();
|
// TODO Send stats of speed reminders table
|
||||||
for(int i=1;i<=mainTrack->numDev;i++){
|
|
||||||
if(mainTrack->speedTable[i]==0)
|
StringParser::send(stream,F("<iDCC-EX BASE STATION FOR ARDUINO / %s: V-%s %s/%s\n>"), BOARD_NAME, VERSION, __DATE__, __TIME__ );
|
||||||
continue;
|
|
||||||
CommManager::printf("<T%d %d %d>", i, mainTrack->speedTable[i]>0 ? mainTrack->speedTable[i] : -mainTrack->speedTable[i], mainTrack->speedTable[i]>0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
CommManager::printf("<iDCC++ BASE STATION FOR ARDUINO %s / %s: V-%s / %s %s>", "SAMD21 Command Station", BOARD_NAME, VERSION, __DATE__, __TIME__);
|
|
||||||
CommManager::showInitInfo();
|
|
||||||
Turnout::show();
|
|
||||||
Output::show();
|
|
||||||
|
|
||||||
break;
|
// TODO send status of turnouts etc etc
|
||||||
|
return;
|
||||||
|
|
||||||
/***** STORE SETTINGS IN EEPROM ****/
|
/***** STORE SETTINGS IN EEPROM ****/
|
||||||
|
|
||||||
|
@ -321,8 +259,8 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
EEStore::store();
|
EEStore::store();
|
||||||
CommManager::printf("<e %d %d %d>", EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
|
StringParser::send(stream,F("<e %d %d %d>"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
|
||||||
break;
|
return;
|
||||||
|
|
||||||
/***** CLEAR SETTINGS IN EEPROM ****/
|
/***** CLEAR SETTINGS IN EEPROM ****/
|
||||||
|
|
||||||
|
@ -334,21 +272,121 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
EEStore::clear();
|
EEStore::clear();
|
||||||
CommManager::printf("<O>");
|
StringParser::send(stream, F("<O>"));
|
||||||
break;
|
return;
|
||||||
#endif
|
|
||||||
/***** PRINT CARRIAGE RETURN IN stream MONITOR WINDOW ****/
|
|
||||||
|
|
||||||
case ' ': // < >
|
case ' ': // < >
|
||||||
/*
|
/*
|
||||||
* simply prints a carriage return - useful when interacting with Ardiuno through stream monitor window
|
* simply prints a carriage return - useful when interacting with Ardiuno through stream monitor window
|
||||||
*
|
*
|
||||||
* returns: a carriage return
|
* returns: a carriage return
|
||||||
*/
|
*/
|
||||||
StringParser::send(stream,F("\n"));
|
StringParser::send(stream,F("\n"));
|
||||||
break;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
|
} // end of opcode switch
|
||||||
|
|
||||||
|
// Any fallout here sends an <X>
|
||||||
|
StringParser::send(stream, F("<X>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool JMRIParser::parseZ(Stream & stream, int params, int p[]){
|
||||||
|
/*
|
||||||
|
* <Z ID ACTIVATE>: sets output ID to either the "active" or "inactive" state
|
||||||
|
*
|
||||||
|
* ID: the numeric ID (0-32767) of the output to control
|
||||||
|
* ACTIVATE: 0 (active) or 1 (inactive)
|
||||||
|
*
|
||||||
|
* returns: <Y ID ACTIVATE> or <X> if output ID does not exist
|
||||||
|
*
|
||||||
|
* *** SEE OUTPUTS.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "O" COMMAND
|
||||||
|
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (params) {
|
||||||
|
|
||||||
|
case 2: // argument is string with id number of output followed by zero (LOW) or one (HIGH)
|
||||||
|
{
|
||||||
|
Output * o=Output::get(p[0]);
|
||||||
|
if(o==NULL) return false;
|
||||||
|
o->activate(p[1]);
|
||||||
|
StringParser::send(stream,F("<Y %d %d>"), p[0],p[1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // argument is string with id number of output followed by a pin number and invert flag
|
||||||
|
Output::create(p[0],p[1],p[2],1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // argument is a string with id number only
|
||||||
|
Output::remove(p[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0: // no arguments
|
||||||
|
Output::showAll(stream); // verbose show
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//===================================
|
||||||
|
bool JMRIParser::parseT(Stream & stream, int params, int p[]) {
|
||||||
|
switch(params){
|
||||||
|
case 0: // <T>
|
||||||
|
Turnout::showAll(stream); // verbose show
|
||||||
|
break;
|
||||||
|
case 1: // <T id>
|
||||||
|
if (!Turnout::remove(p[0])) break;
|
||||||
|
StringParser::send(stream,F("<O>"));
|
||||||
|
break;
|
||||||
|
case 2: // <T id 0|1>
|
||||||
|
if (!Turnout::activate(p[0],p[1])) return false;
|
||||||
|
Turnout::show(stream,p[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // <T id addr subaddr>
|
||||||
|
if (!Turnout::create(p[0],p[1],p[2])) return false;
|
||||||
|
StringParser::send(stream,F("<O>"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false; // will <x>
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JMRIParser::parseS(Stream & stream, int params, int p[]) {
|
||||||
|
|
||||||
|
switch(params){
|
||||||
|
|
||||||
|
case 3: // argument is string with id number of sensor followed by a pin number and pullUp indicator (0=LOW/1=HIGH)
|
||||||
|
Sensor::create(p[0],p[1],p[2]);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 1: // argument is a string with id number only
|
||||||
|
if (Sensor::remove(p[0])) return true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -1: // no arguments
|
||||||
|
Sensor::show(stream);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default: // invalid number of arguments
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
12
JMRIParser.h
12
JMRIParser.h
|
@ -3,9 +3,15 @@
|
||||||
struct JMRIParser
|
struct JMRIParser
|
||||||
{
|
{
|
||||||
static void parse(Stream & stream,const char * command);
|
static void parse(Stream & stream,const char * command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int MAX_PARAMS=10;
|
|
||||||
static int p[MAX_PARAMS];
|
static bool parseT(Stream & stream, int params, int p[]);
|
||||||
|
static bool parseZ(Stream & stream, int params, int p[]);
|
||||||
|
static bool parseS(Stream & stream, int params, int p[]);
|
||||||
|
|
||||||
|
static const int MAX_PARAMS=10; // longest command sent in
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define BOARD_NAME "not yet configured"
|
||||||
#endif
|
#endif
|
||||||
|
|
173
JMRITurnout.cpp
173
JMRITurnout.cpp
|
@ -1,173 +0,0 @@
|
||||||
/**********************************************************************
|
|
||||||
|
|
||||||
Accessories.cpp
|
|
||||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
|
||||||
|
|
||||||
Part of DCC++ BASE STATION for the Arduino
|
|
||||||
|
|
||||||
**********************************************************************/
|
|
||||||
/**********************************************************************
|
|
||||||
|
|
||||||
DCC++ BASE STATION can keep track of the direction of any turnout that is controlled
|
|
||||||
by a DCC stationary accessory decoder. All turnouts, as well as any other DCC accessories
|
|
||||||
connected in this fashion, can always be operated using the DCC BASE STATION Accessory command:
|
|
||||||
|
|
||||||
<a ADDRESS SUBADDRESS ACTIVATE>
|
|
||||||
|
|
||||||
However, this general command simply sends the appropriate DCC instruction packet to the main tracks
|
|
||||||
to operate connected accessories. It does not store or retain any information regarding the current
|
|
||||||
status of that accessory.
|
|
||||||
|
|
||||||
To have this sketch store and retain the direction of DCC-connected turnouts, as well as automatically
|
|
||||||
invoke the required <a> command as needed, first define/edit/delete such turnouts using the following
|
|
||||||
variations of the "T" command:
|
|
||||||
|
|
||||||
<T ID ADDRESS SUBADDRESS>: creates a new turnout ID, with specified ADDRESS and SUBADDRESS
|
|
||||||
if turnout ID already exists, it is updated with specificed ADDRESS and SUBADDRESS
|
|
||||||
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
|
|
||||||
|
|
||||||
<T ID>: deletes definition of turnout ID
|
|
||||||
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
|
|
||||||
|
|
||||||
<T>: lists all defined turnouts
|
|
||||||
returns: <H ID ADDRESS SUBADDRESS THROW> for each defined turnout or <X> if no turnouts defined
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
ID: the numeric ID (0-32767) of the turnout to control
|
|
||||||
ADDRESS: the primary address of the decoder controlling this turnout (0-511)
|
|
||||||
SUBADDRESS: the subaddress of the decoder controlling this turnout (0-3)
|
|
||||||
|
|
||||||
Once all turnouts have been properly defined, use the <E> command to store their definitions to EEPROM.
|
|
||||||
If you later make edits/additions/deletions to the turnout definitions, you must invoke the <E> command if you want those
|
|
||||||
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
|
|
||||||
|
|
||||||
To "throw" turnouts that have been defined use:
|
|
||||||
|
|
||||||
<T ID THROW>: sets turnout ID to either the "thrown" or "unthrown" position
|
|
||||||
returns: <H ID THROW>, or <X> if turnout ID does not exist
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
ID: the numeric ID (0-32767) of the turnout to control
|
|
||||||
THROW: 0 (unthrown) or 1 (thrown)
|
|
||||||
|
|
||||||
When controlled as such, the Arduino updates and stores the direction of each Turnout in EEPROM so
|
|
||||||
that it is retained even without power. A list of the current directions of each Turnout in the form <H ID THROW> is generated
|
|
||||||
by this sketch whenever the <s> status command is invoked. This provides an efficient way of initializing
|
|
||||||
the directions of any Turnouts being monitored or controlled by a separate interface or GUI program.
|
|
||||||
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
//#include "EEStore.h"
|
|
||||||
//#include <EEPROM.h>
|
|
||||||
#include "JMRITurnout.h"
|
|
||||||
#include "StringParser.h"
|
|
||||||
#include "DCC.h"
|
|
||||||
|
|
||||||
void JMRITurnout::parse(Stream & stream, int params, int p[]) {
|
|
||||||
|
|
||||||
switch(params){
|
|
||||||
case 0: // <T>
|
|
||||||
showAll(stream); // verbose show
|
|
||||||
return;
|
|
||||||
case 1: // <T id>
|
|
||||||
if (!remove(p[0])) break;
|
|
||||||
StringParser::send(stream,F("<O>"));
|
|
||||||
return;
|
|
||||||
case 2: // <T id 0|1>
|
|
||||||
if (!activate(p[0],p[1])) break;
|
|
||||||
show(stream,p[0],false);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 3: // <T id addr subaddr>
|
|
||||||
if (!create(p[0],p[1],p[2])) break;
|
|
||||||
StringParser::send(stream,F("<O>"));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break; // will <x>
|
|
||||||
}
|
|
||||||
StringParser::send(stream,F("<X>"));
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////// ALL PRIVATE BELOW HERE //////////////////
|
|
||||||
JMRITurnout::TurnoutEntry JMRITurnout::table[MAX_TURNOUTS];
|
|
||||||
|
|
||||||
bool JMRITurnout::create(int id, int add, byte subAdd){
|
|
||||||
if (id<0 || id>=MAX_TURNOUTS || table[id].address!=0) return false;
|
|
||||||
table[id].address=add;
|
|
||||||
table[id].subAddress=subAdd;
|
|
||||||
table[id].thrown=false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool JMRITurnout::invalid(int id) {
|
|
||||||
return id<0 || id>=MAX_TURNOUTS || table[id].address==0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JMRITurnout::activate(int id,bool thrown){
|
|
||||||
if (invalid(id)) return false;
|
|
||||||
table[id].thrown=thrown;
|
|
||||||
DCC::setAccessory(table[id].address,table[id].subAddress,table[id].thrown);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JMRITurnout::remove(int id){
|
|
||||||
if (invalid(id)) return false;
|
|
||||||
table[id].address=0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool JMRITurnout::show(Stream & stream ,int id, bool all){
|
|
||||||
if (invalid(id)) return false;
|
|
||||||
if (all) StringParser::send(stream, F("<H %d %d %d %d>"),
|
|
||||||
id, table[id].address,
|
|
||||||
table[id].subAddress, table[id].thrown);
|
|
||||||
else StringParser::send(stream, F("<H %d %d>"),
|
|
||||||
id, table[id].thrown);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMRITurnout::showAll(Stream & stream ){
|
|
||||||
for (int id=0;id<MAX_TURNOUTS;id++) {
|
|
||||||
if (table[id].address!=0) show(stream,id,true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
#ifdef NOT_YET_IMPLEMENTED
|
|
||||||
void Turnout::load(){
|
|
||||||
struct TurnoutData data;
|
|
||||||
Turnout *tt;
|
|
||||||
|
|
||||||
for(int i=0;i<EEStore::eeStore->data.nTurnouts;i++){
|
|
||||||
EEPROM.get(EEStore::pointer(),data);
|
|
||||||
tt=create(data.id,data.address,data.subAddress);
|
|
||||||
tt->data.tStatus=data.tStatus;
|
|
||||||
tt->num=EEStore::pointer();
|
|
||||||
EEStore::advance(sizeof(tt->data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void Turnout::store(){
|
|
||||||
Turnout *tt;
|
|
||||||
|
|
||||||
tt=firstTurnout;
|
|
||||||
EEStore::eeStore->data.nTurnouts=0;
|
|
||||||
|
|
||||||
while(tt!=NULL){
|
|
||||||
tt->num=EEStore::pointer();
|
|
||||||
EEPROM.put(EEStore::pointer(),tt->data);
|
|
||||||
EEStore::advance(sizeof(tt->data));
|
|
||||||
tt=tt->nextTurnout;
|
|
||||||
EEStore::eeStore->data.nTurnouts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
|
@ -1,30 +0,0 @@
|
||||||
#ifndef JMRITurnout_h
|
|
||||||
#define TMRITurnout_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
class JMRITurnout {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void parse(Stream & stream, int params, int p[]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static void showAll(Stream & stream);
|
|
||||||
static bool show(Stream & stream ,int id, bool all);
|
|
||||||
static bool create(int id, int address, byte subAddress);
|
|
||||||
static bool remove(int id);
|
|
||||||
static bool activate(int id, bool thrown);
|
|
||||||
static bool invalid(int id);
|
|
||||||
|
|
||||||
struct TurnoutEntry {
|
|
||||||
int address;
|
|
||||||
byte subAddress;
|
|
||||||
bool thrown;
|
|
||||||
};
|
|
||||||
static TurnoutEntry table[MAX_TURNOUTS];
|
|
||||||
};
|
|
||||||
#endif
|
|
186
Outputs.cpp
Normal file
186
Outputs.cpp
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
DCC++ BASE STATION supports optional OUTPUT control of any unused Arduino Pins for custom purposes.
|
||||||
|
Pins can be activited or de-activated. The default is to set ACTIVE pins HIGH and INACTIVE pins LOW.
|
||||||
|
However, this default behavior can be inverted for any pin in which case ACTIVE=LOW and INACTIVE=HIGH.
|
||||||
|
|
||||||
|
Definitions and state (ACTIVE/INACTIVE) for pins are retained in EEPROM and restored on power-up.
|
||||||
|
The default is to set each defined pin to active or inactive according to its restored state.
|
||||||
|
However, the default behavior can be modified so that any pin can be forced to be either active or inactive
|
||||||
|
upon power-up regardless of its previous state before power-down.
|
||||||
|
|
||||||
|
To have this sketch utilize one or more Arduino pins as custom outputs, first define/edit/delete
|
||||||
|
output definitions using the following variation of the "Z" command:
|
||||||
|
|
||||||
|
<Z ID PIN IFLAG>: creates a new output ID, with specified PIN and IFLAG values.
|
||||||
|
if output ID already exists, it is updated with specificed PIN and IFLAG.
|
||||||
|
note: output state will be immediately set to ACTIVE/INACTIVE and pin will be set to HIGH/LOW
|
||||||
|
according to IFLAG value specifcied (see below).
|
||||||
|
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
|
||||||
|
|
||||||
|
<Z ID>: deletes definition of output ID
|
||||||
|
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
|
||||||
|
|
||||||
|
<Z>: lists all defined output pins
|
||||||
|
returns: <Y ID PIN IFLAG STATE> for each defined output pin or <X> if no output pins defined
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
ID: the numeric ID (0-32767) of the output
|
||||||
|
PIN: the arduino pin number to use for the output
|
||||||
|
STATE: the state of the output (0=INACTIVE / 1=ACTIVE)
|
||||||
|
IFLAG: defines the operational behavior of the output based on bits 0, 1, and 2 as follows:
|
||||||
|
|
||||||
|
IFLAG, bit 0: 0 = forward operation (ACTIVE=HIGH / INACTIVE=LOW)
|
||||||
|
1 = inverted operation (ACTIVE=LOW / INACTIVE=HIGH)
|
||||||
|
|
||||||
|
IFLAG, bit 1: 0 = state of pin restored on power-up to either ACTIVE or INACTIVE depending
|
||||||
|
on state before power-down; state of pin set to INACTIVE when first created
|
||||||
|
1 = state of pin set on power-up, or when first created, to either ACTIVE of INACTIVE
|
||||||
|
depending on IFLAG, bit 2
|
||||||
|
|
||||||
|
IFLAG, bit 2: 0 = state of pin set to INACTIVE uponm power-up or when first created
|
||||||
|
1 = state of pin set to ACTIVE uponm power-up or when first created
|
||||||
|
|
||||||
|
Once all outputs have been properly defined, use the <E> command to store their definitions to EEPROM.
|
||||||
|
If you later make edits/additions/deletions to the output definitions, you must invoke the <E> command if you want those
|
||||||
|
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
|
||||||
|
|
||||||
|
To change the state of outputs that have been defined use:
|
||||||
|
|
||||||
|
<Z ID STATE>: sets output ID to either ACTIVE or INACTIVE state
|
||||||
|
returns: <Y ID STATE>, or <X> if turnout ID does not exist
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
ID: the numeric ID (0-32767) of the turnout to control
|
||||||
|
STATE: the state of the output (0=INACTIVE / 1=ACTIVE)
|
||||||
|
|
||||||
|
When controlled as such, the Arduino updates and stores the direction of each output in EEPROM so
|
||||||
|
that it is retained even without power. A list of the current states of each output in the form <Y ID STATE> is generated
|
||||||
|
by this sketch whenever the <s> status command is invoked. This provides an efficient way of initializing
|
||||||
|
the state of any outputs being monitored or controlled by a separate interface or GUI program.
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#include "Outputs.h"
|
||||||
|
#include "EEStore.h"
|
||||||
|
#include "StringParser.h"
|
||||||
|
void Output::activate(int s){
|
||||||
|
data.oStatus=(s>0); // if s>0, set status to active, else inactive
|
||||||
|
digitalWrite(data.pin,data.oStatus ^ bitRead(data.iFlag,0)); // set state of output pin to HIGH or LOW depending on whether bit zero of iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW)
|
||||||
|
if(num>0)
|
||||||
|
EEPROM.put(num,data.oStatus);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Output* Output::get(int n){
|
||||||
|
Output *tt;
|
||||||
|
for(tt=firstOutput;tt!=NULL && tt->data.id!=n;tt=tt->nextOutput);
|
||||||
|
return(tt);
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool Output::remove(int n){
|
||||||
|
Output *tt,*pp=NULL;
|
||||||
|
|
||||||
|
for(tt=firstOutput;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextOutput);
|
||||||
|
|
||||||
|
if(tt==NULL) return false;
|
||||||
|
|
||||||
|
if(tt==firstOutput)
|
||||||
|
firstOutput=tt->nextOutput;
|
||||||
|
else
|
||||||
|
pp->nextOutput=tt->nextOutput;
|
||||||
|
|
||||||
|
free(tt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Output::showAll(Stream & stream){
|
||||||
|
for(Output * tt=firstOutput;tt!=NULL;tt=tt->nextOutput){
|
||||||
|
StringParser::send(stream,F("<Y %d %d %d %d>"), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Output::show(Stream & stream){
|
||||||
|
for(Output * tt=firstOutput;tt!=NULL;tt=tt->nextOutput){
|
||||||
|
StringParser::send(stream,F("<Y %d %d>"), tt->data.id, tt->data.oStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Output::load(){
|
||||||
|
struct OutputData data;
|
||||||
|
Output *tt;
|
||||||
|
|
||||||
|
for(int i=0;i<EEStore::eeStore->data.nOutputs;i++){
|
||||||
|
EEPROM.get(EEStore::pointer(),data);
|
||||||
|
tt=create(data.id,data.pin,data.iFlag);
|
||||||
|
tt->data.oStatus=bitRead(tt->data.iFlag,1)?bitRead(tt->data.iFlag,2):data.oStatus; // restore status to EEPROM value is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag
|
||||||
|
digitalWrite(tt->data.pin,tt->data.oStatus ^ bitRead(tt->data.iFlag,0));
|
||||||
|
pinMode(tt->data.pin,OUTPUT);
|
||||||
|
tt->num=EEStore::pointer();
|
||||||
|
EEStore::advance(sizeof(tt->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Output::store(){
|
||||||
|
Output *tt;
|
||||||
|
|
||||||
|
tt=firstOutput;
|
||||||
|
EEStore::eeStore->data.nOutputs=0;
|
||||||
|
|
||||||
|
while(tt!=NULL){
|
||||||
|
tt->num=EEStore::pointer();
|
||||||
|
EEPROM.put(EEStore::pointer(),tt->data);
|
||||||
|
EEStore::advance(sizeof(tt->data));
|
||||||
|
tt=tt->nextOutput;
|
||||||
|
EEStore::eeStore->data.nOutputs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Output *Output::create(int id, int pin, int iFlag, int v){
|
||||||
|
Output *tt;
|
||||||
|
|
||||||
|
if(firstOutput==NULL){
|
||||||
|
firstOutput=(Output *)calloc(1,sizeof(Output));
|
||||||
|
tt=firstOutput;
|
||||||
|
} else if((tt=get(id))==NULL){
|
||||||
|
tt=firstOutput;
|
||||||
|
while(tt->nextOutput!=NULL)
|
||||||
|
tt=tt->nextOutput;
|
||||||
|
tt->nextOutput=(Output *)calloc(1,sizeof(Output));
|
||||||
|
tt=tt->nextOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tt==NULL) return tt;
|
||||||
|
|
||||||
|
tt->data.id=id;
|
||||||
|
tt->data.pin=pin;
|
||||||
|
tt->data.iFlag=iFlag;
|
||||||
|
tt->data.oStatus=0;
|
||||||
|
|
||||||
|
if(v==1){
|
||||||
|
tt->data.oStatus=bitRead(tt->data.iFlag,1)?bitRead(tt->data.iFlag,2):0; // sets status to 0 (INACTIVE) is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag
|
||||||
|
digitalWrite(tt->data.pin,tt->data.oStatus ^ bitRead(tt->data.iFlag,0));
|
||||||
|
pinMode(tt->data.pin,OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(tt);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Output *Output::firstOutput=NULL;
|
32
Outputs.h
Normal file
32
Outputs.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef Outputs_h
|
||||||
|
#define Outputs_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
struct OutputData {
|
||||||
|
uint8_t oStatus;
|
||||||
|
uint8_t id;
|
||||||
|
uint8_t pin;
|
||||||
|
uint8_t iFlag;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Output{
|
||||||
|
public:
|
||||||
|
void activate(int s);
|
||||||
|
static Output* get(int);
|
||||||
|
static bool remove(int);
|
||||||
|
static void load();
|
||||||
|
static void store();
|
||||||
|
static Output *create(int, int, int, int=0);
|
||||||
|
static void show(Stream & stream);
|
||||||
|
static void showAll(Stream & stream);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Output *firstOutput;
|
||||||
|
int num;
|
||||||
|
struct OutputData data;
|
||||||
|
Output *nextOutput;
|
||||||
|
|
||||||
|
}; // Output
|
||||||
|
|
||||||
|
#endif
|
177
Sensors.cpp
Normal file
177
Sensors.cpp
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
DCC++ BASE STATION supports Sensor inputs that can be connected to any Arduino Pin
|
||||||
|
not in use by this program. Sensors can be of any type (infrared, magentic, mechanical...).
|
||||||
|
The only requirement is that when "activated" the Sensor must force the specified Arduino
|
||||||
|
Pin LOW (i.e. to ground), and when not activated, this Pin should remain HIGH (e.g. 5V),
|
||||||
|
or be allowed to float HIGH if use of the Arduino Pin's internal pull-up resistor is specified.
|
||||||
|
|
||||||
|
To ensure proper voltage levels, some part of the Sensor circuitry
|
||||||
|
MUST be tied back to the same ground as used by the Arduino.
|
||||||
|
|
||||||
|
The Sensor code below utilizes exponential smoothing to "de-bounce" spikes generated by
|
||||||
|
mechanical switches and transistors. This avoids the need to create smoothing circuitry
|
||||||
|
for each sensor. You may need to change these parameters through trial and error for your specific sensors.
|
||||||
|
|
||||||
|
To have this sketch monitor one or more Arduino pins for sensor triggers, first define/edit/delete
|
||||||
|
sensor definitions using the following variation of the "S" command:
|
||||||
|
|
||||||
|
<S ID PIN PULLUP>: creates a new sensor ID, with specified PIN and PULLUP
|
||||||
|
if sensor ID already exists, it is updated with specificed PIN and PULLUP
|
||||||
|
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
|
||||||
|
|
||||||
|
<S ID>: deletes definition of sensor ID
|
||||||
|
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
|
||||||
|
|
||||||
|
<S>: lists all defined sensors
|
||||||
|
returns: <Q ID PIN PULLUP> for each defined sensor or <X> if no sensors defined
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
ID: the numeric ID (0-32767) of the sensor
|
||||||
|
PIN: the arduino pin number the sensor is connected to
|
||||||
|
PULLUP: 1=use internal pull-up resistor for PIN, 0=don't use internal pull-up resistor for PIN
|
||||||
|
|
||||||
|
Once all sensors have been properly defined, use the <E> command to store their definitions to EEPROM.
|
||||||
|
If you later make edits/additions/deletions to the sensor definitions, you must invoke the <E> command if you want those
|
||||||
|
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
|
||||||
|
|
||||||
|
All sensors defined as per above are repeatedly and sequentially checked within the main loop of this sketch.
|
||||||
|
If a Sensor Pin is found to have transitioned from one state to another, one of the following serial messages are generated:
|
||||||
|
|
||||||
|
<Q ID> - for transition of Sensor ID from HIGH state to LOW state (i.e. the sensor is triggered)
|
||||||
|
<q ID> - for transition of Sensor ID from LOW state to HIGH state (i.e. the sensor is no longer triggered)
|
||||||
|
|
||||||
|
Depending on whether the physical sensor is acting as an "event-trigger" or a "detection-sensor," you may
|
||||||
|
decide to ignore the <q ID> return and only react to <Q ID> triggers.
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Sensors.h"
|
||||||
|
#include "EEStore.h"
|
||||||
|
#include "StringParser.h"
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Sensor::check(Stream & stream){
|
||||||
|
Sensor *tt;
|
||||||
|
|
||||||
|
for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||||
|
tt->signal=tt->signal*(1.0-SENSOR_DECAY)+digitalRead(tt->data.pin)*SENSOR_DECAY;
|
||||||
|
|
||||||
|
if(!tt->active && tt->signal<0.5){
|
||||||
|
tt->active=true;
|
||||||
|
StringParser::send(stream,F("<Q %d>"), tt->data.snum);
|
||||||
|
} else if(tt->active && tt->signal>0.9){
|
||||||
|
tt->active=false;
|
||||||
|
StringParser::send(stream,F("<q %d>"), tt->data.snum);
|
||||||
|
}
|
||||||
|
} // loop over all sensors
|
||||||
|
|
||||||
|
} // Sensor::check
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Sensor *Sensor::create(int snum, int pin, int pullUp){
|
||||||
|
Sensor *tt;
|
||||||
|
|
||||||
|
if(firstSensor==NULL){
|
||||||
|
firstSensor=(Sensor *)calloc(1,sizeof(Sensor));
|
||||||
|
tt=firstSensor;
|
||||||
|
} else if((tt=get(snum))==NULL){
|
||||||
|
tt=firstSensor;
|
||||||
|
while(tt->nextSensor!=NULL)
|
||||||
|
tt=tt->nextSensor;
|
||||||
|
tt->nextSensor=(Sensor *)calloc(1,sizeof(Sensor));
|
||||||
|
tt=tt->nextSensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tt==NULL) return tt; // problem allocating memory
|
||||||
|
|
||||||
|
tt->data.snum=snum;
|
||||||
|
tt->data.pin=pin;
|
||||||
|
tt->data.pullUp=(pullUp==0?LOW:HIGH);
|
||||||
|
tt->active=false;
|
||||||
|
tt->signal=1;
|
||||||
|
pinMode(pin,INPUT); // set mode to input
|
||||||
|
digitalWrite(pin,pullUp); // don't use Arduino's internal pull-up resistors for external infrared sensors --- each sensor must have its own 1K external pull-up resistor
|
||||||
|
|
||||||
|
return tt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Sensor* Sensor::get(int n){
|
||||||
|
Sensor *tt;
|
||||||
|
for(tt=firstSensor;tt!=NULL && tt->data.snum!=n;tt=tt->nextSensor);
|
||||||
|
return tt ;
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool Sensor::remove(int n){
|
||||||
|
Sensor *tt,*pp=NULL;
|
||||||
|
|
||||||
|
for(tt=firstSensor;tt!=NULL && tt->data.snum!=n;pp=tt,tt=tt->nextSensor);
|
||||||
|
|
||||||
|
if (tt==NULL) return false;
|
||||||
|
|
||||||
|
if(tt==firstSensor)
|
||||||
|
firstSensor=tt->nextSensor;
|
||||||
|
else
|
||||||
|
pp->nextSensor=tt->nextSensor;
|
||||||
|
|
||||||
|
free(tt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Sensor::show(Stream & stream){
|
||||||
|
for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||||
|
StringParser::send(stream, F("<Q %d %d %d>"), tt->data.snum, tt->data.pin, tt->data.pullUp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Sensor::status(Stream & stream){
|
||||||
|
for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||||
|
StringParser::send(stream,F("<%s %d>"), tt->active?"Q":"q", tt->data.snum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Sensor::load(){
|
||||||
|
struct SensorData data;
|
||||||
|
Sensor *tt;
|
||||||
|
|
||||||
|
for(int i=0;i<EEStore::eeStore->data.nSensors;i++){
|
||||||
|
EEPROM.get(EEStore::pointer(),data);
|
||||||
|
tt=create(data.snum,data.pin,data.pullUp);
|
||||||
|
EEStore::advance(sizeof(tt->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Sensor::store(){
|
||||||
|
Sensor *tt;
|
||||||
|
|
||||||
|
tt=firstSensor;
|
||||||
|
EEStore::eeStore->data.nSensors=0;
|
||||||
|
|
||||||
|
while(tt!=NULL){
|
||||||
|
EEPROM.put(EEStore::pointer(),tt->data);
|
||||||
|
EEStore::advance(sizeof(tt->data));
|
||||||
|
tt=tt->nextSensor;
|
||||||
|
EEStore::eeStore->data.nSensors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Sensor *Sensor::firstSensor=NULL;
|
30
Sensors.h
Normal file
30
Sensors.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef Sensor_h
|
||||||
|
#define Sensor_h
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#define SENSOR_DECAY 0.03
|
||||||
|
|
||||||
|
struct SensorData {
|
||||||
|
int snum;
|
||||||
|
uint8_t pin;
|
||||||
|
uint8_t pullUp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sensor{
|
||||||
|
static Sensor *firstSensor;
|
||||||
|
SensorData data;
|
||||||
|
boolean active;
|
||||||
|
float signal;
|
||||||
|
Sensor *nextSensor;
|
||||||
|
static void load();
|
||||||
|
static void store();
|
||||||
|
static Sensor *create(int, int, int);
|
||||||
|
static Sensor* get(int);
|
||||||
|
static bool remove(int);
|
||||||
|
static void show(Stream & stream);
|
||||||
|
static void status(Stream & stream);
|
||||||
|
static void check(Stream & stream);
|
||||||
|
}; // Sensor
|
||||||
|
|
||||||
|
#endif
|
119
Turnouts.cpp
Normal file
119
Turnouts.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#include "Turnouts.h"
|
||||||
|
#include "EEStore.h"
|
||||||
|
#include "StringParser.h"
|
||||||
|
|
||||||
|
bool Turnout::activate(int n,bool state){
|
||||||
|
Turnout * tt=get(n);
|
||||||
|
if (tt==NULL) return false;
|
||||||
|
tt->data.tStatus=state;
|
||||||
|
DCC::setAccessory(tt->data.address, tt->data.subAddress, tt->data.tStatus);
|
||||||
|
if(n>0)
|
||||||
|
EEPROM.put(n,tt->data.tStatus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Turnout* Turnout::get(int n){
|
||||||
|
Turnout *tt;
|
||||||
|
for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;tt=tt->nextTurnout);
|
||||||
|
return(tt);
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool Turnout::remove(int n){
|
||||||
|
Turnout *tt,*pp=NULL;
|
||||||
|
|
||||||
|
for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextTurnout);
|
||||||
|
|
||||||
|
if(tt==NULL) return false;
|
||||||
|
|
||||||
|
if(tt==firstTurnout)
|
||||||
|
firstTurnout=tt->nextTurnout;
|
||||||
|
else
|
||||||
|
pp->nextTurnout=tt->nextTurnout;
|
||||||
|
|
||||||
|
free(tt);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Turnout::show(Stream & stream, int n){
|
||||||
|
for(Turnout *tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){
|
||||||
|
if (tt->data.id==n) {
|
||||||
|
StringParser::send(stream,F("<H %d %d>"), tt->data.id, tt->data.tStatus);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Turnout::showAll(Stream & stream){
|
||||||
|
for(Turnout * tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){
|
||||||
|
StringParser::send(stream,F("<H %d %d %d %d>"), tt->data.id, tt->data.address, tt->data.subAddress, tt->data.tStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Turnout::load(){
|
||||||
|
struct TurnoutData data;
|
||||||
|
Turnout *tt;
|
||||||
|
|
||||||
|
for(int i=0;i<EEStore::eeStore->data.nTurnouts;i++){
|
||||||
|
EEPROM.get(EEStore::pointer(),data);
|
||||||
|
tt=create(data.id,data.address,data.subAddress);
|
||||||
|
tt->data.tStatus=data.tStatus;
|
||||||
|
tt->num=EEStore::pointer();
|
||||||
|
EEStore::advance(sizeof(tt->data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Turnout::store(){
|
||||||
|
Turnout *tt;
|
||||||
|
|
||||||
|
tt=firstTurnout;
|
||||||
|
EEStore::eeStore->data.nTurnouts=0;
|
||||||
|
|
||||||
|
while(tt!=NULL){
|
||||||
|
tt->num=EEStore::pointer();
|
||||||
|
EEPROM.put(EEStore::pointer(),tt->data);
|
||||||
|
EEStore::advance(sizeof(tt->data));
|
||||||
|
tt=tt->nextTurnout;
|
||||||
|
EEStore::eeStore->data.nTurnouts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Turnout *Turnout::create(int id, int add, int subAdd){
|
||||||
|
Turnout *tt;
|
||||||
|
|
||||||
|
if(firstTurnout==NULL){
|
||||||
|
firstTurnout=(Turnout *)calloc(1,sizeof(Turnout));
|
||||||
|
tt=firstTurnout;
|
||||||
|
} else if((tt=get(id))==NULL){
|
||||||
|
tt=firstTurnout;
|
||||||
|
while(tt->nextTurnout!=NULL)
|
||||||
|
tt=tt->nextTurnout;
|
||||||
|
tt->nextTurnout=(Turnout *)calloc(1,sizeof(Turnout));
|
||||||
|
tt=tt->nextTurnout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tt==NULL) return(tt);
|
||||||
|
|
||||||
|
tt->data.id=id;
|
||||||
|
tt->data.address=add;
|
||||||
|
tt->data.subAddress=subAdd;
|
||||||
|
tt->data.tStatus=0;
|
||||||
|
return(tt);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Turnout *Turnout::firstTurnout=NULL;
|
29
Turnouts.h
Normal file
29
Turnouts.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef Turnouts_h
|
||||||
|
#define Turnouts_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "DCC.h"
|
||||||
|
|
||||||
|
struct TurnoutData {
|
||||||
|
uint8_t tStatus;
|
||||||
|
uint8_t subAddress;
|
||||||
|
int id;
|
||||||
|
int address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Turnout{
|
||||||
|
static Turnout *firstTurnout;
|
||||||
|
int num;
|
||||||
|
struct TurnoutData data;
|
||||||
|
Turnout *nextTurnout;
|
||||||
|
static bool activate(int n, bool state);
|
||||||
|
static Turnout* get(int);
|
||||||
|
static bool remove(int);
|
||||||
|
static void load();
|
||||||
|
static void store();
|
||||||
|
static Turnout *create(int, int, int);
|
||||||
|
static void show(Stream & stream, int n);
|
||||||
|
static void showAll(Stream & stream);
|
||||||
|
}; // Turnout
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user