From 46070e29992b050c184ff7a06c8392a19adb2a68 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 4 Mar 2023 18:55:13 +1000 Subject: [PATCH 01/97] Non-blocking implemented --- IO_EXIOExpander.h | 19 ++++++++++--------- version.h | 3 ++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 063b5dd..0296c93 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -82,7 +82,7 @@ private: _command4Buffer[2] = _firstVpin & 0xFF; _command4Buffer[3] = _firstVpin >> 8; // Send config, if EXIOPINS returned, we're good, setup pin buffers, otherwise go offline - I2CManager.read(_i2cAddress, _receive3Buffer, 3, _command4Buffer, 4); + I2CManager.read(_i2cAddress, _receive3Buffer, 3, _command4Buffer, 4, &_i2crb); if (_receive3Buffer[0] == EXIOPINS) { _numDigitalPins = _receive3Buffer[1]; _numAnaloguePins = _receive3Buffer[2]; @@ -98,10 +98,10 @@ private: } // We now need to retrieve the analogue pin map _command1Buffer[0] = EXIOINITA; - I2CManager.read(_i2cAddress, _analoguePinMap, _numAnaloguePins, _command1Buffer, 1); + I2CManager.read(_i2cAddress, _analoguePinMap, _numAnaloguePins, _command1Buffer, 1, &_i2crb); // Attempt to get version, if we don't get it, we don't care, don't go offline _command1Buffer[0] = EXIOVER; - I2CManager.read(_i2cAddress, _versionBuffer, 3, _command1Buffer, 1); + I2CManager.read(_i2cAddress, _versionBuffer, 3, _command1Buffer, 1, &_i2crb); _majorVer = _versionBuffer[0]; _minorVer = _versionBuffer[1]; _patchVer = _versionBuffer[2]; @@ -125,7 +125,7 @@ private: _digitalOutBuffer[0] = EXIODPUP; _digitalOutBuffer[1] = pin; _digitalOutBuffer[2] = pullup; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3); + I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3, &_i2crb); if (_command1Buffer[0] == EXIORDY) { return true; } else { @@ -142,7 +142,7 @@ private: int pin = vpin - _firstVpin; _command2Buffer[0] = EXIOENAN; _command2Buffer[1] = pin; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _command2Buffer, 2); + I2CManager.read(_i2cAddress, _command1Buffer, 1, _command2Buffer, 2, &_i2crb); if (_command1Buffer[0] == EXIORDY) { return true; } else { @@ -157,9 +157,9 @@ private: (void)currentMicros; // remove warning if (_deviceState == DEVSTATE_FAILED) return; _command1Buffer[0] = EXIORDD; - I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1); + I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); _command1Buffer[0] = EXIORDAN; - I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _command1Buffer, 1); + I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _command1Buffer, 1, &_i2crb); } // Obtain the correct analogue input value @@ -191,7 +191,7 @@ private: _digitalOutBuffer[0] = EXIOWRD; _digitalOutBuffer[1] = pin; _digitalOutBuffer[2] = value; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3); + I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3, &_i2crb); if (_command1Buffer[0] != EXIORDY) { DIAG(F("Vpin %d cannot be used as a digital output pin"), (int)vpin); } @@ -211,7 +211,7 @@ private: _servoBuffer[4] = profile; _servoBuffer[5] = duration & 0xFF; _servoBuffer[6] = duration >> 8; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _servoBuffer, 7); + I2CManager.read(_i2cAddress, _command1Buffer, 1, _servoBuffer, 7, &_i2crb); if (_command1Buffer[0] != EXIORDY) { DIAG(F("Vpin %d cannot be used as a servo/PWM pin"), (int)vpin); } @@ -242,6 +242,7 @@ private: byte _receive3Buffer[3]; byte _servoBuffer[7]; uint8_t* _analoguePinMap; + I2CRB _i2crb; // EX-IOExpander protocol flags enum { diff --git a/version.h b/version.h index a97aaec..eebf8ed 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.20" +#define VERSION "4.2.21" +// 4.2.21 - Implement non-blocking I2C for EX-IOExpander device driver // 4.2.20 - & commands for multi-track gauges // - Reinstate but remember its a bit useless when TM involved. // 4.2.19 - Bugfix for analog reading of track current sensor offset. From bec8aea5a548fdd6a3c80be5335bc39067fa3a6d Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 6 Mar 2023 11:57:14 +0000 Subject: [PATCH 02/97] TM Broadcasts TM changes will trigger TM state broadcasts --- .gitignore | 1 + CommandDistributor.cpp | 4 +++ CommandDistributor.h | 1 + TrackManager.cpp | 66 ++++++++++++++++++++++-------------------- TrackManager.h | 2 ++ version.h | 3 +- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index c8e40c2..8f01818 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ my*.h !my*.example.h .vscode/extensions.json .vscode/extensions.json +compile_commands.json diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 0c9185d..ad11b22 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -233,3 +233,7 @@ void CommandDistributor::broadcastText(const FSH * msg) { broadcastReply(WITHROTTLE_TYPE, F("Hm%S\n"), msg); #endif } + +void CommandDistributor::broadcastTrackState(FSH* format,byte trackLetter,int16_t dcAddr) { + broadcastReply(COMMAND_TYPE, format,trackLetter,dcAddr); +} diff --git a/CommandDistributor.h b/CommandDistributor.h index bbbc44c..a5751e9 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -51,6 +51,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastText(const FSH * msg); + static void broadcastTrackState(FSH* format,byte trackLetter,int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); diff --git a/TrackManager.cpp b/TrackManager.cpp index 33d1d8a..633ed06 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -25,6 +25,7 @@ #include "MotorDriver.h" #include "DCCTimer.h" #include "DIAG.h" +#include"CommandDistributor.h" // Virtualised Motor shield multi-track hardware Interface #define FOR_EACH_TRACK(t) for (byte t=0;t<=lastTrack;t++) @@ -203,6 +204,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[t]->setPower(POWERMODE::OFF); trackMode[t]=TRACK_MODE_OFF; track[t]->makeProgTrack(false); // revoke prog track special handling + streamTrackState(NULL,t); } track[trackToSet]->makeProgTrack(true); // set for prog track special handling } else { @@ -210,7 +212,8 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr } trackMode[trackToSet]=mode; trackDCAddr[trackToSet]=dcAddr; - + streamTrackState(NULL,trackToSet); + // When a track is switched, we must clear any side effects of its previous // state, otherwise trains run away or just dont move. @@ -290,36 +293,7 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) if (params==0) { // <=> List track assignments FOR_EACH_TRACK(t) - if (track[t]!=NULL) { - StringFormatter::send(stream,F("<= %c "),'A'+t); - switch(trackMode[t]) { - case TRACK_MODE_MAIN: - StringFormatter::send(stream,F("MAIN")); - if (track[t]->trackPWM) - StringFormatter::send(stream,F("+")); - break; - case TRACK_MODE_PROG: - StringFormatter::send(stream,F("PROG")); - if (track[t]->trackPWM) - StringFormatter::send(stream,F("+")); - break; - case TRACK_MODE_OFF: - StringFormatter::send(stream,F("OFF")); - break; - case TRACK_MODE_EXT: - StringFormatter::send(stream,F("EXT")); - break; - case TRACK_MODE_DC: - StringFormatter::send(stream,F("DC %d"),trackDCAddr[t]); - break; - case TRACK_MODE_DCX: - StringFormatter::send(stream,F("DCX %d"),trackDCAddr[t]); - break; - default: - break; // unknown, dont care - } - StringFormatter::send(stream,F(">\n")); - } + streamTrackState(stream,t); return true; } @@ -349,6 +323,36 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) return false; } +void TrackManager::streamTrackState(Print* stream, byte t) { + // null stream means send to commandDistributor for broadcast + if (track[t]==NULL) return; + auto format=F(""); + switch(trackMode[t]) { + case TRACK_MODE_MAIN: + format=F("<= %c MAIN>\n"); + break; + case TRACK_MODE_PROG: + format=F("<= %c PROG>\n"); + break; + case TRACK_MODE_OFF: + format=F("<= %c OFF>\n"); + break; + case TRACK_MODE_EXT: + format=F("<= %c EXT>\n"); + break; + case TRACK_MODE_DC: + format=F("<= %c DC %d>\n"); + break; + case TRACK_MODE_DCX: + format=F("<= %c DCX %d>\n"); + break; + default: + break; // unknown, dont care + } + if (stream) StringFormatter::send(stream,format,'A'+t,trackDCAddr[t]); + else CommandDistributor::broadcastTrackState(format,'A'+t,trackDCAddr[t]); +} + byte TrackManager::nextCycleTrack=MAX_TRACKS; void TrackManager::loop() { diff --git a/TrackManager.h b/TrackManager.h index 4a548cc..ef4a47c 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -80,6 +80,8 @@ class TrackManager { static void reportGauges(Print* stream); static void reportCurrent(Print* stream); static void reportObsoleteCurrent(Print* stream); + static void streamTrackState(Print* stream, byte t); + static int16_t joinRelay; static bool progTrackSyncMain; // true when prog track is a siding switched to main static bool progTrackBoosted; // true when prog track is not current limited diff --git a/version.h b/version.h index eebf8ed..789ef2c 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.21" +#define VERSION "4.2.22" +// 4.2.22 - Implement broadcast of Track Manager changes // 4.2.21 - Implement non-blocking I2C for EX-IOExpander device driver // 4.2.20 - & commands for multi-track gauges // - Reinstate but remember its a bit useless when TM involved. From 1d2943600820d758d083efe7ab65ed65b7909194 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 6 Mar 2023 13:47:59 +0000 Subject: [PATCH 03/97] Compiler pedantics --- CommandDistributor.cpp | 2 +- CommandDistributor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index ad11b22..1714b73 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -234,6 +234,6 @@ void CommandDistributor::broadcastText(const FSH * msg) { #endif } -void CommandDistributor::broadcastTrackState(FSH* format,byte trackLetter,int16_t dcAddr) { +void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter,int16_t dcAddr) { broadcastReply(COMMAND_TYPE, format,trackLetter,dcAddr); } diff --git a/CommandDistributor.h b/CommandDistributor.h index a5751e9..e1dbfec 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -51,7 +51,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastText(const FSH * msg); - static void broadcastTrackState(FSH* format,byte trackLetter,int16_t dcAddr); + static void broadcastTrackState(const FSH* format,byte trackLetter,int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); From f94a5f971ef47338f648439925a432130a3a6a90 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 7 Mar 2023 16:20:00 +0100 Subject: [PATCH 04/97] Bugfix signalpin2 was not set up in shadow port --- GITHUB_SHA.h | 2 +- MotorDriver.cpp | 17 +++++++++++++++++ version.h | 3 ++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 75a9be6..54a7905 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202302281422Z" +#define GITHUB_SHA "devel-202303071416Z" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index a6a1766..0236215 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -75,6 +75,23 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i dualSignal=true; getFastPin(F("SIG2"),signalPin2,fastSignalPin2); pinMode(signalPin2, OUTPUT); + + fastSignalPin2.shadowinout = NULL; + if (HAVE_PORTA(fastSignalPin2.inout == &PORTA)) { + DIAG(F("Found PORTA pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTA; + } + if (HAVE_PORTB(fastSignalPin2.inout == &PORTB)) { + DIAG(F("Found PORTB pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTB; + } + if (HAVE_PORTC(fastSignalPin2.inout == &PORTC)) { + DIAG(F("Found PORTC pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTC; + } } else dualSignal=false; diff --git a/version.h b/version.h index 789ef2c..a4520fb 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.22" +#define VERSION "4.2.23" +// 4.2.23 - Bugfix signalpin2 was not set up in shadow port // 4.2.22 - Implement broadcast of Track Manager changes // 4.2.21 - Implement non-blocking I2C for EX-IOExpander device driver // 4.2.20 - & commands for multi-track gauges From 59c6c1e5afefbcaf7c4a5d2d0dd2d7a261481d12 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Thu, 9 Mar 2023 15:59:25 +0000 Subject: [PATCH 05/97] Update examples and comments. --- config.example.h | 4 +-- myHal.cpp_example.txt | 78 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/config.example.h b/config.example.h index b253ab3..2a8a11a 100644 --- a/config.example.h +++ b/config.example.h @@ -125,10 +125,10 @@ The configuration file for DCC-EX Command Station // define LCD_DRIVER for I2C address 0x27, 16 cols, 2 rows // #define LCD_DRIVER 0x27,16,2 -//OR define OLED_DRIVER width,height in pixels (address auto detected) +//OR define OLED_DRIVER width,height[,address] in pixels (address auto detected if not supplied) // 128x32 or 128x64 I2C SSD1306-based devices are supported. // Use 132,64 for a SH1106-based I2C device with a 128x64 display. -// #define OLED_DRIVER 128,32 +// #define OLED_DRIVER 128,32,0x3c // Define scroll mode as 0, 1 or 2 // * #define SCROLLMODE 0 is scroll continuous (fill screen if poss), diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index b9e68b7..d93ea5c 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -17,9 +17,11 @@ // Include devices you need. #include "IODevice.h" -#include "IO_HCSR04.h" // Ultrasonic range sensor -#include "IO_VL53L0X.h" // Laser time-of-flight sensor -#include "IO_DFPlayer.h" // MP3 sound player +//#include "IO_HALDisplay.h" // Auxiliary display devices (LCD/OLED) +//#include "IO_HCSR04.h" // Ultrasonic range sensor +//#include "IO_VL53L0X.h" // Laser time-of-flight sensor +//#include "IO_DFPlayer.h" // MP3 sound player +//#include "IO_TouchKeypad.h // Touch keypad with 16 keys //#include "IO_EXTurntable.h" // Turntable-EX turntable controller //#include "IO_EXFastClock.h" // FastClock driver @@ -31,6 +33,61 @@ void halSetup() { + //======================================================================= + // The following directives define auxiliary display devices. + // These can be defined in addition to the system display (display + // number 0) that is defined in config.h. + // A write to a line which is beyond the length of the screen will overwrite + // the bottom line, unless the line number is 255 in which case the + // screen contents will scroll up before the text is written to the + // bottom line. + //======================================================================= + // + // Create a 128x32 OLED display device as display number 1 + // (line 0 is written by EX-RAIL 'SCREEN(1, 0, "text")'). + + //HALDisplay::create(1, 0x3d, 128, 32); + + // Create a 20x4 LCD display device as display number 2 + // (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")'). + + // HALDisplay(2, 0x27, 20, 4); + + + //======================================================================= + // User Add-ins + //======================================================================= + // User add-ins can be created when you want to do something that + // can't be done in EX-RAIL but does not merit a HAL driver. The + // user add-in is a C++ function that is executed periodically by the + // HAL subsystem. + + // Example: The function will be executed once per second and will display, + // on screen #3, the first eight entries (assuming an 8-line display) + // from the loco speed table. + + // Put the following block of code in myHal.cpp OUTSIDE of the + // halSetup() function: + // + // void updateLocoScreen() { + // for (int i=0; i<8; i++) { + // if (DCC::speedTable[i].loco > 0) { + // int speed = DCC::speedTable[i].speedCode; + // char direction = (speed & 0x80) ? 'R' : 'F'; + // speed = speed & 0x7f; + // if (speed > 0) speed = speed - 1; + // SCREEN(3, i, F("Loco:%4d %3d %c"), DCC::speedTable[i].loco, + // speed, direction); + // } + // } + // } + // + // Put the following line INSIDE the halSetup() function: + // + // UserAddin::create(updateLocoScreen, 1000); + // + + //======================================================================= // The following directive defines a PCA9685 PWM Servo driver module. //======================================================================= @@ -176,6 +233,21 @@ void halSetup() { // DFPlayer::create(10000, 10, Serial1); + //======================================================================= + // 16-pad capacitative touch key pad based on TP229 IC. + //======================================================================= + // Parameters below: + // 11000 = first VPIN allocated + // 16 = number of VPINs allocated + // 25 = local GPIO pin number for clock signal + // 24 = local GPIO pin number for data signal + // + // Pressing the key pads numbered 1-16 cause each of the nominated digital VPINs + // (11000-11015 in this case) to be activated. + + // TouchKeypad::create(11000, 16, 25, 24); + + //======================================================================= // The following directive defines an EX-Turntable turntable instance. //======================================================================= From 679e5885c4553c62ceaaf06d1f0c8f26205f97c8 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Thu, 9 Mar 2023 15:59:52 +0000 Subject: [PATCH 06/97] Update IO_HALDisplay.h Enable I2C address overlap check on HAL display. --- IO_HALDisplay.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IO_HALDisplay.h b/IO_HALDisplay.h index 5ef9efc..f2ca3af 100644 --- a/IO_HALDisplay.h +++ b/IO_HALDisplay.h @@ -82,10 +82,10 @@ private: public: // Static function to handle "HALDisplay::create(...)" calls. static void create(I2CAddress i2cAddress, int width, int height) { - /* if (checkNoOverlap(i2cAddress)) */ new HALDisplay(0, i2cAddress, width, height); + if (checkNoOverlap(0, 0, i2cAddress)) new HALDisplay(0, i2cAddress, width, height); } static void create(uint8_t displayNo, I2CAddress i2cAddress, int width, int height) { - /* if (checkNoOverlap(i2cAddress)) */ new HALDisplay(displayNo, i2cAddress, width, height); + if (checkNoOverlap(0, 0, i2cAddress)) new HALDisplay(displayNo, i2cAddress, width, height); } protected: From 471b8ac8e11d225d2e81e3adf2df91459a9a1b67 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Thu, 9 Mar 2023 16:28:07 +0000 Subject: [PATCH 07/97] Update I2CManager.cpp Rearrange I2C short-circuit check to before I2C is initialised. --- I2CManager.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/I2CManager.cpp b/I2CManager.cpp index 6d5db41..aad6418 100644 --- a/I2CManager.cpp +++ b/I2CManager.cpp @@ -72,18 +72,23 @@ static const FSH * guessI2CDeviceType(uint8_t address) { void I2CManagerClass::begin(void) { if (!_beginCompleted) { _beginCompleted = true; + + // Check for short-circuit or floating lines (no pull-up) on I2C before enabling I2C + const FSH *message = F("WARNING: Possible short-circuit or inadequate pullup on I2C %S line"); + pinMode(SDA, INPUT); + if (!digitalRead(SDA)) + DIAG(message, F("SDA")); + pinMode(SCL, INPUT); + if (!digitalRead(SCL)) + DIAG(message, F("SCL")); + + // Now initialise I2C _initialise(); #if defined(I2C_USE_WIRE) DIAG(F("I2CManager: Using Wire library")); #endif - // Check for short-circuits on I2C - if (!digitalRead(SDA)) - DIAG(F("WARNING: Possible short-circuit on I2C SDA line")); - if (!digitalRead(SCL)) - DIAG(F("WARNING: Possible short-circuit on I2C SCL line")); - // Probe and list devices. Use standard mode // (clock speed 100kHz) for best device compatibility. _setClock(100000); From 45cf610028f9d8fc27ffe4859f148beeb7dd119d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 10 Mar 2023 17:49:51 +0100 Subject: [PATCH 08/97] Bugfix Ethernet shield: Static IP now possible --- EthernetInterface.cpp | 2 +- GITHUB_SHA.h | 2 +- version.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 7c5d768..5cf531c 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -136,7 +136,7 @@ bool EthernetInterface::checkLink() { DIAG(F("Ethernet cable connected")); connected=true; #ifdef IP_ADDRESS - setLocalIP(IP_ADDRESS); // for static IP, set it again + Ethernet.setLocalIP(IP_ADDRESS); // for static IP, set it again #endif IPAddress ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static) server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 54a7905..db56eac 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303071416Z" +#define GITHUB_SHA "devel-202303101548Z" diff --git a/version.h b/version.h index a4520fb..6e2d0ba 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.23" +#define VERSION "4.2.24" +// 4.3.24 - Bugfix Ethernet shield: Static IP now possible // 4.2.23 - Bugfix signalpin2 was not set up in shadow port // 4.2.22 - Implement broadcast of Track Manager changes // 4.2.21 - Implement non-blocking I2C for EX-IOExpander device driver From 5e60fb4e2701e5ad1d1099f3e4e6d655c9b46df6 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 11 Mar 2023 22:46:11 +0000 Subject: [PATCH 09/97] SAMD21 odd byte boundary --- EXRAIL2.cpp | 9 +++------ version.h | 5 +++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 3bd8da7..8541cd2 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -105,12 +105,9 @@ uint16_t RMFT2::getOperand(byte n) { // getOperand static version, must be provided prog counter from loop etc. uint16_t RMFT2::getOperand(int progCounter,byte n) { int offset=progCounter+1+(n*3); - if (offset&1) { - byte lsb=GETHIGHFLASH(RouteCode,offset); - byte msb=GETHIGHFLASH(RouteCode,offset+1); - return msb<<8|lsb; - } - return GETHIGHFLASHW(RouteCode,offset); + byte lsb=GETHIGHFLASH(RouteCode,offset); + byte msb=GETHIGHFLASH(RouteCode,offset+1); + return msb<<8|lsb; } LookList::LookList(int16_t size) { diff --git a/version.h b/version.h index 6e2d0ba..8afe38a 100644 --- a/version.h +++ b/version.h @@ -4,8 +4,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.24" -// 4.3.24 - Bugfix Ethernet shield: Static IP now possible +#define VERSION "4.2.25" +// 4.2.25 - Bugfix SAMD21 Exrail odd byte boundary +// 4.2.24 - Bugfix Ethernet shield: Static IP now possible // 4.2.23 - Bugfix signalpin2 was not set up in shadow port // 4.2.22 - Implement broadcast of Track Manager changes // 4.2.21 - Implement non-blocking I2C for EX-IOExpander device driver From 0cc07ed1dff8afd1d9727bb911bf9e3b957698ed Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 13 Mar 2023 05:29:22 +1000 Subject: [PATCH 10/97] Starting on driver feedback --- IO_EXIOExpander.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 0296c93..da01ae7 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -156,10 +156,15 @@ private: void _loop(unsigned long currentMicros) override { (void)currentMicros; // remove warning if (_deviceState == DEVSTATE_FAILED) return; - _command1Buffer[0] = EXIORDD; - I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); - _command1Buffer[0] = EXIORDAN; - I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _command1Buffer, 1, &_i2crb); + if (_i2crb.isBusy()) return; + if (_commandFlag) { + _command1Buffer[0] = EXIORDD; + I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + } else { + _command1Buffer[0] = EXIORDAN; + I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _command1Buffer, 1, &_i2crb); + } + _commandFlag = !_commandFlag; } // Obtain the correct analogue input value @@ -243,6 +248,7 @@ private: byte _servoBuffer[7]; uint8_t* _analoguePinMap; I2CRB _i2crb; + bool _commandFlag = 0; // EX-IOExpander protocol flags enum { From 95d01202045dceda8babb746dfa2015a28aabf7a Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Mon, 13 Mar 2023 08:38:28 +1000 Subject: [PATCH 11/97] Implement status checks --- IO_EXIOExpander.h | 49 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index da01ae7..ddcdf56 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -155,16 +155,25 @@ private: // Main loop, collect both digital and analogue pin states continuously (faster sensor/input reads) void _loop(unsigned long currentMicros) override { (void)currentMicros; // remove warning - if (_deviceState == DEVSTATE_FAILED) return; - if (_i2crb.isBusy()) return; - if (_commandFlag) { - _command1Buffer[0] = EXIORDD; - I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return + uint8_t status = _i2crb.status; + if (status == I2C_STATUS_PENDING) return; // If device busy, return + if (status == I2C_STATUS_OK) { // If device ok, read input data + if (_commandFlag) { + _command1Buffer[0] = EXIORDD; + I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + } else { + _command1Buffer[0] = EXIORDAN; + byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state + I2CManager.read(_i2cAddress, _tempAnalogue, _analoguePinBytes, _command1Buffer, 1, &_i2crb); + memcpy(_analogueInputStates, _tempAnalogue, _analoguePinBytes); // Copy temp buffer to states + } + _commandFlag = !_commandFlag; + // Need to delay here: digital in IO_Base 4000UL, analogue in IO_AnalogueInputs 10000UL (fast) or 1000000UL(slow) } else { - _command1Buffer[0] = EXIORDAN; - I2CManager.read(_i2cAddress, _analogueInputStates, _analoguePinBytes, _command1Buffer, 1, &_i2crb); + DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); + _deviceState = DEVSTATE_FAILED; } - _commandFlag = !_commandFlag; } // Obtain the correct analogue input value @@ -196,9 +205,14 @@ private: _digitalOutBuffer[0] = EXIOWRD; _digitalOutBuffer[1] = pin; _digitalOutBuffer[2] = value; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3, &_i2crb); - if (_command1Buffer[0] != EXIORDY) { - DIAG(F("Vpin %d cannot be used as a digital output pin"), (int)vpin); + uint8_t status = I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3); + if (status != I2C_STATUS_OK) { + DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); + _deviceState = DEVSTATE_FAILED; + } else { + if (_command1Buffer[0] != EXIORDY) { + DIAG(F("Vpin %d cannot be used as a digital output pin"), (int)vpin); + } } } @@ -216,9 +230,14 @@ private: _servoBuffer[4] = profile; _servoBuffer[5] = duration & 0xFF; _servoBuffer[6] = duration >> 8; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _servoBuffer, 7, &_i2crb); - if (_command1Buffer[0] != EXIORDY) { - DIAG(F("Vpin %d cannot be used as a servo/PWM pin"), (int)vpin); + uint8_t status = I2CManager.read(_i2cAddress, _command1Buffer, 1, _servoBuffer, 7); + if (status != I2C_STATUS_OK) { + DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); + _deviceState = DEVSTATE_FAILED; + } else { + if (_command1Buffer[0] != EXIORDY) { + DIAG(F("Vpin %d cannot be used as a servo/PWM pin"), (int)vpin); + } } } @@ -248,7 +267,7 @@ private: byte _servoBuffer[7]; uint8_t* _analoguePinMap; I2CRB _i2crb; - bool _commandFlag = 0; + bool _commandFlag = 1; // EX-IOExpander protocol flags enum { From d89dd0d1fa4a05161b5ac9955c7dd893e070f95d Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 13 Mar 2023 00:53:42 +0000 Subject: [PATCH 12/97] command ref work in progress --- Release_Notes/CommandRef.md | 76 +++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/Release_Notes/CommandRef.md b/Release_Notes/CommandRef.md index 6420471..c06e388 100644 --- a/Release_Notes/CommandRef.md +++ b/Release_Notes/CommandRef.md @@ -4,24 +4,37 @@ General points: - Commands below have a single character opcode and parameters. Even is actually read as - Keyword parameters are shown in upper case but may be entered in mixed case. - - value parameters are numeric. + - value parameters are decimal numeric (unless otherwise noted) - [something] indicates its optional. - - Not all commands have a response, and not all responses come from the last commands that you have issued. + - Not all commands have a response, and broadcasts mean that not all responses come from the last commands that you have issued. Startup status - + Return status like + + also returns defined turnout list: + 1=thrown -Track power management -<1> -<1 MAIN|PROG|JOIN> -<0> -<0 MAIN|PROG> +Track power management. After power commands a power state is broadcast to all throttles. + +<1> Power on all +<1 MAIN|PROG|JOIN> Power on MAIN or PROG track +<1 JOIN> Power on MAIN and PROG track but send main track data on both. +<0> Power off all tracks +<0 MAIN|PROG> Power off main or prog track + +Basic manual loco control + Throttle loco. + speed in JMRI-form (-1=ESTOP, 0=STOP, 1..126 = DCC speeds 2..127) + direction 1=forward, 0=reverse + For response see broadcast + + Set loco function 1=ON, 0-OFF + For response see broadcast + + emergency stop all locos + Control turnout id, 0=C=Closed, 1=T=Thrown + response broadcast -Basic manual control - - - - DCC accessory control @@ -35,25 +48,30 @@ Note: Turnouts are best defined in myAutomation.h where a turnout description ca + Valid commands respond with + +Outputs (Used by JMRI, not required by EXRAIL) + Define an output pin that JMRI can set by id + Activate an output pin by id -Outputs - - +Sensors (Used by JMRI, not required by EXRAIL) + define a sensor to be monitored. + Responses and as sensor changes -Sensors - +Decoder programming - main track + POM write value to cv on loco + POM write bit to cv on loco -Decoder programming - - - - - - - - - +Decoder Programming - prog track + Clear consist and write new cab id (includes long/short settings) + Responds or for error + Write value to cv + + Read cv value, much faster if prediction is correct. + Read CV bit + + Read drive-away loco id. (May be a consist id) @@ -152,6 +170,8 @@ Obsolete commands/formats + V command is much faster if prediction is correct. + V command is much faster if prediction is correct. Broadcast responses Note: broadcasts are sent to all throttles when appropriate (usually because something has changed) From df3eb11eb9e0d0f721c22937b03af6c9f0cb4d11 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:19:20 +1000 Subject: [PATCH 13/97] Add read refresh delays --- .vscode/settings.json | 3 ++- IO_EXIOExpander.h | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ffa498a..6f7d828 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,6 @@ "string_view": "cpp", "initializer_list": "cpp", "cstdint": "cpp" - } + }, + "cmake.configureOnOpen": false } diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index ddcdf56..9efe8f9 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -154,19 +154,24 @@ private: // Main loop, collect both digital and analogue pin states continuously (faster sensor/input reads) void _loop(unsigned long currentMicros) override { - (void)currentMicros; // remove warning if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return uint8_t status = _i2crb.status; if (status == I2C_STATUS_PENDING) return; // If device busy, return if (status == I2C_STATUS_OK) { // If device ok, read input data if (_commandFlag) { - _command1Buffer[0] = EXIORDD; - I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay 10ms for digital read refresh + _lastDigitalRead = currentMicros; + _command1Buffer[0] = EXIORDD; + I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + } } else { - _command1Buffer[0] = EXIORDAN; - byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state - I2CManager.read(_i2cAddress, _tempAnalogue, _analoguePinBytes, _command1Buffer, 1, &_i2crb); - memcpy(_analogueInputStates, _tempAnalogue, _analoguePinBytes); // Copy temp buffer to states + if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay 50ms for analogue read refresh + _lastAnalogueRead = currentMicros; + _command1Buffer[0] = EXIORDAN; + byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state + I2CManager.read(_i2cAddress, _tempAnalogue, _analoguePinBytes, _command1Buffer, 1, &_i2crb); + memcpy(_analogueInputStates, _tempAnalogue, _analoguePinBytes); // Copy temp buffer to states + } } _commandFlag = !_commandFlag; // Need to delay here: digital in IO_Base 4000UL, analogue in IO_AnalogueInputs 10000UL (fast) or 1000000UL(slow) @@ -268,6 +273,10 @@ private: uint8_t* _analoguePinMap; I2CRB _i2crb; bool _commandFlag = 1; + unsigned long _lastDigitalRead = 0; + unsigned long _lastAnalogueRead = 0; + const unsigned long _digitalRefresh = 10000UL; + const unsigned long _analogueRefresh = 50000UL; // EX-IOExpander protocol flags enum { From c83741d2b4a29e1dfa71dfb7184a617b1e270eec Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:20:27 +1000 Subject: [PATCH 14/97] Add read refresh delays --- IO_EXIOExpander.h | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index ddcdf56..9efe8f9 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -154,19 +154,24 @@ private: // Main loop, collect both digital and analogue pin states continuously (faster sensor/input reads) void _loop(unsigned long currentMicros) override { - (void)currentMicros; // remove warning if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return uint8_t status = _i2crb.status; if (status == I2C_STATUS_PENDING) return; // If device busy, return if (status == I2C_STATUS_OK) { // If device ok, read input data if (_commandFlag) { - _command1Buffer[0] = EXIORDD; - I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay 10ms for digital read refresh + _lastDigitalRead = currentMicros; + _command1Buffer[0] = EXIORDD; + I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); + } } else { - _command1Buffer[0] = EXIORDAN; - byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state - I2CManager.read(_i2cAddress, _tempAnalogue, _analoguePinBytes, _command1Buffer, 1, &_i2crb); - memcpy(_analogueInputStates, _tempAnalogue, _analoguePinBytes); // Copy temp buffer to states + if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay 50ms for analogue read refresh + _lastAnalogueRead = currentMicros; + _command1Buffer[0] = EXIORDAN; + byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state + I2CManager.read(_i2cAddress, _tempAnalogue, _analoguePinBytes, _command1Buffer, 1, &_i2crb); + memcpy(_analogueInputStates, _tempAnalogue, _analoguePinBytes); // Copy temp buffer to states + } } _commandFlag = !_commandFlag; // Need to delay here: digital in IO_Base 4000UL, analogue in IO_AnalogueInputs 10000UL (fast) or 1000000UL(slow) @@ -268,6 +273,10 @@ private: uint8_t* _analoguePinMap; I2CRB _i2crb; bool _commandFlag = 1; + unsigned long _lastDigitalRead = 0; + unsigned long _lastAnalogueRead = 0; + const unsigned long _digitalRefresh = 10000UL; + const unsigned long _analogueRefresh = 50000UL; // EX-IOExpander protocol flags enum { From ca2e5e6ce3c956af843357e4dd594c175089a5c4 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:21:55 +1000 Subject: [PATCH 15/97] Undo vscode change --- .vscode/settings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6f7d828..ffa498a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,5 @@ "string_view": "cpp", "initializer_list": "cpp", "cstdint": "cpp" - }, - "cmake.configureOnOpen": false + } } From 5fc925bfc6d6431f90a072219e2d14e3dcd56979 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:23:42 +1000 Subject: [PATCH 16/97] Delete extensions.json --- .vscode/extensions.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 080e70d..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} From a51cefbdeb60d1bdabebfa234f1c5378878b0760 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:23:55 +1000 Subject: [PATCH 17/97] Delete settings.json --- .vscode/settings.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ffa498a..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "files.associations": { - "array": "cpp", - "deque": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "string_view": "cpp", - "initializer_list": "cpp", - "cstdint": "cpp" - } -} From 42bddb587ed7500de89eb37110c68cf03695c70a Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:25:30 +1000 Subject: [PATCH 18/97] Update .gitignore --- .gitignore | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.gitignore b/.gitignore index 8f01818..6237359 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,9 @@ Release/* .pio/ .vscode/ config.h -.vscode/* -# mySetup.h mySetup.cpp myHal.cpp -# myAutomation.h myFilter.cpp -# myAutomation.h -# myLayout.h my*.h !my*.example.h -.vscode/extensions.json -.vscode/extensions.json compile_commands.json From 25676aab6b50a480807f7339672a4853245129ea Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:32:08 +1000 Subject: [PATCH 19/97] Update comments --- IO_EXIOExpander.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 9efe8f9..84433b7 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -159,13 +159,13 @@ private: if (status == I2C_STATUS_PENDING) return; // If device busy, return if (status == I2C_STATUS_OK) { // If device ok, read input data if (_commandFlag) { - if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay 10ms for digital read refresh + if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay for digital read refresh _lastDigitalRead = currentMicros; _command1Buffer[0] = EXIORDD; I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); } } else { - if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay 50ms for analogue read refresh + if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh _lastAnalogueRead = currentMicros; _command1Buffer[0] = EXIORDAN; byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state @@ -174,7 +174,6 @@ private: } } _commandFlag = !_commandFlag; - // Need to delay here: digital in IO_Base 4000UL, analogue in IO_AnalogueInputs 10000UL (fast) or 1000000UL(slow) } else { DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); _deviceState = DEVSTATE_FAILED; @@ -275,8 +274,8 @@ private: bool _commandFlag = 1; unsigned long _lastDigitalRead = 0; unsigned long _lastAnalogueRead = 0; - const unsigned long _digitalRefresh = 10000UL; - const unsigned long _analogueRefresh = 50000UL; + const unsigned long _digitalRefresh = 10000UL; // Delay refreshing digital inputs for 10ms + const unsigned long _analogueRefresh = 50000UL; // Delay refreshing analogue inputs for 50ms // EX-IOExpander protocol flags enum { From 48cd567bda87482d7e5e5287d9290ca0c96a98eb Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 14 Mar 2023 19:04:08 +1000 Subject: [PATCH 20/97] Update version after testing --- version.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 8afe38a..21f8e5e 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,11 @@ #include "StringFormatter.h" -#define VERSION "4.2.25" +#define VERSION "4.2.26" +// 4.2.26 - EX-IOExpander device driver enhancements +// - Enhance I2C error checking +// - Introduce delays to _loop to allow room for other I2C device comms +// - Improve analogue read reliability // 4.2.25 - Bugfix SAMD21 Exrail odd byte boundary // 4.2.24 - Bugfix Ethernet shield: Static IP now possible // 4.2.23 - Bugfix signalpin2 was not set up in shadow port From 27ba55198679610b37cd2c2542fc5d3ab6f21b85 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 14 Mar 2023 20:50:24 +0100 Subject: [PATCH 21/97] Bugfix LCD showed random characters in SCROLLMODE 2 --- Display.cpp | 15 +++++++++------ GITHUB_SHA.h | 2 +- version.h | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Display.cpp b/Display.cpp index 2c44778..a1a1ec1 100644 --- a/Display.cpp +++ b/Display.cpp @@ -1,5 +1,6 @@ /* * © 2021, Chris Harlow, Neil McKechnie. All rights reserved. + * © 2023, Harald Barth. * * This file is part of CommandStation-EX * @@ -52,7 +53,7 @@ Display::Display(DisplayDevice *deviceDriver) { _deviceDriver = deviceDriver; // Get device dimensions in characters (e.g. 16x2). numCharacterColumns = _deviceDriver->getNumCols(); - numCharacterRows = _deviceDriver->getNumRows();; + numCharacterRows = _deviceDriver->getNumRows(); for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++) rowBuffer[row][0] = '\0'; topRow = ROW_INITIAL; // loop2 will fill from row 0 @@ -173,16 +174,18 @@ bool Display::findNextNonBlankRow() { rowNext = 0; else rowNext = rowNext + 1; - if (rowNext >= MAX_CHARACTER_ROWS) rowNext = ROW_INITIAL; #if SCROLLMODE == 1 - // Finished if we've looped back to start - if (rowNext == ROW_INITIAL) { + if (rowNext >= MAX_CHARACTER_ROWS) { + // Finished if we've looped back to start + rowNext = ROW_INITIAL; noMoreRowsToDisplay = true; return false; } #else - // Finished if we're back to the first one shown + if (rowNext >= MAX_CHARACTER_ROWS) + rowNext = 0; if (rowNext == rowFirst) { + // Finished if we're back to the first one shown noMoreRowsToDisplay = true; return false; } @@ -193,4 +196,4 @@ bool Display::findNextNonBlankRow() { } } return false; -} \ No newline at end of file +} diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index db56eac..be808f8 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303101548Z" +#define GITHUB_SHA "devel-202303141949Z" diff --git a/version.h b/version.h index 21f8e5e..b8f547f 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.26" +#define VERSION "4.2.27" +// 4.2.27 - Bugfix LCD showed random characters in SCROLLMODE 2 // 4.2.26 - EX-IOExpander device driver enhancements // - Enhance I2C error checking // - Introduce delays to _loop to allow room for other I2C device comms From 325d4bce73fe35c2eeef0449100321e751ad5c66 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Wed, 15 Mar 2023 09:20:42 +0000 Subject: [PATCH 22/97] Update IO_DFPlayer.h Rework delay between command: instead of sending null characters, which doesn't work on some chip sets, send commands from the _loop() function with a 100ms delay between consecutive commands. --- IO_DFPlayer.h | 91 +++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 50 deletions(-) diff --git a/IO_DFPlayer.h b/IO_DFPlayer.h index 4439fbc..51dcee3 100644 --- a/IO_DFPlayer.h +++ b/IO_DFPlayer.h @@ -75,20 +75,9 @@ private: bool _playing = false; uint8_t _inputIndex = 0; unsigned long _commandSendTime; // Allows timeout processing - uint8_t _lastVolumeLevel = MAXVOLUME; - - // When two commands are sent in quick succession, the device sometimes - // fails to execute one. A delay is required between successive commands. - // This could be implemented by buffering commands and outputting them - // from the loop() function, but it would somewhat complicate the - // driver. A simpler solution is to output a number of NUL pad characters - // between successive command strings if there isn't sufficient elapsed time - // between them. At 9600 baud, each pad character takes approximately - // 1ms to complete. Experiments indicate that the minimum number of pads - // for reliable operation is 17. This gives 17.7ms between the end of one - // command and the beginning of the next, or 28ms between successive commands - // being completed. I've allowed 20 characters, which is almost 21ms. - const int numPadCharacters = 20; // Number of pad characters between commands + uint8_t _requestedVolumeLevel = MAXVOLUME; + uint8_t _currentVolume = MAXVOLUME; + int _requestedSong = -1; // -1=none, 0=stop, >0=file number public: @@ -148,11 +137,39 @@ protected: } else _inputIndex = 0; // Unrecognised character sequence, start again! } + // Check if the initial prompt to device has timed out. Allow 5 seconds if (_deviceState == DEVSTATE_INITIALISING && currentMicros - _commandSendTime > 5000000UL) { DIAG(F("DFPlayer device not responding on serial port")); _deviceState = DEVSTATE_FAILED; } + + // When two commands are sent in quick succession, the device will often fail to + // execute one. Testing has indicated that a delay of 100ms or more is required + // between successive commands to get reliable operation. + // If 100ms has elapsed since the last thing sent, then check if there's some output to do. + if (currentMicros - _commandSendTime > 100000UL) { + if (_currentVolume > _requestedVolumeLevel) { + // Change volume before changing song if volume is reducing. + _currentVolume = _requestedVolumeLevel; + sendPacket(0x06, _currentVolume); + _commandSendTime = currentMicros; + } else if (_requestedSong > 0) { + // Change song + sendPacket(0x03, _requestedSong); + _requestedSong = -1; + _commandSendTime = currentMicros; + } else if (_requestedSong == 0) { + sendPacket(0x16); // Stop playing + _requestedSong = -1; + _commandSendTime = currentMicros; + } else if (_currentVolume < _requestedVolumeLevel) { + // Change volume after changing song if volume is increasing. + _currentVolume = _requestedVolumeLevel; + sendPacket(0x06, _currentVolume); + _commandSendTime = currentMicros; + } + } delayUntil(currentMicros + 10000); // Only enter every 10ms } @@ -165,14 +182,14 @@ protected: #ifdef DIAG_IO DIAG(F("DFPlayer: Play %d"), pin+1); #endif - sendPacket(0x03, pin+1); + _requestedSong = pin+1; _playing = true; } else { // Value 0, stop playing #ifdef DIAG_IO DIAG(F("DFPlayer: Stop")); #endif - sendPacket(0x16); + _requestedSong = 0; // No song _playing = false; } } @@ -181,10 +198,6 @@ protected: // Volume may be specified as second parameter to writeAnalogue. // If value is zero, the player stops playing. // WriteAnalogue on second pin sets the output volume. - // If starting a new file and setting volume, then avoid a short burst of loud noise by - // the following strategy: - // - If the volume is increasing, start playing the song before setting the volume, - // - If the volume is decreasing, decrease it and then start playing. // void _writeAnalogue(VPIN vpin, int value, uint8_t volume=0, uint16_t=0) override { uint8_t pin = vpin - _firstVpin; @@ -198,28 +211,18 @@ protected: if (pin == 0) { // Play track - if (value > 0) { - if (volume != 0) { - if (volume <= _lastVolumeLevel) - sendPacket(0x06, volume); // Set volume before starting - sendPacket(0x03, value); // Play track - _playing = true; - if (volume > _lastVolumeLevel) - sendPacket(0x06, volume); // Set volume after starting - _lastVolumeLevel = volume; - } else { - // Volume not changed, just play - sendPacket(0x03, value); - _playing = true; - } + if (value > 0 && volume != 0) { + if (volume != 0) + _requestedVolumeLevel = volume; + _requestedSong = value; + _playing = true; } else { - sendPacket(0x16); // Stop play + _requestedSong = 0; // stop playing _playing = false; } } else if (pin == 1) { // Set volume (0-30) - sendPacket(0x06, value); - _lastVolumeLevel = volume; + _requestedVolumeLevel = volume; } } @@ -246,7 +249,6 @@ private: void sendPacket(uint8_t command, uint16_t arg = 0) { - unsigned long currentMillis = millis(); uint8_t out[] = { 0x7E, 0xFF, 06, @@ -260,19 +262,8 @@ private: setChecksum(out); - // Check how long since the last command was sent. - // Each character takes approx 1ms at 9600 baud - unsigned long minimumGap = numPadCharacters + sizeof(out); - if (currentMillis - _commandSendTime < minimumGap) { - // Output some pad characters to add an - // artificial delay between commands - for (int i=0; iwrite((uint8_t)0); - } - - // Now output the command + // Output the command _serial->write(out, sizeof(out)); - _commandSendTime = currentMillis; } uint16_t calcChecksum(uint8_t* packet) From 4a1210fa64119f391b5c94394e8cdf8d4ee0eec3 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Wed, 15 Mar 2023 09:31:54 +0000 Subject: [PATCH 23/97] Remove HA mode from STM32 In some pin configurations for DC track mode, the use of analogWrite will conflict with other timer uses including HA mode. Consequently, the HA mode support has been temporarily removed pending a suitable solution for this. Original use of Timer11 has been reinstated. --- DCCTimerSTM32.cpp | 155 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 47 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 2504a26..83ac99b 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -40,7 +40,7 @@ HardwareSerial Serial1(PB7, PA15); // Rx=PB7, Tx=PA15 -- CN7 pins 17 and 21 - F HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14 - F411RE #elif defined(ARDUINO_NUCLEO_F446RE) // Nucleo-64 boards don't have Serial1 defined by default -HardwareSerial Serial1(PA10, PB6); // Rx=PA10, Tx=PB6 -- CN10 pins 33 and 17 - F446RE +HardwareSerial Serial1(PA10, PB6); // Rx=PA10 (D2), Tx=PB6 (D10) -- CN10 pins 17 and 9 - F446RE // Serial2 is defined to use USART2 by default, but is in fact used as the diag console // via the debugger on the Nucleo-64. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc. #elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) @@ -50,17 +50,106 @@ HardwareSerial Serial1(PG9, PG14); // Rx=PG9, Tx=PG14 -- D0, D1 - F412ZG/F446ZE #warning Serial1 not defined #endif +/////////////////////////////////////////////////////////////////////////////////////////////// +// Experimental code for High Accuracy (HA) DCC Signal mode +// Warning - use of TIM2 and TIM3 can affect the use of analogWrite() function on certain pins, +// which is used by the DC motor types. +/////////////////////////////////////////////////////////////////////////////////////////////// + +// INTERRUPT_CALLBACK interruptHandler=0; +// // Let's use STM32's timer #2 which supports hardware pulse generation on pin D13. +// // Also, timer #3 will do hardware pulses on pin D12. This gives +// // accurate timing, independent of the latency of interrupt handling. +// // We only need to interrupt on one of these (TIM2), the other will just generate +// // pulses. +// HardwareTimer timer(TIM2); +// HardwareTimer timerAux(TIM3); +// static bool tim2ModeHA = false; +// static bool tim3ModeHA = false; + +// // Timer IRQ handler +// void Timer_Handler() { +// interruptHandler(); +// } + +// void DCCTimer::begin(INTERRUPT_CALLBACK callback) { +// interruptHandler=callback; +// noInterrupts(); + +// // adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES); +// timer.pause(); +// timerAux.pause(); +// timer.setPrescaleFactor(1); +// timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); +// timer.attachInterrupt(Timer_Handler); +// timer.refresh(); +// timerAux.setPrescaleFactor(1); +// timerAux.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); +// timerAux.refresh(); + +// timer.resume(); +// timerAux.resume(); + +// interrupts(); +// } + +// bool DCCTimer::isPWMPin(byte pin) { +// // Timer 2 Channel 1 controls pin D13, and Timer3 Channel 1 controls D12. +// // Enable the appropriate timer channel. +// switch (pin) { +// case 12: +// return true; +// case 13: +// return true; +// default: +// return false; +// } +// } + +// void DCCTimer::setPWM(byte pin, bool high) { +// // Set the timer so that, at the next counter overflow, the requested +// // pin state is activated automatically before the interrupt code runs. +// // TIM2 is timer, TIM3 is timerAux. +// switch (pin) { +// case 12: +// if (!tim3ModeHA) { +// timerAux.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, D12); +// tim3ModeHA = true; +// } +// if (high) +// TIM3->CCMR1 = (TIM3->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_0; +// else +// TIM3->CCMR1 = (TIM3->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_1; +// break; +// case 13: +// if (!tim2ModeHA) { +// timer.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, D13); +// tim2ModeHA = true; +// } +// if (high) +// TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_0; +// else +// TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_1; +// break; +// } +// } + +// void DCCTimer::clearPWM() { +// timer.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, NC); +// tim2ModeHA = false; +// timerAux.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, NC); +// tim3ModeHA = false; +// } +/////////////////////////////////////////////////////////////////////////////////////////////// + INTERRUPT_CALLBACK interruptHandler=0; -// Let's use STM32's timer #2 which supports hardware pulse generation on pin D13. -// Also, timer #3 will do hardware pulses on pin D12. This gives -// accurate timing, independent of the latency of interrupt handling. -// We only need to interrupt on one of these (TIM2), the other will just generate -// pulses. -HardwareTimer timer(TIM2); -HardwareTimer timerAux(TIM3); +// Let's use STM32's timer #11 until disabused of this notion +// Timer #11 is used for "servo" library, but as DCC-EX is not using +// this libary, we should be free and clear. +HardwareTimer timer(TIM11); // Timer IRQ handler -void Timer_Handler() { +void Timer11_Handler() { interruptHandler(); } @@ -70,59 +159,31 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { // adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES); timer.pause(); - timerAux.pause(); timer.setPrescaleFactor(1); +// timer.setOverflow(CLOCK_CYCLES * 2); timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); - timer.attachInterrupt(Timer_Handler); + timer.attachInterrupt(Timer11_Handler); timer.refresh(); - timerAux.setPrescaleFactor(1); - timerAux.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); - timerAux.refresh(); - timer.resume(); - timerAux.resume(); interrupts(); } bool DCCTimer::isPWMPin(byte pin) { - // Timer 2 Channel 1 controls pin D13, and Timer3 Channel 1 controls D12. - // Enable the appropriate timer channel. - switch (pin) { - case 12: - timerAux.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, D12); - return true; - case 13: - timer.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, D13); - return true; - default: - return false; - } + //TODO: SAMD whilst this call to digitalPinHasPWM will reveal which pins can do PWM, + // there's no support yet for High Accuracy, so for now return false + // return digitalPinHasPWM(pin); + return false; } void DCCTimer::setPWM(byte pin, bool high) { - // Set the timer so that, at the next counter overflow, the requested - // pin state is activated automatically before the interrupt code runs. - // TIM2 is timer, TIM3 is timerAux. - switch (pin) { - case 12: - if (high) - TIM3->CCMR1 = (TIM3->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_0; - else - TIM3->CCMR1 = (TIM3->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_1; - break; - case 13: - if (high) - TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_0; - else - TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC1M_Msk) | TIM_CCMR1_OC1M_1; - break; - } + // TODO: High Accuracy mode is not supported as yet, and may never need to be + (void) pin; + (void) high; } void DCCTimer::clearPWM() { - timer.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, NC); - timerAux.setMode(1, TIMER_OUTPUT_COMPARE_INACTIVE, NC); + return; } void DCCTimer::getSimulatedMacAddress(byte mac[6]) { From 75b5806eb707e84c16f3d7f9619b5a7e38ea5d6e Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Wed, 15 Mar 2023 09:39:20 +0000 Subject: [PATCH 24/97] Shorten I2C error message --- I2CManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2CManager.cpp b/I2CManager.cpp index aad6418..d0d8550 100644 --- a/I2CManager.cpp +++ b/I2CManager.cpp @@ -74,7 +74,7 @@ void I2CManagerClass::begin(void) { _beginCompleted = true; // Check for short-circuit or floating lines (no pull-up) on I2C before enabling I2C - const FSH *message = F("WARNING: Possible short-circuit or inadequate pullup on I2C %S line"); + const FSH *message = F("WARNING: Check I2C %S line for short/pullup"); pinMode(SDA, INPUT); if (!digitalRead(SDA)) DIAG(message, F("SDA")); From 3d35e78533668c482436d8940746a4f0fa32ef9b Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Wed, 15 Mar 2023 09:39:30 +0000 Subject: [PATCH 25/97] Update version.h --- version.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index b8f547f..6aafcd5 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.27" +#define VERSION "4.2.28" +// 4.2.28 - Reinstate use of timer11 in STM32 - remove HA mode. +// - Update IO_DFPlayer to work with MP3-TF-16P rev3. // 4.2.27 - Bugfix LCD showed random characters in SCROLLMODE 2 // 4.2.26 - EX-IOExpander device driver enhancements // - Enhance I2C error checking From 278347756ac578a182daf96a383cfd5793bd22c5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 15 Mar 2023 16:41:02 +0100 Subject: [PATCH 26/97] Bugfix Scroll LCD without empty lines and consistent --- Display.cpp | 94 ++++++++++++++++++++++++++++++++++------------------ Display.h | 1 + GITHUB_SHA.h | 2 +- version.h | 3 +- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/Display.cpp b/Display.cpp index a1a1ec1..03d6039 100644 --- a/Display.cpp +++ b/Display.cpp @@ -129,39 +129,76 @@ Display *Display::loop2(bool force) { // No non-blank lines left, so draw a blank line buffer[0] = 0; } - _deviceDriver->setRowNative(slot); // Set position for display - charIndex = 0; - bufferPointer = &buffer[0]; +#if SCROLLMODE==2 + if (buffer[0] == 0 && needScroll){ // surpresses empty line +#else + if (false){ +#endif + charIndex = MAX_CHARACTER_COLS; + slot--; + } else { + _deviceDriver->setRowNative(slot); // Set position for display + charIndex = 0; + bufferPointer = &buffer[0]; + } + rowNext++; } else { // Write next character, or a space to erase current position. char ch = *bufferPointer; if (ch) { - _deviceDriver->writeNative(ch); + _deviceDriver->writeNative(ch); bufferPointer++; } else { _deviceDriver->writeNative(' '); } + } - if (++charIndex >= MAX_CHARACTER_COLS) { - // Screen slot completed, move to next slot on screen - bufferPointer = 0; - slot++; - if (slot >= numCharacterRows) { - // Last slot on screen written, reset ready for next screen update. + if (++charIndex >= MAX_CHARACTER_COLS) { + // Screen slot completed, move to next slot on screen + bufferPointer = 0; + slot++; + if (slot >= numCharacterRows) { + // Last slot on screen written, reset ready for next screen update. +#if SCROLLMODE==2 || SCROLLMODE==1 + if (!noMoreRowsToDisplay) { + needScroll = true; + } + if (needScroll) { #if SCROLLMODE==2 - if (!noMoreRowsToDisplay) { - // On next refresh, restart one row on from previous start. - rowNext = rowFirst; - findNextNonBlankRow(); - } + // SCROLLMODE 2 rotates through rowFirst and we + // (ab)use findNextBlankRow() to figure out + // next valid row which can be start row. + rowNext = rowFirst + 1; + noMoreRowsToDisplay = false; + findNextNonBlankRow(); + if (rowNext == ROW_INITIAL) + rowNext = 0; + rowFirst = ROW_INITIAL; +#else + // SCROLLMODE 1 just alternates when the + // flag indicates that we have come to the end + if (noMoreRowsToDisplay) + rowNext = 0; #endif - noMoreRowsToDisplay = false; - slot = 0; - rowFirst = ROW_INITIAL; - lastScrollTime = currentMillis; - return NULL; - } + } else { + // SCROLLMODE 1 or 2 but not scroll active + rowNext = 0; + } +#else + // this is for SCROLLMODE 0 but what should it do? + rowNext = 0; +#endif + rowFirst = ROW_INITIAL; + + noMoreRowsToDisplay = false; + slot = 0; + lastScrollTime = currentMillis; + return NULL; } +#if SCROLLMODE==2 + if (needScroll) + noMoreRowsToDisplay = false; +#endif } } while (force); @@ -172,28 +209,19 @@ bool Display::findNextNonBlankRow() { while (!noMoreRowsToDisplay) { if (rowNext == ROW_INITIAL) rowNext = 0; - else - rowNext = rowNext + 1; -#if SCROLLMODE == 1 if (rowNext >= MAX_CHARACTER_ROWS) { // Finished if we've looped back to start rowNext = ROW_INITIAL; noMoreRowsToDisplay = true; return false; } -#else - if (rowNext >= MAX_CHARACTER_ROWS) - rowNext = 0; - if (rowNext == rowFirst) { - // Finished if we're back to the first one shown - noMoreRowsToDisplay = true; - return false; - } -#endif if (rowBuffer[rowNext][0] != 0) { + //rowBuffer[rowNext][0] = '0' + rowNext; // usefull for debug + //rowBuffer[rowNext][1] = '0' + rowFirst; // usefull for debug // Found non-blank row return true; } + rowNext = rowNext + 1; } return false; } diff --git a/Display.h b/Display.h index be2479d..dc59dbb 100644 --- a/Display.h +++ b/Display.h @@ -56,6 +56,7 @@ private: char buffer[MAX_CHARACTER_COLS + 1]; char* bufferPointer = 0; bool noMoreRowsToDisplay = false; + bool needScroll = false; uint16_t numCharacterRows; uint16_t numCharacterColumns = MAX_CHARACTER_COLS; diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index be808f8..4d37765 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303141949Z" +#define GITHUB_SHA "devel-202303151539Z" diff --git a/version.h b/version.h index 6aafcd5..66fc351 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.28" +#define VERSION "4.2.29" +// 4.2.29 - Bugfix Scroll LCD without empty lines and consistent // 4.2.28 - Reinstate use of timer11 in STM32 - remove HA mode. // - Update IO_DFPlayer to work with MP3-TF-16P rev3. // 4.2.27 - Bugfix LCD showed random characters in SCROLLMODE 2 From 30730615966646d1a6aae14996bef9b9720de6b4 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Wed, 15 Mar 2023 18:10:27 +0000 Subject: [PATCH 27/97] Update IO_DFPlayer.h Bugfix, volume not working correctly. --- IO_DFPlayer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IO_DFPlayer.h b/IO_DFPlayer.h index 51dcee3..e150ef7 100644 --- a/IO_DFPlayer.h +++ b/IO_DFPlayer.h @@ -160,7 +160,7 @@ protected: _requestedSong = -1; _commandSendTime = currentMicros; } else if (_requestedSong == 0) { - sendPacket(0x16); // Stop playing + sendPacket(0x0e); // Pause playing _requestedSong = -1; _commandSendTime = currentMicros; } else if (_currentVolume < _requestedVolumeLevel) { @@ -211,8 +211,8 @@ protected: if (pin == 0) { // Play track - if (value > 0 && volume != 0) { - if (volume != 0) + if (value > 0) { + if (volume > 0) _requestedVolumeLevel = volume; _requestedSong = value; _playing = true; @@ -222,7 +222,7 @@ protected: } } else if (pin == 1) { // Set volume (0-30) - _requestedVolumeLevel = volume; + _requestedVolumeLevel = value; } } From 5dd2770442c3a56889f980f199989ee1cbee38b3 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Fri, 17 Mar 2023 21:15:26 +0000 Subject: [PATCH 28/97] Update IO_HCSR04.h Modify mode of measurement so that the driver doesn't loop for long periods waiting for the incoming pulse to complete. Original loop behaviour can be reinstated by adding LOOP option in create call (see comment header in file). --- IO_HCSR04.h | 200 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 73 deletions(-) diff --git a/IO_HCSR04.h b/IO_HCSR04.h index e7b5622..26f0ecd 100644 --- a/IO_HCSR04.h +++ b/IO_HCSR04.h @@ -30,7 +30,7 @@ * * This driver polls the HC-SR04 by sending the trigger pulse and then measuring * the length of the received pulse. If the calculated distance is less than - * the threshold, the output state returned by a read() call changes to 1. If + * the threshold, the output _state returned by a read() call changes to 1. If * the distance is greater than the threshold plus a hysteresis margin, the * output changes to 0. The device also supports readAnalogue(), which returns * the measured distance in cm, or 32767 if the distance exceeds the @@ -48,6 +48,20 @@ * Note: The timing accuracy required for measuring the pulse length means that * the pins have to be direct Arduino pins; GPIO pins on an IO Extender cannot * provide the required accuracy. + * + * Example configuration: + * HCSR04::create(23000, 32, 33, 80, 85); + * + * Where 23000 is the VPIN allocated, + * 32 is the pin connected to the HCSR04 trigger terminal, + * 33 is the pin connected to the HCSR04 echo terminal, + * 80 is the distance in cm below which pin 23000 will be active, + * and 85 is the distance in cm above which pin 23000 will be inactive. + * + * Alternative configuration, which hogs the processor until the measurement is complete + * (old behaviour, more accurate but higher impact on other CS tasks): + * HCSR04::create(23000, 32, 33, 80, 85, HCSR04::LOOP); + * */ #ifndef IO_HCSR04_H @@ -61,38 +75,52 @@ private: // pins must be arduino GPIO pins, not extender pins or HAL pins. int _trigPin = -1; int _echoPin = -1; - // Thresholds for setting active state in cm. + // Thresholds for setting active _state in cm. uint8_t _onThreshold; // cm uint8_t _offThreshold; // cm // Last measured distance in cm. uint16_t _distance; - // Active=1/inactive=0 state + // Active=1/inactive=0 _state uint8_t _value = 0; - // Factor for calculating the distance (cm) from echo time (ms). + // Factor for calculating the distance (cm) from echo time (us). // Based on a speed of sound of 345 metres/second. - const uint16_t factor = 58; // ms/cm + const uint16_t factor = 58; // us/cm + // Limit the time spent looping by dropping out when the expected + // worst case threshold value is greater than an arbitrary value. + const uint16_t maxPermittedLoopTime = 10 * factor; // max in us + unsigned long _startTime = 0; + unsigned long _maxTime = 0; + enum {DORMANT, MEASURING}; // _state values + uint8_t _state = DORMANT; + uint8_t _counter = 0; + uint16_t _options = 0; public: + enum Options { + LOOP = 1, // Option HCSR04::LOOP reinstates old behaviour, i.e. complete measurement in one loop entry. + }; // Static create function provides alternative way to create object - static void create(VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold) { + static void create(VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold, uint16_t options = 0) { if (checkNoOverlap(vpin)) - new HCSR04(vpin, trigPin, echoPin, onThreshold, offThreshold); + new HCSR04(vpin, trigPin, echoPin, onThreshold, offThreshold, options); } protected: - // Constructor perfroms static initialisation of the device object - HCSR04 (VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold) { + // Constructor performs static initialisation of the device object + HCSR04 (VPIN vpin, int trigPin, int echoPin, uint16_t onThreshold, uint16_t offThreshold, uint16_t options) { _firstVpin = vpin; _nPins = 1; _trigPin = trigPin; _echoPin = echoPin; _onThreshold = onThreshold; _offThreshold = offThreshold; + _options = options; addDevice(this); } // _begin function called to perform dynamic initialisation of the device void _begin() override { + _state = 0; pinMode(_trigPin, OUTPUT); pinMode(_echoPin, INPUT); ArduinoPins::fastWriteDigital(_trigPin, 0); @@ -112,11 +140,97 @@ protected: return _distance; } - // _loop function - read HC-SR04 once every 50 milliseconds. + // _loop function - read HC-SR04 once every 100 milliseconds. void _loop(unsigned long currentMicros) override { - read_HCSR04device(); - // Delay next loop entry until 50ms have elapsed. - delayUntil(currentMicros + 50000UL); + unsigned long waitTime; + switch(_state) { + case DORMANT: // Issue pulse + // If receive pin is still set on from previous call, do nothing till next entry. + if (ArduinoPins::fastReadDigital(_echoPin)) return; + + // Send 10us pulse to trigger transmitter + ArduinoPins::fastWriteDigital(_trigPin, 1); + delayMicroseconds(10); + ArduinoPins::fastWriteDigital(_trigPin, 0); + + // Wait, with timeout, for echo pin to become set. + // Measured time delay is just under 500us, so + // wait for max of 1000us. + _startTime = micros(); + _maxTime = 1000; + + while (!ArduinoPins::fastReadDigital(_echoPin)) { + // Not set yet, see if we've timed out. + waitTime = micros() - _startTime; + if (waitTime > _maxTime) { + // Timeout waiting for pulse start, abort the read and start again + _state = DORMANT; + return; + } + } + + // Echo pulse started, so wait for echo pin to reset, and measure length of pulse + _startTime = micros(); + _maxTime = factor * _offThreshold; + _state = MEASURING; + // If maximum measurement time is high, then skip until next loop entry before + // starting to look for pulse end. + // This gives better accuracy at shorter distance thresholds but without extending + // loop execution time for longer thresholds. If LOOP option is set on, then + // the entire measurement will be done in one loop entry, i.e. the code will fall + // through into the measuring phase. + if (!(_options & LOOP) && _maxTime > maxPermittedLoopTime) break; + /* fallthrough */ + + case MEASURING: // Check if echo pulse has finished + do { + waitTime = micros() - _startTime; + if (!ArduinoPins::fastReadDigital(_echoPin)) { + // Echo pulse completed; check if pulse length is below threshold and if so set value. + if (waitTime <= factor * _onThreshold) { + // Measured time is within the onThreshold, so value is one. + _value = 1; + // If the new distance value is less than the current, use it immediately. + // But if the new distance value is longer, then it may be erroneously long + // (because of extended loop times delays), so apply a delay to distance increases. + uint16_t estimatedDistance = waitTime / factor; + if (estimatedDistance < _distance) + _distance = estimatedDistance; + else + _distance += 1; // Just increase distance slowly. + _counter = 0; + //DIAG(F("HCSR04: Pulse Len=%l Distance=%d"), waitTime, _distance); + } + _state = DORMANT; + } else { + // Echo pulse hasn't finished, so check if maximum time has elapsed + // If pulse is too long then set return value to zero, + // and finish without waiting for end of pulse. + if (waitTime > _maxTime) { + // Pulse length longer than maxTime, value is provisionally zero. + // But don't change _value unless provisional value is zero for 10 consecutive measurements + if (_value == 1) { + if (++_counter >= 10) { + _value = 0; + _distance = 32767; + _counter = 0; + } + } + _state = DORMANT; // start again + } + } + // If there's lots of time remaining before the expected completion time, + // then exit and wait for next loop entry. Otherwise, loop until we finish. + // If option LOOP is set, then we loop until finished anyway. + uint32_t remainingTime = _maxTime - waitTime; + if (!(_options & LOOP) && remainingTime < maxPermittedLoopTime) return; + } while (_state == MEASURING) ; + break; + } + // Datasheet recommends a wait of at least 60ms between measurement cycles + if (_state == DORMANT) + delayUntil(currentMicros+60000UL); // wait 60ms till next measurement + } void _display() override { @@ -124,66 +238,6 @@ protected: _firstVpin, _trigPin, _echoPin, _onThreshold, _offThreshold); } -private: - // This polls the HC-SR04 device by sending a pulse and measuring the duration of - // the pulse observed on the receive pin. In order to be kind to the rest of the CS - // software, no interrupts are used and interrupts are not disabled. The pulse duration - // is measured in a loop, using the micros() function. Therefore, interrupts from other - // sources may affect the result. However, interrupts response code in CS typically takes - // much less than the 58us frequency for the DCC interrupt, and 58us corresponds to only 1cm - // in the HC-SR04. - // To reduce chatter on the output, hysteresis is applied on reset: the output is set to 1 when the - // measured distance is less than the onThreshold, and is set to 0 if the measured distance is - // greater than the offThreshold. - // - void read_HCSR04device() { - // uint16 enough to time up to 65ms - uint16_t startTime, waitTime = 0, currentTime, maxTime; - - // If receive pin is still set on from previous call, abort the read. - if (ArduinoPins::fastReadDigital(_echoPin)) - return; - - // Send 10us pulse to trigger transmitter - ArduinoPins::fastWriteDigital(_trigPin, 1); - delayMicroseconds(10); - ArduinoPins::fastWriteDigital(_trigPin, 0); - - // Wait for receive pin to be set - startTime = currentTime = micros(); - maxTime = factor * _offThreshold * 2; - while (!ArduinoPins::fastReadDigital(_echoPin)) { - // lastTime = currentTime; - currentTime = micros(); - waitTime = currentTime - startTime; - if (waitTime > maxTime) { - // Timeout waiting for pulse start, abort the read - return; - } - } - - // Wait for receive pin to reset, and measure length of pulse - startTime = currentTime = micros(); - maxTime = factor * _offThreshold; - while (ArduinoPins::fastReadDigital(_echoPin)) { - currentTime = micros(); - waitTime = currentTime - startTime; - // If pulse is too long then set return value to zero, - // and finish without waiting for end of pulse. - if (waitTime > maxTime) { - // Pulse length longer than maxTime, reset value. - _value = 0; - _distance = 32767; - return; - } - } - // Check if pulse length is below threshold, if so set value. - //DIAG(F("HCSR04: Pulse Len=%l Distance=%d"), waitTime, distance); - _distance = waitTime / factor; // in centimetres - if (_distance < _onThreshold) - _value = 1; - } - }; #endif //IO_HCSR04_H From e55dc51bdbd97fc68d284abd4683291c29b11c30 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Sat, 18 Mar 2023 15:05:21 +0000 Subject: [PATCH 29/97] EX-IOExpander updates --- IO_EXIOExpander.h | 333 ++++++++++++++++++++++++++++++---------------- version.h | 3 +- 2 files changed, 217 insertions(+), 119 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 84433b7..9c631e0 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -34,11 +34,16 @@ * device in use. There is no way for the device driver to sanity check pins are used for the * correct purpose, however the EX-IOExpander device's pin map will prevent pins being used * incorrectly (eg. A6/7 on Nano cannot be used for digital input/output). +* +* The total number of pins cannot exceed 256 because of the communications packet format. +* The number of analogue inputs cannot exceed 16 because of a limit on the maximum +* I2C packet size of 32 bytes (in the Wire library). */ #ifndef IO_EX_IOEXPANDER_H #define IO_EX_IOEXPANDER_H +#include "IODevice.h" #include "I2CManager.h" #include "DIAG.h" #include "FSH.h" @@ -64,134 +69,203 @@ public: if (checkNoOverlap(vpin, nPins, i2cAddress)) new EXIOExpander(vpin, nPins, i2cAddress); } -private: +private: // Constructor EXIOExpander(VPIN firstVpin, int nPins, I2CAddress i2cAddress) { _firstVpin = firstVpin; + // Number of pins cannot exceed 256 (1 byte) because of I2C message structure. + if (nPins > 256) nPins = 256; _nPins = nPins; - _i2cAddress = i2cAddress; + _I2CAddress = i2cAddress; addDevice(this); } void _begin() { + uint8_t status; // Initialise EX-IOExander device I2CManager.begin(); - if (I2CManager.exists(_i2cAddress)) { - _command4Buffer[0] = EXIOINIT; - _command4Buffer[1] = _nPins; - _command4Buffer[2] = _firstVpin & 0xFF; - _command4Buffer[3] = _firstVpin >> 8; + if (I2CManager.exists(_I2CAddress)) { // Send config, if EXIOPINS returned, we're good, setup pin buffers, otherwise go offline - I2CManager.read(_i2cAddress, _receive3Buffer, 3, _command4Buffer, 4, &_i2crb); - if (_receive3Buffer[0] == EXIOPINS) { - _numDigitalPins = _receive3Buffer[1]; - _numAnaloguePins = _receive3Buffer[2]; - _digitalPinBytes = (_numDigitalPins + 7)/8; - _digitalInputStates=(byte*) calloc(_digitalPinBytes,1); - _analoguePinBytes = _numAnaloguePins * 2; - _analogueInputStates = (byte*) calloc(_analoguePinBytes, 1); - _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); - } else { - DIAG(F("ERROR configuring EX-IOExpander device, I2C:%s"), _i2cAddress.toString()); - _deviceState = DEVSTATE_FAILED; - return; - } + // NB The I2C calls here are done as blocking calls, as they're not time-critical + // during initialisation and the reads require waiting for a response anyway. + // Hence we can allocate I/O buffers from the stack. + uint8_t receiveBuffer[3]; + uint8_t commandBuffer[4] = {EXIOINIT, (uint8_t)_nPins, (uint8_t)(_firstVpin & 0xFF), (uint8_t)(_firstVpin >> 8)}; + status = I2CManager.read(_I2CAddress, receiveBuffer, sizeof(receiveBuffer), commandBuffer, sizeof(commandBuffer)); + if (status == I2C_STATUS_OK) { + if (receiveBuffer[0] == EXIOPINS) { + _numDigitalPins = receiveBuffer[1]; + _numAnaloguePins = receiveBuffer[2]; + + // See if we already have suitable buffers assigned + size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; + if (_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (_digitalPinBytes > 0) free(_digitalInputStates); + _digitalInputStates = (byte*) calloc(_digitalPinBytes, 1); + _digitalPinBytes = digitalBytesNeeded; + } + size_t analogueBytesNeeded = _numAnaloguePins * sizeof(_analogueInputValues[0]); + if (_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (_analoguePinBytes > 0) { + free(_analogueInputBuffer); + free(_analogueInputValues); + free(_analoguePinMap); + } + _analogueInputValues = (int16_t*) calloc(_analoguePinBytes, 1); + _analogueInputBuffer = (uint8_t*) calloc(_analoguePinBytes, 1); + _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + } + } else { + DIAG(F("EX-IOExpander I2C:%s ERROR configuring device"), _I2CAddress.toString()); + _deviceState = DEVSTATE_FAILED; + return; + } + } // We now need to retrieve the analogue pin map - _command1Buffer[0] = EXIOINITA; - I2CManager.read(_i2cAddress, _analoguePinMap, _numAnaloguePins, _command1Buffer, 1, &_i2crb); - // Attempt to get version, if we don't get it, we don't care, don't go offline - _command1Buffer[0] = EXIOVER; - I2CManager.read(_i2cAddress, _versionBuffer, 3, _command1Buffer, 1, &_i2crb); - _majorVer = _versionBuffer[0]; - _minorVer = _versionBuffer[1]; - _patchVer = _versionBuffer[2]; - DIAG(F("EX-IOExpander device found, I2C:%s, Version v%d.%d.%d"), - _i2cAddress.toString(), _versionBuffer[0], _versionBuffer[1], _versionBuffer[2]); + if (status == I2C_STATUS_OK) { + commandBuffer[0] = EXIOINITA; + status = I2CManager.read(_I2CAddress, _analoguePinMap, _numAnaloguePins, commandBuffer, 1); + } + if (status == I2C_STATUS_OK) { + // Attempt to get version, if we don't get it, we don't care, don't go offline + uint8_t versionBuffer[3]; + commandBuffer[0] = EXIOVER; + if (I2CManager.read(_I2CAddress, versionBuffer, sizeof(versionBuffer), commandBuffer, 1) == I2C_STATUS_OK) { + _majorVer = versionBuffer[0]; + _minorVer = versionBuffer[1]; + _patchVer = versionBuffer[2]; + } + DIAG(F("EX-IOExpander device found, I2C:%s, Version v%d.%d.%d"), + _I2CAddress.toString(), _majorVer, _minorVer, _patchVer); + #ifdef DIAG_IO - _display(); + _display(); #endif + } + if (status != I2C_STATUS_OK) + reportError(status); + } else { - DIAG(F("EX-IOExpander device not found, I2C:%s"), _i2cAddress.toString()); + DIAG(F("EX-IOExpander I2C:%s device not found"), _I2CAddress.toString()); _deviceState = DEVSTATE_FAILED; } } - // Digital input pin configuration, used to enable on EX-IOExpander device and set pullups if in use + // Digital input pin configuration, used to enable on EX-IOExpander device and set pullups if requested. + // Configuration isn't done frequently so we can use blocking I2C calls here, and so buffers can + // be allocated from the stack to reduce RAM allocation. bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { if (paramCount != 1) return false; int pin = vpin - _firstVpin; if (configType == CONFIGURE_INPUT) { - bool pullup = params[0]; - _digitalOutBuffer[0] = EXIODPUP; - _digitalOutBuffer[1] = pin; - _digitalOutBuffer[2] = pullup; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3, &_i2crb); - if (_command1Buffer[0] == EXIORDY) { - return true; - } else { - DIAG(F("Vpin %d cannot be used as a digital input pin"), (int)vpin); - return false; - } - } else { + uint8_t pullup = params[0]; + uint8_t outBuffer[] = {EXIODPUP, (uint8_t)pin, pullup}; + uint8_t responseBuffer[1]; + uint8_t status = I2CManager.read(_I2CAddress, responseBuffer, sizeof(responseBuffer), + outBuffer, sizeof(outBuffer)); + if (status == I2C_STATUS_OK) { + if (responseBuffer[0] == EXIORDY) { + return true; + } else { + DIAG(F("EXIOVpin %u cannot be used as a digital input pin"), (int)vpin); + } + } else + reportError(status); + } else if (configType == CONFIGURE_ANALOGINPUT) { + // TODO: Consider moving code from _configureAnalogIn() to here and remove _configureAnalogIn + // from IODevice class definition. Not urgent, but each virtual function defined + // means increasing the RAM requirement of every HAL device driver, whether it's relevant + // to the driver or not. return false; } + return false; } - // Analogue input pin configuration, used to enable on EX-IOExpander device + // Analogue input pin configuration, used to enable an EX-IOExpander device. + // Use I2C blocking calls and allocate buffers from stack to save RAM. int _configureAnalogIn(VPIN vpin) override { int pin = vpin - _firstVpin; - _command2Buffer[0] = EXIOENAN; - _command2Buffer[1] = pin; - I2CManager.read(_i2cAddress, _command1Buffer, 1, _command2Buffer, 2, &_i2crb); - if (_command1Buffer[0] == EXIORDY) { - return true; - } else { - DIAG(F("Vpin %d cannot be used as an analogue input pin"), (int)vpin); - return false; - } - return true; + uint8_t commandBuffer[] = {EXIOENAN, (uint8_t)pin}; + uint8_t responseBuffer[1]; + uint8_t status = I2CManager.read(_I2CAddress, responseBuffer, sizeof(responseBuffer), + commandBuffer, sizeof(commandBuffer)); + if (status == I2C_STATUS_OK) { + if (responseBuffer[0] == EXIORDY) { + return true; + } else { + DIAG(F("EX-IOExpander: Vpin %u cannot be used as an analogue input pin"), (int)vpin); + } + } else + reportError(status); + + return false; } // Main loop, collect both digital and analogue pin states continuously (faster sensor/input reads) void _loop(unsigned long currentMicros) override { if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return + + // Request block is used for analogue and digital reads from the IOExpander, which are performed + // on a cyclic basis. Writes are performed synchronously as and when requested. + + if (_i2crb.isBusy()) return; // If I2C operation still in progress, return + uint8_t status = _i2crb.status; - if (status == I2C_STATUS_PENDING) return; // If device busy, return - if (status == I2C_STATUS_OK) { // If device ok, read input data - if (_commandFlag) { - if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay for digital read refresh - _lastDigitalRead = currentMicros; - _command1Buffer[0] = EXIORDD; - I2CManager.read(_i2cAddress, _digitalInputStates, _digitalPinBytes, _command1Buffer, 1, &_i2crb); - } - } else { - if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh - _lastAnalogueRead = currentMicros; - _command1Buffer[0] = EXIORDAN; - byte _tempAnalogue[_analoguePinBytes]; // Setup temp buffer so reads come from known state - I2CManager.read(_i2cAddress, _tempAnalogue, _analoguePinBytes, _command1Buffer, 1, &_i2crb); - memcpy(_analogueInputStates, _tempAnalogue, _analoguePinBytes); // Copy temp buffer to states - } + if (status == I2C_STATUS_OK) { // If device request ok, read input data + + // First check if we need to process received data + if (_readState == RDS_ANALOGUE) { + // Read of analogue values was in progress, so process received values + // Here we need to copy the values from input buffer to the analogue value array. We need to + // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed + // while the value is being read). + memcpy(_analogueInputValues, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states + _readState = RDS_IDLE; + + } else if (_readState == RDS_DIGITAL) { + // Read of digital states was in progress, so process received values + // The received digital states are placed directly into the digital buffer on receipt, + // so don't need any further processing at this point (unless we want to check for + // changes and notify them to subscribers, to avoid the need for polling - see IO_GPIOBase.h). + _readState = RDS_IDLE; + } + } else + reportError(status, false); // report eror but don't go offline. + + // If we're not doing anything now, check to see if a new input transfer is due. + if (_readState == RDS_IDLE) { + if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay for digital read refresh + // Issue new read request for digital states. As the request is non-blocking, the buffer has to + // be allocated from heap (object state). + _readCommandBuffer[0] = EXIORDD; + I2CManager.read(_I2CAddress, _digitalInputStates, (_numDigitalPins+7)/8, _readCommandBuffer, 1, &_i2crb); + // non-blocking read + _lastDigitalRead = currentMicros; + _readState = RDS_DIGITAL; + } else if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh + // Issue new read for analogue input states + _readCommandBuffer[0] = EXIORDAN; + I2CManager.read(_I2CAddress, _analogueInputBuffer, + _numAnaloguePins * sizeof(_analogueInputBuffer[0]), _readCommandBuffer, 1, &_i2crb); + _lastAnalogueRead = currentMicros; + _readState = RDS_ANALOGUE; } - _commandFlag = !_commandFlag; - } else { - DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); - _deviceState = DEVSTATE_FAILED; } } - // Obtain the correct analogue input value + // Obtain the correct analogue input value, with reference to the analogue + // pin map. + // (QUESTION: Why isn't this mapping done in the remote node before transmission?) int _readAnalogue(VPIN vpin) override { if (_deviceState == DEVSTATE_FAILED) return 0; int pin = vpin - _firstVpin; - uint8_t _pinLSBByte; for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { - if (_analoguePinMap[aPin] == pin) { - _pinLSBByte = aPin * 2; - } + if (_analoguePinMap[aPin] == pin) + return _analogueInputValues[aPin]; } - uint8_t _pinMSBByte = _pinLSBByte + 1; - return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; + return 0; // Pin not found } // Obtain the correct digital input value @@ -203,75 +277,98 @@ private: return value; } + // Write digital value. We could have an output buffer of states, that is periodically + // written to the device if there are any changes; this would reduce the I2C overhead + // if lots of output requests are being made. We could also cache the last value + // sent so that we don't write the same value over and over to the output. + // However, for the time being, we just write the current value (blocking I2C) to the + // IOExpander node. As it is a blocking request, we can use buffers allocated from + // the stack to save RAM allocation. void _write(VPIN vpin, int value) override { + uint8_t digitalOutBuffer[3]; + uint8_t responseBuffer[1]; if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - _digitalOutBuffer[0] = EXIOWRD; - _digitalOutBuffer[1] = pin; - _digitalOutBuffer[2] = value; - uint8_t status = I2CManager.read(_i2cAddress, _command1Buffer, 1, _digitalOutBuffer, 3); + digitalOutBuffer[0] = EXIOWRD; + digitalOutBuffer[1] = pin; + digitalOutBuffer[2] = value; + uint8_t status = I2CManager.read(_I2CAddress, responseBuffer, 1, digitalOutBuffer, 3); if (status != I2C_STATUS_OK) { - DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); - _deviceState = DEVSTATE_FAILED; + reportError(status); } else { - if (_command1Buffer[0] != EXIORDY) { - DIAG(F("Vpin %d cannot be used as a digital output pin"), (int)vpin); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("Vpin %u cannot be used as a digital output pin"), (int)vpin); } } } + // Write analogue (integer) value. Write the parameters (blocking I2C) to the + // IOExpander node. As it is a blocking request, we can use buffers allocated from + // the stack to reduce RAM allocation. void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { + uint8_t servoBuffer[7]; + uint8_t responseBuffer[1]; + if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; #ifdef DIAG_IO - DIAG(F("Servo: WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"), + DIAG(F("Servo: WriteAnalogue Vpin:%u Value:%d Profile:%d Duration:%d %S"), vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif - _servoBuffer[0] = EXIOWRAN; - _servoBuffer[1] = pin; - _servoBuffer[2] = value & 0xFF; - _servoBuffer[3] = value >> 8; - _servoBuffer[4] = profile; - _servoBuffer[5] = duration & 0xFF; - _servoBuffer[6] = duration >> 8; - uint8_t status = I2CManager.read(_i2cAddress, _command1Buffer, 1, _servoBuffer, 7); + servoBuffer[0] = EXIOWRAN; + servoBuffer[1] = pin; + servoBuffer[2] = value & 0xFF; + servoBuffer[3] = value >> 8; + servoBuffer[4] = profile; + servoBuffer[5] = duration & 0xFF; + servoBuffer[6] = duration >> 8; + uint8_t status = I2CManager.read(_I2CAddress, responseBuffer, 1, servoBuffer, 7); if (status != I2C_STATUS_OK) { DIAG(F("EX-IOExpander I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); _deviceState = DEVSTATE_FAILED; } else { - if (_command1Buffer[0] != EXIORDY) { - DIAG(F("Vpin %d cannot be used as a servo/PWM pin"), (int)vpin); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("Vpin %u cannot be used as a servo/PWM pin"), (int)vpin); } } } + // Display device information and status. void _display() override { - DIAG(F("EX-IOExpander I2C:%s v%d.%d.%d Vpins %d-%d %S"), - _i2cAddress.toString(), _majorVer, _minorVer, _patchVer, + DIAG(F("EX-IOExpander I2C:%s v%d.%d.%d Vpins %u-%u %S"), + _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, (int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("")); } - I2CAddress _i2cAddress; + // Helper function for error handling + void reportError(uint8_t status, bool fail=true) { + DIAG(F("EX-IOExpander I2C:%s Error:%d (%S)"), _I2CAddress.toString(), + status, I2CManager.getErrorMessage(status)); + if (fail) + _deviceState = DEVSTATE_FAILED; + } + uint8_t _numDigitalPins = 0; uint8_t _numAnaloguePins = 0; - byte _digitalOutBuffer[3]; - uint8_t _versionBuffer[3]; + uint8_t _majorVer = 0; uint8_t _minorVer = 0; uint8_t _patchVer = 0; - byte* _digitalInputStates; - byte* _analogueInputStates; - uint8_t _digitalPinBytes = 0; - uint8_t _analoguePinBytes = 0; - byte _command1Buffer[1]; - byte _command2Buffer[2]; - byte _command4Buffer[4]; - byte _receive3Buffer[3]; - byte _servoBuffer[7]; + + uint8_t* _digitalInputStates; + int16_t* _analogueInputValues; + uint8_t* _analogueInputBuffer; // buffer for I2C input transfers + uint8_t _readCommandBuffer[1]; + + uint8_t _digitalPinBytes = 0; // Size of allocated memory buffer (may be longer than needed) + uint8_t _analoguePinBytes = 0; // Size of allocated memory buffers (may be longer than needed) uint8_t* _analoguePinMap; I2CRB _i2crb; - bool _commandFlag = 1; + + enum {RDS_IDLE, RDS_DIGITAL, RDS_ANALOGUE}; // Read operation states + uint8_t _readState = RDS_IDLE; + unsigned long _lastDigitalRead = 0; unsigned long _lastAnalogueRead = 0; const unsigned long _digitalRefresh = 10000UL; // Delay refreshing digital inputs for 10ms diff --git a/version.h b/version.h index 66fc351..1801b02 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.29" +#define VERSION "4.2.30" +// 4.2.30 - Fixes/enhancements to EX-IOExpander device driver. // 4.2.29 - Bugfix Scroll LCD without empty lines and consistent // 4.2.28 - Reinstate use of timer11 in STM32 - remove HA mode. // - Update IO_DFPlayer to work with MP3-TF-16P rev3. From c4b4e11a6773d1cf980b2161b1d279a794a90e5f Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Sat, 18 Mar 2023 15:30:14 +0000 Subject: [PATCH 30/97] Update IO_EXIOExpander.h Avoid repeated error messages for a single fault. --- IO_EXIOExpander.h | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 9c631e0..6d43778 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -210,29 +210,31 @@ private: // Request block is used for analogue and digital reads from the IOExpander, which are performed // on a cyclic basis. Writes are performed synchronously as and when requested. - if (_i2crb.isBusy()) return; // If I2C operation still in progress, return + if (_readState != RDS_IDLE) { + if (_i2crb.isBusy()) return; // If I2C operation still in progress, return - uint8_t status = _i2crb.status; - if (status == I2C_STATUS_OK) { // If device request ok, read input data + uint8_t status = _i2crb.status; + if (status == I2C_STATUS_OK) { // If device request ok, read input data - // First check if we need to process received data - if (_readState == RDS_ANALOGUE) { - // Read of analogue values was in progress, so process received values - // Here we need to copy the values from input buffer to the analogue value array. We need to - // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed - // while the value is being read). - memcpy(_analogueInputValues, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states - _readState = RDS_IDLE; + // First check if we need to process received data + if (_readState == RDS_ANALOGUE) { + // Read of analogue values was in progress, so process received values + // Here we need to copy the values from input buffer to the analogue value array. We need to + // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed + // while the value is being read). + memcpy(_analogueInputValues, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states - } else if (_readState == RDS_DIGITAL) { - // Read of digital states was in progress, so process received values - // The received digital states are placed directly into the digital buffer on receipt, - // so don't need any further processing at this point (unless we want to check for - // changes and notify them to subscribers, to avoid the need for polling - see IO_GPIOBase.h). - _readState = RDS_IDLE; - } - } else - reportError(status, false); // report eror but don't go offline. + } else if (_readState == RDS_DIGITAL) { + // Read of digital states was in progress, so process received values + // The received digital states are placed directly into the digital buffer on receipt, + // so don't need any further processing at this point (unless we want to check for + // changes and notify them to subscribers, to avoid the need for polling - see IO_GPIOBase.h). + } + } else + reportError(status, false); // report eror but don't go offline. + + _readState = RDS_IDLE; + } // If we're not doing anything now, check to see if a new input transfer is due. if (_readState == RDS_IDLE) { From 329dc414523381fc18f93ed1960415ecd2cc3b38 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 18 Mar 2023 18:52:01 +0000 Subject: [PATCH 31/97] Remove implicit AUTOSTART --- EXRAIL2.cpp | 13 +++++++------ myAutomation.example.h | 3 ++- version.h | 4 +++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 8541cd2..bb5989a 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1,7 +1,7 @@ /* * © 2021 Neil McKechnie * © 2021-2023 Harald Barth - * © 2020-2022 Chris Harlow + * © 2020-2023 Chris Harlow * All rights reserved. * * This file is part of CommandStation-EX @@ -24,8 +24,8 @@ F1. [DONE] DCC accessory packet opcodes (short and long form) F2. [DONE] ONAccessory catchers F3. [DONE] Turnout descriptions for Withrottle - F4. Oled announcements (depends on HAL) - F5. Withrottle roster info + F4. [DONE] Oled announcements (depends on HAL) + F5. [DONE] Withrottle roster info F6. Multi-occupancy semaphore F7. [DONE see AUTOSTART] Self starting sequences F8. Park/unpark @@ -240,8 +240,9 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_AUTOSTART: // automatically create a task from here at startup. - // but we will do one at 0 anyway by default. - if (progCounter>0) new RMFT2(progCounter); + // Removed if (progCounter>0) check 4.2.31 because + // default start it top of file is now removed. . + new RMFT2(progCounter); break; default: // Ignore @@ -252,7 +253,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { DIAG(F("EXRAIL %db, fl=%d"),progCounter,MAX_FLAGS); - new RMFT2(0); // add the startup route + // Removed for 4.2.31 new RMFT2(0); // add the startup route diag=saved_diag; } diff --git a/myAutomation.example.h b/myAutomation.example.h index 1382a52..e080f2f 100644 --- a/myAutomation.example.h +++ b/myAutomation.example.h @@ -23,7 +23,8 @@ * */ -// This is the startup sequence, AKA SEQUENCE(0) +// This is the startup sequence, +AUTOSTART SENDLOCO(3,1) // send loco 3 off along route 1 SENDLOCO(10,2) // send loco 10 off along route 2 DONE // This just ends the startup thread, leaving 2 others running. diff --git a/version.h b/version.h index 1801b02..f886a1f 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.30" +#define VERSION "4.2.31" +// 4.2.31 - Removes EXRAIL statup from top of file. (BREAKING CHANGE !!) +// Just add AUTOSTART to the top of your myAutomation.h to restore this function. // 4.2.30 - Fixes/enhancements to EX-IOExpander device driver. // 4.2.29 - Bugfix Scroll LCD without empty lines and consistent // 4.2.28 - Reinstate use of timer11 in STM32 - remove HA mode. From fe183419941a70d75ca6a0a02acb6df6c82d5952 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 18 Mar 2023 18:53:48 +0000 Subject: [PATCH 32/97] Update myAutomation.example.h better example with power on --- myAutomation.example.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/myAutomation.example.h b/myAutomation.example.h index e080f2f..8338452 100644 --- a/myAutomation.example.h +++ b/myAutomation.example.h @@ -24,7 +24,8 @@ */ // This is the startup sequence, -AUTOSTART +AUTOSTART +POWERON // turn on track power SENDLOCO(3,1) // send loco 3 off along route 1 SENDLOCO(10,2) // send loco 10 off along route 2 DONE // This just ends the startup thread, leaving 2 others running. From 2e1a2d38e3b9f8c6f437906d76c35181f0d4417f Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Sun, 19 Mar 2023 01:20:20 +0000 Subject: [PATCH 33/97] Update IO_EXIOExpander.h Reinstate byte-wise processing of analogue input values. --- IO_EXIOExpander.h | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 6d43778..1aa75b7 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -105,16 +105,16 @@ private: _digitalInputStates = (byte*) calloc(_digitalPinBytes, 1); _digitalPinBytes = digitalBytesNeeded; } - size_t analogueBytesNeeded = _numAnaloguePins * sizeof(_analogueInputValues[0]); + size_t analogueBytesNeeded = _numAnaloguePins * 2; if (_analoguePinBytes < analogueBytesNeeded) { // Free any existing buffers and allocate new ones. if (_analoguePinBytes > 0) { free(_analogueInputBuffer); - free(_analogueInputValues); + free(_analogueInputStates); free(_analoguePinMap); } - _analogueInputValues = (int16_t*) calloc(_analoguePinBytes, 1); - _analogueInputBuffer = (uint8_t*) calloc(_analoguePinBytes, 1); + _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); } } else { @@ -129,6 +129,7 @@ private: status = I2CManager.read(_I2CAddress, _analoguePinMap, _numAnaloguePins, commandBuffer, 1); } if (status == I2C_STATUS_OK) { + DIAG(F("Map: %d %d %d %d %d %d"), _analoguePinMap[0], _analoguePinMap[1], _analoguePinMap[2], _analoguePinMap[3], _analoguePinMap[4], _analoguePinMap[5]); // Attempt to get version, if we don't get it, we don't care, don't go offline uint8_t versionBuffer[3]; commandBuffer[0] = EXIOVER; @@ -222,7 +223,7 @@ private: // Here we need to copy the values from input buffer to the analogue value array. We need to // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed // while the value is being read). - memcpy(_analogueInputValues, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states + memcpy(_analogueInputStates, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states } else if (_readState == RDS_DIGITAL) { // Read of digital states was in progress, so process received values @@ -250,7 +251,7 @@ private: // Issue new read for analogue input states _readCommandBuffer[0] = EXIORDAN; I2CManager.read(_I2CAddress, _analogueInputBuffer, - _numAnaloguePins * sizeof(_analogueInputBuffer[0]), _readCommandBuffer, 1, &_i2crb); + _numAnaloguePins * 2, _readCommandBuffer, 1, &_i2crb); _lastAnalogueRead = currentMicros; _readState = RDS_ANALOGUE; } @@ -259,15 +260,18 @@ private: // Obtain the correct analogue input value, with reference to the analogue // pin map. - // (QUESTION: Why isn't this mapping done in the remote node before transmission?) + // Obtain the correct analogue input value int _readAnalogue(VPIN vpin) override { if (_deviceState == DEVSTATE_FAILED) return 0; int pin = vpin - _firstVpin; for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { - if (_analoguePinMap[aPin] == pin) - return _analogueInputValues[aPin]; + if (_analoguePinMap[aPin] == pin) { + uint8_t _pinLSBByte = aPin * 2; + uint8_t _pinMSBByte = _pinLSBByte + 1; + return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; + } } - return 0; // Pin not found + return -1; // pin not found in table } // Obtain the correct digital input value @@ -359,7 +363,7 @@ private: uint8_t _patchVer = 0; uint8_t* _digitalInputStates; - int16_t* _analogueInputValues; + uint8_t* _analogueInputStates; uint8_t* _analogueInputBuffer; // buffer for I2C input transfers uint8_t _readCommandBuffer[1]; From 43c7baf8f5e6bf6d598aa4fc797b49a011e0609f Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Sun, 19 Mar 2023 22:06:02 +0000 Subject: [PATCH 34/97] Fix display scrolling on LCD and OLED Eliminate spurious blanking of screen in mode 1, duplicated lines of text in mode 2, and non-display of more than the first screen-full of lines in mode 0. --- Display.cpp | 180 +++++++++++++++++++++++++--------------------------- Display.h | 16 ++--- version.h | 3 +- 3 files changed, 96 insertions(+), 103 deletions(-) diff --git a/Display.cpp b/Display.cpp index 03d6039..ecb4216 100644 --- a/Display.cpp +++ b/Display.cpp @@ -1,6 +1,5 @@ /* * © 2021, Chris Harlow, Neil McKechnie. All rights reserved. - * © 2023, Harald Barth. * * This file is part of CommandStation-EX * @@ -37,7 +36,7 @@ * not held up significantly. The exception to this is when * the loop2() function is called with force=true, where * a screen update is executed to completion. This is normally - * only noMoreRowsToDisplay during start-up. + * only done during start-up. * The scroll mode is selected by defining SCROLLMODE as 0, 1 or 2 * in the config.h. * #define SCROLLMODE 0 is scroll continuous (fill screen if poss), @@ -52,11 +51,10 @@ Display::Display(DisplayDevice *deviceDriver) { _deviceDriver = deviceDriver; // Get device dimensions in characters (e.g. 16x2). - numCharacterColumns = _deviceDriver->getNumCols(); - numCharacterRows = _deviceDriver->getNumRows(); + numScreenColumns = _deviceDriver->getNumCols(); + numScreenRows = _deviceDriver->getNumRows(); for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++) rowBuffer[row][0] = '\0'; - topRow = ROW_INITIAL; // loop2 will fill from row 0 addDisplay(0); // Add this display as display number 0 }; @@ -70,20 +68,19 @@ void Display::_clear() { _deviceDriver->clearNative(); for (uint8_t row = 0; row < MAX_CHARACTER_ROWS; row++) rowBuffer[row][0] = '\0'; - topRow = ROW_INITIAL; // loop2 will fill from row 0 } void Display::_setRow(uint8_t line) { hotRow = line; hotCol = 0; - rowBuffer[hotRow][0] = 0; // Clear existing text + rowBuffer[hotRow][0] = '\0'; // Clear existing text } size_t Display::_write(uint8_t b) { if (hotRow >= MAX_CHARACTER_ROWS || hotCol >= MAX_CHARACTER_COLS) return -1; rowBuffer[hotRow][hotCol] = b; hotCol++; - rowBuffer[hotRow][hotCol] = 0; + rowBuffer[hotRow][hotCol] = '\0'; return 1; } @@ -110,8 +107,8 @@ Display *Display::loop2(bool force) { return NULL; } else { // force full screen update from the beginning. - rowFirst = ROW_INITIAL; - rowNext = ROW_INITIAL; + rowFirst = 0; + rowCurrent = 0; bufferPointer = 0; noMoreRowsToDisplay = false; slot = 0; @@ -119,109 +116,104 @@ Display *Display::loop2(bool force) { do { if (bufferPointer == 0) { - // Find a line of data to write to the screen. - if (rowFirst == ROW_INITIAL) rowFirst = rowNext; - if (findNextNonBlankRow()) { + // Search for non-blank row + while (!noMoreRowsToDisplay) { + if (!isCurrentRowBlank()) break; + moveToNextRow(); + if (rowCurrent == rowFirst) noMoreRowsToDisplay = true; + } + + if (noMoreRowsToDisplay) { + // No non-blank lines left, so draw blank line + buffer[0] = '\0'; + } else { // Non-blank line found, so copy it (including terminator) for (uint8_t i = 0; i <= MAX_CHARACTER_COLS; i++) - buffer[i] = rowBuffer[rowNext][i]; - } else { - // No non-blank lines left, so draw a blank line - buffer[0] = 0; + buffer[i] = rowBuffer[rowCurrent][i]; } -#if SCROLLMODE==2 - if (buffer[0] == 0 && needScroll){ // surpresses empty line -#else - if (false){ -#endif - charIndex = MAX_CHARACTER_COLS; - slot--; - } else { - _deviceDriver->setRowNative(slot); // Set position for display - charIndex = 0; - bufferPointer = &buffer[0]; - } - rowNext++; + _deviceDriver->setRowNative(slot); // Set position for display + charIndex = 0; + bufferPointer = &buffer[0]; } else { // Write next character, or a space to erase current position. char ch = *bufferPointer; if (ch) { - _deviceDriver->writeNative(ch); + _deviceDriver->writeNative(ch); bufferPointer++; } else { _deviceDriver->writeNative(' '); } - } - if (++charIndex >= MAX_CHARACTER_COLS) { - // Screen slot completed, move to next slot on screen - bufferPointer = 0; - slot++; - if (slot >= numCharacterRows) { - // Last slot on screen written, reset ready for next screen update. -#if SCROLLMODE==2 || SCROLLMODE==1 - if (!noMoreRowsToDisplay) { - needScroll = true; - } - if (needScroll) { -#if SCROLLMODE==2 - // SCROLLMODE 2 rotates through rowFirst and we - // (ab)use findNextBlankRow() to figure out - // next valid row which can be start row. - rowNext = rowFirst + 1; - noMoreRowsToDisplay = false; - findNextNonBlankRow(); - if (rowNext == ROW_INITIAL) - rowNext = 0; - rowFirst = ROW_INITIAL; + if (++charIndex >= MAX_CHARACTER_COLS) { + // Screen slot completed, move to next nonblank row + bufferPointer = 0; + for (;;) { + moveToNextRow(); + if (rowCurrent == rowFirst) { + noMoreRowsToDisplay = true; + break; + } + if (!isCurrentRowBlank()) break; + } + // Move to next screen slot, if available + slot++; + if (slot >= numScreenRows) { + // Last slot on screen written, so get ready for next screen update. +#if SCROLLMODE==0 + // Scrollmode 0 scrolls continuously. If the rows fit on the screen, + // then restart at row 0, but otherwise continue with the row + // after the last one displayed. + if (countNonBlankRows() <= numScreenRows) + rowCurrent = 0; + rowFirst = rowCurrent; +#elif SCROLLMODE==1 + // Scrollmode 1 scrolls by page, so if the last page has just completed then + // next time restart with row 0. + if (noMoreRowsToDisplay) + rowFirst = rowCurrent = 0; #else - // SCROLLMODE 1 just alternates when the - // flag indicates that we have come to the end - if (noMoreRowsToDisplay) - rowNext = 0; + // Scrollmode 2 scrolls by row. If the rows don't fit on the screen, + // then start one row further on next time. If they do fit, then + // show them in order and start next page at row 0. + if (countNonBlankRows() <= numScreenRows) { + rowFirst = rowCurrent = 0; + } else { + // Find first non-blank row after the previous first row + rowCurrent = rowFirst; + do { + moveToNextRow(); + } while (isCurrentRowBlank()); + rowFirst = rowCurrent; + } #endif - } else { - // SCROLLMODE 1 or 2 but not scroll active - rowNext = 0; - } -#else - // this is for SCROLLMODE 0 but what should it do? - rowNext = 0; -#endif - rowFirst = ROW_INITIAL; - - noMoreRowsToDisplay = false; - slot = 0; - lastScrollTime = currentMillis; - return NULL; + noMoreRowsToDisplay = false; + slot = 0; + lastScrollTime = currentMillis; + return NULL; + } } -#if SCROLLMODE==2 - if (needScroll) - noMoreRowsToDisplay = false; -#endif } } while (force); return NULL; } -bool Display::findNextNonBlankRow() { - while (!noMoreRowsToDisplay) { - if (rowNext == ROW_INITIAL) - rowNext = 0; - if (rowNext >= MAX_CHARACTER_ROWS) { - // Finished if we've looped back to start - rowNext = ROW_INITIAL; - noMoreRowsToDisplay = true; - return false; - } - if (rowBuffer[rowNext][0] != 0) { - //rowBuffer[rowNext][0] = '0' + rowNext; // usefull for debug - //rowBuffer[rowNext][1] = '0' + rowFirst; // usefull for debug - // Found non-blank row - return true; - } - rowNext = rowNext + 1; - } - return false; +bool Display::isCurrentRowBlank() { + return (rowBuffer[rowCurrent][0] == '\0'); } + +void Display::moveToNextRow() { + // Skip blank rows + if (++rowCurrent >= MAX_CHARACTER_ROWS) + rowCurrent = 0; +} + +uint8_t Display::countNonBlankRows() { + uint8_t count = 0; + for (uint8_t rowNumber=0; rowNumber Date: Mon, 20 Mar 2023 10:03:02 +0100 Subject: [PATCH 35/97] exchange pin number to track letter --- GITHUB_SHA.h | 2 +- MotorDriver.cpp | 5 ++--- MotorDriver.h | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 4d37765..a35bfaa 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303151539Z" +#define GITHUB_SHA "devel-202303200902Z" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 0236215..a62dc9c 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -137,8 +137,7 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i if (currentPin==UNUSED_PIN) DIAG(F("** WARNING ** No current or short detection")); else { - DIAG(F("CurrentPin=A%d, TripValue=%d"), - currentPin-A0, rawCurrentTripValue); + DIAG(F("Track %c, TripValue=%d"), trackLetter, rawCurrentTripValue); // self testing diagnostic for the non-float converters... may be removed when happy // DIAG(F("senseFactorInternal=%d raw2mA(1000)=%d mA2Raw(1000)=%d"), @@ -163,7 +162,7 @@ void MotorDriver::setPower(POWERMODE mode) { // when switching a track On, we need to check the crrentOffset with the pin OFF if (powerMode==POWERMODE::OFF && currentPin!=UNUSED_PIN) { senseOffset = ADCee::read(currentPin); - DIAG(F("CurrentPin A%d sensOffset=%d"),currentPin-A0,senseOffset); + DIAG(F("Track %c sensOffset=%d"),trackLetter,senseOffset); } IODevice::write(powerPin,invertPower ? LOW : HIGH); diff --git a/MotorDriver.h b/MotorDriver.h index 51f7654..77b2ec8 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -182,11 +182,13 @@ class MotorDriver { isProgTrack = on; } void checkPowerOverload(bool useProgLimit, byte trackno); + void setTrackLetter(char c); #ifdef ANALOG_READ_INTERRUPT bool sampleCurrentFromHW(); void startCurrentFromHW(); #endif private: + char trackLetter = '?'; bool isProgTrack = false; // tells us if this is a prog track void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result); void getFastPin(const FSH* type,int pin, FASTPIN & result) { From 4087cd6e298f2259b265c296d161fcb1bc1c5a41 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 20 Mar 2023 19:05:53 +1000 Subject: [PATCH 36/97] Fixed non-working analogue inputs --- IO_EXIOExpander.h | 3 +-- version.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 1aa75b7..f4705f9 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -129,7 +129,6 @@ private: status = I2CManager.read(_I2CAddress, _analoguePinMap, _numAnaloguePins, commandBuffer, 1); } if (status == I2C_STATUS_OK) { - DIAG(F("Map: %d %d %d %d %d %d"), _analoguePinMap[0], _analoguePinMap[1], _analoguePinMap[2], _analoguePinMap[3], _analoguePinMap[4], _analoguePinMap[5]); // Attempt to get version, if we don't get it, we don't care, don't go offline uint8_t versionBuffer[3]; commandBuffer[0] = EXIOVER; @@ -223,7 +222,7 @@ private: // Here we need to copy the values from input buffer to the analogue value array. We need to // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed // while the value is being read). - memcpy(_analogueInputStates, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states + memcpy(_analogueInputStates, _analogueInputBuffer, sizeof(_analogueInputBuffer)); // Copy I2C input buffer to states } else if (_readState == RDS_DIGITAL) { // Read of digital states was in progress, so process received values diff --git a/version.h b/version.h index d4144b8..b1704de 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.32" +#define VERSION "4.2.33" +// 4.2.33 - Fix EX-IOExpander non-working analogue inputs // 4.2.32 - Fix LCD/Display bugfixes from 4.2.29 // 4.2.31 - Removes EXRAIL statup from top of file. (BREAKING CHANGE !!) // Just add AUTOSTART to the top of your myAutomation.h to restore this function. From 21c99c86944b9ff100ed89a78c922d529ee6d6d9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 20 Mar 2023 10:11:14 +0100 Subject: [PATCH 37/97] improve Wifi connect messages --- GITHUB_SHA.h | 2 +- WifiInterface.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index a35bfaa..ee808a7 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303200902Z" +#define GITHUB_SHA "devel-202303200910Z" diff --git a/WifiInterface.cpp b/WifiInterface.cpp index f69cc2f..21d474b 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -125,17 +125,18 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con wifiState = setup2( SSid, password, hostname, port, channel); if (wifiState == WIFI_NOAT) { - DIAG(F("++ Wifi Setup NO AT ++")); - return wifiState; + LCD(4, F("WiFi no AT chip")); + return wifiState; } if (wifiState == WIFI_CONNECTED) { StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo - checkForOK(200, true); + checkForOK(200, true); + DIAG(F("WiFi CONNECTED")); + // LCD already shows IP + } else { + LCD(4,F("WiFi DISCON.")); } - - - DIAG(F("++ Wifi Setup %S ++"), wifiState == WIFI_CONNECTED ? F("CONNECTED") : F("DISCONNECTED")); return wifiState; } From 7ab5f556d9c9e3b76239b4becb968d276bfe1bdc Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 21 Mar 2023 05:30:33 +1000 Subject: [PATCH 38/97] Using correct size for memcpy --- IO_EXIOExpander.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index f4705f9..675d66c 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -116,6 +116,7 @@ private: _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + _analoguePinBytes = analogueBytesNeeded; } } else { DIAG(F("EX-IOExpander I2C:%s ERROR configuring device"), _I2CAddress.toString()); @@ -222,7 +223,7 @@ private: // Here we need to copy the values from input buffer to the analogue value array. We need to // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed // while the value is being read). - memcpy(_analogueInputStates, _analogueInputBuffer, sizeof(_analogueInputBuffer)); // Copy I2C input buffer to states + memcpy(_analogueInputStates, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states } else if (_readState == RDS_DIGITAL) { // Read of digital states was in progress, so process received values From a75ca00e3c8b24e4c9576dac49d94c1210c6b45f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 20 Mar 2023 21:22:48 +0100 Subject: [PATCH 39/97] exchange pin number to track letter part 2 --- Display.cpp | 2 +- MotorDriver.h | 4 +++- TrackManager.cpp | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Display.cpp b/Display.cpp index ecb4216..d581149 100644 --- a/Display.cpp +++ b/Display.cpp @@ -216,4 +216,4 @@ uint8_t Display::countNonBlankRows() { } return count; } - \ No newline at end of file + diff --git a/MotorDriver.h b/MotorDriver.h index 77b2ec8..990fd71 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -182,7 +182,9 @@ class MotorDriver { isProgTrack = on; } void checkPowerOverload(bool useProgLimit, byte trackno); - void setTrackLetter(char c); + inline void setTrackLetter(char c) { + trackLetter = c; + }; #ifdef ANALOG_READ_INTERRUPT bool sampleCurrentFromHW(); void startCurrentFromHW(); diff --git a/TrackManager.cpp b/TrackManager.cpp index 633ed06..fcd0c10 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -129,6 +129,7 @@ void TrackManager::addTrack(byte t, MotorDriver* driver) { track[t]=driver; if (driver) { track[t]->setPower(POWERMODE::OFF); + track[t]->setTrackLetter('A'+t); lastTrack=t; } } From d51281f1f2fa0554d56037c39ffcec54db16b53c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 20 Mar 2023 21:24:42 +0100 Subject: [PATCH 40/97] github tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index ee808a7..46d55b9 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303200910Z" +#define GITHUB_SHA "devel-202303202024Z" From b183439a5b798a956e5d5c37648fb0ce9b33b8c1 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 21 Mar 2023 05:30:33 +1000 Subject: [PATCH 41/97] Using correct size for memcpy --- IO_EXIOExpander.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index f4705f9..675d66c 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -116,6 +116,7 @@ private: _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + _analoguePinBytes = analogueBytesNeeded; } } else { DIAG(F("EX-IOExpander I2C:%s ERROR configuring device"), _I2CAddress.toString()); @@ -222,7 +223,7 @@ private: // Here we need to copy the values from input buffer to the analogue value array. We need to // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed // while the value is being read). - memcpy(_analogueInputStates, _analogueInputBuffer, sizeof(_analogueInputBuffer)); // Copy I2C input buffer to states + memcpy(_analogueInputStates, _analogueInputBuffer, _analoguePinBytes); // Copy I2C input buffer to states } else if (_readState == RDS_DIGITAL) { // Read of digital states was in progress, so process received values From a3d03ac68c17fca97ad44f3719dc603e87787d63 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 21 Mar 2023 07:04:08 +1000 Subject: [PATCH 42/97] Fix validated, update version --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index b1704de..259dbaa 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.33" +#define VERSION "4.2.34" +// 4.2.34 - Completely fix EX-IOExpander analogue inputs // 4.2.33 - Fix EX-IOExpander non-working analogue inputs // 4.2.32 - Fix LCD/Display bugfixes from 4.2.29 // 4.2.31 - Removes EXRAIL statup from top of file. (BREAKING CHANGE !!) From f0ee8aeb85cc16c1f9ffcaef4cecb0315f553626 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 23 Mar 2023 19:52:49 +0000 Subject: [PATCH 43/97] z Commands --- DCCEXParser.cpp | 16 ++++++++++++++-- Release_Notes/CommandRef.md | 12 +++++++++--- version.h | 3 ++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 9e2cf5f..4d97b20 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -344,6 +344,20 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) return; break; + case 'z': // direct pin manipulation + if (p[0]==0) break; + if (params==1) { // + if (p[0]>0) IODevice::write(p[0],HIGH); + else IODevice::write(-p[0],LOW); + return; + } + if (params>=2 && params<=4) { // + // unused params default to 0 + IODevice::writeAnalogue(p[0],p[1],p[2],p[3]); + return; + } + break; + case 'Z': // OUTPUT if (parseZ(stream, params, p)) return; @@ -516,9 +530,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F("\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA)); CommandDistributor::broadcastPower(); // is the only "get power status" command we have Turnout::printAll(stream); //send all Turnout states - Output::printAll(stream); //send all Output states Sensor::printAll(stream); //send all Sensor states - // TODO Send stats of speed reminders table return; #ifndef DISABLE_EEPROM diff --git a/Release_Notes/CommandRef.md b/Release_Notes/CommandRef.md index c06e388..9d328d7 100644 --- a/Release_Notes/CommandRef.md +++ b/Release_Notes/CommandRef.md @@ -50,9 +50,12 @@ Note: Turnouts are best defined in myAutomation.h where a turnout description ca Valid commands respond with -Outputs (Used by JMRI, not required by EXRAIL) - Define an output pin that JMRI can set by id - Activate an output pin by id +Direct pin manipulation (replaces Set pin LOW + Set pin HIGH + Set pin analog value + Set pin analig with profile + set pin analog with profile and value Sensors (Used by JMRI, not required by EXRAIL) @@ -172,6 +175,9 @@ Obsolete commands/formats V command is much faster if prediction is correct. V command is much faster if prediction is correct. + (use (use direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs // 4.2.33 - Fix EX-IOExpander non-working analogue inputs // 4.2.32 - Fix LCD/Display bugfixes from 4.2.29 From 51a480dff3aa556d72db98e4cb3952dd9c3db59f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 24 Mar 2023 00:24:03 +0000 Subject: [PATCH 44/97] doc typo only --- Release_Notes/CommandRef.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Release_Notes/CommandRef.md b/Release_Notes/CommandRef.md index 9d328d7..6bade54 100644 --- a/Release_Notes/CommandRef.md +++ b/Release_Notes/CommandRef.md @@ -51,10 +51,10 @@ Note: Turnouts are best defined in myAutomation.h where a turnout description ca Valid commands respond with Direct pin manipulation (replaces Set pin LOW - Set pin HIGH + Set pin HIGH + Set pin LOW Set pin analog value - Set pin analig with profile + Set pin analog with profile set pin analog with profile and value From 1ec378281b5e8d6e7900d2ffe058b23f84cfd8d2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 25 Mar 2023 12:14:58 +0100 Subject: [PATCH 45/97] do not broadcast a turnout state that has not changed --- GITHUB_SHA.h | 2 +- Turnouts.cpp | 35 +++++++++++++---------------------- version.h | 3 ++- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 46d55b9..9a36e4a 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303202024Z" +#define GITHUB_SHA "devel-202303251113Z" diff --git a/Turnouts.cpp b/Turnouts.cpp index 582ea7d..c02e97c 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -110,15 +110,16 @@ /* static */ bool Turnout::setClosedStateOnly(uint16_t id, bool closeFlag) { Turnout *tt = get(id); if (!tt) return false; - tt->_turnoutData.closed = closeFlag; - // I know it says setClosedStateOnly, but we need to tell others - // that the state has changed too. - #if defined(EXRAIL_ACTIVE) - RMFT2::turnoutEvent(id, closeFlag); - #endif - - CommandDistributor::broadcastTurnout(id, closeFlag); + // that the state has changed too. But we only broadcast if there + // really has been a change. + if (tt->_turnoutData.closed != closeFlag) { + tt->_turnoutData.closed = closeFlag; + CommandDistributor::broadcastTurnout(id, closeFlag); + } +#if defined(EXRAIL_ACTIVE) + RMFT2::turnoutEvent(id, closeFlag); +#endif return true; } @@ -128,31 +129,21 @@ // type should be placed in the virtual function setClosedInternal(bool) which is // called from here. /* static */ bool Turnout::setClosed(uint16_t id, bool closeFlag) { - #if defined(DIAG_IO) - if (closeFlag) - DIAG(F("Turnout::close(%d)"), id); - else - DIAG(F("Turnout::throw(%d)"), id); - #endif +#if defined(DIAG_IO) + DIAG(F("Turnout(%d,%c)"), id, closeFlag ? 'c':'t'); +#endif Turnout *tt = Turnout::get(id); if (!tt) return false; bool ok = tt->setClosedInternal(closeFlag); if (ok) { - + tt->setClosedStateOnly(id, closeFlag); #ifndef DISABLE_EEPROM // Write byte containing new closed/thrown state to EEPROM if required. Note that eepromAddress // is always zero for LCN turnouts. if (EEStore::eeStore->data.nTurnouts > 0 && tt->_eepromAddress > 0) EEPROM.put(tt->_eepromAddress, tt->_turnoutData.flags); #endif - - #if defined(EXRAIL_ACTIVE) - RMFT2::turnoutEvent(id, closeFlag); - #endif - - // Send message to JMRI etc. - CommandDistributor::broadcastTurnout(id, closeFlag); } return ok; } diff --git a/version.h b/version.h index 25867b1..6a597fe 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.35" +#define VERSION "4.2.36rc1" +// 4.2.36 - do not broadcast a turnout state that has not changed // 4.2.35 - add direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs // 4.2.33 - Fix EX-IOExpander non-working analogue inputs From 8a425fe0ef2b2851e57b7a4c01c2de753823cc0c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 25 Mar 2023 19:28:37 +0100 Subject: [PATCH 46/97] do not broadcast a turnout state that has not changed 2 --- GITHUB_SHA.h | 2 +- Turnouts.cpp | 10 +++++----- version.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 9a36e4a..8809d55 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303251113Z" +#define GITHUB_SHA "devel-202303251828Z" diff --git a/Turnouts.cpp b/Turnouts.cpp index c02e97c..e8f55af 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -123,7 +123,7 @@ return true; } - +#define DIAG_IO // Static setClosed function is invoked from close(), throw() etc. to perform the // common parts of the turnout operation. Code which is specific to a turnout // type should be placed in the virtual function setClosedInternal(bool) which is @@ -289,7 +289,6 @@ #ifndef IO_NO_HAL IODevice::writeAnalogue(_servoTurnoutData.vpin, close ? _servoTurnoutData.closedPosition : _servoTurnoutData.thrownPosition, _servoTurnoutData.profile); - _turnoutData.closed = close; #else (void)close; // avoid compiler warnings #endif @@ -387,7 +386,6 @@ // and Close writes a 0. // RCN-213 specifies that Throw is 0 and Close is 1. DCC::setAccessory(_dccTurnoutData.address, _dccTurnoutData.subAddress, close ^ !rcn213Compliant); - _turnoutData.closed = close; return true; } @@ -463,7 +461,6 @@ bool VpinTurnout::setClosedInternal(bool close) { IODevice::write(_vpinTurnoutData.vpin, close); - _turnoutData.closed = close; return true; } @@ -514,7 +511,10 @@ bool LCNTurnout::setClosedInternal(bool close) { // Assume that the LCN command still uses 1 for throw and 0 for close... LCN::send('T', _turnoutData.id, !close); - // The _turnoutData.closed flag should be updated by a message from the LCN master, later. + // The _turnoutData.closed flag should be updated by a message from the LCN master. + // but in this implementation it is updated in setClosedStateOnly() instead. + // If the LCN master updates this, setClosedStateOnly() and all setClosedInternal() + // have to be updated accordingly so that the closed flag is only set once. return true; } diff --git a/version.h b/version.h index 6a597fe..d8c03cf 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,7 @@ #include "StringFormatter.h" -#define VERSION "4.2.36rc1" +#define VERSION "4.2.36rc2" // 4.2.36 - do not broadcast a turnout state that has not changed // 4.2.35 - add direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs From bdd4bc9999b37e168850bdeac662ad2173e59ea6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 25 Mar 2023 22:26:57 +0100 Subject: [PATCH 47/97] version --- GITHUB_SHA.h | 2 +- version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 8809d55..4f86707 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303251828Z" +#define GITHUB_SHA "devel-202303252126Z" diff --git a/version.h b/version.h index d8c03cf..f9484a1 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,7 @@ #include "StringFormatter.h" -#define VERSION "4.2.36rc2" +#define VERSION "4.2.36" // 4.2.36 - do not broadcast a turnout state that has not changed // 4.2.35 - add direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs From f348857ddba2ab8897517846a1191680019c8887 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Mon, 27 Mar 2023 12:39:11 +0100 Subject: [PATCH 48/97] Add FLAGS device for EX-RAIL state communications. Improve VPIN display in messages. FLAGS HAL device added to IODevice.h, which allows use of SET/RESET// to set and reset a VPIN state, and to allow /IF/IFNOT/AT/WAITFOR/etc. to monitor the VPIN state. Also, correct handling of VPINs above 32767 in DIAG calls within IODevice.cpp and IODevice.h. --- IODevice.cpp | 24 +-- IODevice.h | 69 ++++++++ config.h.txt | 169 ++++++++++++++++++ myHal.cpp.txt | 465 ++++++++++++++++++++++++++++++++++++++++++++++++++ version.h | 4 +- 5 files changed, 719 insertions(+), 12 deletions(-) create mode 100644 config.h.txt create mode 100644 myHal.cpp.txt diff --git a/IODevice.cpp b/IODevice.cpp index e907c23..03ccf85 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -169,7 +169,7 @@ bool IODevice::hasCallback(VPIN vpin) { // Display (to diagnostics) details of the device. void IODevice::_display() { - DIAG(F("Unknown device Vpins:%d-%d %S"), + DIAG(F("Unknown device Vpins:%u-%u %S"), (int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState==DEVSTATE_FAILED ? F("OFFLINE") : F("")); } @@ -179,7 +179,7 @@ bool IODevice::configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, i IODevice *dev = findDevice(vpin); if (dev) return dev->_configure(vpin, configType, paramCount, params); #ifdef DIAG_IO - DIAG(F("IODevice::configure(): Vpin ID %d not found!"), (int)vpin); + DIAG(F("IODevice::configure(): VPIN %u not found!"), (int)vpin); #endif return false; } @@ -191,7 +191,7 @@ int IODevice::read(VPIN vpin) { return dev->_read(vpin); } #ifdef DIAG_IO - DIAG(F("IODevice::read(): Vpin %d not found!"), (int)vpin); + DIAG(F("IODevice::read(): VPIN %u not found!"), (int)vpin); #endif return false; } @@ -203,7 +203,7 @@ int IODevice::readAnalogue(VPIN vpin) { return dev->_readAnalogue(vpin); } #ifdef DIAG_IO - DIAG(F("IODevice::readAnalogue(): Vpin %d not found!"), (int)vpin); + DIAG(F("IODevice::readAnalogue(): VPIN %u not found!"), (int)vpin); #endif return -1023; } @@ -213,7 +213,7 @@ int IODevice::configureAnalogIn(VPIN vpin) { return dev->_configureAnalogIn(vpin); } #ifdef DIAG_IO - DIAG(F("IODevice::configureAnalogIn(): Vpin %d not found!"), (int)vpin); + DIAG(F("IODevice::configureAnalogIn(): VPIN %u not found!"), (int)vpin); #endif return -1023; } @@ -227,7 +227,7 @@ void IODevice::write(VPIN vpin, int value) { return; } #ifdef DIAG_IO - DIAG(F("IODevice::write(): Vpin ID %d not found!"), (int)vpin); + DIAG(F("IODevice::write(): VPIN %u not found!"), (int)vpin); #endif } @@ -246,7 +246,7 @@ void IODevice::writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t para return; } #ifdef DIAG_IO - DIAG(F("IODevice::writeAnalogue(): Vpin ID %d not found!"), (int)vpin); + DIAG(F("IODevice::writeAnalogue(): VPIN %u not found!"), (int)vpin); #endif } @@ -314,9 +314,11 @@ IODevice *IODevice::findDeviceFollowing(VPIN vpin) { // Private helper function to check for vpin overlap. Run during setup only. // returns true if pins DONT overlap with existing device +// TODO: Move the I2C address reservation and checks into the I2CManager code. +// That will enable non-HAL devices to reserve I2C addresses too. bool IODevice::checkNoOverlap(VPIN firstPin, uint8_t nPins, I2CAddress i2cAddress) { #ifdef DIAG_IO - DIAG(F("Check no overlap %d %d %s"), firstPin,nPins,i2cAddress.toString()); + DIAG(F("Check no overlap %u %u %s"), firstPin,nPins,i2cAddress.toString()); #endif VPIN lastPin=firstPin+nPins-1; for (IODevice *dev = _firstDevice; dev != 0; dev = dev->_nextDevice) { @@ -327,7 +329,7 @@ bool IODevice::checkNoOverlap(VPIN firstPin, uint8_t nPins, I2CAddress i2cAddres VPIN lastDevPin=firstDevPin+dev->_nPins-1; bool noOverlap= firstPin>lastDevPin || lastPin= NUM_DIGITAL_PINS) return false; #ifdef DIAG_IO - DIAG(F("Arduino _configurePullup Pin:%d Val:%d"), pin, p[0]); + DIAG(F("Arduino _configurePullup pin:%d Val:%d"), pin, p[0]); #endif pinMode(pin, p[0] ? INPUT_PULLUP : INPUT); return true; @@ -528,7 +530,7 @@ int ArduinoPins::_configureAnalogIn(VPIN vpin) { } void ArduinoPins::_display() { - DIAG(F("Arduino Vpins:%d-%d"), (int)_firstVpin, (int)_firstVpin+_nPins-1); + DIAG(F("Arduino Vpins:%u-%u"), (int)_firstVpin, (int)_firstVpin+_nPins-1); } ///////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/IODevice.h b/IODevice.h index 51b5aa0..769e111 100644 --- a/IODevice.h +++ b/IODevice.h @@ -467,6 +467,75 @@ protected: } }; +///////////////////////////////////////////////////////////////////////////////////////////////////// +// +// This HAL device driver is intended for communication in automation +// sequences. A VPIN can be SET or RESET within a sequence, and its +// current state checked elsewhere using IF, IFNOT, AT etc. or monitored +// from JMRI using a Sensor object (DCC-EX command). +// Alternatively, the flag can be set from JMRI and other interfaces +// using the command, to enable or disable actions within a sequence. +// +// Example of configuration in halSetup.h: +// +// FLAGS::create(32000, 128); +// +// or in myAutomation.h: +// +// HAL(FLAGS, 32000, 128); +// +// Both create 128 flags numbered with VPINs 32000-32127. +// +// + +class FLAGS : IODevice { +private: + uint8_t *_states = NULL; + +public: + static void create(VPIN firstVpin, unsigned int nPins) { + if (checkNoOverlap(firstVpin, nPins)) + new FLAGS(firstVpin, nPins); + } + +protected: + // Constructor performs static initialisation of the device object + FLAGS (VPIN firstVpin, int nPins) { + _firstVpin = firstVpin; + _nPins = nPins; + _states = (uint8_t *)calloc(1, (_nPins+7)/8); + if (!_states) { + DIAG(F("FLAGS: ERROR Memory Allocation Failure")); + return; + } + + addDevice(this); + } + + int _read(VPIN vpin) override { + int pin = vpin - _firstVpin; + if (pin >= _nPins || pin < 0) return 0; + uint8_t mask = 1 << (pin & 7); + return (_states[pin>>3] & mask) ? 1 : 0; + } + + void _write(VPIN vpin, int value) override { + int pin = vpin - _firstVpin; + if (pin >= _nPins || pin < 0) return; + uint8_t mask = 1 << (pin & 7); + if (value) + _states[pin>>3] |= mask; + else + _states[pin>>3] &= ~mask; + } + + void _display() override { + DIAG(F("FLAGS configured on VPINs %u-%u"), + _firstVpin, _firstVpin+_nPins-1); + } + +}; + #include "IO_MCP23008.h" #include "IO_MCP23017.h" #include "IO_PCF8574.h" diff --git a/config.h.txt b/config.h.txt new file mode 100644 index 0000000..98cc3fc --- /dev/null +++ b/config.h.txt @@ -0,0 +1,169 @@ +/********************************************************************** + +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 STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), +// new MotorDriver(3, 12, UNUSED_PIN, 9, A0, 0.488, 1500, UNUSED_PIN), +// new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 0.488, 1500, UNUSED_PIN) + +#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) +// +// If DONT_TOUCH_WIFI_CONF is set, all WIFI config will be done with +// the <+> commands and this sketch will not change anything over +// AT commands and the other WIFI_* defines below do not have any effect. +//#define DONT_TOUCH_WIFI_CONF +// +// WIFI_SSID is the network name IF you want to use your existing home network. +// Do NOT change this if you want to use the WiFi in Access Point (AP) mode. +// +// If you do NOT set the WIFI_SSID, the WiFi chip will first try +// to connect to the previously configured network and if that fails +// fall back to Access Point mode. The SSID of the AP will be +// automatically set to DCCEX_*. +// +// Your SSID may not conain ``"'' (double quote, ASCII 0x22). +#define WIFI_SSID "Your network name" +// +// WIFI_PASSWORD is the network password for your home network or if +// you want to change the password from default AP mode password +// to the AP password you want. +// Your password may not conain ``"'' (double quote, ASCII 0x22). +#define WIFI_PASSWORD "deadcafe" +// +// WIFI_HOSTNAME: You probably don't need to change this +#define WIFI_HOSTNAME "dccex" +// +///////////////////////////////////////////////////////////////////////////////////// +// +// Wifi connect timeout in milliseconds. Default is 14000 (14 seconds). You only need +// to set this if you have an extremely slow Wifi router. +// +#define WIFI_CONNECT_TIMEOUT 14000 + +///////////////////////////////////////////////////////////////////////////////////// +// +// ENABLE_ETHERNET: Set to true if you have an Arduino Ethernet card (wired). This +// is not for Wifi. You will then need the Arduino Ethernet library as well +// +//#define ENABLE_ETHERNET true + + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP +// +//#define IP_ADDRESS { 192, 168, 1, 31 } + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE +// +// Uncomment to use with Ethernet Shields +// +// Ethernet Shields do not have have a MAC address in hardware. There may be one on +// a sticker on the Shield that you should use. Otherwise choose one of the ones below +// Be certain that no other device on your network has this same MAC address! +// +// 52:b8:8a:8e:ce:21 +// e3:e9:73:e1:db:0d +// 54:2b:13:52:ac:0c + +// NOTE: This is not used with ESP8266 WiFi modules. + +//#define MAC_ADDRESS { 0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 } // MAC address of your networking card found on the sticker on your card or take one from above + +// +// #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 Hitachi HD44780 +// controller and a PCF8574 based I2C 'backpack', +// OR an I2C Oled screen based on SSD1306 (128x64 or 128x32) controller, +// OR an I2C Oled screen based on SH1106 (132x64) controller. +// To enable, uncomment one of the lines below + +// define LCD_DRIVER for I2C LCD address 0x3f,16 cols, 2 rows +//#define LCD_DRIVER {SubBus_4,0x27},20,4 + +//OR define OLED_DRIVER width,height in pixels (address auto detected) +#if defined(ARDUINO_ARCH_STM32) +#define OLED_DRIVER 0x3c, 128, 64 +#else +#define OLED_DRIVER {SubBus_0,0x3c}, 128, 32 +#endif + +#define SCROLLMODE 1 + +///////////////////////////////////////////////////////////////////////////////////// +// DISABLE EEPROM +// +// If you do not need the EEPROM at all, you can disable all the code that saves +// data in the EEPROM. You might want to do that if you are in a Arduino UNO +// and want to use the EX-RAIL automation. Otherwise you do not have enough RAM +// to do that. Of course, then none of the EEPROM related commands work. +// +#define DISABLE_EEPROM + + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE TURNOUTS/ACCESSORIES FOLLOW NORM RCN-213 +// +// According to norm RCN-213 a DCC packet with a 1 is closed/straight +// and one with a 0 is thrown/diverging. In DCC++ Classic, and in previous +// versions of DCC++EX, a turnout throw command was implemented in the packet as +// '1' and a close command as '0'. The #define below makes the states +// match with the norm. But we don't want to cause havoc on existent layouts, +// so we define this only for new installations. If you don't want this, +// don't add it to your config.h. +//#define DCC_TURNOUTS_RCN_213 + +// The following #define likewise inverts the behaviour of the command +// for triggering DCC Accessory Decoders, so that generates a +// DCC packet with D=1 (close turnout) and generates D=0 +// (throw turnout). +//#define DCC_ACCESSORY_RCN_213 + +///////////////////////////////////////////////////////////////////////////////////// diff --git a/myHal.cpp.txt b/myHal.cpp.txt new file mode 100644 index 0000000..b5b612e --- /dev/null +++ b/myHal.cpp.txt @@ -0,0 +1,465 @@ +#include "defines.h" +#include "IODevice.h" + +#ifndef IO_NO_HAL + +#include "IO_VL53L0X.h" +#include "IO_HCSR04.h" +#include "Sensors.h" +#include "Turnouts.h" +#include "IO_DFPlayer.h" +//#include "IO_Wire.h" +#include "IO_AnalogueInputs.h" +#if __has_include("IO_Servo.h") +#include "IO_Servo.h" +#include "IO_PCA9685pwm.h" +#endif + +#include "IO_HALDisplay.h" +#include "LiquidCrystal_I2C.h" + +#if __has_include("IO_CMRI.h") +#include "IO_CMRI.h" +#endif + +//#include "IO_ExampleSerial.h" + +//#include "IO_EXFastclock.h" +//#include "IO_EXTurntable.h" + +#if __has_include("IO_ExternalEEPROM.h") +#include "IO_ExternalEEPROM.h" +#endif + +#if __has_include("IO_Network.h") +#include "IO_Network.h" +#include "Net_RF24.h" +#include "Net_ENC28J60.h" +#include "Net_Ethernet.h" +#define NETWORK_PRESENT +#endif + +#include "IO_TouchKeypad.h" + +#define WIRE_TEST 0 +#define TESTHARNESS 1 +#define I2C_STRESS_TEST 0 +#define I2C_SETCLOCK 0 + +#include "DCC.h" + + +#if 0 // Long Strings +#define s10 "0123456789" +#define s100 s10 s10 s10 s10 s10 s10 s10 s10 s10 s10 +#define s1k s100 s100 s100 s100 s100 s100 s100 s100 s100 s100 +#define s10k s1k s1k s1k s1k s1k s1k s1k s1k s1k s1k +#define s32k s10k s10k s10k s1k s1k +volatile const char PROGMEM ss1[] = s32k; +#endif + + +#if TESTHARNESS + +// Function to be invoked by test harness +void myTest() { + // DIAG(F("VL53L0X #1 Test: dist=%d signal=%d ambient=%d value=%d"), + // IODevice::readAnalogue(5000), + // IODevice::readAnalogue(5001), + // IODevice::readAnalogue(5002), + // IODevice::read(5000)); + // DIAG(F("VL53L0X #2 Test: dist=%d signal=%d ambient=%d value=%d"), + // IODevice::readAnalogue(5003), + // IODevice::readAnalogue(5004), + // IODevice::readAnalogue(5005), + // IODevice::read(5003)); + // DIAG(F("HCSR04 Test: dist=%d value=%d"), + // IODevice::readAnalogue(2000), + // IODevice::read(2000)); + // DIAG(F("ADS111x Test: %d %d %d %d %d"), + // IODevice::readAnalogue(4500), + // IODevice::readAnalogue(4501), + // IODevice::readAnalogue(4502), + // IODevice::readAnalogue(4503), + // IODevice::readAnalogue(A5) + // ); + // DIAG(F("RF24 Test: 4000:%d 4002:%d"), + // IODevice::read(4000), + // IODevice::read(4002) + // ); + DIAG(F("EXPANDER: 2212:%d 2213:%d 2214:%d"), + IODevice::readAnalogue(2212), + IODevice::readAnalogue(2213), + IODevice::readAnalogue(2214)); +} +#endif + +#if I2C_STRESS_TEST +static bool initialised = false; +static uint8_t lastStatus = 0; +static const int nRBs = 3; // request blocks concurrently +static const int I2cTestPeriod = 1; // milliseconds +static I2CAddress testDevice = {SubBus_6, 0x27}; +static I2CRB rb[nRBs]; +static uint8_t readBuffer[nRBs*32]; // nRB x 32-byte input buffer +static uint8_t writeBuffer[nRBs]; // nRB x 1-byte output buffer +static unsigned long count = 0; +static unsigned long errors = 0; +static unsigned long lastOutput = millis(); + +void I2CTest() { + if (!initialised) { + // I2C Loading for stress test. + // Write value then read back 32 times + for (int i=0; i 60000) { // 1 minute + DIAG(F("I2CTest: Count=%l Errors=%l"), count, errors); + count = errors = 0; + lastOutput = millis(); + } +} +#endif + +void updateLocoScreen() { + for (int i=0; i<8; i++) { + if (DCC::speedTable[i].loco > 0) { + int speed = DCC::speedTable[i].speedCode; + char direction = (speed & 0x80) ? 'R' : 'F'; + speed = speed & 0x7f; + if (speed > 0) speed = speed - 1; + SCREEN(3, i, F("Loco:%4d %3d %c"), DCC::speedTable[i].loco, + speed, direction); + } + } +} + +void updateTime() { + uint8_t buffer[20]; + I2CAddress rtc = {SubBus_1, 0x68}; // Real-time clock I2C address + buffer[0] = 0; + + // Set time - only needs to be done once if battery is ok. + static bool timeSet = false; + if (!timeSet) { + // I2CManager.read(rtc, buffer+1, sizeof(buffer)-1); + // uint8_t year = 23; // 2023 + // uint8_t day = 2; // tuesday + // uint8_t date = 21; // 21st + // uint8_t month = 2; // feb + // uint8_t hours = 23; // xx: + // uint8_t minutes = 25; // :xx + // buffer[1] = 0; // seconds + // buffer[2] = ((minutes / 10) << 4) | (minutes % 10); + // buffer[3] = ((hours / 10) << 4) | (hours % 10); + // buffer[4] = day; + // buffer[5] = ((date/10) << 4) + date%10; // 24th + // buffer[6] = ((month/10) << 4) + month%10; // feb + // buffer[7] = ((year/10) << 4) + year%10; // xx23 + // for (uint8_t i=8; i> 4; + uint8_t seconds1 = buffer[1] & 0xf; + uint8_t minutes10 = buffer[2] >> 4; + uint8_t minutes1 = buffer[2] & 0xf; + uint8_t hours10 = buffer[3] >> 4; + uint8_t hours1 = buffer[3] & 0xf; + SCREEN(10, 0, F("Departures %d%d:%d%d:%d%d"), + hours10, hours1, minutes10, minutes1, seconds10, seconds1); + } +} + +void showCharacterSet() { + if (millis() < 3000) return; + const uint8_t lineLen = 20; + char buffer[lineLen+1]; + static uint8_t nextChar = 0x20; + for (uint8_t row=0; row<8; row+=1) { + for (uint8_t col=0; col::create(10, {SubBus_5, 0x3c}, 132, 64); // SH1106 + // UserAddin::create(updateLocoScreen, 1000); + // UserAddin::create(showCharacterSet, 5000); + // UserAddin::create(updateTime, 1000); + + HALDisplay::create(10, {SubBus_4, 0x3c}, 128, 32); + HALDisplay::create(10, {SubBus_7, 0x3c}, 128, 32); + + //HALDisplay::create(10, {SubBus_4, 0x27}, 20, 4); + + // Draw double boxes with X O O X inside. + // SCREEN(3, 2, F("\xc9\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xbb")); + // SCREEN(3, 3, F("\xba X \xba O \xba O \xba O \xba X \xba")); + // SCREEN(3, 4, F("\xcc\xcd\xcd\xcd\xce\xcd\xcd\xcd\xce\xcd\xcd\xcd\xce\xcd\xcd\xcd\xce\xcd\xcd\xcd\xb9")); + // SCREEN(3, 5, F("\xba X \xba O \xba O \xba O \xba X \xba")); + // SCREEN(3, 6, F("\xc8\xcd\xcd\xcd\xca\xcd\xcd\xcd\xca\xcd\xcd\xcd\xca\xcd\xcd\xcd\xca\xcd\xcd\xcd\xbc")); + + // Draw single boxes with X O O X inside. + // SCREEN(3, 0, F("Summary Data:")); + // SCREEN(3, 1, F("\xda\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xbf")); + // SCREEN(3, 2, F("\xb3 X \xb3 O \xb3 O \xb3 O \xb3 X \xb3")); + // SCREEN(3, 3, F("\xc3\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xb4")); + // SCREEN(3, 4, F("\xb3 X \xb3 O \xb3 O \xb3 O \xb3 X \xb3")); + // SCREEN(3, 5, F("\xc3\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xb4")); + // SCREEN(3, 6, F("\xb3 X \xb3 O \xb3 O \xb3 O \xb3 X \xb3")); + // SCREEN(3, 7, F("\xc0\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xd9")); + + // Blocks of different greyness + // SCREEN(3, 0, F("\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xdb\xdb\xdb\xdb")); + // SCREEN(3, 1, F("\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xdb\xdb\xdb\xdb")); + // SCREEN(3, 2, F("\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xdb\xdb\xdb\xdb")); + + // DCCEX logo + // SCREEN(3, 1, F("\xb0\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\x20\xb0\xb0\xb0\x20\xb0\x20\xb0")); + // SCREEN(3, 2, F("\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\xb0")); + // SCREEN(3, 3, F("\xb0\x20\xb0\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\xb0\xb0\x20\x20\x20\xb0\x20")); + // SCREEN(3, 4, F("\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\xb0")); + // SCREEN(3, 5, F("\xb0\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\x20\xb0\xb0\xb0\x20\xb0\x20\xb0")); + // SCREEN(3, 7, F("\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1")); + +#if 0 + // List versions of devices that respond to the version request + for (uint8_t address = 8; address<0x78; address++) { + uint8_t buffer[3]; + uint8_t status = I2CManager.read(0x7c, buffer, sizeof(buffer), 1, address); + if (status == I2C_STATUS_OK) { + uint16_t manufacturer = ((uint16_t)buffer[0] << 4 ) | (buffer[1] >> 4); + uint16_t deviceID = ((uint16_t)(buffer[1] & 0x0f) << 5) | (buffer[2] >> 3); + uint16_t dieRevision = buffer[2] & 0x1f; + DIAG(F("Addr %s version: %x %x %x"), address.toString(), manufacturer, deviceID, dieRevision); + } + } +#endif + +#if I2C_STRESS_TEST + UserAddin::create(I2CTest, I2cTestPeriod); +#endif + +#if WIRE_TEST + // Test of Wire-I2CManager interface + Wire.begin(); + Wire.setClock(400000); + Wire.beginTransmission(0x23); + Wire.print("Hello"); + uint8_t status = Wire.endTransmission(); + if (status==0) DIAG(F("Wire: device Found on 0x23")); + + Wire.beginTransmission(0x23); + Wire.write(0xde); + Wire.endTransmission(false); // don't send stop + Wire.requestFrom(0x23, 1); + if (Wire.available()) { + DIAG(F("Wire: value=x%x"), Wire.read()); + } + uint8_t st = I2CManager.write(0x33, 0, 0); + DIAG(F("I2CManager 0x33 st=%d \"%S\""), st, + I2CManager.getErrorMessage(st)); +#endif + +#if I2C_SETCLOCK + // Test I2C clock changes + // Set up two I2C request blocks + I2CRB rb1, rb2; + uint8_t readBuff[32]; + rb1.setRequestParams(0x23, readBuff, sizeof(readBuff), readBuff, sizeof(readBuff)); + rb2.setRequestParams(0x23, readBuff, sizeof(readBuff), readBuff, sizeof(readBuff)); + // First set clock to 400kHz and then issue requests + I2CManager.forceClock(400000); + I2CManager.queueRequest(&rb1); + I2CManager.queueRequest(&rb2); + // Wait a little to allow the first transaction to start + delayMicroseconds(2); + // ... then request a clock speed change + I2CManager.forceClock(100000); + DIAG(F("I2CClock: rb1 status=%d"), rb1.wait()); + DIAG(F("I2CClock: rb2 status=%d"), rb2.wait()); + // Reset clock speed + I2CManager.forceClock(400000); +#endif + + EXIOExpander::create(2200, 18, {SubBus_0, 0x65}); + //UserAddin::create(myTest, 1000); + // ServoTurnout::create(2200, 2200, 400, 200, 0); + // ServoTurnout::create(2200, 2200, 400, 200, 0); + + TouchKeypad::create(2300, 16, 25, 24); + + // GPIO + PCF8574::create(800, 8, {SubBus_1, 0x23}); + //PCF8574::create(808, 8, {SubBus_2, 0x27}); + PCF8574::create(65000, 8, 0x27); + + MCP23017::create(164,16,{SubBus_3, 0x20}); + //MCP23017::create(180,16,{SubBus_0, 0x27}); + Sensor::create(170, 170, 1); // Hall effect, enable pullup. + Sensor::create(171, 171, 1); + + // PWM (LEDs and Servos) + // For servos, use default 50Hz pulses. + PCA9685::create(100, 16, {SubBus_1, 0x41}); + // For LEDs, use 1kHz pulses. + PCA9685::create(116, 16, {SubBus_1, 0x40}, 1000); + + // 4-pin Analogue Input Module + //ADS111x::create(4500, 4, 0x48); + + // Laser Time-Of-Flight Sensors + VL53L0X::create(5000, 3, {SubBus_0, 0x60}, 300, 310, 46); + //VL53L0X::create(5003, 3, {SubBus_6, 0x61}, 300, 310, 47); + Sensor::create(5000, 5000, 0); + Sensor::create(5003, 5003, 0); + // Monitor reset digital on first TOF + //Sensor::create(46,46,0); + + // // External 24C256 EEPROM (256kBytes) on I2C address 0x50. + // ExternalEEPROM::create({SubBus_0, 0x50}, 256); + + // Play up to 10 sounds on pins 10000-10009. Player is connected to Serial1 or Serial2. + #if defined(HAVE_HWSERIAL1) && !defined(ARDUINO_ARCH_STM32) + DFPlayer::create(10000, 14, Serial1); + #elif defined(ARDUINO_ARCH_STM32) + DFPlayer::create(10000, 10, Serial3); // Pins PC11 (RX) and PC10 (TX) + #endif + + // Ultrasound echo device + HCSR04::create(2000, 32, 33, 80, 85 /*, HCSR04::LOOP */); + Sensor::create(2000, 2000, 0); + +#if __has_include("IO_CMRI.h") + CMRIbus::create(0, Serial2, 115200, 50, 40); // 50ms cycle, pin 40 for DE/!RE pins + CMRInode::create(25000, 72, 0, 0, 'M'); // SMINI address 0 + for (int pin=0; pin<24; pin++) { + Sensor::create(25000+pin, 25000+pin, 0); + } +#endif + + //CMRInode::create(25072, 72, 0, 13, 'M'); // SMINI address 13 + //CMRInode::create(25144, 288, 0, 14, 'C', 144, 144); // CPNODE address 14 + +#ifdef NETWORK_PRESENT + // Define remote pins to be used. The range of remote pins is like a common data area shared + // between all nodes. + // For outputs, a write to a remote VPIN causes a message to be sent to another node, which then performs + // the write operation on the device VPIN that is local to that node. + // For inputs, the state of remote input VPIN is read on the node where it is connected, and then + // sent to other nodes in the system where the state is saved and processed. Updates are sent on change, and + // also periodically if no changes. + // + // Each definition is a triple of remote node, remote pin, indexed by relative pin. Up to 224 rpins can + // be configured (per node). This is to fit into a 32-byte packet. + REMOTEPINS rpins[] = { + {30,164,RPIN_IN} , //4000 Node 30, first MCP23017 pin, input + {30,165,RPIN_IN}, //4001 Node 30, second MCP23017 pin, input + {30,166,RPIN_OUT}, //4002 Node 30, third MCP23017 pin, output + {30,166,RPIN_OUT}, //4003 Node 30, fourth MCP23017 pin, output + {30,100,RPIN_INOUT}, //4004 Node 30, first PCA9685 servo pin + {30,101,RPIN_INOUT}, //4005 Node 30, second PCA9685 servo pin + {30,102,RPIN_INOUT}, //4006 Node 30, third PCA9685 servo pin + {30,103,RPIN_INOUT}, //4007 Node 30, fourth PCA9685 servo pin + {30,24,RPIN_IN}, //4008 Node 30, Arduino pin D24 + {30,25,RPIN_IN}, //4009 Node 30, Arduino pin D25 + {30,26,RPIN_IN}, //4010 Node 30, Arduino pin D26 + {30,27,RPIN_IN}, //4011 Node 30, Arduino pin D27 + {30,1000,RPIN_OUT}, //4012 Node 30, DFPlayer playing flag (when read) / Song selector (when written) + {30,5000,RPIN_IN}, //4013 Node 30, VL53L0X detect pin + {30,VPIN_NONE,0}, //4014 Node 30, spare + {30,VPIN_NONE,0}, //4015 Node 30, spare + + {31,164,RPIN_IN} , //4016 Node 31, first MCP23017 pin, input + {31,165,RPIN_IN}, //4017 Node 31, second MCP23017 pin, input + {31,166,RPIN_OUT}, //4018 Node 31, third MCP23017 pin, output + {31,166,RPIN_OUT}, //4019 Node 31, fourth MCP23017 pin, output + {31,100,RPIN_INOUT}, //4020 Node 31, first PCA9685 servo pin + {31,101,RPIN_INOUT}, //4021 Node 31, second PCA9685 servo pin + {31,102,RPIN_INOUT}, //4022 Node 31, third PCA9685 servo pin + {31,103,RPIN_INOUT}, //4023 Node 31, fourth PCA9685 servo pin + {31,24,RPIN_IN}, //4024 Node 31, Arduino pin D24 + {31,25,RPIN_IN}, //4025 Node 31, Arduino pin D25 + {31,26,RPIN_IN}, //4026 Node 31, Arduino pin D26 + {31,27,RPIN_IN}, //4027 Node 31, Arduino pin D27 + {31,3,RPIN_IN}, //4028 Node 31, Arduino pin D3 + {31,VPIN_NONE,0}, //4029 Node 31, spare + {31,VPIN_NONE,0}, //4030 Node 31, spare + {31,VPIN_NONE,0} //4031 Node 31, spare + }; + // FirstVPIN, nPins, thisNode, pinDefs, CEPin, CSNPin + // Net_RF24 *rf24Driver = new Net_RF24(48, 49); + // Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, rf24Driver); + #if NODE==30 + //Net_ENC28J60 *encDriver = new Net_ENC28J60(49); + //Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, encDriver); + #elif NODE==31 + Net_ENC28J60 *encDriver = new Net_ENC28J60(53); + Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, encDriver); + #else + Net_Ethernet *etherDriver = new Net_Ethernet(); + Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, etherDriver); + #endif + for (int i=0; i<=32; i++) + Sensor::create(4000+i, 4000+i, 0); +#endif + +#ifdef ARDUINO_ARCH_STM32 +//PCF8574::create(1900, 8, 0x27); +Sensor::create(1900,100,1); +Sensor::create(1901,101,1); +#endif + +} +#endif // IO_NO_HAL diff --git a/version.h b/version.h index f9484a1..47d82db 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.36" +#define VERSION "4.2.37" +// 4.2.37 - Add new FLAGS HAL device for communications to/from EX-RAIL; +// - Fix diag display of high VPINs within IODevice class. // 4.2.36 - do not broadcast a turnout state that has not changed // 4.2.35 - add direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs From 60ea7f081a48e830500a1c35e5a9ed7986419ad2 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Mon, 27 Mar 2023 13:03:19 +0100 Subject: [PATCH 49/97] Correct display of high VPIN numbers in diagnostic output. No functional change. VPINs are unsigned integers in the range 0-65535 (although the highest values are special, 65535=VPIN_NONE). Values above 32767 were erroneously being displayed as negative. This has been fixed, which is a pre-requisite for allowing VPINs above 32767 to be used. --- EXRAIL2.cpp | 4 ++-- IODevice.cpp | 2 +- IO_AnalogueInputs.h | 2 +- IO_DCCAccessory.cpp | 2 +- IO_DFPlayer.h | 4 ++-- IO_EXTurntable.h | 4 ++-- IO_ExampleSerial.h | 6 +++--- IO_GPIOBase.h | 2 +- IO_HCSR04.h | 2 +- IO_PCA9685.cpp | 8 ++++---- IO_PCA9685pwm.h | 4 ++-- IO_RotaryEncoder.h | 2 +- IO_Servo.h | 8 ++++---- IO_TouchKeypad.h | 2 +- IO_VL53L0X.h | 2 +- IO_duinoNodes.h | 2 +- 16 files changed, 28 insertions(+), 28 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index bb5989a..52d10e8 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -198,7 +198,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_IFNOT: { int16_t pin = (int16_t)operand; if (pin<0) pin = -pin; - DIAG(F("EXRAIL input vpin %d"),pin); + DIAG(F("EXRAIL input VPIN %u"),pin); IODevice::configureInput((VPIN)pin,true); break; } @@ -208,7 +208,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_IFGTE: case OPCODE_IFLT: case OPCODE_DRIVE: { - DIAG(F("EXRAIL analog input vpin %d"),(VPIN)operand); + DIAG(F("EXRAIL analog input VPIN %u"),(VPIN)operand); IODevice::configureAnalogIn((VPIN)operand); break; } diff --git a/IODevice.cpp b/IODevice.cpp index 03ccf85..af6efc8 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -329,7 +329,7 @@ bool IODevice::checkNoOverlap(VPIN firstPin, uint8_t nPins, I2CAddress i2cAddres VPIN lastDevPin=firstDevPin+dev->_nPins-1; bool noOverlap= firstPin>lastDevPin || lastPin> 8; uint8_t stepsLSB = value & 0xFF; #ifdef DIAG_IO - DIAG(F("EX-Turntable WriteAnalogue Vpin:%d Value:%d Activity:%d Duration:%d"), + DIAG(F("EX-Turntable WriteAnalogue VPIN:%u Value:%d Activity:%d Duration:%d"), vpin, value, activity, duration); DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"), _I2CAddress.toString(), stepsMSB, stepsLSB, activity); @@ -114,7 +114,7 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ // Display Turnetable-EX device driver info. void EXTurntable::_display() { - DIAG(F("EX-Turntable I2C:%s Configured on Vpins:%d-%d %S"), _I2CAddress.toString(), (int)_firstVpin, + DIAG(F("EX-Turntable I2C:%s Configured on Vpins:%u-%u %S"), _I2CAddress.toString(), (int)_firstVpin, (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_ExampleSerial.h b/IO_ExampleSerial.h index da421c5..5c70eb4 100644 --- a/IO_ExampleSerial.h +++ b/IO_ExampleSerial.h @@ -84,7 +84,7 @@ protected: void _write(VPIN vpin, int value) { int pin = vpin -_firstVpin; #ifdef DIAG_IO - DIAG(F("IO_ExampleSerial::_write Pin:%d Value:%d"), (int)vpin, value); + DIAG(F("IO_ExampleSerial::_write VPIN:%u Value:%d"), (int)vpin, value); #endif // Send a command string over the serial line _serial->print('#'); @@ -153,10 +153,10 @@ protected: // Display information about the device, and perhaps its current condition (e.g. active, disabled etc). // Here we display the current values held for the pins. void _display() { - DIAG(F("IO_ExampleSerial Configured on VPins:%d-%d"), (int)_firstVpin, + DIAG(F("IO_ExampleSerial Configured on Vpins:%u-%u"), (int)_firstVpin, (int)_firstVpin+_nPins-1); for (int i=0; i<_nPins; i++) - DIAG(F(" VPin %2d: %d"), _firstVpin+i, _pinValues[i]); + DIAG(F(" VPin %2u: %d"), _firstVpin+i, _pinValues[i]); } diff --git a/IO_GPIOBase.h b/IO_GPIOBase.h index 66b9ff6..94265c3 100644 --- a/IO_GPIOBase.h +++ b/IO_GPIOBase.h @@ -196,7 +196,7 @@ void GPIOBase::_loop(unsigned long currentMicros) { template void GPIOBase::_display() { - DIAG(F("%S I2C:%s Configured on Vpins:%d-%d %S"), _deviceName, _I2CAddress.toString(), + DIAG(F("%S I2C:%s Configured on Vpins:%u-%u %S"), _deviceName, _I2CAddress.toString(), _firstVpin, _firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_HCSR04.h b/IO_HCSR04.h index 26f0ecd..be1d303 100644 --- a/IO_HCSR04.h +++ b/IO_HCSR04.h @@ -234,7 +234,7 @@ protected: } void _display() override { - DIAG(F("HCSR04 Configured on Vpin:%d TrigPin:%d EchoPin:%d On:%dcm Off:%dcm"), + DIAG(F("HCSR04 Configured on VPIN:%u TrigPin:%d EchoPin:%d On:%dcm Off:%dcm"), _firstVpin, _trigPin, _echoPin, _onThreshold, _offThreshold); } diff --git a/IO_PCA9685.cpp b/IO_PCA9685.cpp index 71f3661..3c8b37a 100644 --- a/IO_PCA9685.cpp +++ b/IO_PCA9685.cpp @@ -46,7 +46,7 @@ bool PCA9685::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, i if (configType != CONFIGURE_SERVO) return false; if (paramCount != 5) return false; #ifdef DIAG_IO - DIAG(F("PCA9685 Configure VPIN:%d Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), + DIAG(F("PCA9685 Configure VPIN:%u Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), vpin, params[0], params[1], params[2], params[3], params[4]); #endif @@ -118,7 +118,7 @@ void PCA9685::_begin() { // For this function, the configured profile is used. void PCA9685::_write(VPIN vpin, int value) { #ifdef DIAG_IO - DIAG(F("PCA9685 Write Vpin:%d Value:%d"), vpin, value); + DIAG(F("PCA9685 Write VPIN:%u Value:%d"), vpin, value); #endif int pin = vpin - _firstVpin; if (value) value = 1; @@ -145,7 +145,7 @@ void PCA9685::_write(VPIN vpin, int value) { // void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { #ifdef DIAG_IO - DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"), + DIAG(F("PCA9685 WriteAnalogue VPIN:%u Value:%d Profile:%d Duration:%d %S"), vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif if (_deviceState == DEVSTATE_FAILED) return; @@ -262,7 +262,7 @@ void PCA9685::writeDevice(uint8_t pin, int value) { // Display details of this device. void PCA9685::_display() { - DIAG(F("PCA9685 I2C:%s Configured on Vpins:%d-%d %S"), _I2CAddress.toString(), (int)_firstVpin, + DIAG(F("PCA9685 I2C:%s Configured on Vpins:%u-%u %S"), _I2CAddress.toString(), (int)_firstVpin, (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_PCA9685pwm.h b/IO_PCA9685pwm.h index 042516b..a12cb84 100644 --- a/IO_PCA9685pwm.h +++ b/IO_PCA9685pwm.h @@ -121,7 +121,7 @@ private: void _writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) override { (void)param1; (void)param2; // suppress compiler warning #ifdef DIAG_IO - DIAG(F("PCA9685pwm WriteAnalogue Vpin:%d Value:%d %S"), + DIAG(F("PCA9685pwm WriteAnalogue VPIN:%u Value:%d %S"), vpin, value, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif if (_deviceState == DEVSTATE_FAILED) return; @@ -134,7 +134,7 @@ private: // Display details of this device. void _display() override { - DIAG(F("PCA9685pwm I2C:%s Configured on Vpins:%d-%d %S"), _I2CAddress.toString(), (int)_firstVpin, + DIAG(F("PCA9685pwm I2C:%s Configured on Vpins:%u-%u %S"), _I2CAddress.toString(), (int)_firstVpin, (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index b271b48..09d0c4d 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -104,7 +104,7 @@ private: } void _display() override { - DIAG(F("Rotary Encoder I2C:%s v%d.%d.%d Configured on Vpin:%d-%d %S"), _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, + DIAG(F("Rotary Encoder I2C:%s v%d.%d.%d Configured on VPIN:%u-%d %S"), _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, (int)_firstVpin, _firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_Servo.h b/IO_Servo.h index 8f95463..216b886 100644 --- a/IO_Servo.h +++ b/IO_Servo.h @@ -98,7 +98,7 @@ private: if (configType != CONFIGURE_SERVO) return false; if (paramCount != 5) return false; #ifdef DIAG_IO - DIAG(F("Servo: Configure VPIN:%d Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), + DIAG(F("Servo: Configure VPIN:%u Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), vpin, params[0], params[1], params[2], params[3], params[4]); #endif @@ -165,7 +165,7 @@ private: void _write(VPIN vpin, int value) override { if (_deviceState == DEVSTATE_FAILED) return; #ifdef DIAG_IO - DIAG(F("Servo Write Vpin:%d Value:%d"), vpin, value); + DIAG(F("Servo Write VPIN:%u Value:%d"), vpin, value); #endif int pin = vpin - _firstVpin; if (value) value = 1; @@ -193,7 +193,7 @@ private: // void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { #ifdef DIAG_IO - DIAG(F("Servo: WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"), + DIAG(F("Servo: WriteAnalogue VPIN:%u Value:%d Profile:%d Duration:%d %S"), vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif if (_deviceState == DEVSTATE_FAILED) return; @@ -288,7 +288,7 @@ private: // Display details of this device. void _display() override { - DIAG(F("Servo Configured on Vpins:%d-%d, slave pins:%d-%d %S"), + DIAG(F("Servo Configured on Vpins:%u-%u, slave pins:%d-%d %S"), (int)_firstVpin, (int)_firstVpin+_nPins-1, (int)_firstSlavePin, (int)_firstSlavePin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); diff --git a/IO_TouchKeypad.h b/IO_TouchKeypad.h index 8984bce..48e3b25 100644 --- a/IO_TouchKeypad.h +++ b/IO_TouchKeypad.h @@ -124,7 +124,7 @@ protected: // Display information about the device, and perhaps its current condition (e.g. active, disabled etc). void _display() { - DIAG(F("TouchKeypad Configured on VPins:%d-%d SCL=%d SDO=%d"), (int)_firstVpin, + DIAG(F("TouchKeypad Configured on Vpins:%u-%u SCL=%d SDO=%d"), (int)_firstVpin, (int)_firstVpin+_nPins-1, _clockPin, _dataPin); } diff --git a/IO_VL53L0X.h b/IO_VL53L0X.h index 7c80518..08fe44e 100644 --- a/IO_VL53L0X.h +++ b/IO_VL53L0X.h @@ -319,7 +319,7 @@ protected: } void _display() override { - DIAG(F("VL53L0X I2C:%s Configured on Vpins:%d-%d On:%dmm Off:%dmm %S"), + DIAG(F("VL53L0X I2C:%s Configured on Vpins:%u-%u On:%dmm Off:%dmm %S"), _I2CAddress.toString(), _firstVpin, _firstVpin+_nPins-1, _onThreshold, _offThreshold, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_duinoNodes.h b/IO_duinoNodes.h index 73de1a1..60ef2ea 100644 --- a/IO_duinoNodes.h +++ b/IO_duinoNodes.h @@ -121,7 +121,7 @@ void _loopOutput() { } void _display() override { - DIAG(F("IO_duinoNodes %SPUT Configured on VPins:%d-%d shift=%d"), + DIAG(F("IO_duinoNodes %SPUT Configured on Vpins:%u-%u shift=%d"), _pinMap?F("IN"):F("OUT"), (int)_firstVpin, (int)_firstVpin+_nPins-1, _nShiftBytes*8); From 86c30206724fadf70854345d7e90d459988b8e02 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Mon, 27 Mar 2023 13:08:14 +0100 Subject: [PATCH 50/97] Correct display of high VPIN numbers in diagnostic output. No functional change. VPINs are unsigned integers in the range 0-65535 (although the highest values are special, 65535=VPIN_NONE). Values above 32767 were erroneously being displayed as negative. This has been fixed, which is a pre-requisite for allowing VPINs above 32767 to be used. --- DCCEXParser.cpp | 2 +- EXRAIL2.cpp | 4 ++-- IODevice.cpp | 2 +- IO_AnalogueInputs.h | 4 ++-- IO_DCCAccessory.cpp | 2 +- IO_DFPlayer.h | 4 ++-- IO_EXTurntable.h | 4 ++-- IO_ExampleSerial.h | 6 +++--- IO_GPIOBase.h | 2 +- IO_HCSR04.h | 2 +- IO_PCA9685.cpp | 8 ++++---- IO_PCA9685pwm.h | 4 ++-- IO_RotaryEncoder.h | 2 +- IO_Servo.h | 12 ++++++------ IO_TouchKeypad.h | 2 +- IO_VL53L0X.h | 2 +- IO_duinoNodes.h | 2 +- 17 files changed, 32 insertions(+), 32 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 4d97b20..102d271 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -967,7 +967,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) break; case HASH_KEYWORD_ANIN: // Display analogue input value - DIAG(F("VPIN=%d value=%d"), p[1], IODevice::readAnalogue(p[1])); + DIAG(F("VPIN=%u value=%d"), p[1], IODevice::readAnalogue(p[1])); break; #if !defined(IO_NO_HAL) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index bb5989a..52d10e8 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -198,7 +198,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_IFNOT: { int16_t pin = (int16_t)operand; if (pin<0) pin = -pin; - DIAG(F("EXRAIL input vpin %d"),pin); + DIAG(F("EXRAIL input VPIN %u"),pin); IODevice::configureInput((VPIN)pin,true); break; } @@ -208,7 +208,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_IFGTE: case OPCODE_IFLT: case OPCODE_DRIVE: { - DIAG(F("EXRAIL analog input vpin %d"),(VPIN)operand); + DIAG(F("EXRAIL analog input VPIN %u"),(VPIN)operand); IODevice::configureAnalogIn((VPIN)operand); break; } diff --git a/IODevice.cpp b/IODevice.cpp index 03ccf85..af6efc8 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -329,7 +329,7 @@ bool IODevice::checkNoOverlap(VPIN firstPin, uint8_t nPins, I2CAddress i2cAddres VPIN lastDevPin=firstDevPin+dev->_nPins-1; bool noOverlap= firstPin>lastDevPin || lastPin> 8; uint8_t stepsLSB = value & 0xFF; #ifdef DIAG_IO - DIAG(F("EX-Turntable WriteAnalogue Vpin:%d Value:%d Activity:%d Duration:%d"), + DIAG(F("EX-Turntable WriteAnalogue VPIN:%u Value:%d Activity:%d Duration:%d"), vpin, value, activity, duration); DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"), _I2CAddress.toString(), stepsMSB, stepsLSB, activity); @@ -114,7 +114,7 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ // Display Turnetable-EX device driver info. void EXTurntable::_display() { - DIAG(F("EX-Turntable I2C:%s Configured on Vpins:%d-%d %S"), _I2CAddress.toString(), (int)_firstVpin, + DIAG(F("EX-Turntable I2C:%s Configured on Vpins:%u-%u %S"), _I2CAddress.toString(), (int)_firstVpin, (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_ExampleSerial.h b/IO_ExampleSerial.h index da421c5..5c70eb4 100644 --- a/IO_ExampleSerial.h +++ b/IO_ExampleSerial.h @@ -84,7 +84,7 @@ protected: void _write(VPIN vpin, int value) { int pin = vpin -_firstVpin; #ifdef DIAG_IO - DIAG(F("IO_ExampleSerial::_write Pin:%d Value:%d"), (int)vpin, value); + DIAG(F("IO_ExampleSerial::_write VPIN:%u Value:%d"), (int)vpin, value); #endif // Send a command string over the serial line _serial->print('#'); @@ -153,10 +153,10 @@ protected: // Display information about the device, and perhaps its current condition (e.g. active, disabled etc). // Here we display the current values held for the pins. void _display() { - DIAG(F("IO_ExampleSerial Configured on VPins:%d-%d"), (int)_firstVpin, + DIAG(F("IO_ExampleSerial Configured on Vpins:%u-%u"), (int)_firstVpin, (int)_firstVpin+_nPins-1); for (int i=0; i<_nPins; i++) - DIAG(F(" VPin %2d: %d"), _firstVpin+i, _pinValues[i]); + DIAG(F(" VPin %2u: %d"), _firstVpin+i, _pinValues[i]); } diff --git a/IO_GPIOBase.h b/IO_GPIOBase.h index 66b9ff6..94265c3 100644 --- a/IO_GPIOBase.h +++ b/IO_GPIOBase.h @@ -196,7 +196,7 @@ void GPIOBase::_loop(unsigned long currentMicros) { template void GPIOBase::_display() { - DIAG(F("%S I2C:%s Configured on Vpins:%d-%d %S"), _deviceName, _I2CAddress.toString(), + DIAG(F("%S I2C:%s Configured on Vpins:%u-%u %S"), _deviceName, _I2CAddress.toString(), _firstVpin, _firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_HCSR04.h b/IO_HCSR04.h index 26f0ecd..be1d303 100644 --- a/IO_HCSR04.h +++ b/IO_HCSR04.h @@ -234,7 +234,7 @@ protected: } void _display() override { - DIAG(F("HCSR04 Configured on Vpin:%d TrigPin:%d EchoPin:%d On:%dcm Off:%dcm"), + DIAG(F("HCSR04 Configured on VPIN:%u TrigPin:%d EchoPin:%d On:%dcm Off:%dcm"), _firstVpin, _trigPin, _echoPin, _onThreshold, _offThreshold); } diff --git a/IO_PCA9685.cpp b/IO_PCA9685.cpp index 71f3661..3c8b37a 100644 --- a/IO_PCA9685.cpp +++ b/IO_PCA9685.cpp @@ -46,7 +46,7 @@ bool PCA9685::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, i if (configType != CONFIGURE_SERVO) return false; if (paramCount != 5) return false; #ifdef DIAG_IO - DIAG(F("PCA9685 Configure VPIN:%d Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), + DIAG(F("PCA9685 Configure VPIN:%u Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), vpin, params[0], params[1], params[2], params[3], params[4]); #endif @@ -118,7 +118,7 @@ void PCA9685::_begin() { // For this function, the configured profile is used. void PCA9685::_write(VPIN vpin, int value) { #ifdef DIAG_IO - DIAG(F("PCA9685 Write Vpin:%d Value:%d"), vpin, value); + DIAG(F("PCA9685 Write VPIN:%u Value:%d"), vpin, value); #endif int pin = vpin - _firstVpin; if (value) value = 1; @@ -145,7 +145,7 @@ void PCA9685::_write(VPIN vpin, int value) { // void PCA9685::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { #ifdef DIAG_IO - DIAG(F("PCA9685 WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"), + DIAG(F("PCA9685 WriteAnalogue VPIN:%u Value:%d Profile:%d Duration:%d %S"), vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif if (_deviceState == DEVSTATE_FAILED) return; @@ -262,7 +262,7 @@ void PCA9685::writeDevice(uint8_t pin, int value) { // Display details of this device. void PCA9685::_display() { - DIAG(F("PCA9685 I2C:%s Configured on Vpins:%d-%d %S"), _I2CAddress.toString(), (int)_firstVpin, + DIAG(F("PCA9685 I2C:%s Configured on Vpins:%u-%u %S"), _I2CAddress.toString(), (int)_firstVpin, (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_PCA9685pwm.h b/IO_PCA9685pwm.h index 042516b..a12cb84 100644 --- a/IO_PCA9685pwm.h +++ b/IO_PCA9685pwm.h @@ -121,7 +121,7 @@ private: void _writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t param2) override { (void)param1; (void)param2; // suppress compiler warning #ifdef DIAG_IO - DIAG(F("PCA9685pwm WriteAnalogue Vpin:%d Value:%d %S"), + DIAG(F("PCA9685pwm WriteAnalogue VPIN:%u Value:%d %S"), vpin, value, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif if (_deviceState == DEVSTATE_FAILED) return; @@ -134,7 +134,7 @@ private: // Display details of this device. void _display() override { - DIAG(F("PCA9685pwm I2C:%s Configured on Vpins:%d-%d %S"), _I2CAddress.toString(), (int)_firstVpin, + DIAG(F("PCA9685pwm I2C:%s Configured on Vpins:%u-%u %S"), _I2CAddress.toString(), (int)_firstVpin, (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index b271b48..09d0c4d 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -104,7 +104,7 @@ private: } void _display() override { - DIAG(F("Rotary Encoder I2C:%s v%d.%d.%d Configured on Vpin:%d-%d %S"), _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, + DIAG(F("Rotary Encoder I2C:%s v%d.%d.%d Configured on VPIN:%u-%d %S"), _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, (int)_firstVpin, _firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_Servo.h b/IO_Servo.h index 8f95463..9e3211e 100644 --- a/IO_Servo.h +++ b/IO_Servo.h @@ -98,7 +98,7 @@ private: if (configType != CONFIGURE_SERVO) return false; if (paramCount != 5) return false; #ifdef DIAG_IO - DIAG(F("Servo: Configure VPIN:%d Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), + DIAG(F("Servo: Configure VPIN:%u Apos:%d Ipos:%d Profile:%d Duration:%d state:%d"), vpin, params[0], params[1], params[2], params[3], params[4]); #endif @@ -140,12 +140,12 @@ private: // Get reference to slave device. _slaveDevice = findDevice(_firstSlavePin); if (!_slaveDevice) { - DIAG(F("Servo: Slave device not found on pins %d-%d"), + DIAG(F("Servo: Slave device not found on Vpins %u-%u"), _firstSlavePin, _firstSlavePin+_nPins-1); _deviceState = DEVSTATE_FAILED; } if (_slaveDevice != findDevice(_firstSlavePin+_nPins-1)) { - DIAG(F("Servo: Slave device does not cover all pins %d-%d"), + DIAG(F("Servo: Slave device does not cover all Vpins %u-%u"), _firstSlavePin, _firstSlavePin+_nPins-1); _deviceState = DEVSTATE_FAILED; } @@ -165,7 +165,7 @@ private: void _write(VPIN vpin, int value) override { if (_deviceState == DEVSTATE_FAILED) return; #ifdef DIAG_IO - DIAG(F("Servo Write Vpin:%d Value:%d"), vpin, value); + DIAG(F("Servo Write VPIN:%u Value:%d"), vpin, value); #endif int pin = vpin - _firstVpin; if (value) value = 1; @@ -193,7 +193,7 @@ private: // void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { #ifdef DIAG_IO - DIAG(F("Servo: WriteAnalogue Vpin:%d Value:%d Profile:%d Duration:%d %S"), + DIAG(F("Servo: WriteAnalogue VPIN:%u Value:%d Profile:%d Duration:%d %S"), vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif if (_deviceState == DEVSTATE_FAILED) return; @@ -288,7 +288,7 @@ private: // Display details of this device. void _display() override { - DIAG(F("Servo Configured on Vpins:%d-%d, slave pins:%d-%d %S"), + DIAG(F("Servo Configured on Vpins:%u-%u, slave pins:%d-%d %S"), (int)_firstVpin, (int)_firstVpin+_nPins-1, (int)_firstSlavePin, (int)_firstSlavePin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); diff --git a/IO_TouchKeypad.h b/IO_TouchKeypad.h index 8984bce..48e3b25 100644 --- a/IO_TouchKeypad.h +++ b/IO_TouchKeypad.h @@ -124,7 +124,7 @@ protected: // Display information about the device, and perhaps its current condition (e.g. active, disabled etc). void _display() { - DIAG(F("TouchKeypad Configured on VPins:%d-%d SCL=%d SDO=%d"), (int)_firstVpin, + DIAG(F("TouchKeypad Configured on Vpins:%u-%u SCL=%d SDO=%d"), (int)_firstVpin, (int)_firstVpin+_nPins-1, _clockPin, _dataPin); } diff --git a/IO_VL53L0X.h b/IO_VL53L0X.h index 7c80518..08fe44e 100644 --- a/IO_VL53L0X.h +++ b/IO_VL53L0X.h @@ -319,7 +319,7 @@ protected: } void _display() override { - DIAG(F("VL53L0X I2C:%s Configured on Vpins:%d-%d On:%dmm Off:%dmm %S"), + DIAG(F("VL53L0X I2C:%s Configured on Vpins:%u-%u On:%dmm Off:%dmm %S"), _I2CAddress.toString(), _firstVpin, _firstVpin+_nPins-1, _onThreshold, _offThreshold, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); } diff --git a/IO_duinoNodes.h b/IO_duinoNodes.h index 73de1a1..60ef2ea 100644 --- a/IO_duinoNodes.h +++ b/IO_duinoNodes.h @@ -121,7 +121,7 @@ void _loopOutput() { } void _display() override { - DIAG(F("IO_duinoNodes %SPUT Configured on VPins:%d-%d shift=%d"), + DIAG(F("IO_duinoNodes %SPUT Configured on Vpins:%u-%u shift=%d"), _pinMap?F("IN"):F("OUT"), (int)_firstVpin, (int)_firstVpin+_nPins-1, _nShiftBytes*8); From f8b054cf6ab0dfa05451f4ee0e07c53d89a51942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Mon, 27 Mar 2023 16:44:47 +0200 Subject: [PATCH 51/97] [ESP32] Use GPIO 35/A2 and 34/A3 for current sensing (#325) * [ESP32] Use GPIO 35/A2 and 34/A3 for current sensing while used in combination with the standard Motor Shield * Update version.h changelog --- MotorDrivers.h | 6 +++--- version.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/MotorDrivers.h b/MotorDrivers.h index c066052..cc13f54 100644 --- a/MotorDrivers.h +++ b/MotorDrivers.h @@ -73,12 +73,12 @@ #elif defined(ARDUINO_ARCH_ESP32) // STANDARD shield on an ESPDUINO-32 (ESP32 in Uno form factor). The shield must be eiter the // 3.3V compatible R3 version or it has to be modified to not supply more than 3.3V to the -// analog inputs. Here we use analog inputs A4 and A5 as A0 and A1 are wired in a way so that +// analog inputs. Here we use analog inputs A2 and A3 as A0 and A1 are wired in a way so that // they are not useable at the same time as WiFi (what a bummer). The numbers below are the // actual GPIO numbers. In comments the numbers the pins have on an Uno. #define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \ - new MotorDriver(25/* 3*/, 19/*12*/, UNUSED_PIN, 13/*9*/, 36/*A4*/, 0.70, 1500, UNUSED_PIN), \ - new MotorDriver(23/*11*/, 18/*13*/, UNUSED_PIN, 12/*8*/, 39/*A5*/, 0.70, 1500, UNUSED_PIN) + new MotorDriver(25/* 3*/, 19/*12*/, UNUSED_PIN, 13/*9*/, 35/*A2*/, 0.70, 1500, UNUSED_PIN), \ + new MotorDriver(23/*11*/, 18/*13*/, UNUSED_PIN, 12/*8*/, 34/*A3*/, 0.70, 1500, UNUSED_PIN) #else // STANDARD shield on any Arduino Uno or Mega compatible with the original specification. diff --git a/version.h b/version.h index 47d82db..5cb5cea 100644 --- a/version.h +++ b/version.h @@ -8,12 +8,13 @@ // 4.2.37 - Add new FLAGS HAL device for communications to/from EX-RAIL; // - Fix diag display of high VPINs within IODevice class. // 4.2.36 - do not broadcast a turnout state that has not changed +// - Use A2/A3 for current sensing on ESP32 + Motor Shield // 4.2.35 - add direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs // 4.2.33 - Fix EX-IOExpander non-working analogue inputs // 4.2.32 - Fix LCD/Display bugfixes from 4.2.29 // 4.2.31 - Removes EXRAIL statup from top of file. (BREAKING CHANGE !!) -// Just add AUTOSTART to the top of your myAutomation.h to restore this function. +// Just add AUTOSTART to the top of your myAutomation.h to restore this function. // 4.2.30 - Fixes/enhancements to EX-IOExpander device driver. // 4.2.29 - Bugfix Scroll LCD without empty lines and consistent // 4.2.28 - Reinstate use of timer11 in STM32 - remove HA mode. From 89664eff9d947ab84be7900ae75d2db9ffac82e6 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 30 Mar 2023 06:54:18 +1000 Subject: [PATCH 52/97] Cleaned up warning --- IO_RotaryEncoder.h | 3 ++- version.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index 09d0c4d..00a8249 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -98,7 +98,8 @@ private: void _write(VPIN vpin, int value) override { if (vpin == _firstVpin + 1) { - byte _feedbackBuffer[2] = {RE_OP, value}; + if (value != 0) value = 0x01; + byte _feedbackBuffer[2] = {RE_OP, (byte)value}; I2CManager.write(_I2CAddress, _feedbackBuffer, 2); } } diff --git a/version.h b/version.h index 5cb5cea..c29f773 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.37" +#define VERSION "4.2.38" +// 4.2.38 - Clean up compiler warning when IO_RotaryEncoder.h included // 4.2.37 - Add new FLAGS HAL device for communications to/from EX-RAIL; // - Fix diag display of high VPINs within IODevice class. // 4.2.36 - do not broadcast a turnout state that has not changed From 6b535654f8027c4dffcd3e8de32fd66fdda27a0e Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Fri, 31 Mar 2023 16:40:40 +0100 Subject: [PATCH 53/97] DFplayer driver now polls device to detect failures and errors. Add cyclic (1-second) poll of DFplayer device to detect if it goes unresponsive. --- IO_DFPlayer.h | 157 ++++++++++++++++++++++++++++++++++++-------------- version.h | 3 +- 2 files changed, 115 insertions(+), 45 deletions(-) diff --git a/IO_DFPlayer.h b/IO_DFPlayer.h index f3ebaaa..c12ae0b 100644 --- a/IO_DFPlayer.h +++ b/IO_DFPlayer.h @@ -1,5 +1,5 @@ /* - * © 2022, Neil McKechnie. All rights reserved. + * © 2023, Neil McKechnie. All rights reserved. * * This file is part of DCC++EX API * @@ -33,10 +33,13 @@ * and Serialn is the name of the Serial port connected to the DFPlayer (e.g. Serial1). * * Example: - * In mySetup function within mySetup.cpp: + * In halSetup function within myHal.cpp: * DFPlayer::create(3500, 5, Serial1); + * or in myAutomation.h: + * HAL(DFPlayer, 3500, 5, Serial1) * - * Writing an analogue value 1-2999 to the first pin (3500) will play the numbered file from the SD card; + * Writing an analogue value 1-2999 to the first pin (3500) will play the numbered file from the + * SD card; e.g. a value of 1 will play the first file, 2 for the second file etc. * Writing an analogue value 0 to the first pin (3500) will stop the file playing; * Writing an analogue value 0-30 to the second pin (3501) will set the volume; * Writing a digital value of 1 to a pin will play the file corresponding to that pin, e.g. @@ -61,6 +64,10 @@ * card (as listed by the DIR command in Windows). This may not match the order of the files * as displayed by Windows File Manager, which sorts the file names. It is suggested that * files be copied into an empty SDcard in the desired order, one at a time. + * + * The driver now polls the device for its current status every second. Should the device + * fail to respond it will be marked off-line and its busy indicator cleared, to avoid + * lock-ups in automation scripts that are executing for a WAITFOR(). */ #ifndef IO_DFPlayer_h @@ -74,7 +81,10 @@ private: HardwareSerial *_serial; bool _playing = false; uint8_t _inputIndex = 0; - unsigned long _commandSendTime; // Allows timeout processing + unsigned long _commandSendTime; // Time (us) that last transmit took place. + unsigned long _timeoutTime; + uint8_t _recvCMD; // Last received command code byte + bool _awaitingResponse = false; uint8_t _requestedVolumeLevel = MAXVOLUME; uint8_t _currentVolume = MAXVOLUME; int _requestedSong = -1; // -1=none, 0=stop, >0=file number @@ -102,80 +112,135 @@ protected: // Send a query to the device to see if it responds sendPacket(0x42); - _commandSendTime = micros(); + _timeoutTime = micros() + 5000000UL; // 5 second timeout + _awaitingResponse = true; } void _loop(unsigned long currentMicros) override { - // Check for incoming data on _serial, and update busy flag accordingly. - // Expected message is in the form "7E FF 06 3D xx xx xx xx xx EF" - while (_serial->available()) { - int c = _serial->read(); - if (c == 0x7E && _inputIndex == 0) - _inputIndex = 1; - else if ((c==0xFF && _inputIndex==1) - || (c==0x3D && _inputIndex==3) - || (_inputIndex >=4 && _inputIndex <= 8)) - _inputIndex++; - else if (c==0x06 && _inputIndex==2) { - // Valid message prefix, so consider the device online - if (_deviceState==DEVSTATE_INITIALISING) { - _deviceState = DEVSTATE_NORMAL; - #ifdef DIAG_IO - _display(); - #endif - } - _inputIndex++; - } else if (c==0xEF && _inputIndex==9) { - // End of play - if (_playing) { - #ifdef DIAG_IO - DIAG(F("DFPlayer: Finished")); - #endif - _playing = false; - } - _inputIndex = 0; - } else - _inputIndex = 0; // Unrecognised character sequence, start again! - } - // Check if the initial prompt to device has timed out. Allow 5 seconds - if (_deviceState == DEVSTATE_INITIALISING && currentMicros - _commandSendTime > 5000000UL) { + // Read responses from device + processIncoming(); + + // Check if a command sent to device has timed out. Allow 0.5 second for response + if (_awaitingResponse && (int32_t)(currentMicros - _timeoutTime) > 0) { DIAG(F("DFPlayer device not responding on serial port")); _deviceState = DEVSTATE_FAILED; + _awaitingResponse = false; + _playing = false; } + // Send any commands that need to go. + processOutgoing(currentMicros); + + delayUntil(currentMicros + 10000); // Only enter every 10ms + } + + // Check for incoming data on _serial, and update busy flag and other state accordingly + void processIncoming() { + // Expected message is in the form "7E FF 06 3D xx xx xx xx xx EF" + bool ok = false; + while (_serial->available()) { + int c = _serial->read(); + switch (_inputIndex) { + case 0: + if (c == 0x7E) ok = true; + break; + case 1: + if (c == 0xFF) ok = true; + break; + case 2: + if (c== 0x06) ok = true; + break; + case 3: + _recvCMD = c; // CMD byte + ok = true; + break; + case 6: + switch (_recvCMD) { + case 0x42: + // Response to status query + _playing = (c != 0); + // Mark the device online and cancel timeout + if (_deviceState==DEVSTATE_INITIALISING) { + _deviceState = DEVSTATE_NORMAL; + #ifdef DIAG_IO + _display(); + #endif + } + _awaitingResponse = false; + break; + case 0x3d: + // End of play + if (_playing) { + #ifdef DIAG_IO + DIAG(F("DFPlayer: Finished")); + #endif + _playing = false; + } + break; + case 0x40: + // Error code + DIAG(F("DFPlayer: Error %d returned from device"), c); + _playing = false; + break; + } + ok = true; + break; + case 4: case 5: case 7: case 8: + ok = true; // Skip over these bytes in message. + break; + case 9: + if (c==0xef) { + // Message finished + } + break; + default: + break; + } + if (ok) + _inputIndex++; // character as expected, so increment index + else + _inputIndex = 0; // otherwise reset. + } + } + + // Send any commands that need to be sent + void processOutgoing(unsigned long currentMicros) { + // When two commands are sent in quick succession, the device will often fail to // execute one. Testing has indicated that a delay of 100ms or more is required // between successive commands to get reliable operation. // If 100ms has elapsed since the last thing sent, then check if there's some output to do. - if (currentMicros - _commandSendTime > 100000UL) { + if (((int32_t)currentMicros - _commandSendTime) > 100000) { if (_currentVolume > _requestedVolumeLevel) { // Change volume before changing song if volume is reducing. _currentVolume = _requestedVolumeLevel; sendPacket(0x06, _currentVolume); - _commandSendTime = currentMicros; } else if (_requestedSong > 0) { // Change song sendPacket(0x03, _requestedSong); _requestedSong = -1; - _commandSendTime = currentMicros; } else if (_requestedSong == 0) { sendPacket(0x0e); // Pause playing _requestedSong = -1; - _commandSendTime = currentMicros; } else if (_currentVolume < _requestedVolumeLevel) { // Change volume after changing song if volume is increasing. _currentVolume = _requestedVolumeLevel; sendPacket(0x06, _currentVolume); - _commandSendTime = currentMicros; + } else if ((int32_t)currentMicros - _commandSendTime > 1000000) { + // Poll device every second that other commands aren't being sent, + // to check if it's still connected and responding. + sendPacket(0x42); + _timeoutTime = currentMicros + 5000000UL; // Timeout if no response within 5 seconds + _awaitingResponse = true; } } - delayUntil(currentMicros + 10000); // Only enter every 10ms } // Write with value 1 starts playing a song. The relative pin number is the file number. // Write with value 0 stops playing. void _write(VPIN vpin, int value) override { + if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; if (value) { // Value 1, start playing @@ -200,6 +265,7 @@ protected: // WriteAnalogue on second pin sets the output volume. // void _writeAnalogue(VPIN vpin, int value, uint8_t volume=0, uint16_t=0) override { + if (_deviceState == DEVSTATE_FAILED) return; uint8_t pin = vpin - _firstVpin; #ifdef DIAG_IO @@ -228,6 +294,7 @@ protected: // A read on any pin indicates whether the player is still playing. int _read(VPIN) override { + if (_deviceState == DEVSTATE_FAILED) return false; return _playing; } @@ -264,6 +331,8 @@ private: // Output the command _serial->write(out, sizeof(out)); + + _commandSendTime = micros(); } uint16_t calcChecksum(uint8_t* packet) diff --git a/version.h b/version.h index c29f773..da95f93 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.38" +#define VERSION "4.2.39" +// 4.2.39 - DFplayer driver now polls device to detect failures and errors. // 4.2.38 - Clean up compiler warning when IO_RotaryEncoder.h included // 4.2.37 - Add new FLAGS HAL device for communications to/from EX-RAIL; // - Fix diag display of high VPINs within IODevice class. From 6fbaca79300677b355e15215f78e35cb5f724857 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Fri, 31 Mar 2023 16:50:18 +0100 Subject: [PATCH 54/97] Update IO_DFPlayer.h Ensure device goes off-line when not responding. --- IO_DFPlayer.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/IO_DFPlayer.h b/IO_DFPlayer.h index c12ae0b..4c2b2f1 100644 --- a/IO_DFPlayer.h +++ b/IO_DFPlayer.h @@ -231,8 +231,10 @@ protected: // Poll device every second that other commands aren't being sent, // to check if it's still connected and responding. sendPacket(0x42); - _timeoutTime = currentMicros + 5000000UL; // Timeout if no response within 5 seconds - _awaitingResponse = true; + if (!_awaitingResponse) { + _timeoutTime = currentMicros + 5000000UL; // Timeout if no response within 5 seconds + _awaitingResponse = true; + } } } } From 1b4faa92cd708a403d6f26890a1c94fc1bad0304 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Fri, 31 Mar 2023 17:58:30 +0100 Subject: [PATCH 55/97] Update IO_DFPlayer.h Reinstate STOP command in place of PAUSE, as PAUSE was being reported differently to STOP in the status response. --- IO_DFPlayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_DFPlayer.h b/IO_DFPlayer.h index 4c2b2f1..edd416d 100644 --- a/IO_DFPlayer.h +++ b/IO_DFPlayer.h @@ -221,7 +221,7 @@ protected: sendPacket(0x03, _requestedSong); _requestedSong = -1; } else if (_requestedSong == 0) { - sendPacket(0x0e); // Pause playing + sendPacket(0x16); // Stop playing _requestedSong = -1; } else if (_currentVolume < _requestedVolumeLevel) { // Change volume after changing song if volume is increasing. From 95c1b1da312082c571d6d23799f6603e413a3c66 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:48:11 +1000 Subject: [PATCH 56/97] Figuring out commands --- installer.ps1 | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 installer.ps1 diff --git a/installer.ps1 b/installer.ps1 new file mode 100644 index 0000000..e740d80 --- /dev/null +++ b/installer.ps1 @@ -0,0 +1,53 @@ +# +# © 2023 Peter Cole +# +# This file is part of EX-CommandStation +# +# 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 . +# + +# 32/64 bit Win installer +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip + +param( + [Parameter()] + [String]$BUILD_DIR +) + +if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +# Use the current date/time stamp to create a unique directory if one is not specified. + $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' + $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE +} + +if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" +} else { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" +} + +Write-Output "Downloading installer to $OUTFILE" + +$ProgressPreference = "SilentlyContinue" +Invoke-WebRequest -Uri $URL -OutFile $OUTFILE + +$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" + +Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +$ProgressPreference = "Continue" + +Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file From 305e0902f47bafb24d012ae6c2dd738c3cef4b45 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 4 Apr 2023 19:30:59 +1000 Subject: [PATCH 57/97] Got tag list --- installer.ps1 | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index e740d80..defccd6 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -21,33 +21,39 @@ # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + param( [Parameter()] - [String]$BUILD_DIR + [String]$buildDirectory ) -if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { # Use the current date/time stamp to create a unique directory if one is not specified. - $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' - $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate } if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" } else { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } -Write-Output "Downloading installer to $OUTFILE" +Write-Output "Downloading installer to $arduinoCLIZip" $ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $URL -OutFile $OUTFILE +Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip -$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" -Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force $ProgressPreference = "Continue" -Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file +Write-Output "Installing using directory $buildDirectory" + +foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { + $tag.getType() +} From b18df1405cc0317cf419977a7f57a3626ed84258 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Wed, 5 Apr 2023 05:30:00 +1000 Subject: [PATCH 58/97] Got tag version and URL --- installer.ps1 | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index defccd6..3b95535 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -20,10 +20,11 @@ # 32/64 bit Win installer # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +# +# For script errors set ExecutionPolicy: +# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - -param( +Param( [Parameter()] [String]$buildDirectory ) @@ -34,6 +35,8 @@ if (!$PSBoundParameters.ContainsKey('buildDirectory')) { $buildDirectory = $env:TEMP + "\" + $buildDate } +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -54,6 +57,12 @@ $ProgressPreference = "Continue" Write-Output "Installing using directory $buildDirectory" -foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { - $tag.getType() +$tagList = Invoke-RestMethod -Uri $gitHubAPITags + +# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip + +foreach ($tag in $tagList) { + $version = $tag.ref.split("/")[2] + $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + Write-Output "$version : $versionURL" } From de6e91a7782576c460d4d2011ffea9ad5cf49994 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:54:48 +1000 Subject: [PATCH 59/97] Working on logic --- installer.ps1 | 119 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 3b95535..d232187 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -1,4 +1,4 @@ -# +<# # © 2023 Peter Cole # # This file is part of EX-CommandStation @@ -15,28 +15,31 @@ # # You should have received a copy of the GNU General Public License # along with CommandStation. If not, see . -# +#> -# 32/64 bit Win installer -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip -# -# For script errors set ExecutionPolicy: -# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +<############################################ +For script errors set ExecutionPolicy: +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +############################################> +<############################################ +Optional command line parameters: + $buildDirectory - specify an existing directory rather than generating a new unique one + $version - specify an exact version to download +############################################> Param( [Parameter()] - [String]$buildDirectory + [String]$buildDirectory, + [Parameter()] + [String]$version ) -if (!$PSBoundParameters.ContainsKey('buildDirectory')) { -# Use the current date/time stamp to create a unique directory if one is not specified. - $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' - $buildDirectory = $env:TEMP + "\" + $buildDate -} - +<############################################ +Define global parameters here such as known URLs etc. +############################################> +$installerVersion = "v0.0.1" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - +$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -44,25 +47,87 @@ if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchi $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } - -Write-Output "Downloading installer to $arduinoCLIZip" - -$ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip - $arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" -Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force -$ProgressPreference = "Continue" +<############################################ +Set default action for progress indicators, warnings, and errors +############################################> +$global:ProgressPreference = "SilentlyContinue" +$global:WarningPreference = "SilentlyContinue" +$global:ErrorActionPreference = "SilentlyContinue" +<############################################ +If $buildDirectory not provided, generate a new time/date stamp based directory to use +############################################> +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate +} +$commandStationDirectory = $buildDirectory + "\CommandStation-EX" + +<############################################ +Write out intro message and prompt to continue +############################################> +@" +Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVersion) + +Current installer options: +- EX-CommandStation will be built in $commandStationDirectory +- Arduino CLI will be in $arduinoCLIDirectory +"@ + + +<############################################ +Create build directory if it doesn't exist, or fail +############################################> +if (!(Test-Path -PathType Container -Path $buildDirectory)) { + try { + New-Item -ItemType Directory -Path $buildDirectory | Out-Null + } + catch { + Write-Output "Could not create build directory $buildDirectory" + Exit + } +} + +<############################################ +See if we have the Arduino CLI already, otherwise download and extract it +############################################> +if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { + if (!(Test-Path -PathType Container -Path $arduinoCLIDirectory)) { + try { + New-Item -ItemType Directory -Path $arduinoCLIDirectory | Out-Null + } + catch { + Write-Output "Arduino CLI does not exist and cannot create directory $arduinoCLIDirectory" + Exit + } + } + Write-Output "Downloading and extracting Arduino CLI" + try { + Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip + } + catch { + Write-Output "Failed to download Arduino CLI" + Exit + } + try { + Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force + } + catch { + Write-Output "Failed to extract Arduino CLI" + } +} + +<# Write-Output "Installing using directory $buildDirectory" $tagList = Invoke-RestMethod -Uri $gitHubAPITags -# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip - foreach ($tag in $tagList) { $version = $tag.ref.split("/")[2] - $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + $versionURL = $gitHubURLPrefix + $tag.ref Write-Output "$version : $versionURL" } +#> \ No newline at end of file From 18a992bf0867a64d335fce149bfffd053d4afb0e Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 6 Apr 2023 05:31:11 +1000 Subject: [PATCH 60/97] Start getting tag list --- installer.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/installer.ps1 b/installer.ps1 index d232187..296fb6d 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -120,6 +120,31 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } +<# +Get the list of tags +#> +try { + $tagList = Invoke-RestMethod -Uri $gitHubAPITags +} +catch { + Write-Output "Failed to obtain list of available EX-CommandStation versions" + Exit +} + +<# +Get latest two Prod and Devel releases, add to hash table for selection by user +#> +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Prod") { + Write-Output $tag.ref + } +} +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Devel") { + Write-Output $tag.ref + } +} + <# Write-Output "Installing using directory $buildDirectory" From 9571088e1b0aa918cce3998845b57dc2f5d7090f Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 6 Apr 2023 07:03:59 +1000 Subject: [PATCH 61/97] Added disable logic --- IODevice.cpp | 28 ++++++++++++++++++++++------ version.h | 3 ++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/IODevice.cpp b/IODevice.cpp index af6efc8..2ed21b6 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -63,15 +63,31 @@ void IODevice::begin() { if (exrailHalSetup) exrailHalSetup(); - // Predefine two PCA9685 modules 0x40-0x41 + // Predefine two PCA9685 modules 0x40-0x41 if no conflicts // Allocates 32 pins 100-131 - PCA9685::create(100, 16, 0x40); - PCA9685::create(116, 16, 0x41); + if (checkNoOverlap(100, 16, 0x40)) { + PCA9685::create(100, 16, 0x40); + } else { + DIAG(F("Default PCA9685 at I2C 0x40 disabled due to configured user device")); + } + if (checkNoOverlap(116, 16, 0x41)) { + PCA9685::create(116, 16, 0x41); + } else { + DIAG(F("Default PCA9685 at I2C 0x41 disabled due to configured user device")); + } - // Predefine two MCP23017 module 0x20/0x21 + // Predefine two MCP23017 module 0x20/0x21 if no conflicts // Allocates 32 pins 164-195 - MCP23017::create(164, 16, 0x20); - MCP23017::create(180, 16, 0x21); + if (checkNoOverlap(164, 16, 0x20)) { + MCP23017::create(164, 16, 0x20); + } else { + DIAG(F("Default MCP23017 at I2C 0x20 disabled due to configured user device")); + } + if (checkNoOverlap(180, 16, 0x21)) { + MCP23017::create(180, 16, 0x21); + } else { + DIAG(F("Default MCP23017 at I2C 0x21 disabled due to configured user device")); + } } // reset() function to reinitialise all devices diff --git a/version.h b/version.h index da95f93..399f68d 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.39" +#define VERSION "4.2.40" +// 4.2.40 - Automatically detect conflicting default I2C devices and disable // 4.2.39 - DFplayer driver now polls device to detect failures and errors. // 4.2.38 - Clean up compiler warning when IO_RotaryEncoder.h included // 4.2.37 - Add new FLAGS HAL device for communications to/from EX-RAIL; From 273f55b1435fad720c982d9679da266859106a11 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 5 Apr 2023 23:19:43 +0100 Subject: [PATCH 62/97] 4.2.41 Hal setup and DNOU8 fix --- CommandStation-EX.ino | 8 ++++---- IO_duinoNodes.h | 1 + version.h | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 3c40bba..7363566 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -75,6 +75,9 @@ void setup() DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com")); +// Initialise HAL layer before reading EEprom or setting up MotorDrivers + IODevice::begin(); + DISPLAY_START ( // This block is still executed for DIAGS if display not in use LCD(0,F("DCC-EX v%S"),F(VERSION)); @@ -96,10 +99,7 @@ void setup() #if ETHERNET_ON EthernetInterface::setup(); #endif // ETHERNET_ON - - // Initialise HAL layer before reading EEprom or setting up MotorDrivers - IODevice::begin(); - + // As the setup of a motor shield may require a read of the current sense input from the ADC, // let's make sure to initialise the ADCee class! ADCee::begin(); diff --git a/IO_duinoNodes.h b/IO_duinoNodes.h index 60ef2ea..c764db2 100644 --- a/IO_duinoNodes.h +++ b/IO_duinoNodes.h @@ -55,6 +55,7 @@ public: pinMode(_clockPin,OUTPUT); pinMode(_dataPin,_pinMap?INPUT_PULLUP:OUTPUT); _display(); + if (!_pinMap) _loopOutput(); } // loop called by HAL supervisor diff --git a/version.h b/version.h index 399f68d..a8c8bde 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.40" +#define VERSION "4.2.41" +// 4.2.41 - Move HAl startup to ASAP in setup() +// - Fix DNOU8 output pin setup to all LOW // 4.2.40 - Automatically detect conflicting default I2C devices and disable // 4.2.39 - DFplayer driver now polls device to detect failures and errors. // 4.2.38 - Clean up compiler warning when IO_RotaryEncoder.h included From de06c0ae3eb9422f8e4d85e7d0d5de432507658e Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:06:22 +1000 Subject: [PATCH 63/97] Working on CLI commands --- installer.ps1 | 129 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 296fb6d..b803e50 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -31,7 +31,7 @@ Param( [Parameter()] [String]$buildDirectory, [Parameter()] - [String]$version + [String]$configDirectory ) <############################################ @@ -120,30 +120,131 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } -<# +<############################################ Get the list of tags -#> +############################################> try { - $tagList = Invoke-RestMethod -Uri $gitHubAPITags + $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags } catch { Write-Output "Failed to obtain list of available EX-CommandStation versions" Exit } -<# -Get latest two Prod and Devel releases, add to hash table for selection by user -#> -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Prod") { - Write-Output $tag.ref +<############################################ +Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want +############################################> +$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" +$tagList = @{} +foreach ($tag in $gitHubTags) { + $tagHash = @{} + $tagHash["Ref"] = $tag.ref + $version = $tag.ref.split("/")[2] + $null = $version -match $versionMatch + $tagHash["Major"] = [int]$Matches[1] + $tagHash["Minor"] = [int]$Matches[2] + $tagHash["Patch"] = [int]$Matches[3] + $tagHash["Type"] = $Matches[4] + $tagList.Add($version, $tagHash) +} + +<############################################ +Get latest two Prod and Devel for user to select +############################################> +$userList = @{} +$prodCount = 1 +$devCount = 1 +$select = 1 +foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { + if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { + $userList[$select] = $tag + $select++ + $prodCount++ + } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { + $userList[$select] = $tag + $select++ + $devCount++ } } -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Devel") { - Write-Output $tag.ref - } + +<############################################ +Display options for user to select and get the selection +############################################> +foreach ($selection in $userList.Keys | Sort-Object $selection) { + Write-Output "$selection - $($userList[$selection])" } +Write-Output "5 - Exit" +$userSelection = 0 +do { + [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" +} until ( + (($userSelection -ge 1) -and ($userSelection -le 5)) +) +if ($userSelection -eq 5) { + Write-Output "Exiting installer" + Exit +} else { + $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" +} + +<############################################ +Download the chosen version to the build directory +############################################> +$downladFile = $buildDirectory + "\CommandStation-EX.zip" +Write-Output "Downloading and extracting $($userList[$userSelection])" +try { + Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile +} +catch { + Write-Output "Error downloading EX-CommandStation zip file" + Exit +} + +<############################################ +Extract and rename to CommandStation-EX to allow building +############################################> +try { + Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force +} +catch { + Write-Output "Failed to extract EX-CommandStation zip file" + Exit +} + +$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") +Write-Output $folderName +try { + Rename-Item -Path $folderName -NewName $commandStationDirectory +} +catch { + Write-Output "Could not rename folder" + Exit +} + +<############################################ +If config directory provided, copy files here +############################################> + + +<############################################ +Once files all together, identify available board(s) +############################################> +# Need to do an initial board list to download everything first +& $arduinoCLI board list | Out-Null +# Run again to generate the list of discovered boards +& $arduinoCLI board list --format json + +<############################################ +Get user to select board +############################################> + + + + +<############################################ +Upload the sketch to the selected board +############################################> +#$arduinoCLI upload -b fqbn -p port $commandStationDirectory <# Write-Output "Installing using directory $buildDirectory" From 9f212c27bf3ec38576fd7a4525db6bde85ee7e24 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 7 Apr 2023 08:06:32 +1000 Subject: [PATCH 64/97] Initial test working! --- installer.ps1 | 142 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 16 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index b803e50..1c47fc3 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -34,6 +34,24 @@ Param( [String]$configDirectory ) +<############################################ +List of supported devices with FQBN in case clones used that aren't detected +############################################> +$supportedDevices = @( + @{ + name = "Arduino Mega or Mega 2560" + fqbn = "arduino:avr:mega" + }, + @{ + name = "Arduino Nano" + fqbn = "arduino:avr:nano" + }, + @{ + name = "Arduino Uno" + fqbn = "arduino:avr:uno" + } +) + <############################################ Define global parameters here such as known URLs etc. ############################################> @@ -75,6 +93,9 @@ Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVers Current installer options: - EX-CommandStation will be built in $commandStationDirectory - Arduino CLI will be in $arduinoCLIDirectory + +Available EX-CommandStation versions: +------------------------------------- "@ @@ -176,7 +197,7 @@ foreach ($selection in $userList.Keys | Sort-Object $selection) { Write-Output "5 - Exit" $userSelection = 0 do { - [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" + [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" } until ( (($userSelection -ge 1) -and ($userSelection -le 5)) ) @@ -200,6 +221,14 @@ catch { Exit } +<############################################ +If folder exists, bail out and tell user +############################################> +if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { + Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" + Exit +} + <############################################ Extract and rename to CommandStation-EX to allow building ############################################> @@ -224,36 +253,117 @@ catch { <############################################ If config directory provided, copy files here ############################################> - +# To be done +# If exists copy config.h, myAutomation.h, myHal.cpp, mySetup.h <############################################ Once files all together, identify available board(s) ############################################> # Need to do an initial board list to download everything first -& $arduinoCLI board list | Out-Null -# Run again to generate the list of discovered boards -& $arduinoCLI board list --format json +try { + & $arduinoCLI board list | Out-Null +} +catch { + Write-Output "Failed to update Arduino CLI board list" + Exit +} +# Run again to generate the list of discovered boards into a custom object +try { + $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to obtain list of boards" + Exit +} <############################################ Get user to select board ############################################> +if ($boardList.count -eq 0) { + Write-Output "Could not find any attached devices, please ensure your device is plugged in to a USB port and Windows recognises it" + Exit +} else { +@" +Devices attached to COM ports: +------------------------------ +"@ + $boardSelect = 1 + foreach ($board in $boardList) { + if ($board.matching_boards.name) { + $boardName = $board.matching_boards.name + } else { + $boardName = "Unknown device" + } + $port = $board.port.address + Write-Output "$boardSelect - $boardName on port $port" + $boardSelect++ + } + Write-Output "$boardSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "`r`nSelect the device to use from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($boardList.count + 1))) + ) + if ($userSelection -eq ($boardList.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $selectedBoard = $userSelection - 1 + } +} +<############################################ +If the board is unknown, need to choose which one +############################################> +if ($null -eq $boardList[$selectedBoard].matching_boards.name) { + Write-Output "The device selected is unknown, these boards are supported:`r`n" + $deviceSelect = 1 + foreach ($device in $supportedDevices) { + Write-Output "$deviceSelect - $($supportedDevices[$deviceSelect - 1].name)" + $deviceSelect++ + } + Write-Output "$deviceSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "Select the board type from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($supportedDevices.count + 1))) + ) + if ($userSelection -eq ($supportedDevices.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $deviceName = $supportedDevices[$userSelection - 1].name + $deviceFQBN = $supportedDevices[$userSelection - 1].fqbn + } +} else { + $deviceName = $boardList[$selectedBoard].matching_boards.name + $deviceFQBN = $boardList[$selectedBoard].matching_boards.fqbn + $devicePort = $boardList[$selectedBoard].port.address +} <############################################ Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory - -<# -Write-Output "Installing using directory $buildDirectory" - -$tagList = Invoke-RestMethod -Uri $gitHubAPITags - -foreach ($tag in $tagList) { - $version = $tag.ref.split("/")[2] - $versionURL = $gitHubURLPrefix + $tag.ref - Write-Output "$version : $versionURL" +Write-Output "Compiling for $deviceName" +try { + $output = & $arduinoCLI compile -b $deviceFQBN $commandStationDirectory --format jsonmini | ConvertFrom-Json } -#> \ No newline at end of file +catch { + Write-Output "Failed to compile" + Exit +} +Write-Output "$output" +Write-Output "Now uploading to $deviceName on port $devicePort" +try { + $output = & $arduinoCLI upload -t -b $deviceFQBN -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to upload" + Exit +} +Write-Output "$output" From c3d2e5b2222302a927b46cdd8c1cc7993bb546f4 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Fri, 7 Apr 2023 13:59:49 +1000 Subject: [PATCH 65/97] Fix to PIO build target names for Teensy --- platformio.ini | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/platformio.ini b/platformio.ini index 1fc7291..a8022dc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,11 +20,11 @@ default_envs = ESP32 Nucleo-F411RE Nucleo-F446RE - Teensy3.2 - Teensy3.5 - Teensy3.6 - Teensy4.0 - Teensy4.1 + Teensy3_2 + Teensy3_5 + Teensy3_6 + Teensy4_0 + Teensy4_1 src_dir = . include_dir = . @@ -192,7 +192,7 @@ build_flags = -std=c++17 -Os -g2 -Wunused-variable -DDIAG_LOOPTIMES ; -DDIAG_IO monitor_speed = 115200 monitor_echo = yes -[env:Teensy3.2] +[env:Teensy3_2] platform = teensy board = teensy31 framework = arduino @@ -200,7 +200,7 @@ build_flags = -std=c++17 -Os -g2 lib_deps = ${env.lib_deps} lib_ignore = NativeEthernet -[env:Teensy3.5] +[env:Teensy3_5] platform = teensy board = teensy35 framework = arduino @@ -208,7 +208,7 @@ build_flags = -std=c++17 -Os -g2 lib_deps = ${env.lib_deps} lib_ignore = NativeEthernet -[env:Teensy3.6] +[env:Teensy3_6] platform = teensy board = teensy36 framework = arduino @@ -216,7 +216,7 @@ build_flags = -std=c++17 -Os -g2 lib_deps = ${env.lib_deps} lib_ignore = NativeEthernet -[env:Teensy4.0] +[env:Teensy4_0] platform = teensy board = teensy40 framework = arduino @@ -224,7 +224,7 @@ build_flags = -std=c++17 -Os -g2 lib_deps = ${env.lib_deps} lib_ignore = NativeEthernet -[env:Teensy4.1] +[env:Teensy4_1] platform = teensy board = teensy41 framework = arduino From ef3d36ae25027bc0d5e93a2573fc22405dce4b05 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:48:11 +1000 Subject: [PATCH 66/97] Figuring out commands --- installer.ps1 | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 installer.ps1 diff --git a/installer.ps1 b/installer.ps1 new file mode 100644 index 0000000..e740d80 --- /dev/null +++ b/installer.ps1 @@ -0,0 +1,53 @@ +# +# © 2023 Peter Cole +# +# This file is part of EX-CommandStation +# +# 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 . +# + +# 32/64 bit Win installer +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip +# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip + +param( + [Parameter()] + [String]$BUILD_DIR +) + +if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +# Use the current date/time stamp to create a unique directory if one is not specified. + $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' + $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE +} + +if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" +} else { + $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" +} + +Write-Output "Downloading installer to $OUTFILE" + +$ProgressPreference = "SilentlyContinue" +Invoke-WebRequest -Uri $URL -OutFile $OUTFILE + +$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" + +Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +$ProgressPreference = "Continue" + +Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file From 3a3071f35bd4ddfdaea854b560cd34f326b03029 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 4 Apr 2023 19:30:59 +1000 Subject: [PATCH 67/97] Got tag list --- installer.ps1 | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index e740d80..defccd6 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -21,33 +21,39 @@ # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + param( [Parameter()] - [String]$BUILD_DIR + [String]$buildDirectory ) -if (!$PSBoundParameters.ContainsKey('BUILD_DIR')) { +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { # Use the current date/time stamp to create a unique directory if one is not specified. - $BUILD_DATE = Get-Date -Format 'yyyyMMdd-HHmmss' - $BUILD_DIR = $env:TEMP + "\" + $BUILD_DATE + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate } if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" } else { - $URL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $OUTFILE = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } -Write-Output "Downloading installer to $OUTFILE" +Write-Output "Downloading installer to $arduinoCLIZip" $ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $URL -OutFile $OUTFILE +Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip -$CLI_INSTALL = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" -Expand-Archive -Path $OUTFILE -DestinationPath $CLI_INSTALL -Force +Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force $ProgressPreference = "Continue" -Write-Output "Installing using directory $BUILD_DIR" \ No newline at end of file +Write-Output "Installing using directory $buildDirectory" + +foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { + $tag.getType() +} From 61db37c7ea2717b4c2ddfd952bdab87cf4e52a31 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Wed, 5 Apr 2023 05:30:00 +1000 Subject: [PATCH 68/97] Got tag version and URL --- installer.ps1 | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index defccd6..3b95535 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -20,10 +20,11 @@ # 32/64 bit Win installer # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip # https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip +# +# For script errors set ExecutionPolicy: +# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - -param( +Param( [Parameter()] [String]$buildDirectory ) @@ -34,6 +35,8 @@ if (!$PSBoundParameters.ContainsKey('buildDirectory')) { $buildDirectory = $env:TEMP + "\" + $buildDate } +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" + if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -54,6 +57,12 @@ $ProgressPreference = "Continue" Write-Output "Installing using directory $buildDirectory" -foreach ($tag in Invoke-RestMethod -Uri $gitHubAPITags | Format-List -Property ref) { - $tag.getType() +$tagList = Invoke-RestMethod -Uri $gitHubAPITags + +# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip + +foreach ($tag in $tagList) { + $version = $tag.ref.split("/")[2] + $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + Write-Output "$version : $versionURL" } From d2c7e7fb8d9a536a00536e5c7aaa0f3130b8b9f8 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:54:48 +1000 Subject: [PATCH 69/97] Working on logic --- installer.ps1 | 119 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 27 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 3b95535..d232187 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -1,4 +1,4 @@ -# +<# # © 2023 Peter Cole # # This file is part of EX-CommandStation @@ -15,28 +15,31 @@ # # You should have received a copy of the GNU General Public License # along with CommandStation. If not, see . -# +#> -# 32/64 bit Win installer -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip -# https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip -# -# For script errors set ExecutionPolicy: -# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +<############################################ +For script errors set ExecutionPolicy: +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass +############################################> +<############################################ +Optional command line parameters: + $buildDirectory - specify an existing directory rather than generating a new unique one + $version - specify an exact version to download +############################################> Param( [Parameter()] - [String]$buildDirectory + [String]$buildDirectory, + [Parameter()] + [String]$version ) -if (!$PSBoundParameters.ContainsKey('buildDirectory')) { -# Use the current date/time stamp to create a unique directory if one is not specified. - $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' - $buildDirectory = $env:TEMP + "\" + $buildDate -} - +<############################################ +Define global parameters here such as known URLs etc. +############################################> +$installerVersion = "v0.0.1" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" - +$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" @@ -44,25 +47,87 @@ if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchi $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" } - -Write-Output "Downloading installer to $arduinoCLIZip" - -$ProgressPreference = "SilentlyContinue" -Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip - $arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" -Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force -$ProgressPreference = "Continue" +<############################################ +Set default action for progress indicators, warnings, and errors +############################################> +$global:ProgressPreference = "SilentlyContinue" +$global:WarningPreference = "SilentlyContinue" +$global:ErrorActionPreference = "SilentlyContinue" +<############################################ +If $buildDirectory not provided, generate a new time/date stamp based directory to use +############################################> +if (!$PSBoundParameters.ContainsKey('buildDirectory')) { + $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' + $buildDirectory = $env:TEMP + "\" + $buildDate +} +$commandStationDirectory = $buildDirectory + "\CommandStation-EX" + +<############################################ +Write out intro message and prompt to continue +############################################> +@" +Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVersion) + +Current installer options: +- EX-CommandStation will be built in $commandStationDirectory +- Arduino CLI will be in $arduinoCLIDirectory +"@ + + +<############################################ +Create build directory if it doesn't exist, or fail +############################################> +if (!(Test-Path -PathType Container -Path $buildDirectory)) { + try { + New-Item -ItemType Directory -Path $buildDirectory | Out-Null + } + catch { + Write-Output "Could not create build directory $buildDirectory" + Exit + } +} + +<############################################ +See if we have the Arduino CLI already, otherwise download and extract it +############################################> +if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { + if (!(Test-Path -PathType Container -Path $arduinoCLIDirectory)) { + try { + New-Item -ItemType Directory -Path $arduinoCLIDirectory | Out-Null + } + catch { + Write-Output "Arduino CLI does not exist and cannot create directory $arduinoCLIDirectory" + Exit + } + } + Write-Output "Downloading and extracting Arduino CLI" + try { + Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip + } + catch { + Write-Output "Failed to download Arduino CLI" + Exit + } + try { + Expand-Archive -Path $arduinoCLIZip -DestinationPath $arduinoCLIDirectory -Force + } + catch { + Write-Output "Failed to extract Arduino CLI" + } +} + +<# Write-Output "Installing using directory $buildDirectory" $tagList = Invoke-RestMethod -Uri $gitHubAPITags -# Example zip: https://github.com/DCC-EX/CommandStation-EX/archive/refs/tags/v4.2.36-Devel.zip - foreach ($tag in $tagList) { $version = $tag.ref.split("/")[2] - $versionURL = "https://github.com/DCC-EX/CommandStation-EX/archive/" + $tag.ref + $versionURL = $gitHubURLPrefix + $tag.ref Write-Output "$version : $versionURL" } +#> \ No newline at end of file From 72ceb6391335898ed1d7dbd81a55772be592622f Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 6 Apr 2023 05:31:11 +1000 Subject: [PATCH 70/97] Start getting tag list --- installer.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/installer.ps1 b/installer.ps1 index d232187..296fb6d 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -120,6 +120,31 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } +<# +Get the list of tags +#> +try { + $tagList = Invoke-RestMethod -Uri $gitHubAPITags +} +catch { + Write-Output "Failed to obtain list of available EX-CommandStation versions" + Exit +} + +<# +Get latest two Prod and Devel releases, add to hash table for selection by user +#> +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Prod") { + Write-Output $tag.ref + } +} +foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { + if ($tag.ref -Like "*Devel") { + Write-Output $tag.ref + } +} + <# Write-Output "Installing using directory $buildDirectory" From 91bc9df44eb6d9630af0013e5e4f55ba9943d517 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:06:22 +1000 Subject: [PATCH 71/97] Working on CLI commands --- installer.ps1 | 129 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 296fb6d..b803e50 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -31,7 +31,7 @@ Param( [Parameter()] [String]$buildDirectory, [Parameter()] - [String]$version + [String]$configDirectory ) <############################################ @@ -120,30 +120,131 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } -<# +<############################################ Get the list of tags -#> +############################################> try { - $tagList = Invoke-RestMethod -Uri $gitHubAPITags + $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags } catch { Write-Output "Failed to obtain list of available EX-CommandStation versions" Exit } -<# -Get latest two Prod and Devel releases, add to hash table for selection by user -#> -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Prod") { - Write-Output $tag.ref +<############################################ +Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want +############################################> +$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" +$tagList = @{} +foreach ($tag in $gitHubTags) { + $tagHash = @{} + $tagHash["Ref"] = $tag.ref + $version = $tag.ref.split("/")[2] + $null = $version -match $versionMatch + $tagHash["Major"] = [int]$Matches[1] + $tagHash["Minor"] = [int]$Matches[2] + $tagHash["Patch"] = [int]$Matches[3] + $tagHash["Type"] = $Matches[4] + $tagList.Add($version, $tagHash) +} + +<############################################ +Get latest two Prod and Devel for user to select +############################################> +$userList = @{} +$prodCount = 1 +$devCount = 1 +$select = 1 +foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { + if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { + $userList[$select] = $tag + $select++ + $prodCount++ + } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { + $userList[$select] = $tag + $select++ + $devCount++ } } -foreach ($tag in $tagList | Sort-Object -Property ref -Descending) { - if ($tag.ref -Like "*Devel") { - Write-Output $tag.ref - } + +<############################################ +Display options for user to select and get the selection +############################################> +foreach ($selection in $userList.Keys | Sort-Object $selection) { + Write-Output "$selection - $($userList[$selection])" } +Write-Output "5 - Exit" +$userSelection = 0 +do { + [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" +} until ( + (($userSelection -ge 1) -and ($userSelection -le 5)) +) +if ($userSelection -eq 5) { + Write-Output "Exiting installer" + Exit +} else { + $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" +} + +<############################################ +Download the chosen version to the build directory +############################################> +$downladFile = $buildDirectory + "\CommandStation-EX.zip" +Write-Output "Downloading and extracting $($userList[$userSelection])" +try { + Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile +} +catch { + Write-Output "Error downloading EX-CommandStation zip file" + Exit +} + +<############################################ +Extract and rename to CommandStation-EX to allow building +############################################> +try { + Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force +} +catch { + Write-Output "Failed to extract EX-CommandStation zip file" + Exit +} + +$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") +Write-Output $folderName +try { + Rename-Item -Path $folderName -NewName $commandStationDirectory +} +catch { + Write-Output "Could not rename folder" + Exit +} + +<############################################ +If config directory provided, copy files here +############################################> + + +<############################################ +Once files all together, identify available board(s) +############################################> +# Need to do an initial board list to download everything first +& $arduinoCLI board list | Out-Null +# Run again to generate the list of discovered boards +& $arduinoCLI board list --format json + +<############################################ +Get user to select board +############################################> + + + + +<############################################ +Upload the sketch to the selected board +############################################> +#$arduinoCLI upload -b fqbn -p port $commandStationDirectory <# Write-Output "Installing using directory $buildDirectory" From 751b46b6bb4ceef91d31d018ad4b6c378e7fe8d0 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 7 Apr 2023 08:06:32 +1000 Subject: [PATCH 72/97] Initial test working! --- installer.ps1 | 142 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 16 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index b803e50..1c47fc3 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -34,6 +34,24 @@ Param( [String]$configDirectory ) +<############################################ +List of supported devices with FQBN in case clones used that aren't detected +############################################> +$supportedDevices = @( + @{ + name = "Arduino Mega or Mega 2560" + fqbn = "arduino:avr:mega" + }, + @{ + name = "Arduino Nano" + fqbn = "arduino:avr:nano" + }, + @{ + name = "Arduino Uno" + fqbn = "arduino:avr:uno" + } +) + <############################################ Define global parameters here such as known URLs etc. ############################################> @@ -75,6 +93,9 @@ Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVers Current installer options: - EX-CommandStation will be built in $commandStationDirectory - Arduino CLI will be in $arduinoCLIDirectory + +Available EX-CommandStation versions: +------------------------------------- "@ @@ -176,7 +197,7 @@ foreach ($selection in $userList.Keys | Sort-Object $selection) { Write-Output "5 - Exit" $userSelection = 0 do { - [int]$userSelection = Read-Host "Select the version to install from the list above (1 - 5)" + [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" } until ( (($userSelection -ge 1) -and ($userSelection -le 5)) ) @@ -200,6 +221,14 @@ catch { Exit } +<############################################ +If folder exists, bail out and tell user +############################################> +if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { + Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" + Exit +} + <############################################ Extract and rename to CommandStation-EX to allow building ############################################> @@ -224,36 +253,117 @@ catch { <############################################ If config directory provided, copy files here ############################################> - +# To be done +# If exists copy config.h, myAutomation.h, myHal.cpp, mySetup.h <############################################ Once files all together, identify available board(s) ############################################> # Need to do an initial board list to download everything first -& $arduinoCLI board list | Out-Null -# Run again to generate the list of discovered boards -& $arduinoCLI board list --format json +try { + & $arduinoCLI board list | Out-Null +} +catch { + Write-Output "Failed to update Arduino CLI board list" + Exit +} +# Run again to generate the list of discovered boards into a custom object +try { + $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to obtain list of boards" + Exit +} <############################################ Get user to select board ############################################> +if ($boardList.count -eq 0) { + Write-Output "Could not find any attached devices, please ensure your device is plugged in to a USB port and Windows recognises it" + Exit +} else { +@" +Devices attached to COM ports: +------------------------------ +"@ + $boardSelect = 1 + foreach ($board in $boardList) { + if ($board.matching_boards.name) { + $boardName = $board.matching_boards.name + } else { + $boardName = "Unknown device" + } + $port = $board.port.address + Write-Output "$boardSelect - $boardName on port $port" + $boardSelect++ + } + Write-Output "$boardSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "`r`nSelect the device to use from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($boardList.count + 1))) + ) + if ($userSelection -eq ($boardList.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $selectedBoard = $userSelection - 1 + } +} +<############################################ +If the board is unknown, need to choose which one +############################################> +if ($null -eq $boardList[$selectedBoard].matching_boards.name) { + Write-Output "The device selected is unknown, these boards are supported:`r`n" + $deviceSelect = 1 + foreach ($device in $supportedDevices) { + Write-Output "$deviceSelect - $($supportedDevices[$deviceSelect - 1].name)" + $deviceSelect++ + } + Write-Output "$deviceSelect - Exit" + $userSelection = 0 + do { + [int]$userSelection = Read-Host "Select the board type from the list above" + } until ( + (($userSelection -ge 1) -and ($userSelection -le ($supportedDevices.count + 1))) + ) + if ($userSelection -eq ($supportedDevices.count + 1)) { + Write-Output "Exiting installer" + Exit + } else { + $deviceName = $supportedDevices[$userSelection - 1].name + $deviceFQBN = $supportedDevices[$userSelection - 1].fqbn + } +} else { + $deviceName = $boardList[$selectedBoard].matching_boards.name + $deviceFQBN = $boardList[$selectedBoard].matching_boards.fqbn + $devicePort = $boardList[$selectedBoard].port.address +} <############################################ Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory - -<# -Write-Output "Installing using directory $buildDirectory" - -$tagList = Invoke-RestMethod -Uri $gitHubAPITags - -foreach ($tag in $tagList) { - $version = $tag.ref.split("/")[2] - $versionURL = $gitHubURLPrefix + $tag.ref - Write-Output "$version : $versionURL" +Write-Output "Compiling for $deviceName" +try { + $output = & $arduinoCLI compile -b $deviceFQBN $commandStationDirectory --format jsonmini | ConvertFrom-Json } -#> \ No newline at end of file +catch { + Write-Output "Failed to compile" + Exit +} +Write-Output "$output" +Write-Output "Now uploading to $deviceName on port $devicePort" +try { + $output = & $arduinoCLI upload -t -b $deviceFQBN -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json +} +catch { + Write-Output "Failed to upload" + Exit +} +Write-Output "$output" From 2e518fcac229a18efe6f4a0c6f9610198d62b03b Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 7 Apr 2023 19:30:15 +1000 Subject: [PATCH 73/97] Enable using existing configs --- installer.ps1 | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 1c47fc3..0b4e94b 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -55,7 +55,7 @@ $supportedDevices = @( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.1" +$installerVersion = "v0.0.2" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { @@ -253,8 +253,18 @@ catch { <############################################ If config directory provided, copy files here ############################################> -# To be done -# If exists copy config.h, myAutomation.h, myHal.cpp, mySetup.h +$configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") +if ($PSBoundParameters.ContainsKey('configDirectory')) { + if (Test-Path -PathType Container -Path $configDirectory) { + foreach ($file in $configFiles) { + if (Test-Path -PathType Leaf -Path "$configDirectory\$file") { + Copy-Item -Path "$configDirectory\$file" -Destination "$commandStationDirectory\$file" + } + } + } else { + Write-Output "Provided configuration directory $configDirectory does not exist, skipping" + } +} <############################################ Once files all together, identify available board(s) @@ -338,6 +348,7 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { } else { $deviceName = $supportedDevices[$userSelection - 1].name $deviceFQBN = $supportedDevices[$userSelection - 1].fqbn + $devicePort = $boardList[$selectedBoard].port.address } } else { $deviceName = $boardList[$selectedBoard].matching_boards.name @@ -349,21 +360,13 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory -Write-Output "Compiling for $deviceName" +Write-Output "Compiling and uploading to $deviceName on $devicePort" +Write-Output "& $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory" try { - $output = & $arduinoCLI compile -b $deviceFQBN $commandStationDirectory --format jsonmini | ConvertFrom-Json + $output = & $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json } catch { Write-Output "Failed to compile" Exit } Write-Output "$output" -Write-Output "Now uploading to $deviceName on port $devicePort" -try { - $output = & $arduinoCLI upload -t -b $deviceFQBN -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json -} -catch { - Write-Output "Failed to upload" - Exit -} -Write-Output "$output" From 5d0de6b807ff21e27a8676729a0927c4df21a9f7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 7 Apr 2023 20:44:40 +0200 Subject: [PATCH 74/97] platformio wants this --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index a8022dc..d85c76c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -53,7 +53,7 @@ monitor_speed = 115200 monitor_echo = yes build_flags = -std=c++17 -[env:Arduino M0] +[env:Arduino-M0] platform = atmelsam board = mzeroUSB framework = arduino From ff6034dff2ebcaca75376547f4d59c4302b89195 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 7 Apr 2023 20:45:21 +0200 Subject: [PATCH 75/97] curl only needed when downloading --- installer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer.sh b/installer.sh index 98de771..17a9c04 100755 --- a/installer.sh +++ b/installer.sh @@ -69,10 +69,10 @@ else # need to do this config better cp -p config.example.h config.h fi -need curl if test -x "$ACLI" ; then : all well else + need curl curl "$ACLIINSTALL" > acliinstall.sh chmod +x acliinstall.sh ./acliinstall.sh From 0d823703809120711c42b535968f6051993d06c4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 7 Apr 2023 20:46:20 +0200 Subject: [PATCH 76/97] devel date --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 4f86707..fbd3f68 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202303252126Z" +#define GITHUB_SHA "devel-202304071845Z" From a100d709ce5ef60e5c87f4ba599e5b222f2fc23b Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 06:22:09 +1000 Subject: [PATCH 77/97] Install core library, add batch wrapper --- install_via_powershell.cmd | 15 +++++++++++++++ installer.ps1 | 29 +++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 install_via_powershell.cmd diff --git a/install_via_powershell.cmd b/install_via_powershell.cmd new file mode 100644 index 0000000..f367b21 --- /dev/null +++ b/install_via_powershell.cmd @@ -0,0 +1,15 @@ +@ECHO OFF + +FOR /f "tokens=*" %%a IN ('powershell Get-ExecutionPolicy -Scope CurrentUser') DO SET PS_POLICY=%%a + +IF NOT %PS_POLICY=="Bypass" ( + powershell Set-ExecutionPolicy -Scope CurrentUser Bypass +) + +powershell %~dp0%installer.ps1 + +IF NOT %PS_POLICY=="Bypass" ( + powershell Set-ExecutionPolicy -Scope CurrentUser %PS_POLICY% +) + +PAUSE \ No newline at end of file diff --git a/installer.ps1 b/installer.ps1 index 0b4e94b..65b0a61 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -55,7 +55,7 @@ $supportedDevices = @( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.2" +$installerVersion = "v0.0.3" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { @@ -241,7 +241,6 @@ catch { } $folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") -Write-Output $folderName try { Rename-Item -Path $folderName -NewName $commandStationDirectory } @@ -356,12 +355,24 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { $devicePort = $boardList[$selectedBoard].port.address } +<############################################ +Install core libraries for the platform +############################################> +$platformArray = $deviceFQBN.split(":") +$platform = $platformArray[0] + ":" + $platformArray[1] +try { + & $arduinoCLI core install $platform +} +catch { + Write-Output "Error install core libraries" + Exit +} + <############################################ Upload the sketch to the selected board ############################################> #$arduinoCLI upload -b fqbn -p port $commandStationDirectory Write-Output "Compiling and uploading to $deviceName on $devicePort" -Write-Output "& $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory" try { $output = & $arduinoCLI compile -b $deviceFQBN -u -t -p $devicePort $commandStationDirectory --format jsonmini | ConvertFrom-Json } @@ -369,4 +380,14 @@ catch { Write-Output "Failed to compile" Exit } -Write-Output "$output" +if ($output.success -eq "True") { + Write-Output "`r`nCongratulations! DCC-EX EX-CommandStation $($userList[$userSelection]) has been installed on your $deviceName`r`n" +} else { + Write-Output "`r`nThere was an error installing $($userList[$userSelection]) on your $($deviceName), please take note of the errors provided:`r`n" + if ($null -ne $output.compiler_err) { + Write-Output "Compiler error: $($output.compiler_err)`r`n" + } + if ($null -ne $output.builder_result) { + Write-Output "Builder result: $($output.builder_result)`r`n" + } +} From 4d236446b039047ba706532f7d34fb3fe973d375 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 06:57:09 +1000 Subject: [PATCH 78/97] Add core index update --- installer.ps1 | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 65b0a61..a92edae 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -34,6 +34,22 @@ Param( [String]$configDirectory ) +<############################################ +Define global parameters here such as known URLs etc. +############################################> +$installerVersion = "v0.0.4" +$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" +$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" +if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" +} else { + $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" +} +$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" + <############################################ List of supported devices with FQBN in case clones used that aren't detected ############################################> @@ -52,22 +68,6 @@ $supportedDevices = @( } ) -<############################################ -Define global parameters here such as known URLs etc. -############################################> -$installerVersion = "v0.0.3" -$gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" -$gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" -if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { - $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" -} else { - $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" -} -$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" -$arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" - <############################################ Set default action for progress indicators, warnings, and errors ############################################> @@ -266,9 +266,17 @@ if ($PSBoundParameters.ContainsKey('configDirectory')) { } <############################################ -Once files all together, identify available board(s) +Make sure Arduino CLI core index updated and list of boards populated ############################################> # Need to do an initial board list to download everything first +try { + & $arduinoCLI core update-index | Out-Null +} +catch { + Write-Output "Failed to update Arduino CLI core index" + Exit +} +# Need to do an initial board list to download everything first try { & $arduinoCLI board list | Out-Null } @@ -276,7 +284,10 @@ catch { Write-Output "Failed to update Arduino CLI board list" Exit } -# Run again to generate the list of discovered boards into a custom object + +<############################################ +Once files all together, identify available board(s) +############################################> try { $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json } From fe035f4096d2723e0f0336fd45a4a5ccbf0eb928 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 08:38:02 +1000 Subject: [PATCH 79/97] Move from temp to user home dir --- installer.ps1 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index a92edae..ac2ec9e 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -37,17 +37,18 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.4" +$installerVersion = "v0.0.5" +$userDirectory = $env:USERPROFILE + "\" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture -eq "64-bit") { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_64bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_64bit.zip" + $arduinoCLIZip = $userDirectory + "Downloads\" + "arduino-cli_latest_Windows_64bit.zip" } else { $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" - $arduinoCLIZip = $env:TEMP + "\" + "arduino-cli_latest_Windows_32bit.zip" + $arduinoCLIZip = $userDirectory + "Downloads\" + "arduino-cli_latest_Windows_32bit.zip" } -$arduinoCLIDirectory = $env:TEMP + "\" + "arduino-cli_installer" +$arduinoCLIDirectory = $userDirectory + "arduino-cli_installer" $arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" <############################################ @@ -80,7 +81,7 @@ If $buildDirectory not provided, generate a new time/date stamp based directory ############################################> if (!$PSBoundParameters.ContainsKey('buildDirectory')) { $buildDate = Get-Date -Format 'yyyyMMdd-HHmmss' - $buildDirectory = $env:TEMP + "\" + $buildDate + $buildDirectory = $userDirectory + "EX-CommandStation-Installer\" + $buildDate } $commandStationDirectory = $buildDirectory + "\CommandStation-EX" @@ -94,8 +95,6 @@ Current installer options: - EX-CommandStation will be built in $commandStationDirectory - Arduino CLI will be in $arduinoCLIDirectory -Available EX-CommandStation versions: -------------------------------------- "@ @@ -191,6 +190,11 @@ foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$ <############################################ Display options for user to select and get the selection ############################################> +@" + +Available EX-CommandStation versions: +------------------------------------- +"@ foreach ($selection in $userList.Keys | Sort-Object $selection) { Write-Output "$selection - $($userList[$selection])" } From e7d9626a729e52fb05c1cbc2f86b75cdde63f48e Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 8 Apr 2023 08:39:42 +1000 Subject: [PATCH 80/97] Fix CLI directory name --- installer.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index ac2ec9e..cb3bf73 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -37,7 +37,7 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.5" +$installerVersion = "v0.0.6" $userDirectory = $env:USERPROFILE + "\" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" @@ -48,7 +48,7 @@ if ((Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchi $arduinoCLIURL = "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Windows_32bit.zip" $arduinoCLIZip = $userDirectory + "Downloads\" + "arduino-cli_latest_Windows_32bit.zip" } -$arduinoCLIDirectory = $userDirectory + "arduino-cli_installer" +$arduinoCLIDirectory = $userDirectory + "arduino-cli" $arduinoCLI = $arduinoCLIDirectory + "\arduino-cli.exe" <############################################ From 758991763882513111443766a22ce18ebc0b5aca Mon Sep 17 00:00:00 2001 From: pmantoine Date: Sat, 8 Apr 2023 14:15:38 +0800 Subject: [PATCH 81/97] STM32 ADCee fix --- DCCTimerSTM32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 83ac99b..de2c235 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -237,7 +237,7 @@ int16_t ADCee::ADCmax() { } int ADCee::init(uint8_t pin) { - uint id = pin - A0; + uint8_t id = pin - A0; int value = 0; PinName stmpin = digitalPin[analogInputPin[id]]; uint32_t stmgpio = stmpin / 16; // 16-bits per GPIO port group on STM32 From 3868bb19ac17da648eacdc0a0d6809b3375aa023 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Sat, 8 Apr 2023 14:51:10 +0800 Subject: [PATCH 82/97] More uint type fixes --- DCCTimerSAMD.cpp | 4 ++-- DCCTimerSTM32.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DCCTimerSAMD.cpp b/DCCTimerSAMD.cpp index 463cc64..f878ae5 100644 --- a/DCCTimerSAMD.cpp +++ b/DCCTimerSAMD.cpp @@ -162,7 +162,7 @@ uint16_t ADCee::usedpins = 0; int * ADCee::analogvals = NULL; int ADCee::init(uint8_t pin) { - uint id = pin - A0; + uint8_t id = pin - A0; int value = 0; if (id > NUM_ADC_INPUTS) @@ -210,7 +210,7 @@ int ADCee::read(uint8_t pin, bool fromISR) { #pragma GCC push_options #pragma GCC optimize ("-O3") void ADCee::scan() { - static uint id = 0; // id and mask are the same thing but it is faster to + static uint8_t id = 0; // id and mask are the same thing but it is faster to static uint16_t mask = 1; // increment and shift instead to calculate mask from id static bool waiting = false; diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index de2c235..8038d71 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -306,7 +306,7 @@ int ADCee::read(uint8_t pin, bool fromISR) { #pragma GCC push_options #pragma GCC optimize ("-O3") void ADCee::scan() { - static uint id = 0; // id and mask are the same thing but it is faster to + static uint8_t id = 0; // id and mask are the same thing but it is faster to static uint16_t mask = 1; // increment and shift instead to calculate mask from id static bool waiting = false; From c11d8f6359390ae531ee7bd198b2ac2e0306f708 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Sat, 8 Apr 2023 17:22:39 +0100 Subject: [PATCH 83/97] Add TURNOUTL Macro to EXRAIL Add TURNOUTL Macro to EXRAIL and retrospective update to version.h to record addition of EX-FastClock mods. --- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 1 + version.h | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index b4ffc6d..181fb5d 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -134,6 +134,7 @@ #undef STOP #undef THROW #undef TURNOUT +#undef TURNOUTL #undef UNJOIN #undef UNLATCH #undef VIRTUAL_SIGNAL @@ -254,6 +255,7 @@ #define STOP #define THROW(id) #define TURNOUT(id,addr,subaddr,description...) +#define TURNOUTL(id,addr,description...) #define UNJOIN #define UNLATCH(sensor_id) #define VIRTUAL_SIGNAL(id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index ee20c1f..3aa4631 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -361,6 +361,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), +#define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description) #define UNJOIN OPCODE_UNJOIN,0,0, #define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id), #define VIRTUAL_SIGNAL(id) diff --git a/version.h b/version.h index a8c8bde..768e8b2 100644 --- a/version.h +++ b/version.h @@ -5,6 +5,7 @@ #define VERSION "4.2.41" +// 4.2.42 - Added EXRAIL TURNOUTL Macro definition // 4.2.41 - Move HAl startup to ASAP in setup() // - Fix DNOU8 output pin setup to all LOW // 4.2.40 - Automatically detect conflicting default I2C devices and disable @@ -72,6 +73,10 @@ // 4.2.11 Exrail IFLOCO feature added // 4.2.10 SIGNAL/SIGNALH bug fix as they were inverted // IO_EXIOExpander.h input speed optimisation +// ONCLOCK and ONCLOCKTIME command added to EXRAIL for EX-FastCLock +// Serial command added for EX-FastClock +// Broadcast added for EX-FastClock +// IO_EXFastClock.h added for I2C FastClock connection // 4.2.9 duinoNodes support // 4.2.8 HIGHMEM (EXRAIL support beyond 64kb) // Withrottle connect/disconnect improvements From 9b7d1ae85889fd7781d0108d67ac40561801449c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 8 Apr 2023 23:33:33 +0200 Subject: [PATCH 84/97] STM32: Use predefined function for pinnames --- DCCTimerSTM32.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 83ac99b..d1f2027 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -237,9 +237,8 @@ int16_t ADCee::ADCmax() { } int ADCee::init(uint8_t pin) { - uint id = pin - A0; int value = 0; - PinName stmpin = digitalPin[analogInputPin[id]]; + PinName stmpin = analogInputToPinName(pin); uint32_t stmgpio = stmpin / 16; // 16-bits per GPIO port group on STM32 uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC channel (only valid for ADC1!) GPIO_TypeDef * gpioBase; @@ -280,6 +279,8 @@ int ADCee::init(uint8_t pin) { analogvals = (int *)calloc(NUM_ADC_INPUTS+1, sizeof(int)); analogchans = (uint32_t *)calloc(NUM_ADC_INPUTS+1, sizeof(uint32_t)); } + + uint8_t id = pin - PNUM_ANALOG_BASE; analogvals[id] = value; // Store sampled value analogchans[id] = adcchan; // Keep track of which ADC channel is used for reading this pin usedpins |= (1 << id); // This pin is now ready @@ -291,7 +292,7 @@ int ADCee::init(uint8_t pin) { * Read function ADCee::read(pin) to get value instead of analogRead(pin) */ int ADCee::read(uint8_t pin, bool fromISR) { - uint8_t id = pin - A0; + uint8_t id = pin - PNUM_ANALOG_BASE; // Was this pin initialised yet? if ((usedpins & (1<CR2 |= (1 << 0); // Switch on ADC1 interrupts(); } -#endif \ No newline at end of file +#endif From bfa33a9df767aab290ee1c27bdf67efaacc4bcfe Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 10 Apr 2023 01:47:00 +0200 Subject: [PATCH 85/97] Fix STM32 set right port mode bits for analog port --- DCCTimerSTM32.cpp | 17 ++++++++++++++--- GITHUB_SHA.h | 2 +- MotorDriver.cpp | 5 ++++- version.h | 3 ++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 5dd72db..5660be1 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -240,7 +240,10 @@ int ADCee::init(uint8_t pin) { int value = 0; PinName stmpin = analogInputToPinName(pin); - uint32_t stmgpio = stmpin / 16; // 16-bits per GPIO port group on STM32 + if (stmpin == NC) // do not continue if this is not an analog pin at all + return -1024; // some silly value as error + + uint32_t stmgpio = STM_PORT(stmpin); // converts to the GPIO port (16-bits per port group on STM32) uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC channel (only valid for ADC1!) GPIO_TypeDef * gpioBase; @@ -258,12 +261,20 @@ int ADCee::init(uint8_t pin) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Power up PORTC gpioBase = GPIOC; break; + default: + return -1023; // some silly value as error } - // Set pin mux mode to analog input - gpioBase->MODER |= (0b011 << (stmpin << 1)); // Set pin mux to analog mode + // Set pin mux mode to analog input, the 32 bit port mode register has 2 bits per pin + gpioBase->MODER |= (0b011 << (STM_PIN(stmpin) << 1)); // Set pin mux to analog mode (binary 11) // Set the sampling rate for that analog input + // This is F411x specific! Different on for example F334 + // STM32F11xC/E Reference manual + // 11.12.4 ADC sample time register 1 (ADC_SMPR1) (channels 10 to 18) + // 11.12.5 ADC sample time register 2 (ADC_SMPR2) (channels 0 to 9) + if (adcchan > 18) + return -1022; // silly value as error if (adcchan < 10) ADC1->SMPR2 |= (0b111 << (adcchan * 3)); // Channel sampling rate 480 cycles else diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index fbd3f68..fbecd70 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202304071845Z" +#define GITHUB_SHA "devel-202304092344Z" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index a62dc9c..28ecba6 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -210,7 +210,10 @@ int MotorDriver::getCurrentRaw(bool fromISR) { (void)fromISR; if (currentPin==UNUSED_PIN) return 0; int current; - current = ADCee::read(currentPin, fromISR)-senseOffset; + current = ADCee::read(currentPin, fromISR); + // here one can diag raw value + // if (fromISR == false) DIAG(F("%c: %d"), trackLetter, current); + current = current-senseOffset; // adjust with offset if (current<0) current=0-current; if ((faultPin != UNUSED_PIN) && isLOW(fastFaultPin) && powerMode==POWERMODE::ON) return (current == 0 ? -1 : -current); diff --git a/version.h b/version.h index 768e8b2..442c351 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.41" +#define VERSION "4.2.43" +// 4.2.43 - Fix STM32 set right port mode bits for analog // 4.2.42 - Added EXRAIL TURNOUTL Macro definition // 4.2.41 - Move HAl startup to ASAP in setup() // - Fix DNOU8 output pin setup to all LOW From 1d29be9de6d604c625d17843a0cc5d83f4996f43 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 10 Apr 2023 19:52:31 +1000 Subject: [PATCH 86/97] Working on 0.0.7 --- installer.ps1 | 395 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 252 insertions(+), 143 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index cb3bf73..56eeec0 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -25,7 +25,7 @@ Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass <############################################ Optional command line parameters: $buildDirectory - specify an existing directory rather than generating a new unique one - $version - specify an exact version to download + $configDirectory - specify a directory containing existing files as per $configFiles ############################################> Param( [Parameter()] @@ -37,7 +37,9 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.6" +$installerVersion = "v0.0.7" +$configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") +$wifiBoards = @("arduino:avr:mega", "esp32:esp32:esp32") $userDirectory = $env:USERPROFILE + "\" $gitHubAPITags = "https://api.github.com/repos/DCC-EX/CommandStation-EX/git/refs/tags" $gitHubURLPrefix = "https://github.com/DCC-EX/CommandStation-EX/archive/" @@ -66,6 +68,47 @@ $supportedDevices = @( @{ name = "Arduino Uno" fqbn = "arduino:avr:uno" + }, + @{ + name = "ESP32 Dev Module" + fqbn = "esp32:esp32:esp32" + } +) + +<############################################ +List of supported displays and scroll modes +############################################> +$displayList = @( + @{ + option = "LCD 16 columns x 2 rows" + configLine = "#define LCD_DRIVER 0x27,16,2" + }, + @{ + option = "LCD 16 columns x 4 rows" + configLine = "#define LCD_DRIVER 0x27,16,4" + }, + @{ + option = "OLED 128 x 32" + configLine = "#define OLED_DRIVER 128,32,0x3c" + }, + @{ + option = "OLED 128 x 64" + configLine = "#define OLED_DRIVER 128,64,0x3c" + } +) + +$scrollList = @( + @{ + mode = "Continuous - fill screen if possible" + configLine = "#define SCROLLMODE 0" + }, + @{ + mode = "Page (default) - alternate between pages" + configLine = "#define SCROLLMODE 1" + }, + @{ + mode = "Row - move up one row at a time" + configLine = "#define SCROLLMODE 2" } ) @@ -92,23 +135,27 @@ Write out intro message and prompt to continue Welcome to the DCC-EX PowerShell installer for EX-CommandStation ($installerVersion) Current installer options: + - EX-CommandStation will be built in $commandStationDirectory -- Arduino CLI will be in $arduinoCLIDirectory +- Arduino CLI will downloaded and extracted to $arduinoCLIDirectory + +Before continuing, please ensure: + +- Your computer is connected to the internet +- The device you wish to install EX-CommandStation on is connected to a USB port + +This installer will obtain the Arduino CLI (if not already present), and then download and install your chosen version of EX-CommandStation "@ - <############################################ -Create build directory if it doesn't exist, or fail +Prompt user to confirm all is ready to proceed ############################################> -if (!(Test-Path -PathType Container -Path $buildDirectory)) { - try { - New-Item -ItemType Directory -Path $buildDirectory | Out-Null - } - catch { - Write-Output "Could not create build directory $buildDirectory" - Exit - } +$confirmation = Read-Host "Enter 'Y' or 'y' then press to confirm you are ready to proceed, any other key to exit" +if ($confirmation -ne "Y" -and $confirmation -ne "y") { + Exit +} else { + Write-Output "Proceeding to obtain the Arduino CLI..." } <############################################ @@ -140,135 +187,6 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { } } -<############################################ -Get the list of tags -############################################> -try { - $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags -} -catch { - Write-Output "Failed to obtain list of available EX-CommandStation versions" - Exit -} - -<############################################ -Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want -############################################> -$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" -$tagList = @{} -foreach ($tag in $gitHubTags) { - $tagHash = @{} - $tagHash["Ref"] = $tag.ref - $version = $tag.ref.split("/")[2] - $null = $version -match $versionMatch - $tagHash["Major"] = [int]$Matches[1] - $tagHash["Minor"] = [int]$Matches[2] - $tagHash["Patch"] = [int]$Matches[3] - $tagHash["Type"] = $Matches[4] - $tagList.Add($version, $tagHash) -} - -<############################################ -Get latest two Prod and Devel for user to select -############################################> -$userList = @{} -$prodCount = 1 -$devCount = 1 -$select = 1 -foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { - if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { - $userList[$select] = $tag - $select++ - $prodCount++ - } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { - $userList[$select] = $tag - $select++ - $devCount++ - } -} - -<############################################ -Display options for user to select and get the selection -############################################> -@" - -Available EX-CommandStation versions: -------------------------------------- -"@ -foreach ($selection in $userList.Keys | Sort-Object $selection) { - Write-Output "$selection - $($userList[$selection])" -} -Write-Output "5 - Exit" -$userSelection = 0 -do { - [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" -} until ( - (($userSelection -ge 1) -and ($userSelection -le 5)) -) -if ($userSelection -eq 5) { - Write-Output "Exiting installer" - Exit -} else { - $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" -} - -<############################################ -Download the chosen version to the build directory -############################################> -$downladFile = $buildDirectory + "\CommandStation-EX.zip" -Write-Output "Downloading and extracting $($userList[$userSelection])" -try { - Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile -} -catch { - Write-Output "Error downloading EX-CommandStation zip file" - Exit -} - -<############################################ -If folder exists, bail out and tell user -############################################> -if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { - Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" - Exit -} - -<############################################ -Extract and rename to CommandStation-EX to allow building -############################################> -try { - Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force -} -catch { - Write-Output "Failed to extract EX-CommandStation zip file" - Exit -} - -$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") -try { - Rename-Item -Path $folderName -NewName $commandStationDirectory -} -catch { - Write-Output "Could not rename folder" - Exit -} - -<############################################ -If config directory provided, copy files here -############################################> -$configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") -if ($PSBoundParameters.ContainsKey('configDirectory')) { - if (Test-Path -PathType Container -Path $configDirectory) { - foreach ($file in $configFiles) { - if (Test-Path -PathType Leaf -Path "$configDirectory\$file") { - Copy-Item -Path "$configDirectory\$file" -Destination "$commandStationDirectory\$file" - } - } - } else { - Write-Output "Provided configuration directory $configDirectory does not exist, skipping" - } -} - <############################################ Make sure Arduino CLI core index updated and list of boards populated ############################################> @@ -290,7 +208,7 @@ catch { } <############################################ -Once files all together, identify available board(s) +Identify available board(s) ############################################> try { $boardList = & $arduinoCLI board list --format jsonmini | ConvertFrom-Json @@ -370,6 +288,197 @@ if ($null -eq $boardList[$selectedBoard].matching_boards.name) { $devicePort = $boardList[$selectedBoard].port.address } +<############################################ +Get the list of tags +############################################> +try { + $gitHubTags = Invoke-RestMethod -Uri $gitHubAPITags +} +catch { + Write-Output "Failed to obtain list of available EX-CommandStation versions" + Exit +} + +<############################################ +Get our GitHub tag list in a hash so we can sort by version numbers and extract just the ones we want +############################################> +$versionMatch = ".*?v(\d+)\.(\d+).(\d+)-(.*)" +$tagList = @{} +foreach ($tag in $gitHubTags) { + $tagHash = @{} + $tagHash["Ref"] = $tag.ref + $version = $tag.ref.split("/")[2] + $null = $version -match $versionMatch + $tagHash["Major"] = [int]$Matches[1] + $tagHash["Minor"] = [int]$Matches[2] + $tagHash["Patch"] = [int]$Matches[3] + $tagHash["Type"] = $Matches[4] + $tagList.Add($version, $tagHash) +} + +<############################################ +Get latest two Prod and Devel for user to select +############################################> +$userList = @{} +$prodCount = 1 +$devCount = 1 +$select = 1 +foreach ($tag in $tagList.Keys | Sort-Object {$tagList[$_]["Major"]},{$tagList[$_]["Minor"]},{$tagList[$_]["Patch"]} -Descending) { + if (($tagList[$tag]["Type"] -eq "Prod") -and $prodCount -le 2) { + $userList[$select] = $tag + $select++ + $prodCount++ + } elseif (($tagList[$tag]["Type"] -eq "Devel") -and $devCount -le 2) { + $userList[$select] = $tag + $select++ + $devCount++ + } +} + +<############################################ +Display options for user to select and get the selection +############################################> +@" + +Available EX-CommandStation versions: +------------------------------------- +"@ +foreach ($selection in $userList.Keys | Sort-Object $selection) { + Write-Output "$selection - $($userList[$selection])" +} +Write-Output "5 - Exit" +$userSelection = 0 +do { + [int]$userSelection = Read-Host "`r`nSelect the version to install from the list above (1 - 5)" +} until ( + (($userSelection -ge 1) -and ($userSelection -le 5)) +) +if ($userSelection -eq 5) { + Write-Output "Exiting installer" + Exit +} else { + $downloadURL = $gitHubURLPrefix + $tagList[$userList[$userSelection]]["Ref"] + ".zip" +} + +<############################################ +Create build directory if it doesn't exist, or fail +############################################> +if (!(Test-Path -PathType Container -Path $buildDirectory)) { + try { + New-Item -ItemType Directory -Path $buildDirectory | Out-Null + } + catch { + Write-Output "Could not create build directory $buildDirectory" + Exit + } +} + +<############################################ +Download the chosen version to the build directory +############################################> +$downladFile = $buildDirectory + "\CommandStation-EX.zip" +Write-Output "Downloading and extracting $($userList[$userSelection])" +try { + Invoke-WebRequest -Uri $downloadURL -OutFile $downladFile +} +catch { + Write-Output "Error downloading EX-CommandStation zip file" + Exit +} + +<############################################ +If folder exists, bail out and tell user +############################################> +if (Test-Path -PathType Container -Path "$buildDirectory\CommandStation-EX") { + Write-Output "EX-CommandStation directory already exists, please ensure you have copied any user files then delete manually: $buildDirectory\CommandStation-EX" + Exit +} + +<############################################ +Extract and rename to CommandStation-EX to allow building +############################################> +try { + Expand-Archive -Path $downladFile -DestinationPath $buildDirectory -Force +} +catch { + Write-Output "Failed to extract EX-CommandStation zip file" + Exit +} + +$folderName = $buildDirectory + "\CommandStation-EX-" + ($userList[$userSelection] -replace "^v", "") +try { + Rename-Item -Path $folderName -NewName $commandStationDirectory +} +catch { + Write-Output "Could not rename folder" + Exit +} + +<############################################ +If config directory provided, copy files here +############################################> +if ($PSBoundParameters.ContainsKey('configDirectory')) { + if (Test-Path -PathType Container -Path $configDirectory) { + foreach ($file in $configFiles) { + if (Test-Path -PathType Leaf -Path "$configDirectory\$file") { + Copy-Item -Path "$configDirectory\$file" -Destination "$commandStationDirectory\$file" + } + } + } else { + Write-Output "User provided configuration directory $configDirectory does not exist, skipping" + } +} else { + +<############################################ +If no config directory provided, prompt for display option, and WiFi if using Mega +############################################> + Write-Output "`r`nIf you have an LCD or OLED display connected, you can configure it here`r`n" + $displaySelect = 1 + foreach ($display in $displayList) { + Write-Output "$displaySelect - $($displayList[$displaySelect - 1].option)" + $displaySelect++ + } + Write-Output "$($displayList.Count + 1) - I have no display" + Write-Output "$($displayList.Count + 2) - Exit" + do { + [int]$displayChoice = Read-Host "`r`nSelect a display option" + } until ( + ($displayChoice -ge 1 -and $displayChoice -le ($displayList.Count + 2)) + ) + if ($displayChoice -eq ($displayList.Count + 2)) { + Exit + } elseif ($displayChoice -le ($displayList.Count + 1)) { + $displayLine = $displayList[$displayChoice - 1].configLine + $scrollSelect = 1 + $defaultScroll = 1 + Write-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" + foreach ($scroll in $scrollList) { + Write-Output "$scrollSelect - $($scrollList[$scrollSelect - 1].mode)" + $scrollSelect++ + } + Write-Output "$($scrollList.Count + 1) - Exit" + do { + [int]$displayScroll = Read-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" + } until ( + (($displayScroll -ge 1 -and $displayScroll -le ($scrollList.Count + 1)) -or $displayScroll -eq "") + ) + if ($displayScroll -eq ($scrollList.Count + 1)) { + Exit + } elseif ($displayScroll -eq "") { + $displayScroll = $defaultScroll + } + $scrollLine = $scrollList[$displayScroll - 1] + } + if ($wifiBoards.Contains($deviceFQBN)) { + # WiFi prompt + } +} + +<############################################ +If display or WiFi options set, create config.h +############################################> + + <############################################ Install core libraries for the platform ############################################> From 1aae0aed0af18207204987bce46bf21cca09605e Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 11 Apr 2023 05:31:44 +1000 Subject: [PATCH 87/97] Working on config output --- installer.ps1 | 64 +++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index 56eeec0..e1cd44a 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -76,7 +76,7 @@ $supportedDevices = @( ) <############################################ -List of supported displays and scroll modes +List of supported displays ############################################> $displayList = @( @{ @@ -97,19 +97,13 @@ $displayList = @( } ) -$scrollList = @( - @{ - mode = "Continuous - fill screen if possible" - configLine = "#define SCROLLMODE 0" - }, - @{ - mode = "Page (default) - alternate between pages" - configLine = "#define SCROLLMODE 1" - }, - @{ - mode = "Row - move up one row at a time" - configLine = "#define SCROLLMODE 2" - } +<############################################ +Basics of config.h +############################################> +$configLines = @( + "/*", + "This config.h file was generated by the DCC-EX PowerShell installer $version", + "*/`r`n" ) <############################################ @@ -447,37 +441,31 @@ If no config directory provided, prompt for display option, and WiFi if using Me ) if ($displayChoice -eq ($displayList.Count + 2)) { Exit - } elseif ($displayChoice -le ($displayList.Count + 1)) { - $displayLine = $displayList[$displayChoice - 1].configLine - $scrollSelect = 1 - $defaultScroll = 1 - Write-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" - foreach ($scroll in $scrollList) { - Write-Output "$scrollSelect - $($scrollList[$scrollSelect - 1].mode)" - $scrollSelect++ - } - Write-Output "$($scrollList.Count + 1) - Exit" - do { - [int]$displayScroll = Read-Host "`r`n Select a scroll option, or press to accept the default [$($defaultScroll)]" - } until ( - (($displayScroll -ge 1 -and $displayScroll -le ($scrollList.Count + 1)) -or $displayScroll -eq "") - ) - if ($displayScroll -eq ($scrollList.Count + 1)) { - Exit - } elseif ($displayScroll -eq "") { - $displayScroll = $defaultScroll - } - $scrollLine = $scrollList[$displayScroll - 1] + } elseif ($displayChoice -le 1 -and ($displayList.Count + 1)) { + $configLines.Add("// Display configuration") + $configLines.Add($displayList[$displayChoice - 1].configLine) + $configLines.Add("#define SCROLLMODE 1 // Alternate between pages") } if ($wifiBoards.Contains($deviceFQBN)) { # WiFi prompt } -} <############################################ -If display or WiFi options set, create config.h +Write out config.h to a file here only if config directory not provided ############################################> - + $configH = $commandStationDirectory + "\config.h" + foreach ($line in $configLines) { + Write-Output $line + } + Pause + try { + $configLines | Out-File -FilePath $configH + } + catch { + Write-Output "Error writing config file to $configH" + Exit + } +} <############################################ Install core libraries for the platform From 6199cecd42d4f8e5531a9b94ba52b6652b8788fe Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 08:55:04 +1000 Subject: [PATCH 88/97] 0.0.7 Ready for testing --- installer.ps1 | 64 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/installer.ps1 b/installer.ps1 index e1cd44a..9436e0a 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -89,11 +89,11 @@ $displayList = @( }, @{ option = "OLED 128 x 32" - configLine = "#define OLED_DRIVER 128,32,0x3c" + configLine = "#define OLED_DRIVER 128,32" }, @{ option = "OLED 128 x 64" - configLine = "#define OLED_DRIVER 128,64,0x3c" + configLine = "#define OLED_DRIVER 128,64" } ) @@ -102,8 +102,12 @@ Basics of config.h ############################################> $configLines = @( "/*", - "This config.h file was generated by the DCC-EX PowerShell installer $version", - "*/`r`n" + "This config.h file was generated by the DCC-EX PowerShell installer $installerVersion", + "*/", + "", + "// Define standard motor shield", + "#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD", + "" ) <############################################ @@ -148,8 +152,6 @@ Prompt user to confirm all is ready to proceed $confirmation = Read-Host "Enter 'Y' or 'y' then press to confirm you are ready to proceed, any other key to exit" if ($confirmation -ne "Y" -and $confirmation -ne "y") { Exit -} else { - Write-Output "Proceeding to obtain the Arduino CLI..." } <############################################ @@ -165,7 +167,7 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { Exit } } - Write-Output "Downloading and extracting Arduino CLI" + Write-Output "`r`nDownloading and extracting Arduino CLI" try { Invoke-WebRequest -Uri $arduinoCLIURL -OutFile $arduinoCLIZip } @@ -179,6 +181,8 @@ if (!(Test-Path -PathType Leaf -Path $arduinoCLI)) { catch { Write-Output "Failed to extract Arduino CLI" } +} else { + Write-Output "`r`nArduino CLI already downloaded, ensuring it is up to date and you have a board connected" } <############################################ @@ -424,7 +428,7 @@ if ($PSBoundParameters.ContainsKey('configDirectory')) { } else { <############################################ -If no config directory provided, prompt for display option, and WiFi if using Mega +If no config directory provided, prompt for display option ############################################> Write-Output "`r`nIf you have an LCD or OLED display connected, you can configure it here`r`n" $displaySelect = 1 @@ -441,25 +445,49 @@ If no config directory provided, prompt for display option, and WiFi if using Me ) if ($displayChoice -eq ($displayList.Count + 2)) { Exit - } elseif ($displayChoice -le 1 -and ($displayList.Count + 1)) { - $configLines.Add("// Display configuration") - $configLines.Add($displayList[$displayChoice - 1].configLine) - $configLines.Add("#define SCROLLMODE 1 // Alternate between pages") + } elseif ($displayChoice -le ($displayList.Count)) { + $configLines+= "// Display configuration" + $configLines+= "$($displayList[$displayChoice - 1].configLine)" + $configLines+= "#define SCROLLMODE 1 // Alternate between pages" } +<############################################ +If device supports WiFi, prompt to configure +############################################> if ($wifiBoards.Contains($deviceFQBN)) { - # WiFi prompt + Write-Output "`r`nYour chosen board supports WiFi`r`n" + Write-Output "1 - I don't want WiFi, skip this step +2 - Configure my device as an access point I will connect to directly +3 - Configure my device to connect to my home WiFi network +4 - Exit" + do { + [int]$wifiChoice = Read-Host "`r`nSelect a WiFi option" + } until ( + ($wifiChoice -ge 1 -and $wifiChoice -le 4) + ) + if ($wifiChoice -eq 4) { + Exit + } elseif ($wifiChoice -ne 1) { + $configLines+= "" + $configLines+= "// WiFi configuration" + $configLines+= "#define ENABLE_WIFI true" + $configLines+= "#define IP_PORT 2560" + $configLines+= "#define WIFI_HOSTNAME ""dccex""" + $configLines+= "#define WIFI_CHANNEL 1" + if ($wifiChoice -eq 3) { + $wifiSSID = Read-Host "Please enter the SSID of your home network here" + $wifiPassword = Read-Host "Please enter your home network WiFi password here" + $configLines+= "#define WIFI_SSID ""$($wifiSSID)""" + $configLines+= "#define WIFI_PASSWORD ""$($wifiPassword)""" + } + } } <############################################ Write out config.h to a file here only if config directory not provided ############################################> $configH = $commandStationDirectory + "\config.h" - foreach ($line in $configLines) { - Write-Output $line - } - Pause try { - $configLines | Out-File -FilePath $configH + $configLines | Out-File -FilePath $configH -Encoding ascii } catch { Write-Output "Error writing config file to $configH" From bb7cdc5422603465bb4486f24e2a51fd8caf99ed Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 09:42:54 +1000 Subject: [PATCH 89/97] WiFi AP mode compiles --- installer.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/installer.ps1 b/installer.ps1 index 9436e0a..4931081 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -473,6 +473,10 @@ If device supports WiFi, prompt to configure $configLines+= "#define IP_PORT 2560" $configLines+= "#define WIFI_HOSTNAME ""dccex""" $configLines+= "#define WIFI_CHANNEL 1" + if ($wifiChoice -eq 2) { + $configLines+= "#define WIFI_SSID ""Your network name""" + $configLines+= "#define WIFI_PASSWORD ""Your network passwd""" + } if ($wifiChoice -eq 3) { $wifiSSID = Read-Host "Please enter the SSID of your home network here" $wifiPassword = Read-Host "Please enter your home network WiFi password here" From 05b0fc3d2e9524afbfdafaaf4ef3ea024a87eb05 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:26:15 +1000 Subject: [PATCH 90/97] Add exe version --- install_via_powershell.cmd | 2 -- installer.exe | Bin 0 -> 75264 bytes installer.ps1 | 15 +++++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 installer.exe diff --git a/install_via_powershell.cmd b/install_via_powershell.cmd index f367b21..c3a7e03 100644 --- a/install_via_powershell.cmd +++ b/install_via_powershell.cmd @@ -11,5 +11,3 @@ powershell %~dp0%installer.ps1 IF NOT %PS_POLICY=="Bypass" ( powershell Set-ExecutionPolicy -Scope CurrentUser %PS_POLICY% ) - -PAUSE \ No newline at end of file diff --git a/installer.exe b/installer.exe new file mode 100644 index 0000000000000000000000000000000000000000..f783eb2c325a77da38f9b6a62d96ec446f83d8b2 GIT binary patch literal 75264 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 literal 0 HcmV?d00001 diff --git a/installer.ps1 b/installer.ps1 index 4931081..b690824 100644 --- a/installer.ps1 +++ b/installer.ps1 @@ -37,7 +37,7 @@ Param( <############################################ Define global parameters here such as known URLs etc. ############################################> -$installerVersion = "v0.0.7" +$installerVersion = "v0.0.8" $configFiles = @("config.h", "myAutomation.h", "myHal.cpp", "mySetup.h") $wifiBoards = @("arduino:avr:mega", "esp32:esp32:esp32") $userDirectory = $env:USERPROFILE + "\" @@ -431,12 +431,12 @@ if ($PSBoundParameters.ContainsKey('configDirectory')) { If no config directory provided, prompt for display option ############################################> Write-Output "`r`nIf you have an LCD or OLED display connected, you can configure it here`r`n" - $displaySelect = 1 + Write-Output "1 - I have no display, skip this step" + $displaySelect = 2 foreach ($display in $displayList) { - Write-Output "$displaySelect - $($displayList[$displaySelect - 1].option)" + Write-Output "$displaySelect - $($displayList[$displaySelect - 2].option)" $displaySelect++ } - Write-Output "$($displayList.Count + 1) - I have no display" Write-Output "$($displayList.Count + 2) - Exit" do { [int]$displayChoice = Read-Host "`r`nSelect a display option" @@ -445,9 +445,9 @@ If no config directory provided, prompt for display option ) if ($displayChoice -eq ($displayList.Count + 2)) { Exit - } elseif ($displayChoice -le ($displayList.Count)) { + } elseif ($displayChoice -ge 2) { $configLines+= "// Display configuration" - $configLines+= "$($displayList[$displayChoice - 1].configLine)" + $configLines+= "$($displayList[$displayChoice - 2].configLine)" $configLines+= "#define SCROLLMODE 1 // Alternate between pages" } <############################################ @@ -535,3 +535,6 @@ if ($output.success -eq "True") { Write-Output "Builder result: $($output.builder_result)`r`n" } } + +Write-Output "`r`nPress any key to exit the installer" +[void][System.Console]::ReadKey($true) From 3e9537281664e50f4bacae7fb91e0c39555ce0bd Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:48:35 +1000 Subject: [PATCH 91/97] Rename installer exe --- ...ler.exe => EX-CommandStation-installer.exe | Bin 75264 -> 75264 bytes 1 file changed, 0 insertions(+), 0 deletions(-) rename installer.exe => EX-CommandStation-installer.exe (80%) diff --git a/installer.exe b/EX-CommandStation-installer.exe similarity index 80% rename from installer.exe rename to EX-CommandStation-installer.exe index f783eb2c325a77da38f9b6a62d96ec446f83d8b2..88f12a243dc84a5eda81c224c52548563bbd7f4c 100644 GIT binary patch delta 5399 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 delta 5297 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 From 4bad33487569bb6c79d7b4d20d154221ed47ee8b Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:01:25 +1000 Subject: [PATCH 92/97] Update version --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 442c351..612d9d7 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.43" +#define VERSION "4.2.44" +// 4.2.44 - Add PowerShell installer EX-CommandStation-installer.exe // 4.2.43 - Fix STM32 set right port mode bits for analog // 4.2.42 - Added EXRAIL TURNOUTL Macro definition // 4.2.41 - Move HAl startup to ASAP in setup() From 16e44eb11a3d8bdfe3923d9aefed12750bff744d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 11 Apr 2023 12:13:47 +0200 Subject: [PATCH 93/97] Check for max 16 analog channels --- DCCTimerSTM32.cpp | 10 ++++++---- GITHUB_SHA.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 5660be1..fcbf286 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -286,13 +286,15 @@ int ADCee::init(uint8_t pin) { while(!(ADC1->SR & (1 << 1))); // Wait until conversion is complete value = ADC1->DR; // Read value from register - if (analogvals == NULL) - { + uint8_t id = pin - PNUM_ANALOG_BASE; + if (id > 15) { // today we have not enough bits in the mask to support more + return -1021; + } + + if (analogvals == NULL) { // allocate analogvals and analogchans if this is the first invocation of init. analogvals = (int *)calloc(NUM_ADC_INPUTS+1, sizeof(int)); analogchans = (uint32_t *)calloc(NUM_ADC_INPUTS+1, sizeof(uint32_t)); } - - uint8_t id = pin - PNUM_ANALOG_BASE; analogvals[id] = value; // Store sampled value analogchans[id] = adcchan; // Keep track of which ADC channel is used for reading this pin usedpins |= (1 << id); // This pin is now ready diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index fbecd70..0ac9c5f 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202304092344Z" +#define GITHUB_SHA "devel-202304111013Z" From 294b9693c5360a485daf26baad1011004eca4379 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 12 Apr 2023 12:07:08 +0100 Subject: [PATCH 94/97] Additions to FastClock Added ONCLOCKMINS to fastclock to allow hourly repeated events. --- EXRAIL2.cpp | 5 ++++- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 52d10e8..31a0de6 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1129,7 +1129,10 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) { // Hunt for an ONTIME for this time if (Diag::CMD) DIAG(F("Looking for clock event at : %d"), clocktime); - if (change) handleEvent(F("CLOCK"),onClockLookup,clocktime); + if (change) { + handleEvent(F("CLOCK"),onClockLookup,clocktime); + handleEvent(F("CLOCK"),onClockLookup,25*60+clocktime%60); + } } void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) { diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 181fb5d..6ea195e 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -91,6 +91,7 @@ #undef ONCLOSE #undef ONTIME #undef ONCLOCKTIME +#undef ONCLOCKMINS #undef ONGREEN #undef ONRED #undef ONTHROW @@ -209,6 +210,7 @@ #define ONAMBER(signal_id) #define ONTIME(value) #define ONCLOCKTIME(hours,mins) +#define ONCLOCKMINS(mins) #define ONDEACTIVATE(addr,subaddr) #define ONDEACTIVATEL(linear) #define ONCLOSE(turnout_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 3aa4631..446b388 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -316,6 +316,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id), #define ONTIME(value) OPCODE_ONTIME,V(value), #define ONCLOCKTIME(hours,mins) OPCODE_ONTIME,V((STRIP_ZERO(hours)*60)+STRIP_ZERO(mins)), +#define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins) #define ONDEACTIVATE(addr,subaddr) OPCODE_ONDEACTIVATE,V(addr<<2|subaddr), #define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3), #define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id), From ff53b9003491d277241b5c9bb8a5baeb8167aaaf Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 12 Apr 2023 12:11:43 +0100 Subject: [PATCH 95/97] Update version.h Added ONCLOCKMINS for FastClock --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 612d9d7..15c6d0b 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.44" +#define VERSION "4.2.45" +// 4.2.45 - Add ONCLOCKMINS to FastClock to allow hourly repeat events // 4.2.44 - Add PowerShell installer EX-CommandStation-installer.exe // 4.2.43 - Fix STM32 set right port mode bits for analog // 4.2.42 - Added EXRAIL TURNOUTL Macro definition From 75f274e3b75f4975d8968c7bb621e780b1902fa7 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 13 Apr 2023 15:27:22 +0800 Subject: [PATCH 96/97] STM32 unsupported board selection error reporting --- DCCTimerSTM32.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index fcbf286..4f9cbdd 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -1,8 +1,8 @@ /* * © 2023 Neil McKechnie - * © 2022 Paul M. Antoine + * © 2022-23 Paul M. Antoine * © 2021 Mike S - * © 2021 Harald Barth + * © 2021, 2023 Harald Barth * © 2021 Fred Decker * © 2021 Chris Harlow * © 2021 David Cutting @@ -43,11 +43,18 @@ HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14 HardwareSerial Serial1(PA10, PB6); // Rx=PA10 (D2), Tx=PB6 (D10) -- CN10 pins 17 and 9 - F446RE // Serial2 is defined to use USART2 by default, but is in fact used as the diag console // via the debugger on the Nucleo-64. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc. +// NB: USART3 and USART6 are available but as yet undefined #elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) // Nucleo-144 boards don't have Serial1 defined by default HardwareSerial Serial1(PG9, PG14); // Rx=PG9, Tx=PG14 -- D0, D1 - F412ZG/F446ZE +// Serial2 is defined to use USART2 by default, but is in fact used as the diag console +// via the debugger on the Nucleo-144. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc. +// NB: +// On all of the above, USART3, and USART6 are available but as yet undefined +// On F446ZE and F429ZI, UART4, UART5 are also available but as yet undefined +// On F429ZI, UART7 and UART8 are also available but as yet undefined #else -#warning Serial1 not defined +#error STM32 board selected is not yet explicitly supported - so Serial1 peripheral is not defined #endif /////////////////////////////////////////////////////////////////////////////////////////////// @@ -227,6 +234,9 @@ void DCCTimer::reset() { #define NUM_ADC_INPUTS NUM_ANALOG_INPUTS // TODO: may need to use uint32_t on STMF4xx variants with > 16 analog inputs! +#if defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) +#warning STM32 board selected not fully supported - only use ADC1 inputs 0-15 for current sensing! +#endif uint16_t ADCee::usedpins = 0; int * ADCee::analogvals = NULL; uint32_t * analogchans = NULL; From d2cc60812d743abcdc90542f37ca0b55e1533393 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Sun, 16 Apr 2023 15:40:27 +0800 Subject: [PATCH 97/97] Merge ESP32-fixes into DCCTimerESP --- DCCTimer.h | 7 ++++-- DCCTimerESP.cpp | 39 +++++++++++++++++++++++++++++++ ESP32-fixes.cpp | 61 ------------------------------------------------- ESP32-fixes.h | 26 --------------------- MotorDriver.cpp | 12 ++++------ 5 files changed, 48 insertions(+), 97 deletions(-) delete mode 100644 ESP32-fixes.cpp delete mode 100644 ESP32-fixes.h diff --git a/DCCTimer.h b/DCCTimer.h index 2214851..7a9d940 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -1,7 +1,7 @@ /* - * © 2022 Paul M. Antoine + * © 2022-2023 Paul M. Antoine * © 2021 Mike S - * © 2021-2022 Harald Barth + * © 2021-2023 Harald Barth * © 2021 Fred Decker * All rights reserved. * @@ -62,6 +62,9 @@ class DCCTimer { static bool isPWMPin(byte pin); static void setPWM(byte pin, bool high); static void clearPWM(); + static void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency); + static void DCCEXanalogWrite(uint8_t pin, int value); + // Update low ram level. Allow for extra bytes to be specified // by estimation or inspection, that may be used by other // called subroutines. Must be called with interrupts disabled. diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index caaae9d..cf5978e 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -150,6 +150,45 @@ int DCCTimer::freeMemory() { void DCCTimer::reset() { ESP.restart(); } + +#include "esp32-hal.h" +#include "soc/soc_caps.h" + + +#ifdef SOC_LEDC_SUPPORT_HS_MODE +#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1) +#else +#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM) +#endif + +static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 }; +static int cnt_channel = LEDC_CHANNELS; + +void DCCTimer::DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency) { + if (pin < SOC_GPIO_PIN_COUNT) { + if (pin_to_channel[pin] != 0) { + ledcSetup(pin_to_channel[pin], frequency, 8); + } + } +} + +void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { + if (pin < SOC_GPIO_PIN_COUNT) { + if (pin_to_channel[pin] == 0) { + if (!cnt_channel) { + log_e("No more PWM channels available! All %u already used", LEDC_CHANNELS); + return; + } + pin_to_channel[pin] = --cnt_channel; + ledcAttachPin(pin, cnt_channel); + ledcSetup(cnt_channel, 1000, 8); + } else { + ledcAttachPin(pin, pin_to_channel[pin]); + } + ledcWrite(pin_to_channel[pin], value); + } +} + int ADCee::init(uint8_t pin) { pinMode(pin, ANALOG); adc1_config_width(ADC_WIDTH_BIT_12); diff --git a/ESP32-fixes.cpp b/ESP32-fixes.cpp deleted file mode 100644 index e3c350c..0000000 --- a/ESP32-fixes.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * © 2022 Harald Barth - * All rights reserved. - * - * This file is part of CommandStation-EX - * - * 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 . - */ -#ifdef ARDUINO_ARCH_ESP32 -#include -#include "ESP32-fixes.h" - -#include "esp32-hal.h" -#include "soc/soc_caps.h" - - -#ifdef SOC_LEDC_SUPPORT_HS_MODE -#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1) -#else -#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM) -#endif - -static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 }; -static int cnt_channel = LEDC_CHANNELS; - -void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency) { - if (pin < SOC_GPIO_PIN_COUNT) { - if (pin_to_channel[pin] != 0) { - ledcSetup(pin_to_channel[pin], frequency, 8); - } - } -} - -void DCCEXanalogWrite(uint8_t pin, int value) { - if (pin < SOC_GPIO_PIN_COUNT) { - if (pin_to_channel[pin] == 0) { - if (!cnt_channel) { - log_e("No more PWM channels available! All %u already used", LEDC_CHANNELS); - return; - } - pin_to_channel[pin] = --cnt_channel; - ledcAttachPin(pin, cnt_channel); - ledcSetup(cnt_channel, 1000, 8); - } else { - ledcAttachPin(pin, pin_to_channel[pin]); - } - ledcWrite(pin_to_channel[pin], value); - } -} -#endif diff --git a/ESP32-fixes.h b/ESP32-fixes.h deleted file mode 100644 index 0fd4a7f..0000000 --- a/ESP32-fixes.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * © 2022 Harald Barth - * All rights reserved. - * - * This file is part of CommandStation-EX - * - * 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 . - */ -#ifdef ARDUINO_ARCH_ESP32 -#pragma once -#include -void DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency); -void DCCEXanalogWrite(uint8_t pin, int value); -#endif - diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 28ecba6..9f39123 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -1,8 +1,8 @@ /* - * © 2022 Paul M Antoine + * © 2022-2023 Paul M Antoine * © 2021 Mike S * © 2021 Fred Decker - * © 2020-2022 Harald Barth + * © 2020-2023 Harald Barth * © 2020-2021 Chris Harlow * All rights reserved. * @@ -27,10 +27,6 @@ #include "DCCTimer.h" #include "DIAG.h" -#if defined(ARDUINO_ARCH_ESP32) -#include "ESP32-fixes.h" -#endif - bool MotorDriver::commonFaultPin=false; volatile portreg_t shadowPORTA; @@ -286,7 +282,7 @@ void MotorDriver::setDCSignal(byte speedcode) { f = taurustones[ (tSpeed-2)/2 ] ; } } - DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency to 100Hz XXX May move to setup + DCCTimer::DCCEXanalogWriteFrequency(brakePin, f); // set DC PWM frequency to 100Hz XXX May move to setup } #endif if (tSpeed <= 1) brake = 255; @@ -295,7 +291,7 @@ void MotorDriver::setDCSignal(byte speedcode) { if (invertBrake) brake=255-brake; #if defined(ARDUINO_ARCH_ESP32) - DCCEXanalogWrite(brakePin,brake); + DCCTimer::DCCEXanalogWrite(brakePin,brake); #else analogWrite(brakePin,brake); #endif