mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-23 21:01:25 +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 "JMRIParser.h"
|
||||
#include "JMRITurnout.h"
|
||||
#include "DCC.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
|
||||
// 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
|
||||
// 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
|
||||
void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
StringParser::send(stream,F("\nParsing %s\n"),com);
|
||||
|
||||
int p[MAX_PARAMS];
|
||||
bool result;
|
||||
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]) {
|
||||
|
||||
/***** 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>
|
||||
DCC::setThrottle(p[1],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 ****/
|
||||
|
||||
@ -35,7 +42,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
else DCC::setFunction(p[0],p[1]);
|
||||
|
||||
// TODO response?
|
||||
break;
|
||||
return;
|
||||
|
||||
/***** OPERATE STATIONARY ACCESSORY DECODERS ****/
|
||||
|
||||
@ -60,7 +67,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
* returns: NONE
|
||||
*/
|
||||
DCC::setAccessory(p[0],p[1],p[2]);
|
||||
break;
|
||||
return;
|
||||
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE A TURN-OUT ****/
|
||||
|
||||
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
|
||||
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
||||
*/
|
||||
JMRITurnout::parse(stream,params,p);
|
||||
if (parseT(stream,params,p)) return;
|
||||
break;
|
||||
|
||||
|
||||
#ifdef THIS_IS_NOT_YET_COMPLETE
|
||||
|
||||
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE AN OUTPUT PIN ****/
|
||||
|
||||
case 'Z': // <Z ID ACTIVATE>
|
||||
/*
|
||||
* <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
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (parseZ(stream,params,p)) return;
|
||||
break;
|
||||
/***** CREATE/EDIT/REMOVE/SHOW A SENSOR ****/
|
||||
|
||||
case 'S':
|
||||
|
||||
int sn,ss,sm;
|
||||
|
||||
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;
|
||||
}
|
||||
if (parseS(stream,params,p)) return;
|
||||
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 ****/
|
||||
|
||||
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]);
|
||||
break;
|
||||
return;
|
||||
|
||||
/***** 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]);
|
||||
break;
|
||||
return;
|
||||
|
||||
/***** 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]);
|
||||
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 ****/
|
||||
|
||||
@ -231,7 +168,7 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
|
||||
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);
|
||||
break;
|
||||
return;
|
||||
|
||||
/***** 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]));
|
||||
break;
|
||||
return;
|
||||
|
||||
/***** 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::progTrack.setPowerMode(POWERMODE::ON);
|
||||
StringParser::send(stream,F("<p1>"));
|
||||
break;
|
||||
return;
|
||||
|
||||
/***** 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::progTrack.setPowerMode(POWERMODE::OFF);
|
||||
StringParser::send(stream,F("<p0>"));
|
||||
break;
|
||||
return;
|
||||
|
||||
#ifdef THIS_IS_NOT_YET_COMPLETE
|
||||
/***** READ MAIN OPERATIONS TRACK CURRENT ****/
|
||||
|
||||
case 'c': // <c>
|
||||
@ -286,11 +222,19 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
* returns: <a CURRENT>
|
||||
* where CURRENT = 0-1024, based on exponentially-smoothed weighting scheme
|
||||
*/
|
||||
StringParser::send(stream,F("<a %d>"), DCCWaveform:mainTrack->getLastRead());
|
||||
break;
|
||||
StringParser::send(stream,F("<a %d>"), DCCWaveform::mainTrack.getLastCurrent());
|
||||
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 ****/
|
||||
|
||||
case 's': // <s>
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
// mainTrack->showStatus();
|
||||
for(int i=1;i<=mainTrack->numDev;i++){
|
||||
if(mainTrack->speedTable[i]==0)
|
||||
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();
|
||||
// TODO Send stats of speed reminders table
|
||||
|
||||
StringParser::send(stream,F("<iDCC-EX BASE STATION FOR ARDUINO / %s: V-%s %s/%s\n>"), BOARD_NAME, VERSION, __DATE__, __TIME__ );
|
||||
|
||||
break;
|
||||
// TODO send status of turnouts etc etc
|
||||
return;
|
||||
|
||||
/***** STORE SETTINGS IN EEPROM ****/
|
||||
|
||||
@ -321,8 +259,8 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
*/
|
||||
|
||||
EEStore::store();
|
||||
CommManager::printf("<e %d %d %d>", EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
|
||||
break;
|
||||
StringParser::send(stream,F("<e %d %d %d>"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
|
||||
return;
|
||||
|
||||
/***** CLEAR SETTINGS IN EEPROM ****/
|
||||
|
||||
@ -334,21 +272,121 @@ void JMRIParser::parse(Stream & stream,const char *com) {
|
||||
*/
|
||||
|
||||
EEStore::clear();
|
||||
CommManager::printf("<O>");
|
||||
break;
|
||||
#endif
|
||||
/***** PRINT CARRIAGE RETURN IN stream MONITOR WINDOW ****/
|
||||
StringParser::send(stream, F("<O>"));
|
||||
return;
|
||||
|
||||
case ' ': // < >
|
||||
case ' ': // < >
|
||||
/*
|
||||
* simply prints a carriage return - useful when interacting with Ardiuno through stream monitor window
|
||||
*
|
||||
* returns: a carriage return
|
||||
*/
|
||||
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
|
||||
{
|
||||
static void parse(Stream & stream,const char * command);
|
||||
|
||||
|
||||
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
|
||||
|
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