From 7c47bb15620924eb5a6e0a4157b91a3e438c0900 Mon Sep 17 00:00:00 2001 From: FrightRisk <37218136+FrightRisk@users.noreply.github.com> Date: Tue, 29 Sep 2020 17:19:02 -0400 Subject: [PATCH 01/16] Fix Turnout and Output return value --- CommandStation-EX.ino | 16 +++---- DCC.h | 2 +- DCCEXParser.cpp | 21 ++++++--- Outputs.cpp | 2 +- config.example.h | 106 ------------------------------------------ 5 files changed, 25 insertions(+), 122 deletions(-) delete mode 100644 config.example.h diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 5f1f87e..41a4c50 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -16,7 +16,7 @@ //////////////////////////////////////////////////////////////// // // Enables an I2C 2x24 or 4x24 LCD Screen -#if ENABLE_LCD +#ifdef ENABLE_LCD bool lcdEnabled = false; #if defined(LIB_TYPE_PCF8574) LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS); @@ -37,7 +37,7 @@ void setup() // // More display stuff. Need to put this in a .h file and make // it a class -#if ENABLE_LCD +#ifdef ENABLE_LCD Wire.begin(); // Check that we can find the LCD by its address before attempting to use it. Wire.beginTransmission(LCD_ADDRESS); @@ -45,17 +45,17 @@ void setup() { lcdEnabled = true; lcdDisplay.begin(LCD_COLUMNS, LCD_LINES); - lcdDisplay.setBacklight(255); + lcdDisplay.setBacklight(128); lcdDisplay.clear(); lcdDisplay.setCursor(0, 0); lcdDisplay.print("DCC++ EX v"); lcdDisplay.print(VERSION); lcdDisplay.setCursor(0, 1); -#if COMM_INTERFACE >= 1 - lcdDisplay.print("IP: PENDING"); -#else - lcdDisplay.print("SERIAL: READY"); -#endif +//#if COMM_INTERFACE >= 1 +// lcdDisplay.print("IP: PENDING"); +//#else + lcdDisplay.print("SERIAL: READY 1"); +//#endif #if LCD_LINES > 2 lcdDisplay.setCursor(0, 3); lcdDisplay.print("TRACK POWER: OFF"); diff --git a/DCC.h b/DCC.h index 580d8b7..529e326 100644 --- a/DCC.h +++ b/DCC.h @@ -162,7 +162,7 @@ private: #error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560 #endif -#if ENABLE_LCD +#ifdef ENABLE_LCD #include #if defined(LIB_TYPE_PCF8574) #include diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 8a96e23..075bd32 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -424,7 +424,7 @@ bool DCCEXParser::parseZ(Print *stream, int params, int p[]) switch (params) { - + case 2: // { Output *o = Output::get(p[0]); @@ -436,11 +436,16 @@ bool DCCEXParser::parseZ(Print *stream, int params, int p[]) return true; case 3: // - Output::create(p[0], p[1], p[2], 1); + if (!Output::create(p[0], p[1], p[2], 1)) + return false; + StringFormatter::send(stream, F("")); return true; case 1: // - return Output::remove(p[0]); + if (!Output::remove(p[0])) + return false; + StringFormatter::send(stream, F("")); + return true; case 0: // { @@ -548,12 +553,16 @@ bool DCCEXParser::parseS(Print *stream, int params, int p[]) switch (params) { case 3: // create sensor. pullUp indicator (0=LOW/1=HIGH) - Sensor::create(p[0], p[1], p[2]); + if (!Sensor::create(p[0], p[1], p[2])) + return false; + StringFormatter::send(stream, F("")); return true; case 1: // S id> remove sensor - if (Sensor::remove(p[0])) - return true; + if (!Sensor::remove(p[0])) + return false; + StringFormatter::send(stream, F("")); + return true; break; case 0: // lit sensor states diff --git a/Outputs.cpp b/Outputs.cpp index 50a8eda..d72f744 100644 --- a/Outputs.cpp +++ b/Outputs.cpp @@ -90,7 +90,7 @@ void Output::activate(int s){ 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); - + } /////////////////////////////////////////////////////////////////////////////// diff --git a/config.example.h b/config.example.h deleted file mode 100644 index bb3e302..0000000 --- a/config.example.h +++ /dev/null @@ -1,106 +0,0 @@ -/********************************************************************** - -Config.h -COPYRIGHT (c) 2013-2016 Gregg E. Berman -COPYRIGHT (c) 2020 Fred Decker - -The configuration file for DCC++ EX Command Station - -**********************************************************************/ - -///////////////////////////////////////////////////////////////////////////////////// -// NOTE: Before connecting these boards and selecting one in this software -// check the quick install guides!!! Some of these boards require a voltage -// generating resitor on the current sense pin of the device. Failure to select -// the correct resistor could damage the sense pin on your Arduino or destroy -// the device. -// -// DEFINE MOTOR_SHIELD_TYPE BELOW ACCORDING TO THE FOLLOWING TABLE: -// -// STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel -// POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track) -// FUNDUMOTO_SHIELD : Fundumoto Shield, no current sensing (not recommended, no short protection) -// FIREBOX_MK1 : The Firebox MK1 -// FIREBOX_MK1S : The Firebox MK1S -// | -// +-----------------------v -// -#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD - -///////////////////////////////////////////////////////////////////////////////////// -// -// The IP port to talk to a WIFI or Ethernet shield. -// -#define IP_PORT 2560 - -///////////////////////////////////////////////////////////////////////////////////// -// -// NOTE: Only supported on Arduino Mega -// Set to false if you not even want it on the Arduino Mega -// -#define ENABLE_WIFI true - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE WiFi Parameters (only in effect if WIFI is on) -// -#define WIFI_SSID "Your network name" -#define WIFI_PASSWORD "Your network passwd" -#define WIFI_HOSTNAME "dccex" - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP -// -//#define IP_ADDRESS { 192, 168, 1, 200 } - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE -// -// Uncomment to use with Ethernet Shields -// -// NOTE: This is not used with ESP8266 WiFi modules. -// -// #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF } - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE LCD SCREEN USAGE BY THE BASE STATION -// -// Note: This feature requires an I2C enabled LCD screen using a PCF8574 based chipset. -// or one using a Hitachi HD44780. -// -// To enable, uncomment the line below and make sure only the correct LIB_TYPE line -// is uncommented below to select the library used for your LCD backpack - -//#define ENABLE_LCD - -#ifdef ENABLE_LCD - #define LIB_TYPE_PCF8574 - //#define LIB_TYPE_I2C - // This defines the I2C address for the LCD device - #define LCD_ADDRESS 0x27 //common defaults are 0x27 and 0x3F - - // This defines the number of columns the LCD device has - #define LCD_COLUMNS 16 - - // This defines the number of lines the LCD device has - #define LCD_LINES 2 -#endif - - -///////////////////////////////////////////////////////////////////////////////////// -// -// Enable custom command filtering -#define ENABLE_CUSTOM_FILTER false - -///////////////////////////////////////////////////////////////////////////////////// -// -// Enable custom command filtering -#define ENABLE_CUSTOM_CALLBACK false - -///////////////////////////////////////////////////////////////////////////////////// -// -// Enable custom command filtering -#define ENABLE_FREE_MEM_WARNING false From a28792312a134c2b7be31ec77fedb7b5509da9bd Mon Sep 17 00:00:00 2001 From: FrightRisk <37218136+FrightRisk@users.noreply.github.com> Date: Tue, 29 Sep 2020 17:28:03 -0400 Subject: [PATCH 02/16] add back config.example.h file --- config.example.h | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 config.example.h diff --git a/config.example.h b/config.example.h new file mode 100644 index 0000000..bb3e302 --- /dev/null +++ b/config.example.h @@ -0,0 +1,106 @@ +/********************************************************************** + +Config.h +COPYRIGHT (c) 2013-2016 Gregg E. Berman +COPYRIGHT (c) 2020 Fred Decker + +The configuration file for DCC++ EX Command Station + +**********************************************************************/ + +///////////////////////////////////////////////////////////////////////////////////// +// NOTE: Before connecting these boards and selecting one in this software +// check the quick install guides!!! Some of these boards require a voltage +// generating resitor on the current sense pin of the device. Failure to select +// the correct resistor could damage the sense pin on your Arduino or destroy +// the device. +// +// DEFINE MOTOR_SHIELD_TYPE BELOW ACCORDING TO THE FOLLOWING TABLE: +// +// STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel +// POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track) +// FUNDUMOTO_SHIELD : Fundumoto Shield, no current sensing (not recommended, no short protection) +// FIREBOX_MK1 : The Firebox MK1 +// FIREBOX_MK1S : The Firebox MK1S +// | +// +-----------------------v +// +#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD + +///////////////////////////////////////////////////////////////////////////////////// +// +// The IP port to talk to a WIFI or Ethernet shield. +// +#define IP_PORT 2560 + +///////////////////////////////////////////////////////////////////////////////////// +// +// NOTE: Only supported on Arduino Mega +// Set to false if you not even want it on the Arduino Mega +// +#define ENABLE_WIFI true + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE WiFi Parameters (only in effect if WIFI is on) +// +#define WIFI_SSID "Your network name" +#define WIFI_PASSWORD "Your network passwd" +#define WIFI_HOSTNAME "dccex" + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP +// +//#define IP_ADDRESS { 192, 168, 1, 200 } + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE +// +// Uncomment to use with Ethernet Shields +// +// NOTE: This is not used with ESP8266 WiFi modules. +// +// #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF } + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE LCD SCREEN USAGE BY THE BASE STATION +// +// Note: This feature requires an I2C enabled LCD screen using a PCF8574 based chipset. +// or one using a Hitachi HD44780. +// +// To enable, uncomment the line below and make sure only the correct LIB_TYPE line +// is uncommented below to select the library used for your LCD backpack + +//#define ENABLE_LCD + +#ifdef ENABLE_LCD + #define LIB_TYPE_PCF8574 + //#define LIB_TYPE_I2C + // This defines the I2C address for the LCD device + #define LCD_ADDRESS 0x27 //common defaults are 0x27 and 0x3F + + // This defines the number of columns the LCD device has + #define LCD_COLUMNS 16 + + // This defines the number of lines the LCD device has + #define LCD_LINES 2 +#endif + + +///////////////////////////////////////////////////////////////////////////////////// +// +// Enable custom command filtering +#define ENABLE_CUSTOM_FILTER false + +///////////////////////////////////////////////////////////////////////////////////// +// +// Enable custom command filtering +#define ENABLE_CUSTOM_CALLBACK false + +///////////////////////////////////////////////////////////////////////////////////// +// +// Enable custom command filtering +#define ENABLE_FREE_MEM_WARNING false From 9099af31883f6fed1341a9544fa833be78609594 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 25 Oct 2020 23:52:22 +0100 Subject: [PATCH 03/16] Sensors show their status at change (as in classic) --- DCCEXParser.cpp | 8 +++----- Sensors.cpp | 26 ++++++++++++++++++++++++-- Sensors.h | 3 ++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index ece2842..e958ce5 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -96,6 +96,7 @@ void DCCEXParser::loop(Stream &stream) buffer[bufferLength++] = ch; } } + Sensor::checkAll(&stream); // Update and print changes } int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd) @@ -359,11 +360,8 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking) return; case 'Q': // SENSORS - Sensor::checkAll(); - for (Sensor *tt = Sensor::firstSensor; tt != NULL; tt = tt->nextSensor) - { - StringFormatter::send(stream, F("<%c %d>"), tt->active ? 'Q' : 'q', tt->data.snum); - } + Sensor::checkAll(NULL); // Update, don't print changes + Sensor::printAll(stream); // Print all return; case 's': // diff --git a/Sensors.cpp b/Sensors.cpp index 3f55c5f..e0913a2 100644 --- a/Sensors.cpp +++ b/Sensors.cpp @@ -65,27 +65,49 @@ decide to ignore the return and only react to triggers. **********************************************************************/ - +#include "StringFormatter.h" #include "Sensors.h" #include "EEStore.h" +/////////////////////////////////////////////////////////////////////////////// +// +// checks all defined sensors and prints _changed_ sensor states +// to stream unless stream is NULL in which case only internal +// state is updated +// /////////////////////////////////////////////////////////////////////////////// -void Sensor::checkAll(){ +void Sensor::checkAll(Print *stream){ for(Sensor * 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; + if (stream != NULL) StringFormatter::send(stream, F(""), tt->data.snum); } else if(tt->active && tt->signal>0.9){ tt->active=false; + if (stream != NULL) StringFormatter::send(stream, F(""), tt->data.snum); } } // loop over all sensors } // Sensor::checkAll +/////////////////////////////////////////////////////////////////////////////// +// +// prints all sensor states to stream +// +/////////////////////////////////////////////////////////////////////////////// + +void Sensor::printAll(Print *stream){ + + for(Sensor * tt=firstSensor;tt!=NULL;tt=tt->nextSensor){ + if (stream != NULL) + StringFormatter::send(stream, F("<%c %d>"), tt->active ? 'Q' : 'q', tt->data.snum); + } // loop over all sensors +} // Sensor::printAll + /////////////////////////////////////////////////////////////////////////////// Sensor *Sensor::create(int snum, int pin, int pullUp){ diff --git a/Sensors.h b/Sensors.h index 71f60ba..717da69 100644 --- a/Sensors.h +++ b/Sensors.h @@ -40,7 +40,8 @@ struct Sensor{ static Sensor *create(int, int, int); static Sensor* get(int); static bool remove(int); - static void checkAll(); + static void checkAll(Print *); + static void printAll(Print *); }; // Sensor #endif From ecd176042ecbaa5047ab0f6cbf1fb0a9e8753f3c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 26 Oct 2020 00:56:25 +0100 Subject: [PATCH 04/16] checkAll() only checks one now (and should be renamed) --- DCCEXParser.cpp | 3 +-- Sensors.cpp | 31 +++++++++++++++++-------------- Sensors.h | 1 + 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index e958ce5..712313c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -360,8 +360,7 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking) return; case 'Q': // SENSORS - Sensor::checkAll(NULL); // Update, don't print changes - Sensor::printAll(stream); // Print all + Sensor::printAll(stream); return; case 's': // diff --git a/Sensors.cpp b/Sensors.cpp index e0913a2..b9a9c9d 100644 --- a/Sensors.cpp +++ b/Sensors.cpp @@ -72,26 +72,27 @@ decide to ignore the return and only react to triggers. /////////////////////////////////////////////////////////////////////////////// // -// checks all defined sensors and prints _changed_ sensor states +// checks one defined sensors and prints _changed_ sensor state // to stream unless stream is NULL in which case only internal -// state is updated +// state is updated. Then advances to next sensor which will +// be checked att next invocation. // /////////////////////////////////////////////////////////////////////////////// void Sensor::checkAll(Print *stream){ + + if (firstSensor == NULL) return; + if (readingSensor == NULL) readingSensor=firstSensor; + readingSensor->signal=readingSensor->signal*(1.0-SENSOR_DECAY)+digitalRead(readingSensor->data.pin)*SENSOR_DECAY; - for(Sensor * 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; - if (stream != NULL) StringFormatter::send(stream, F(""), tt->data.snum); - } else if(tt->active && tt->signal>0.9){ - tt->active=false; - if (stream != NULL) StringFormatter::send(stream, F(""), tt->data.snum); - } - } // loop over all sensors - + if(!readingSensor->active && readingSensor->signal<0.5){ + readingSensor->active=true; + if (stream != NULL) StringFormatter::send(stream, F(""), readingSensor->data.snum); + } else if(readingSensor->active && readingSensor->signal>0.9){ + readingSensor->active=false; + if (stream != NULL) StringFormatter::send(stream, F(""), readingSensor->data.snum); + } + readingSensor=readingSensor->nextSensor; } // Sensor::checkAll /////////////////////////////////////////////////////////////////////////////// @@ -159,6 +160,7 @@ bool Sensor::remove(int n){ else pp->nextSensor=tt->nextSensor; + if (readingSensor==tt) readingSensor=tt->nextSensor; free(tt); return true; @@ -196,3 +198,4 @@ void Sensor::store(){ /////////////////////////////////////////////////////////////////////////////// Sensor *Sensor::firstSensor=NULL; +Sensor *Sensor::readingSensor=NULL; diff --git a/Sensors.h b/Sensors.h index 717da69..901d1e5 100644 --- a/Sensors.h +++ b/Sensors.h @@ -31,6 +31,7 @@ struct SensorData { struct Sensor{ static Sensor *firstSensor; + static Sensor *readingSensor; SensorData data; boolean active; float signal; From 9149bc0ee2b5586b02307535286f9247f0732fc3 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 26 Oct 2020 09:10:45 +0100 Subject: [PATCH 05/16] no float in Sensor no more --- Sensors.cpp | 30 +++++++++++++++++++++--------- Sensors.h | 2 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Sensors.cpp b/Sensors.cpp index b9a9c9d..c914996 100644 --- a/Sensors.cpp +++ b/Sensors.cpp @@ -83,15 +83,27 @@ void Sensor::checkAll(Print *stream){ if (firstSensor == NULL) return; if (readingSensor == NULL) readingSensor=firstSensor; - readingSensor->signal=readingSensor->signal*(1.0-SENSOR_DECAY)+digitalRead(readingSensor->data.pin)*SENSOR_DECAY; - - if(!readingSensor->active && readingSensor->signal<0.5){ - readingSensor->active=true; - if (stream != NULL) StringFormatter::send(stream, F(""), readingSensor->data.snum); - } else if(readingSensor->active && readingSensor->signal>0.9){ - readingSensor->active=false; - if (stream != NULL) StringFormatter::send(stream, F(""), readingSensor->data.snum); + + bool sensorstate = digitalRead(readingSensor->data.pin); + + if (!sensorstate == readingSensor->active) { // active==true means sensorstate=0/false so sensor unchanged + // no change + if (readingSensor->latchdelay != 0) { + // enable if you want to debug contact jitter + //if (stream != NULL) StringFormatter::send(stream, F("JITTER %d %d\n"), + // readingSensor->latchdelay, readingSensor->data.snum); + readingSensor->latchdelay=0; // reset + } + } else if (readingSensor->latchdelay < 127) { // byte, max 255, good value unknown yet + // change but first increase anti-jitter counter + readingSensor->latchdelay++; + } else { + // make the change + readingSensor->active = !sensorstate; + readingSensor->latchdelay=0; // reset + if (stream != NULL) StringFormatter::send(stream, F("<%c %d>"), readingSensor->active ? 'Q' : 'q', readingSensor->data.snum); } + readingSensor=readingSensor->nextSensor; } // Sensor::checkAll @@ -131,7 +143,7 @@ Sensor *Sensor::create(int snum, int pin, int pullUp){ tt->data.pin=pin; tt->data.pullUp=(pullUp==0?LOW:HIGH); tt->active=false; - tt->signal=1; + tt->latchdelay=0; 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 diff --git a/Sensors.h b/Sensors.h index 901d1e5..36e8157 100644 --- a/Sensors.h +++ b/Sensors.h @@ -34,7 +34,7 @@ struct Sensor{ static Sensor *readingSensor; SensorData data; boolean active; - float signal; + byte latchdelay; Sensor *nextSensor; static void load(); static void store(); From 369a75f958ad1e428e7c0ce01922587fe7a52a25 Mon Sep 17 00:00:00 2001 From: FrightRisk <37218136+FrightRisk@users.noreply.github.com> Date: Mon, 26 Oct 2020 12:57:37 -0400 Subject: [PATCH 06/16] Fix return values for outputs and sensors ( and responses --- DCCEXParser.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 712313c..027e6c9 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -436,10 +436,17 @@ bool DCCEXParser::parseZ(Print *stream, int params, int p[]) case 3: // Output::create(p[0], p[1], p[2], 1); + if (!Output::create(p[0], p[1], p[2], 1)) + return false; + StringFormatter::send(stream, F("")); return true; case 1: // return Output::remove(p[0]); + if (!Output::remove(p[0])) + return false; + StringFormatter::send(stream, F("")); + return true; case 0: // { @@ -548,11 +555,18 @@ bool DCCEXParser::parseS(Print *stream, int params, int p[]) { case 3: // create sensor. pullUp indicator (0=LOW/1=HIGH) Sensor::create(p[0], p[1], p[2]); + if (!Sensor::create(p[0], p[1], p[2])) + return false; + StringFormatter::send(stream, F("")); return true; case 1: // S id> remove sensor if (Sensor::remove(p[0])) return true; + if (!Sensor::remove(p[0])) + return false; + StringFormatter::send(stream, F("")); + return true; break; case 0: // lit sensor states From babdad06cab2ae3148d9c7fbcb08dadbba55fdf4 Mon Sep 17 00:00:00 2001 From: FrightRisk <37218136+FrightRisk@users.noreply.github.com> Date: Mon, 26 Oct 2020 13:28:47 -0400 Subject: [PATCH 07/16] Remove commands that were called twice --- DCCEXParser.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 027e6c9..57ed9e7 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -435,14 +435,12 @@ bool DCCEXParser::parseZ(Print *stream, int params, int p[]) return true; case 3: // - Output::create(p[0], p[1], p[2], 1); if (!Output::create(p[0], p[1], p[2], 1)) return false; StringFormatter::send(stream, F("")); return true; case 1: // - return Output::remove(p[0]); if (!Output::remove(p[0])) return false; StringFormatter::send(stream, F("")); @@ -554,15 +552,12 @@ bool DCCEXParser::parseS(Print *stream, int params, int p[]) switch (params) { case 3: // create sensor. pullUp indicator (0=LOW/1=HIGH) - Sensor::create(p[0], p[1], p[2]); if (!Sensor::create(p[0], p[1], p[2])) return false; StringFormatter::send(stream, F("")); return true; case 1: // S id> remove sensor - if (Sensor::remove(p[0])) - return true; if (!Sensor::remove(p[0])) return false; StringFormatter::send(stream, F("")); From 8101e75daec5025852254afa5e93106a4bac667e Mon Sep 17 00:00:00 2001 From: FrightRisk <37218136+FrightRisk@users.noreply.github.com> Date: Mon, 26 Oct 2020 14:00:25 -0400 Subject: [PATCH 08/16] Remove unneccessary break command --- DCCEXParser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 57ed9e7..b1eb631 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -562,7 +562,6 @@ bool DCCEXParser::parseS(Print *stream, int params, int p[]) return false; StringFormatter::send(stream, F("")); return true; - break; case 0: // lit sensor states for (Sensor *tt = Sensor::firstSensor; tt != NULL; tt = tt->nextSensor) From 019001675e6637b60f4a7e59cd3c9a6e692b81a2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 27 Oct 2020 07:24:48 +0100 Subject: [PATCH 09/16] return false if parseS does not find any sensors --- DCCEXParser.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index b1eb631..9432a4f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -436,7 +436,7 @@ bool DCCEXParser::parseZ(Print *stream, int params, int p[]) case 3: // if (!Output::create(p[0], p[1], p[2], 1)) - return false; + return false; StringFormatter::send(stream, F("")); return true; @@ -554,7 +554,7 @@ bool DCCEXParser::parseS(Print *stream, int params, int p[]) case 3: // create sensor. pullUp indicator (0=LOW/1=HIGH) if (!Sensor::create(p[0], p[1], p[2])) return false; - StringFormatter::send(stream, F("")); + StringFormatter::send(stream, F("")); return true; case 1: // S id> remove sensor @@ -564,6 +564,8 @@ bool DCCEXParser::parseS(Print *stream, int params, int p[]) return true; case 0: // lit sensor states + if (Sensor::firstSensor == NULL) + return false; for (Sensor *tt = Sensor::firstSensor; tt != NULL; tt = tt->nextSensor) { StringFormatter::send(stream, F(""), tt->data.snum, tt->data.pin, tt->data.pullUp); From c6cff3b4bdbce801af655d4156718f224fdfb0d4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 27 Oct 2020 21:50:46 +0100 Subject: [PATCH 10/16] wifi setup tristate --- WifiInterface.cpp | 47 ++++++++++++++++++++++++++++------------------- WifiInterface.h | 6 ++++-- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index b718b4b..65b5047 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -57,7 +57,7 @@ bool WifiInterface::setup(long serial_link_speed, const __FlashStringHelper *hostname, const int port) { - bool wifiUp = false; + wifiSerialState wifiUp = WIFI_NOAT; #if NUM_SERIAL == 0 // no warning about unused parameters. @@ -75,7 +75,7 @@ bool WifiInterface::setup(long serial_link_speed, // Other serials are tried, depending on hardware. #if NUM_SERIAL > 1 - if (!wifiUp) + if (wifiUp == WIFI_NOAT) { Serial2.begin(serial_link_speed); wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port); @@ -83,18 +83,22 @@ bool WifiInterface::setup(long serial_link_speed, #endif #if NUM_SERIAL > 2 - if (!wifiUp) + if (wifiUp == WIFI_NOAT) { Serial3.begin(serial_link_speed); wifiUp = setup(Serial3, wifiESSID, wifiPassword, hostname, port); } #endif - -return wifiUp; + if (wifiUp == WIFI_CONNECTED) + connected = true; + else + connected = false; + return connected; } -bool WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid, const __FlashStringHelper* password, - const __FlashStringHelper* hostname, int port) { +wifiSerialState WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid, const __FlashStringHelper* password, + const __FlashStringHelper* hostname, int port) { + wifiSerialState wifiState; static uint8_t ntry = 0; ntry++; @@ -102,9 +106,14 @@ bool WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid DIAG(F("\n++ Wifi Setup Try %d ++\n"), ntry); - connected = setup2( SSid, password, hostname, port); + wifiState = setup2( SSid, password, hostname, port); + + if (wifiState == WIFI_NOAT) { + DIAG(F("\n++ Wifi Setup NO AT ++\n")); + return wifiState; + } - if (connected) { + if (wifiState == WIFI_CONNECTED) { StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo checkForOK(200, OK_SEARCH, true); } @@ -112,12 +121,12 @@ bool WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid DCCEXParser::setAtCommandCallback(ATCommand); WifiInboundHandler::setup(wifiStream); - DIAG(F("\n++ Wifi Setup %S ++\n"), connected ? F("OK") : F("FAILED")); - return connected; + DIAG(F("\n++ Wifi Setup %S ++\n"), wifiState == WIFI_CONNECTED ? F("CONNECTED") : F("DISCONNECTED")); + return wifiState; } -bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringHelper* password, - const __FlashStringHelper* hostname, int port) { +wifiSerialState WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringHelper* password, + const __FlashStringHelper* hostname, int port) { bool ipOK = false; bool oldCmd = false; @@ -129,12 +138,12 @@ bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringH if (checkForOK(200,IPD_SEARCH, true)) { DIAG(F("\nPreconfigured Wifi already running with data waiting\n")); // loopstate=4; // carry on from correct place... or not as the case may be - return true; + return WIFI_CONNECTED; } StringFormatter::send(wifiStream, F("AT\r\n")); // Is something here that understands AT? if(!checkForOK(200, OK_SEARCH, true)) - return false; // No AT compatible WiFi module here + return WIFI_NOAT; // No AT compatible WiFi module here StringFormatter::send(wifiStream, F("ATE1\r\n")); // Turn on the echo, se we can see what's happening checkForOK(2000, OK_SEARCH, true); // Makes this visible on the console @@ -237,16 +246,16 @@ bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringH checkForOK(10000, OK_SEARCH, true); // ignore result in case it already was off StringFormatter::send(wifiStream, F("AT+CIPMUX=1\r\n")); // configure for multiple connections - if (!checkForOK(10000, OK_SEARCH, true)) return false; + if (!checkForOK(10000, OK_SEARCH, true)) return WIFI_DISCONNECTED; StringFormatter::send(wifiStream, F("AT+CIPSERVER=1,%d\r\n"), port); // turn on server on port - if (!checkForOK(10000, OK_SEARCH, true)) return false; + if (!checkForOK(10000, OK_SEARCH, true)) return WIFI_DISCONNECTED; StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // Display ip addresses to the DIAG - if (!checkForOK(10000, OK_SEARCH, true, false)) return false; + if (!checkForOK(10000, OK_SEARCH, true, false)) return WIFI_DISCONNECTED; DIAG(F("\nPORT=%d\n"),port); - return true; + return WIFI_CONNECTED; } diff --git a/WifiInterface.h b/WifiInterface.h index b2bdd20..033cbc9 100644 --- a/WifiInterface.h +++ b/WifiInterface.h @@ -24,6 +24,8 @@ #include #include +enum wifiSerialState { WIFI_NOAT, WIFI_DISCONNECTED, WIFI_CONNECTED }; + class WifiInterface { @@ -37,11 +39,11 @@ public: static void ATCommand(const byte *command); private: - static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password, + static wifiSerialState setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password, const __FlashStringHelper *hostname, int port); static Stream *wifiStream; static DCCEXParser parser; - static bool setup2(const __FlashStringHelper *SSSid, const __FlashStringHelper *password, + static wifiSerialState setup2(const __FlashStringHelper *SSSid, const __FlashStringHelper *password, const __FlashStringHelper *hostname, int port); static bool checkForOK(const unsigned int timeout, const char *waitfor, bool echo, bool escapeEcho = true); static bool connected; From 7537f4c3d64864ee926074be9daf1359fe0506d7 Mon Sep 17 00:00:00 2001 From: Fred Date: Wed, 28 Oct 2020 14:14:11 -0400 Subject: [PATCH 11/16] Update CommandStation-EX.ino --- CommandStation-EX.ino | 87 ++++++++----------------------------------- 1 file changed, 15 insertions(+), 72 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 41a4c50..9749fea 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -12,19 +12,6 @@ #include "config.h" #include "DCCEX.h" - -//////////////////////////////////////////////////////////////// -// -// Enables an I2C 2x24 or 4x24 LCD Screen -#ifdef ENABLE_LCD -bool lcdEnabled = false; -#if defined(LIB_TYPE_PCF8574) -LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS); -#elif defined(LIB_TYPE_I2C) -LiquidCrystal_I2C lcdDisplay = LiquidCrystal_I2C(LCD_ADDRESS, LCD_COLUMNS, LCD_LINES); -#endif -#endif - // Create a serial command parser for the USB connection, // This supports JMRI or manual diagnostics and commands // to be issued from the USB serial console. @@ -32,70 +19,23 @@ DCCEXParser serialParser; void setup() { - -//////////////////////////////////////////// -// -// More display stuff. Need to put this in a .h file and make -// it a class -#ifdef ENABLE_LCD - Wire.begin(); - // Check that we can find the LCD by its address before attempting to use it. - Wire.beginTransmission(LCD_ADDRESS); - if (Wire.endTransmission() == 0) - { - lcdEnabled = true; - lcdDisplay.begin(LCD_COLUMNS, LCD_LINES); - lcdDisplay.setBacklight(128); - lcdDisplay.clear(); - lcdDisplay.setCursor(0, 0); - lcdDisplay.print("DCC++ EX v"); - lcdDisplay.print(VERSION); - lcdDisplay.setCursor(0, 1); -//#if COMM_INTERFACE >= 1 -// lcdDisplay.print("IP: PENDING"); -//#else - lcdDisplay.print("SERIAL: READY 1"); -//#endif -#if LCD_LINES > 2 - lcdDisplay.setCursor(0, 3); - lcdDisplay.print("TRACK POWER: OFF"); -#endif - } -#endif - // The main sketch has responsibilities during setup() // Responsibility 1: Start the usb connection for diagnostics // This is normally Serial but uses SerialUSB on a SAMD processor Serial.begin(115200); + DIAG(F("DCC++ EX v%S"),F(VERSION)); + + CONDITIONAL_LCD_START { + // This block is ignored if LCD not in use + LCD(0,F("DCC++ EX v%S"),F(VERSION)); + LCD(1,F("Starting")); + } // Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi -// NOTE: References to Serial1 are for the serial port used to connect -// your wifi chip/shield. -#ifdef WIFI_ON - bool wifiUp = false; - const __FlashStringHelper *wifiESSID = F(WIFI_SSID); - const __FlashStringHelper *wifiPassword = F(WIFI_PASSWORD); - const __FlashStringHelper *dccex = F(WIFI_HOSTNAME); - const uint16_t port = IP_PORT; - - Serial1.begin(WIFI_SERIAL_LINK_SPEED); - wifiUp = WifiInterface::setup(Serial1, wifiESSID, wifiPassword, dccex, port); -#if NUM_SERIAL > 1 - if (!wifiUp) - { - Serial2.begin(WIFI_SERIAL_LINK_SPEED); - wifiUp = WifiInterface::setup(Serial2, wifiESSID, wifiPassword, dccex, port); - } -#if NUM_SERIAL > 2 - if (!wifiUp) - { - Serial3.begin(WIFI_SERIAL_LINK_SPEED); - wifiUp = WifiInterface::setup(Serial3, wifiESSID, wifiPassword, dccex, port); - } -#endif // >2 -#endif // >1 +#if WIFI_ON + WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT); #endif // WIFI_ON // Responsibility 3: Start the DCC engine. @@ -108,7 +48,8 @@ void setup() // Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the // waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2 - DCC::begin(MOTOR_SHIELD_TYPE); + DCC::begin(MOTOR_SHIELD_TYPE); + LCD(1,F("Ready")); } void loop() @@ -123,10 +64,12 @@ void loop() serialParser.loop(Serial); // Responsibility 3: Optionally handle any incoming WiFi traffic -#ifdef WIFI_ON +#if WIFI_ON WifiInterface::loop(); #endif + LCDDisplay::loop(); // ignored if LCD not in use + // Optionally report any decrease in memory (will automatically trigger on first call) #if ENABLE_FREE_MEM_WARNING static int ramLowWatermark = 32767; // replaced on first loop @@ -135,7 +78,7 @@ void loop() if (freeNow < ramLowWatermark) { ramLowWatermark = freeNow; - DIAG(F("\nFree RAM=%d\n"), ramLowWatermark); + LCD(2,F("Free RAM=%5db"), ramLowWatermark); } #endif } From 8ed8db11272dceb83a19c890e7a4d969d094210b Mon Sep 17 00:00:00 2001 From: Fred Date: Wed, 28 Oct 2020 14:22:34 -0400 Subject: [PATCH 12/16] Update DCC.h --- DCC.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/DCC.h b/DCC.h index 529e326..b447baf 100644 --- a/DCC.h +++ b/DCC.h @@ -72,6 +72,7 @@ public: static void setFunction(int cab, byte fByte, byte eByte); static void setFn(int cab, byte functionNumber, bool on); static int changeFn(int cab, byte functionNumber, bool pressed); + static int getFn(int cab, byte functionNumber); static void updateGroupflags(byte &flags, int functionNumber); static void setAccessory(int aAdd, byte aNum, bool activate); static bool writeTextPacket(byte *b, int nBytes); @@ -162,16 +163,5 @@ private: #error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560 #endif -#ifdef ENABLE_LCD -#include -#if defined(LIB_TYPE_PCF8574) -#include -extern LiquidCrystal_PCF8574 lcdDisplay; -#elif defined(LIB_TYPE_I2C) -#include -extern LiquidCrystal_I2C lcdDisplay; -#endif -extern bool lcdEnabled; -#endif #endif From 177bf202a8784aacd2a2334ece80c71883521131 Mon Sep 17 00:00:00 2001 From: Fred Date: Wed, 28 Oct 2020 14:27:16 -0400 Subject: [PATCH 13/16] Update Outputs.cpp remove extra blank line --- Outputs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Outputs.cpp b/Outputs.cpp index d72f744..84290fe 100644 --- a/Outputs.cpp +++ b/Outputs.cpp @@ -90,7 +90,6 @@ void Output::activate(int s){ 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); - } /////////////////////////////////////////////////////////////////////////////// From 27f3842ef576aed0c24258541478a10f61806b6c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 28 Oct 2020 20:32:20 +0100 Subject: [PATCH 14/16] do second try to set AP ESSID and PASSWORD if necessary --- WifiInterface.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index be17b02..77294ae 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -223,8 +223,9 @@ bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringH if (oldCmd) { while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE - StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); - checkForOK(16000, OK_SEARCH, true); // can ignore failure as AP mode may still be ok + int i=0; + do StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); + while (i++<2 && !checkForOK(16000, OK_SEARCH, true)); // do twice if necessary but ignore failure as AP mode may still be ok } else { From df9b7813a9bae1dcbfe984ef02cbd789b3d8f9c6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 28 Oct 2020 22:00:07 +0100 Subject: [PATCH 15/16] include ctype and add license notice --- EEStore.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/EEStore.cpp b/EEStore.cpp index a3d2d55..e1131ac 100644 --- a/EEStore.cpp +++ b/EEStore.cpp @@ -1,3 +1,24 @@ +/* + * © 2013-2016 Gregg E. Berman + * © 2020, Chris Harlow. All rights reserved. + * © 2020, Harald Barth. + * + * This file is part of Asbelos DCC API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ +#include // isascii in dump for diagnostics #include "EEStore.h" #include "Turnouts.h" #include "Sensors.h" From 50e85d0b79b8da612bf57f15a09121c9fb710daa Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 28 Oct 2020 23:19:55 +0100 Subject: [PATCH 16/16] better isprint instead --- EEStore.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EEStore.cpp b/EEStore.cpp index e1131ac..14ed925 100644 --- a/EEStore.cpp +++ b/EEStore.cpp @@ -18,7 +18,6 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ -#include // isascii in dump for diagnostics #include "EEStore.h" #include "Turnouts.h" #include "Sensors.h" @@ -98,7 +97,7 @@ void EEStore::dump(int num) { DIAG(F("\nAddr 0x char\n")); for (int n=0 ; n