From 0b9ec7460ba461d5602b6e06843d6be8468f385f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 28 Oct 2023 19:18:59 +0200 Subject: [PATCH 01/16] Bugfix version detection logic and better message --- WifiInterface.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index ab36957..27830bb 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -206,12 +206,13 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, while(!wifiStream->available()); version[i]=wifiStream->read(); StringFormatter::printEscape(version[i]); - if ((version[0] == '0') || - (version[0] == '2' && version[2] == '0') || - (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { - SSid = F("DCCEX_SAYS_BROKEN_FIRMWARE"); - forceAP = true; - } + } + if ((version[0] == '0') || + (version[0] == '2' && version[2] == '0') || + (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { + DIAG(F("You need to up/downgrade the ESP firmware")); + SSid = F("UPDATE_ESP_FIRMWARE"); + forceAP = true; } } checkForOK(2000, true, false); From 659c58b30766a7b8dd2b4d2677d90663af8fefcf Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 28 Oct 2023 19:20:33 +0200 Subject: [PATCH 02/16] version 5.0.5 --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 1610054..69b9002 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.4" +#define VERSION "5.0.5" +// 5.0.5 - Bugfix version detection logic and better message // 5.0.4 - Bugfix: misses default roster. // 5.0.3 - Check bad AT firmware version // 5.0.2 - Bugfix: ESP32 30ms off time From 749a859db551113567faae3248c575dbf6440ece Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 1 Nov 2023 20:13:05 +0000 Subject: [PATCH 03/16] Bugfix TURNOUTL --- EXRAILMacros.h | 2 ++ version.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 66b0111..4bbabfc 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -172,6 +172,8 @@ void RMFT2::printMessage(uint16_t id) { #include "EXRAIL2MacroReset.h" #undef TURNOUT #define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description) +#undef TURNOUTL +#define TURNOUTL(id,addr,description...) O_DESC(id,description) #undef PIN_TURNOUT #define PIN_TURNOUT(id,pin,description...) O_DESC(id,description) #undef SERVO_TURNOUT diff --git a/version.h b/version.h index 69b9002..196d9ca 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.5" +#define VERSION "5.0.6" +// 5.0.6 - Bugfix lost TURNOUTL description // 5.0.5 - Bugfix version detection logic and better message // 5.0.4 - Bugfix: misses default roster. // 5.0.3 - Check bad AT firmware version From a981f83bb9c376d01245c328c5de7d7bf25ebfb2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 6 Nov 2023 22:11:31 +0100 Subject: [PATCH 04/16] Only flag 2.2.0.0-dev as broken, not 2.2.0.0 --- WifiInterface.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 27830bb..8b2251a 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -201,15 +201,16 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, // Display the AT version information StringFormatter::send(wifiStream, F("AT+GMR\r\n")); if (checkForOK(2000, F("AT version:"), true, false)) { - char version[] = "0.0.0.0"; - for (int i=0; i<8;i++) { + char version[] = "0.0.0.0-xxx"; + for (int i=0; i<11;i++) { while(!wifiStream->available()); version[i]=wifiStream->read(); StringFormatter::printEscape(version[i]); } if ((version[0] == '0') || (version[0] == '2' && version[2] == '0') || - (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { + (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0' + && version[7] == '-' && version[8] == 'd' && version[9] == 'e' && version[10] == 'v')) { DIAG(F("You need to up/downgrade the ESP firmware")); SSid = F("UPDATE_ESP_FIRMWARE"); forceAP = true; From 387ea019bdc483667bcbcf45205a56330d615aee Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 6 Nov 2023 22:11:56 +0100 Subject: [PATCH 05/16] version 5.0.7 --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 196d9ca..ca9e99f 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.6" +#define VERSION "5.0.7" +// 5.0.7 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 // 5.0.6 - Bugfix lost TURNOUTL description // 5.0.5 - Bugfix version detection logic and better message // 5.0.4 - Bugfix: misses default roster. From c5168f030fa64330a1f0e09d6637a3817fe5e067 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 10 Jan 2024 08:15:30 +0100 Subject: [PATCH 06/16] Do not crash on turnouts without description --- EXRAIL2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 0e17ea9..1d97e7f 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -259,8 +259,9 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { } void RMFT2::setTurnoutHiddenState(Turnout * t) { - // turnout descriptions are in low flash F strings - t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); + // turnout descriptions are in low flash F strings + const FSH *desc = getTurnoutDescription(t->getId()); + if (desc) t->setHidden(GETFLASH(desc)==0x01); } char RMFT2::getRouteType(int16_t id) { From 818e05b4253a1a0980abb3a0bbef38a8c662bb1a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 10 Jan 2024 08:37:54 +0100 Subject: [PATCH 07/16] version 5.0.8 --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index ca9e99f..bb5bef8 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.7" +#define VERSION "5.0.8" +// 5.0.8 - Bugfix: Do not crash on turnouts without description // 5.0.7 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 // 5.0.6 - Bugfix lost TURNOUTL description // 5.0.5 - Bugfix version detection logic and better message From fb414a7a506f078d2a075d65c7b171ae4399ef63 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 18 Jan 2024 08:20:33 +0100 Subject: [PATCH 08/16] Bugfix: allocate enough bytes for digital pins. Add more sanity checks when allocating memory --- IO_EXIOExpander.h | 88 +++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/IO_EXIOExpander.h b/IO_EXIOExpander.h index 675d66c..db08e70 100644 --- a/IO_EXIOExpander.h +++ b/IO_EXIOExpander.h @@ -1,5 +1,6 @@ /* * © 2022, Peter Cole. All rights reserved. + * © 2024, Harald Barth. All rights reserved. * * This file is part of EX-CommandStation * @@ -98,34 +99,53 @@ private: _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 * 2; - if (_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (_analoguePinBytes > 0) { - free(_analogueInputBuffer); - free(_analogueInputStates); - free(_analoguePinMap); + if (_numDigitalPins>0) { + 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); + if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + _digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpander I2C:%s ERROR alloc %d bytes"), _I2CAddress.toString(), digitalBytesNeeded); + _deviceState = DEVSTATE_FAILED; + _digitalPinBytes = 0; + return; + } } - _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()); - _deviceState = DEVSTATE_FAILED; - return; - } - } - // We now need to retrieve the analogue pin map - if (status == I2C_STATUS_OK) { + if (_numAnaloguePins>0) { + size_t analogueBytesNeeded = _numAnaloguePins * 2; + if (_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (_analoguePinBytes > 0) { + free(_analogueInputBuffer); + free(_analogueInputStates); + free(_analoguePinMap); + } + _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + if (_analogueInputStates != NULL && + _analogueInputBuffer != NULL && + _analoguePinMap != NULL) { + _analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpander I2C:%s ERROR alloc analog pin bytes"), _I2CAddress.toString()); + _deviceState = DEVSTATE_FAILED; + _analoguePinBytes = 0; + return; + } + } + } + } 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 if there are analogue pins + if (status == I2C_STATUS_OK && _numAnaloguePins>0) { commandBuffer[0] = EXIOINITA; status = I2CManager.read(_I2CAddress, _analoguePinMap, _numAnaloguePins, commandBuffer, 1); } @@ -239,7 +259,7 @@ private: // 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 + if (_numDigitalPins>0 && 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; @@ -247,7 +267,7 @@ private: // non-blocking read _lastDigitalRead = currentMicros; _readState = RDS_DIGITAL; - } else if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh + } else if (_numAnaloguePins>0 && currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh // Issue new read for analogue input states _readCommandBuffer[0] = EXIORDAN; I2CManager.read(_I2CAddress, _analogueInputBuffer, @@ -362,14 +382,14 @@ private: uint8_t _minorVer = 0; uint8_t _patchVer = 0; - uint8_t* _digitalInputStates; - uint8_t* _analogueInputStates; - uint8_t* _analogueInputBuffer; // buffer for I2C input transfers + uint8_t* _digitalInputStates = NULL; + uint8_t* _analogueInputStates = NULL; + uint8_t* _analogueInputBuffer = NULL; // 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; + uint8_t _digitalPinBytes = 0; // Size of allocated memory buffer (may be longer than needed) + uint8_t _analoguePinBytes = 0; // Size of allocated memory buffer (may be longer than needed) + uint8_t* _analoguePinMap = NULL; I2CRB _i2crb; enum {RDS_IDLE, RDS_DIGITAL, RDS_ANALOGUE}; // Read operation states From 3b162996ad42546486b812e22d3ed6daee857d19 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 21 Jan 2024 07:13:53 +1000 Subject: [PATCH 09/16] EX-IO fixes in version --- version.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index bb5bef8..bf7048f 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.0.8" +#define VERSION "5.0.9" +// 5.0.9 - EX-IOExpander bug fix for memory allocation +// - EX-IOExpander bug fix to allow for devices with no analogue or no digital pins // 5.0.8 - Bugfix: Do not crash on turnouts without description // 5.0.7 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 // 5.0.6 - Bugfix lost TURNOUTL description From 28d60d49849c8fc4b0ff0f933222c052ba7c90aa Mon Sep 17 00:00:00 2001 From: Peter Akers Date: Fri, 16 Feb 2024 18:02:40 +1000 Subject: [PATCH 10/16] Update README.md --- README.md | 82 +++++++++++++++---------------------------------------- 1 file changed, 22 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index f820075..9c8f627 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,39 @@ -# What is DCC++ EX? -DCC++ EX is the organization maintaining several codebases that together represent a fully open source DCC system. Currently, this includes the following: +# What is DCC-EX? +DCC-EX is a team of dedicated enthusiasts producing open source DCC & DC solutions for you to run your complete model railroad layout. Our easy to use, do-it-yourself, and free open source products run on off-the-shelf Arduino technology and are supported by numerous third party hardware and apps like JMRI, Engine Driver, wiThrottle, Rocrail and more. -* [CommandStation-EX](https://github.com/DCC-EX/CommandStation-EX/releases) - the latest take on the DCC++ command station for controlling your trains. Runs on an Arduino board, and includes advanced features such as a WiThrottle server implementation, turnout operation, general purpose inputs and outputs (I/O), and JMRI integration. -* [exWebThrottle](https://github.com/DCC-EX/exWebThrottle) - a simple web based controller for your DCC++ command station. -* [BaseStation-installer](https://github.com/DCC-EX/BaseStation-Installer) - an installer executable that takes care of downloading and installing DCC++ firmware onto your hardware setup. -* [BaseStation-Classic](https://github.com/DCC-EX/BaseStation-Classic) - the original DCC++ software, packaged in a stable release. No active development, bug fixes only. +Currently, our products include the following: -A basic DCC++ EX hardware setup can use easy to find, widely avalable Arduino boards that you can assemble yourself. - -Both CommandStation-EX and BaseStation-Classic support much of the NMRA Digital Command Control (DCC) [standards](http://www.nmra.org/dcc-working-group "NMRA DCC Working Group"), including: - -* simultaneous control of multiple locomotives -* 2-byte and 4-byte locomotive addressing -* 28 or 128-step speed throttling -* Activate/de-activate all accessory function addresses 0-2048 -* Control of all cab functions F0-F28 and F29-F68 -* Main Track: Write configuration variable bytes and set/clear specific configuration variable (CV) bits (aka Programming on Main or POM) -* Programming Track: Same as the main track with the addition of reading configuration variable bytes -* And many more custom features. see [What's new in CommandStation-EX?](#whats-new-in-commandstation-ex) +* [EX-CommandStation](https://github.com/DCC-EX/CommandStation-EX/releases) +* [EX-WebThrottle](https://github.com/DCC-EX/exWebThrottle) +* [EX-Installer](https://github.com/DCC-EX/EX-Installer) +* [EX-MotoShield8874](https://dcc-ex.com/reference/hardware/motorboards/ex-motor-shield-8874.html#gsc.tab=0) +* [EX-DCCInspector](https://github.com/DCC-EX/DCCInspector-EX) +* [EX-Toolbox](https://github.com/DCC-EX/EX-Toolbox) +* [EX-Turntable](https://github.com/DCC-EX/EX-Turntable) +* [EX-IOExpander](https://github.com/DCC-EX/EX-IOExpander) +* [EX-FastClock](https://github.com/DCC-EX/EX-FastClock) +* [DCCEXProtocol](https://github.com/DCC-EX/DCCEXProtocol) +Details of these projects can be found on [our web site](https://dcc-ex.com/). # What’s in this Repository? -This repository, CommandStation-EX, contains a complete DCC++ EX Commmand Station sketch designed for compiling and uploading into an Arduino Uno, Mega, or Nano. +This repository, CommandStation-EX, contains a complete DCC-EX *EX-CommmandStation* sketch designed for compiling and uploading into an Arduino Uno, Mega, or Nano. To utilize this sketch, you can use the following: -1. (beginner) our [automated installer](https://github.com/DCC-EX/BaseStation-Installer) +1. (recommended for all levels of user) our [automated installer](https://github.com/DCC-EX/EX-Installer) 2. (intermediate) download the latest version from the [releases page](https://github.com/DCC-EX/CommandStation-EX/releases) 3. (advanced) use git clone on this repository -Not using the installer? Open the file "CommandStation-EX.ino" in the -Arduino IDE. Please do not rename the folder containing the sketch -code, nor add any files in that folder. The Arduino IDE relies on the -structure and name of the folder to properly display and compile the -code. Rename or copy config.example.h to config.h. If you do not have -the standard setup, you must edit config.h according to the help texts -in config.h. +Refer to [our web site](https://https://dcc-ex.com/ex-commandstation/get-started/index.html#/) for the hardware required for this project. -## What's new in CommandStation-EX? +**We seriously recommend using the EX-Installer**, however if you choose not to use the installer... -* WiThrottle server built in. Connect Engine Driver or WiThrottle clients directly to your Command Station (or through JMRI as before) -* WiFi and Ethernet shield support -* No more jumpers or soldering! -* Direct support for all the most popular motor control boards including single pin (Arduino) or dual pin (IBT_2) type PWM inputs without the need for an adapter circuit -* I2C Display support (LCD and OLED) -* Improved short circuit detection and automatic reset from an overload -* Current reading, sensing and ACK detection settings in milliAmps instead of just pin readings -* Improved adherence to the NMRA DCC specification -* Complete support for all the old commands and front ends like JMRI -* Railcom cutout (beta) -* Simpler, modular, faster code with an API Library for developers for easy expansion -* New features and functions in JMRI -* Ability to join MAIN and PROG tracks into one MAIN track to run your locos -* "Drive-Away" feature - Throttles with support, like Engine Driver, can allow a loco to be programmed on a usable, electrically isolated programming track and then drive off onto the main track -* Diagnostic commands to test decoders that aren't reading or writing correctly -* Support for Uno, Nano, Mega, Nano Every and Teensy microcontrollers -* User Functions: Filter regular commands (like a turnout or output command) and pass it to your own function or accessory -* Support for LCN (layout control nodes) -* mySetup.h file that acts like an Autoexec.Bat command to send startup commands to the CS -* High Accuracty Waveform option for rock steady DCC signals -* New current response outputs current in mA, overlimit current, and maximum board capable current. Support for new current meter in JMRI -* USB Browser based EX-WebThrottle -* New, simpler, function control command -* Number of locos discovery command `<#>` -* Emergency stop command -* Release cabs from memory command <-> all cabs, <- CAB> for just one loco address -* Automatic slot (register) management -* Automation (coming soon) - -NOTE: DCC-EX is a major rewrite to the code. We started over and rebuilt it from the ground up! For what that means, you can read [HERE](https://dcc-ex.com/about/rewrite.html). +* Open the file ``CommandStation-EX.ino`` in the Arduino IDE or Visual Studio Code (VSC). Please do not rename the folder containing the sketch code, nor add any files in that folder. The Arduino IDE relies on the structure and name of the folder to properly display and compile the code. +* Rename or copy ``config.example.h`` to ``config.h``. +* You must edit ``config.h`` according to the help texts in ``config.h``. # More information -You can learn more at the [DCC++ EX website](https://dcc-ex.com/) +You can learn more at the [DCC-EX website](https://dcc-ex.com/) -- November 14, 2020 From 430161ef60bbcad8f1309f50552adf499b39cd70 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Jun 2024 21:10:57 +0200 Subject: [PATCH 11/16] ESP32: Refuse IDF5 --- DCCTimerESP.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index 7ed3f28..6f64c6e 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -76,8 +76,13 @@ int DCCTimer::freeMemory() { #endif //////////////////////////////////////////////////////////////////////// - #ifdef ARDUINO_ARCH_ESP32 + +#include "esp_idf_version.h" +#if ESP_IDF_VERSION_MAJOR > 4 +#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains version 4 Arduino ESP32 library 3.0.0 is too new. Use 2.0.9 to 2.0.17" +#endif + #include #include #include From 4e491a1e56b5509db9d00ab63b9a86ba4bd901ca Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Jun 2024 21:17:30 +0200 Subject: [PATCH 12/16] Typo --- DCCTimerESP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCTimerESP.cpp b/DCCTimerESP.cpp index 6f64c6e..7fb721f 100644 --- a/DCCTimerESP.cpp +++ b/DCCTimerESP.cpp @@ -80,7 +80,7 @@ int DCCTimer::freeMemory() { #include "esp_idf_version.h" #if ESP_IDF_VERSION_MAJOR > 4 -#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains version 4 Arduino ESP32 library 3.0.0 is too new. Use 2.0.9 to 2.0.17" +#error "DCC-EX does not support compiling with IDF version 5.0 or later. Downgrade your ESP32 library to a version that contains IDF version 4. Arduino ESP32 library 3.0.0 is too new. Use 2.0.9 to 2.0.17" #endif #include From 21dca05257d1208b4f3505cccbc6acfb35977b9d Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Sun, 6 Oct 2024 14:54:37 +1000 Subject: [PATCH 13/16] Add files via upload --- CamParser.cpp | 95 ++++++++++ IO_EXSensorCAM.h | 424 ++++++++++++++++++++++++++++++++++++++++++ myHal.cpp | 363 ++++++++++++++++++++++++++++++++++++ myHal.cpp_example.txt | 65 ++++++- 4 files changed, 937 insertions(+), 10 deletions(-) create mode 100644 CamParser.cpp create mode 100644 IO_EXSensorCAM.h create mode 100644 myHal.cpp diff --git a/CamParser.cpp b/CamParser.cpp new file mode 100644 index 0000000..27ba9a3 --- /dev/null +++ b/CamParser.cpp @@ -0,0 +1,95 @@ + +//sensorCAM parser.cpp version 3.03 Sep 2024 +#include "CamParser.h" +#include "FSH.h" +#include "IO_EXSensorCAM.h" + +#ifndef SENSORCAM_VPIN //define CAM vpin (700?) in config.h +#define SENSORCAM_VPIN 0 +#endif +#define CAM_VPIN SENSORCAM_VPIN +#ifndef SENSORCAM2_VPIN +#define SENSORCAM2_VPIN CAM_VPIN +#endif +#ifndef SENSORCAM3_VPIN +#define SENSORCAM3_VPIN 0 +#endif +const int CAMVPINS[] = {CAM_VPIN,SENSORCAM_VPIN,SENSORCAM2_VPIN,SENSORCAM3_VPIN}; +const int16_t ver=30177; +const int16_t ve =2899; + +VPIN EXSensorCAM::CAMBaseVpin = CAM_VPIN; + +bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) { + (void)stream; // probably unused parameter + VPIN vpin=EXSensorCAM::CAMBaseVpin; //use current CAM selection + + if (paramCount==0) { + DIAG(F("vpin:%d EXSensorCAMs defined at Vpins #1@ %d #2@ %d #3@ %d"),vpin,CAMVPINS[1],CAMVPINS[2],CAMVPINS[3]); + return true; + } + uint8_t camop=p[0]; // cam oprerator + int param1=0; + int16_t param3=9999; // =0 could invoke parameter changes. & -1 gives later errors + + if(camop=='C'){ + if(p[1]>=100) EXSensorCAM::CAMBaseVpin=p[1]; + if(p[1]<4) EXSensorCAM::CAMBaseVpin=CAMVPINS[p[1]]; + DIAG(F("CAM base Vpin: %c %d "),p[0],EXSensorCAM::CAMBaseVpin); + return true; + } + if (camop<100) { //switch CAM# if p[1] dictates + if(p[1]>=100 && p[1]<400) { //limits to CAM# 1 to 3 for now + vpin=CAMVPINS[p[1]/100]; + EXSensorCAM::CAMBaseVpin=vpin; + DIAG(F("switching to CAM %d baseVpin:%d"),p[1]/100,vpin); + p[1]=p[1]%100; //strip off CAM # + } + } + if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam defined + + + // send UPPER case to sensorCAM to flag binary data from a DCCEX-CS parser + switch(paramCount) { + case 1: // produces '^' + if((p[0] == ve) || (p[0] == ver) || (p[0] == 'V')) camop='^'; + if (STRCHR_P((const char *)F("EFGMQRVW^"),camop) == nullptr) return false; + if (camop=='Q') param3=10; // for activation state of all 10 banks of sensors + if (camop=='F') camop=']'; // for Reset/Finish webCAM. + break; // F Coded as ']' else conflicts with + + case 2: // + if (STRCHR_P((const char *)F("ABFILMNOPQRSTUV"),camop)==nullptr) return false; + param1=p[1]; + break; + + case 3: // or + camop=p[0]; + if (p[0]>=100) { //vpin - i.e. NOT 'A' through 'Z' + if (p[1]>236 || p[1]<0) return false; //row + if (p[2]>316 || p[2]<0) return false; //column + camop=0x80; // special 'a' case for IO_SensorCAM + vpin = p[0]; + }else if (STRCHR_P((const char *)F("IJMNT"),camop) == nullptr) return false; + param1 = p[1]; + param3 = p[2]; + break; + + case 4: // + if (camop!='A') return false; //must start with 'a' + if (p[3]>316 || p[3]<0) return false; + if (p[2]>236 || p[2]<0) return false; + if (p[1]>97 || p[1]<0) return false; //treat as bsNo. + vpin = vpin + (p[1]/10)*8 + p[1]%10; //translate p[1] + camop=0x80; // special 'a' case for IO_SensorCAM + param1=p[2]; // row + param3=p[3]; // col + break; + + default: + return false; + } + DIAG(F("CamParser: %d %c %d %d"),vpin,camop,param1,param3); + IODevice::writeAnalogue(vpin,param1,camop,param3); + return true; +} \ No newline at end of file diff --git a/IO_EXSensorCAM.h b/IO_EXSensorCAM.h new file mode 100644 index 0000000..fcc0bd8 --- /dev/null +++ b/IO_EXSensorCAM.h @@ -0,0 +1,424 @@ +/* 2024/08/14 + * © 2024, Barry Daniel ESP32-CAM revision + * + * 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 . +*/ +#define driverVer 305 +// v305 less debug & alpha ordered switch +// v304 static oldb0; t(##[,%%]; +// v303 zipped with CS 5.2.76 and uploaded to repo (with debug) +// v302 SEND=StringFormatter::send, remove Sp(), add 'q', memcpy( .8) -> .7); +// v301 improved 'f','p'&'q' code and driver version calc. Correct bsNo calc. for 'a' +// v300 stripped & revised without expander functionality. Needs sensorCAM.h v300 AND CamParser.cpp +// v222 uses '@'for EXIORDD read. handles and +// v216 includes 'j' command and uses CamParser rather than myFilter.h Incompatible with v203 senorCAM +// v203 added pvtThreshold to 'i' output +// v201 deleted code for compatibility with CAM pre v171. Needs CAM ver201 with o06 only +// v200 rewrite reduces need for double reads of ESP32 slave CAM. Deleted ESP32CAP. +// Inompatible with pre-v170 sensorCAM, unless set S06 to 0 and S07 to 1 (o06 & l07 say) +/* + * The IO_EXSensorCAM.h device driver can integrate with the sensorCAM device. + * It is modelled on the IO_EXIOExpander.h device driver to include specific needs of the ESP32 sensorCAM + * This device driver will configure the device on startup, along with CamParser.cpp + * interacting with the sensorCAM device for all input/output duties. + * + * #include "CamParser.h" in DCCEXParser.cpp + * #include "IO_EXSensorCAM.h" in IODevice.h + * To create EX-SensorCAM devices, define them in myHal.cpp: with + * EXSensorCAM::create(baseVpin,num_vpins,i2c_address) or +♠ * alternatively use HAL(baseVpin,numpins,i2c_address) in myAutomation.h + * also #define SENSORCAM_VPIN baseVpin in config.h + * + * void halSetup() { + * // EXSensorCAM::create(vpin, num_vpins, i2c_address); + * EXSensorCAM::create(700, 80, 0x11); + * } + * + * I2C packet size of 32 bytes (in the Wire library). +*/ +# define DIGITALREFRESH 20000UL // min uSec delay between digital reads of digitalInputStates +#ifndef IO_EX_EXSENSORCAM_H +#define IO_EX_EXSENSORCAM_H +#define SEND StringFormatter::send +#include "IODevice.h" +#include "I2CManager.h" +#include "DIAG.h" +#include "FSH.h" +#include "CamParser.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * IODevice subclass for EX-SensorCAM. +*/ +class EXSensorCAM : public IODevice { + public: + static void create(VPIN vpin, int nPins, I2CAddress i2cAddress) { + if (checkNoOverlap(vpin, nPins, i2cAddress)) + new EXSensorCAM(vpin, nPins, i2cAddress); + } + + static VPIN CAMBaseVpin; + + private: + // Constructor + EXSensorCAM(VPIN firstVpin, int nPins, I2CAddress i2cAddress) { + _firstVpin = firstVpin; + // Number of pins cannot exceed 255 (1 byte) because of I2C message structure. + if (nPins > 80) nPins = 80; + _nPins = nPins; + _I2CAddress = i2cAddress; + addDevice(this); + } +//************************* +void _begin() { + uint8_t status; + // Initialise EX-SensorCAM device + I2CManager.begin(); + if (!I2CManager.exists(_I2CAddress)) { + DIAG(F("EX-SensorCAM I2C:%s device not found"), _I2CAddress.toString()); + _deviceState = DEVSTATE_FAILED; + return; + }else { + uint8_t commandBuffer[4]={EXIOINIT,(uint8_t)_nPins,(uint8_t)(_firstVpin & 0xFF),(uint8_t)(_firstVpin>>8)}; + status = I2CManager.read(_I2CAddress,_inputBuf,sizeof(_inputBuf),commandBuffer,sizeof(commandBuffer)); + //EXIOINIT needed to trigger and send firstVpin to CAM + + if (status == I2C_STATUS_OK) { + // Attempt to get version, non-blocking results in poor placement of response. Can be blocking here! + commandBuffer[0] = '^'; //new version code + + status = I2CManager.read(_I2CAddress, _inputBuf, sizeof(_inputBuf), commandBuffer, 1); + // for ESP32 CAM, read again for good immediate response version data + status = I2CManager.read(_I2CAddress, _inputBuf, sizeof(_inputBuf), commandBuffer, 1); + + if (status == I2C_STATUS_OK) { + _majorVer= _inputBuf[1]/10; + _minorVer= _inputBuf[1]%10; + _patchVer= _inputBuf[2]; + DIAG(F("EX-SensorCAM device found, I2C:%s, Version v%d.%d.%d"), + _I2CAddress.toString(),_majorVer, _minorVer,_patchVer); + } + } + if (status != I2C_STATUS_OK) + reportError(status); + } +} +//************************* +// 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(_verPrint) DIAG(F("_configure() driver IO_EXSensorCAM v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,vpin); + _verPrint=false; //only give driver versions once + if (paramCount != 1) return false; + return true; //at least confirm that CAM is (always) configured (no vpin check!) +} +//************************* +// Analogue input pin configuration, used to enable an EX-IOExpander device. +int _configureAnalogIn(VPIN vpin) override { + DIAG(F("_configureAnalogIn() IO_EXSensorCAM vpin %d"),vpin); + return true; // NOTE: use of EXRAIL IFGTE() etc use "analog" reads. +} +//************************* +// Main loop, collect both digital and "analog" pin states continuously (faster sensor/input reads) +void _loop(unsigned long currentMicros) override { + if (_deviceState == DEVSTATE_FAILED) return; + // Request block is used for "analogue" (cmd. data) and digital reads from the sensorCAM, which + // are performed on a cyclic basis. Writes are performed synchronously as and when requested. + if (_readState != RDS_IDLE) { //expecting a return packet + 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 + //apparently the above checks do not guarantee a good packet! error rate about 1 pkt per 1000 + //there should be a packet in _CAMresponseBuff[32] + if ((_CAMresponseBuff[0] & 0x60) >= 0x60) { //Buff[0] seems to have ascii cmd header (bit6 high) (o06) + int error = processIncomingPkt( _CAMresponseBuff, _CAMresponseBuff[0]); // '~' 'i' 'm' 'n' 't' etc + if (error>0) DIAG(F("CAM packet header(0x%x) not recognised"),_CAMresponseBuff[0]); + }else{ // Header not valid - typically replaced by bank 0 data! To avoid any bad responses set S06 to 0 + // Versions of sensorCAM.h after v300 should return header for '@' of '`'(0x60) (not 0xE6) + // followed by digitalInputStates sensor state array + } + }else reportError(status, false); // report i2c eror but don't go offline. + _readState = RDS_IDLE; + } + + // If we're not doing anything now, check to see if a new state table transfer, or for 't' repeat, is due. + if (_readState == RDS_IDLE) { //check if time for digitalRefresh + if ( currentMicros - _lastDigitalRead > _digitalRefresh) { + // Issue new read request for digital states. + + _readCommandBuffer[0] = '@'; //start new read of digitalInputStates Table // non-blocking read + I2CManager.read(_I2CAddress,_CAMresponseBuff, 32,_readCommandBuffer, 1, &_i2crb); + _lastDigitalRead = currentMicros; + _readState = RDS_DIGITAL; + + }else{ //slip in a repeat if pending + if (currentMicros - _lasttStateRead > _tStateRefresh) // Delay for "analog" command repetitions + if (_savedCmd[2]>1) { //repeat a 't' command + for (int i=0;i<7;i++) _readCommandBuffer[i] =_savedCmd[i]; + int errors = ioESP32(_I2CAddress, _CAMresponseBuff, 32, _readCommandBuffer, 7); + _lasttStateRead = currentMicros; + _savedCmd[2] -= 1; //decrement repeats + if (errors==0) return; + DIAG(F("ioESP32 error %d header 0x%x"),errors,_CAMresponseBuff[0]); + _readState = RDS_TSTATE; //this should stop further cmd requests until packet read (or timeout) + } + } //end repeat 't' + } + } +//************************* +// Obtain the bank of 8 sensors as an "analog" value +// can be used to track the position through a sequential sensor bank +int _readAnalogue(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + return _digitalInputStates[(vpin - _firstVpin) / 8]; +} +//************************* +// Obtain the correct digital sensor input value +int _read(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + return bitRead(_digitalInputStates[pin / 8], pin % 8); +} +//************************* +// Write digital value. +void _write(VPIN vpin, int value) override { + DIAG(F("**_write() vpin %d = %d"),vpin,value); + return ; +} +//************************* +// i2cAddr of ESP32 CAM +// rBuf buffer for return packet +// inbytes number of bytes to request from CAM +// outBuff holds outbytes to be sent to CAM +int ioESP32(uint8_t i2cAddr,uint8_t *rBuf,int inbytes,uint8_t *outBuff,int outbytes) { + uint8_t status = _i2crb.status; + + while( _i2crb.status != I2C_STATUS_OK){status = _i2crb.status;} //wait until bus free + + status = I2CManager.read(i2cAddr, rBuf, inbytes, outBuff, outbytes); + + if (status != I2C_STATUS_OK){ + DIAG(F("EX-SensorCAM I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); + reportError(status); return status; + } + return 0; // 0 for no error != 0 for error number. +} +//************************* +//function to interpret packet from sensorCAM.ino +//i2cAddr to identify CAM# (if # >1) +//rBuf contains packet of up to 32 bytes usually with (ascii) cmd header in rBuf[0] +//sensorCmd command header byte from CAM (in rBuf[0]?) +int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) { +//static uint8_t oldb0; //for debug only + int k; + int b; + char str[] = "11111111"; + // if (sensorCmd <= '~') DIAG(F("processIncomingPkt %c %d %d %d"),rBuf[0],rBuf[1],rBuf[2],rBuf[3]); + switch (sensorCmd){ + case '`': //response to request for digitalInputStates[] table '@'=>'`' + memcpy(_digitalInputStates, rBuf+1, digitalBytesNeeded); +// if ( _digitalInputStates[0]!=oldb0) { oldb0=_digitalInputStates[0]; //debug +// for (k=0;k<5;k++) {Serial.print(" ");Serial.print(_digitalInputStates[k],HEX);} +// } + break; + + case EXIORDY: //some commands give back acknowledgement only + break; + + case CAMERR: //cmd format error code from CAM + DIAG(F("CAM cmd error 0xFE 0x%x"),rBuf[1]); + break; + + case '~': //information from '^' version request + DIAG(F("EX-SensorCAM device found, I2C:%s,CAM Version v%d.%d.%d vpins %u-%u"), + _I2CAddress.toString(), rBuf[1]/10, rBuf[1]%10, rBuf[2],(int) _firstVpin, (int) _firstVpin +_nPins-1); + DIAG(F("IO_EXSensorCAM driver v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,_firstVpin); + break; + + case 'f': + DIAG(F("(f %%%%) frame header 'f' for bsNo %d/%d - showing Quarter sample (1 row) only"), rBuf[1]/8,rBuf[1]%8); + SEND(&USB_SERIAL,F(">4, rBuf[k]&15, k%3==2 ? " " : " "); + Serial.print(" latest grab: "); + for(k=16;k<28;k++) + SEND(&USB_SERIAL,F("%x%x%s"), rBuf[k]>>4, rBuf[k]&15, (k%3==0) ? " " : " "); + Serial.print(" n>\n"); + break; + + case 'i': //information from i%% + k=256*rBuf[5]+rBuf[4]; + DIAG(F("(i%%%%[,$$]) Info: Sensor 0%o(%d) enabled:%d status:%d row=%d x=%d Twin=0%o pvtThreshold=%d A~%d") + ,rBuf[1],rBuf[1],rBuf[3],rBuf[2],rBuf[6],k,rBuf[7],rBuf[9],int(rBuf[8])*16); + break; + + case 'm': + DIAG(F("(m$[,##]) Min/max: $ frames min2flip (trip) %d, maxSensors 0%o, minSensors 0%o, nLED %d," + " threshold %d, TWOIMAGE_MAXBS 0%o"),rBuf[1],rBuf[3],rBuf[2],rBuf[4],rBuf[5],rBuf[6]); + break; + + case 'n': + DIAG(F("(n$[,##]) Nominate: $ nLED %d, ## minSensors 0%o (maxSensors 0%o threshold %d)") + ,rBuf[4],rBuf[2],rBuf[3],rBuf[5]); + break; + + case 'p': + b=rBuf[1]-2; + if(b<4) { Serial.print("\n"); break; } + SEND(&USB_SERIAL,F("\n"); + break; + + case 'q': + for (int i =0; i<8; i++) str[i] = ((rBuf[2] << i) & 0x80 ? '1' : '0'); + DIAG(F("(q $) Query bank %c ENABLED sensors(S%c7-%c0): %s "), rBuf[1], rBuf[1], rBuf[1], str); + break; + + case 't': //threshold etc. from t## //bad pkt if 't' FF's + if(rBuf[1]==0xFF) {Serial.println("");_savedCmd[2] +=1; return 0;} + SEND(&USB_SERIAL,F("127) Serial.print("##* "); + else{ + if(rBuf[2]>rBuf[1]) Serial.print("-?* "); + else Serial.print("--* "); + } + for(int i=3;i<31;i+=2){ + uint8_t valu=rBuf[i]; //get bsn + if(valu==80) break; //80 = end flag + else{ + SEND(&USB_SERIAL,F("%d%d:"), (valu&0x7F)/8,(valu&0x7F)%8); + if(valu>=128) Serial.print("?-"); + else {if(rBuf[i+1]>=128) Serial.print("oo");else Serial.print("--");} + valu=rBuf[i+1]; + SEND(&USB_SERIAL,F("%d%s"),min(valu&0x7F,99),(valu<128) ? "--* ":"##* "); + } + } + Serial.print(" >\n"); + break; + + default: //header not a recognised cmd character + DIAG(F("CAM packet header not valid (0x%x) (0x%x) (0x%x)"),rBuf[0],rBuf[1],rBuf[2]); + return 1; + } + return 0; +} +//************************* +// Write (analogue) 8bit (command) values. Write the parameters to the sensorCAM +void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) override { + uint8_t outputBuffer[7]; + int errors=0; + outputBuffer[0] = camop; + int pin = vpin - _firstVpin; + + if(camop >= 0x80) { //case "a" (4p) also (3p) e.g. + camop=param1; //put row (0-236) in expected place + param1=param3; //put column in expected place + outputBuffer[0] = 'A'; + pin = (pin/8)*10 + pin%8; //restore bsNo. as integer + } + if (_deviceState == DEVSTATE_FAILED) return; + + outputBuffer[1] = pin; //vpin => bsn + outputBuffer[2] = param1 & 0xFF; + outputBuffer[3] = param1 >> 8; + outputBuffer[4] = camop; //command code + outputBuffer[5] = param3 & 0xFF; + outputBuffer[6] = param3 >> 8; + + int count=param1+1; + if(camop=='Q'){ + if(param3<=10) {count=param3; camop='B';} + //if(param1<10) outputBuffer[2] = param1*10; + } + if(camop=='B'){ //then 'b'(b%) cmd - can totally deal with that here. (but can't do b%,# (brightSF)) + if(param1>97) return; + if(param1>9) param1 = param1/10; //accept a bsNo + for(int bnk=param1;bnk%d0) %s"), bnk,b>>4,b&15,bnk,bnk,str ); + } + return; + } + if (outputBuffer[4]=='T') { //then 't' cmd + if(param1<31) { //repeated calls if param < 31 + //for (int i=0;i<7;i++) _savedCmd[i]=outputBuffer[i]; + memcpy( _savedCmd, outputBuffer, 7); + }else _savedCmd[2] = 0; //no repeats if ##>30 + }else _savedCmd[2] = 0; //no repeats unless 't' + + _lasttStateRead = micros(); //don't repeat until _tStateRefresh mSec + + errors = ioESP32(_I2CAddress, _CAMresponseBuff, 32 , outputBuffer, 7); //send to esp32-CAM + if (errors==0) return; + else { // if (_CAMresponseBuff[0] != EXIORDY) //can't be sure what is inBuff[0] ! + DIAG(F("ioESP32 i2c error %d header 0x%x"),errors,_CAMresponseBuff[0]); + } +} +//************************* + // Display device information and status. + void _display() override { + DIAG(F("EX-SensorCAM 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("")); + } +//************************* +// Helper function for error handling +void reportError(uint8_t status, bool fail=true) { + DIAG(F("EX-SensorCAM I2C:%s Error:%d (%S)"), _I2CAddress.toString(), + status, I2CManager.getErrorMessage(status)); + if (fail) _deviceState = DEVSTATE_FAILED; +} +//************************* + uint8_t _numDigitalPins = 80; + size_t digitalBytesNeeded=10; + uint8_t _CAMresponseBuff[34]; + + uint8_t _majorVer = 0; + uint8_t _minorVer = 0; + uint8_t _patchVer = 0; + + uint8_t _digitalInputStates[10]; + I2CRB _i2crb; + uint8_t _inputBuf[12]; + byte _outputBuffer[8]; + + bool _verPrint=true; + + uint8_t _readCommandBuffer[8]; + uint8_t _savedCmd[8]; //for repeat 't' command + //uint8_t _digitalPinBytes = 10; // Size of allocated memory buffer (may be longer than needed) + + enum {RDS_IDLE, RDS_DIGITAL, RDS_TSTATE}; // Read operation states + uint8_t _readState = RDS_IDLE; + //uint8_t cmdBuffer[7]={0,0,0,0,0,0,0}; + unsigned long _lastDigitalRead = 0; + unsigned long _lasttStateRead = 0; + unsigned long _digitalRefresh = DIGITALREFRESH; // Delay refreshing digital inputs for 10ms + const unsigned long _tStateRefresh = 120000UL; // Delay refreshing repeat "tState" inputs + + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + CAMERR = 0xFE + }; +}; +#endif \ No newline at end of file diff --git a/myHal.cpp b/myHal.cpp new file mode 100644 index 0000000..b6b9b36 --- /dev/null +++ b/myHal.cpp @@ -0,0 +1,363 @@ +// Sample myHal.cpp file. +// +// To use this file, copy it to myHal.cpp and uncomment the directives and/or +// edit them to satisfy your requirements. If you only want to use up to +// two MCP23017 GPIO Expander modules and/or up to two PCA9685 Servo modules, +// then you don't need this file as DCC++EX configures these for free! + +// Note that if the file has a .cpp extension it WILL be compiled into the build +// and the halSetup() function WILL be invoked. +// +// To prevent this, temporarily rename the file to myHal.txt or similar. +// + +// The #if directive prevent compile errors for Uno and Nano by excluding the +// HAL directives from the build. +#if !defined(IO_NO_HAL) + +// Include devices you need. +#include "IODevice.h" +//#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 +//#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments). +//#include "IO_I2CDFPlayer.h" // DFPlayer over I2C +#include "IO_EXSensorCAM.h" // sensorCAM driver + +//========================================================================== +// The function halSetup() is invoked from CS if it exists within the build. +// The setup calls are included between the open and close braces "{ ... }". +// Comments (lines preceded by "//") are optional. +//========================================================================== + +void halSetup() { + + I2CManager.setClock(100000); //to set i2c bus clock rate + + //======================================================================= + // 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::create(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. + //======================================================================= + // The parameters are: + // First Vpin=100 + // Number of VPINs=16 (numbered 100-115) + // I2C address of module=0x40 + + //PCA9685::create(100, 16, 0x40); + + + //======================================================================= + // The following directive defines an MCP23017 16-port I2C GPIO Extender module. + //======================================================================= + // The parameters are: + // First Vpin=196 + // Number of VPINs=16 (numbered 196-211) + // I2C address of module=0x22 + + //MCP23017::create(196, 16, 0x22); + + + // Alternative form, which allows the INT pin of the module to request a scan + // by pulling Arduino pin 40 to ground. Means that the I2C isn't being polled + // all the time, only when a change takes place. Multiple modules' INT pins + // may be connected to the same Arduino pin. + + //MCP23017::create(196, 16, 0x22, 40); + + + //======================================================================= + // The following directive defines an MCP23008 8-port I2C GPIO Extender module. + //======================================================================= + // The parameters are: + // First Vpin=300 + // Number of VPINs=8 (numbered 300-307) + // I2C address of module=0x22 + + //MCP23008::create(300, 8, 0x22); + + + //======================================================================= + // The following directive defines a PCF8574 8-port I2C GPIO Extender module. + //======================================================================= + // The parameters are: + // First Vpin=200 + // Number of VPINs=8 (numbered 200-207) + // I2C address of module=0x23 + + //PCF8574::create(200, 8, 0x23); + + + // Alternative form using INT pin (see above) + + //PCF8574::create(200, 8, 0x23, 40); + + + //======================================================================= + // The following directive defines a PCF8575 16-port I2C GPIO Extender module. + //======================================================================= + // The parameters are: + // First Vpin=200 + // Number of VPINs=16 (numbered 200-215) + // I2C address of module=0x23 + + //PCF8575::create(200, 16, 0x23); + + + // Alternative form using INT pin (see above) + + //PCF8575::create(200, 16, 0x23, 40); + + //======================================================================= + // The following directive defines an HCSR04 ultrasonic ranging module. + //======================================================================= + // The parameters are: + // Vpin=2000 (only one VPIN per directive) + // Number of VPINs=1 + // Arduino pin connected to TRIG=30 + // Arduino pin connected to ECHO=31 + // Minimum trigger range=20cm (VPIN goes to 1 when <20cm) + // Maximum trigger range=25cm (VPIN goes to 0 when >25cm) + // Note: Multiple devices can be configured by using a different ECHO pin + // for each one. The TRIG pin can be shared between multiple devices. + // Be aware that the 'ping' of one device may be received by another + // device and position them accordingly! + + //HCSR04::create(2000, 30, 31, 20, 25); + //HCSR04::create(2001, 30, 32, 20, 25); + + + //======================================================================= + // The following directive defines a single VL53L0X Time-of-Flight range sensor. + //======================================================================= + // The parameters are: + // VPIN=5000 + // Number of VPINs=1 + // I2C address=0x29 (default for this chip) + // Minimum trigger range=200mm (VPIN goes to 1 when <20cm) + // Maximum trigger range=250mm (VPIN goes to 0 when >25cm) + + //VL53L0X::create(5000, 1, 0x29, 200, 250); + + // For multiple VL53L0X modules, add another parameter which is a VPIN connected to the + // module's XSHUT pin. This allows the modules to be configured, at start, + // with distinct I2C addresses. In this case, the address 0x29 is only used during + // initialisation to configure each device in turn with the desired unique I2C address. + // The examples below have the modules' XSHUT pins connected to the first two pins of + // the first MCP23017 module (164 and 165), but Arduino pins may be used instead. + // The first module here is given I2C address 0x30 and the second is 0x31. + + //VL53L0X::create(5000, 1, 0x30, 200, 250, 164); + //VL53L0X::create(5001, 1, 0x31, 200, 250, 165); + + + //======================================================================= + // Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module. + //======================================================================= + // Parameters: + // 10000 = first VPIN allocated. + // 10 = number of VPINs allocated. + // Serial1 = name of serial port (usually Serial1 or Serial2). + // With these parameters, up to 10 files may be played on pins 10000-10009. + // Play is started from EX-RAIL with SET(10000) for first mp3 file, SET(10001) + // for second file, etc. Play may also be initiated by writing an analogue + // value to the first pin, e.g. ANOUT(10000,23,0,0) will play the 23rd mp3 file. + // ANOUT(10000,23,30,0) will do the same thing, as well as setting the volume to + // 30 (maximum value). + // Play is stopped by RESET(10000) (or any other allocated VPIN). + // Volume may also be set by writing an analogue value to the second pin for the player, + // e.g. ANOUT(10001,30,0,0) sets volume to maximum (30). + // The EX-RAIL script may check for completion of play by calling WAITFOR(pin), which will only proceed to the + // following line when the player is no longer busy. + // E.g. + // SEQUENCE(1) + // AT(164) // Wait for sensor attached to pin 164 to activate + // SET(10003) // Play fourth MP3 file + // LCD(4, "Playing") // Display message on LCD/OLED + // WAITFOR(10003) // Wait for playing to finish + // LCD(4, "") // Clear LCD/OLED line + // FOLLOW(1) // Go back to start + + // DFPlayer::create(10000, 10, Serial1); + + + //======================================================================= + // Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module on a SC16IS750/SC16IS752 I2C UART + //======================================================================= + // DFPlayer via NXP SC16IS752 I2C Dual UART. + // I2C address range 0x48 - 0x57 + // + // Generic format: + // I2CDFPlayer::create(1st vPin, vPins, I2C address, xtal); + // Parameters: + // 1st vPin : First virtual pin that EX-Rail can control to play a sound, use PLAYSOUND command (alias of ANOUT) + // vPins : Total number of virtual pins allocated (1 vPin is supported currently) + // 1st vPin for UART 0 + // I2C Address : I2C address of the serial controller, in 0x format + // xtal : 0 for 1.8432Mhz, 1 for 14.7456Mhz + // + // The vPin is also a pin that can be read with the WAITFOR(vPin) command indicating if the DFPlayer has finished playing a track + // + + // I2CDFPlayer::create(10000, 1, 0x48, 1); + // + // Configuration example on a multiplexer + // I2CDFPlayer::create(10000, 1, {I2CMux_0, SubBus_0, 0x48}, 1); + + + + //======================================================================= + // 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. + //======================================================================= + // EXTurntable::create(VPIN, Number of VPINs, I2C Address) + // + // The parameters are: + // VPIN=600 + // Number of VPINs=1 (Note there is no reason to change this) + // I2C address=0x60 + // + // Note that the I2C address is defined in the EX-Turntable code, and 0x60 is the default. + + //EXTurntable::create(600, 1, 0x60); + + + //======================================================================= + // The following directive defines an EX-IOExpander instance. + //======================================================================= + // EXIOExpander::create(VPIN, Number of VPINs, I2C Address) + // + // The parameters are: + // VPIN=an available Vpin + // Number of VPINs=pin count (must match device in use as per documentation) + // I2C address=an available I2C address (default 0x65) + // + // Note that the I2C address is defined in the EX-IOExpander code, and 0x65 is the default. + // The example is for an Arduino Nano. + + //EXIOExpander::create(800, 18, 0x65); + +EXIOExpander::create(800, 18, 0x65); // NanoEXIOExpander::create(820, 18, 0x75); // Nano +//EXSensorCAM::create(840, 18, 0x85); // Nano +//EXIOExpander::create(880, 18, 0x95); // Nano +//EXIOExpander::create(780, 18, 0xA5); // Nanoc +//EXIOExpander::create(600, 18, 0xB5); // Nano +//EXIOExpander::create(500, 18, 0xC5); // Nano + + + //======================================================================= + // The following directive defines a rotary encoder instance. + //======================================================================= + // The parameters are: + // firstVpin = First available Vpin to allocate + // numPins= Number of Vpins to allocate, can be either 1 to 3 + // i2cAddress = Available I2C address (default 0x67) + + //RotaryEncoder::create(firstVpin, numPins, i2cAddress); + //RotaryEncoder::create(700, 1, 0x67); + //RotaryEncoder::create(700, 2, 0x67); + //RotaryEncoder::create(700, 3, 0x67); + + //======================================================================= + // The following directive defines an EX-FastClock instance. + //======================================================================= + // EXFastCLock::create(I2C Address) + // + // The parameters are: + // + // I2C address=0x55 (decimal 85) + // + // Note that the I2C address is defined in the EX-FastClock code, and 0x55 is the default. + + + // EXFastClock::create(0x55); + + //======================================================================= + // The following directive defines an ESP32-CAM instance. + //======================================================================= + // EXSensorCAM::create(VPIN, Number of VPINs, I2C Address) + // + // The parameters are: + // VPIN=an available Vpin as start of block of consecutive sensors (up to 80) + // #define SENSORCAM_VPIN0 #00 in config.h if not using 700. + // Number of VPINs=pin count (must not exceed 80) + // I2C address=an available I2C address (default 0x11) + // + // EXSensorCAM::create(700, 80, 0x11); //preference is now to use HAL(700 80 0x11) in myAutomation.h + EXSensorCAM::create(700, 80, 0x11); + //EXSensorCAM::create(600, 80, 0x12); //alternate or second CAM device address creation + //preference is now to use HAL(EXSensorCAM 700 80 0x11) in myAutomation.h rather than :create + } + +#endif diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index 5533554..c204765 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -18,13 +18,15 @@ // Include devices you need. #include "IODevice.h" //#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_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 +//#include "IO_EXTurntable.h" // Turntable-EX turntable controller +//#include "IO_EXFastClock.h" // FastClock driver //#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments). +//#include "IO_I2CDFPlayer.h" // DFPlayer over I2C +#include "IO_EXSensorCAM.h" // sensorCAM driver //========================================================================== // The function halSetup() is invoked from CS if it exists within the build. @@ -34,6 +36,8 @@ void halSetup() { + // I2CManager.setClock(100000); //to set i2c bus clock rate + //======================================================================= // The following directives define auxiliary display devices. // These can be defined in addition to the system display (display @@ -234,6 +238,31 @@ void halSetup() { // DFPlayer::create(10000, 10, Serial1); + //======================================================================= + // Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module on a SC16IS750/SC16IS752 I2C UART + //======================================================================= + // DFPlayer via NXP SC16IS752 I2C Dual UART. + // I2C address range 0x48 - 0x57 + // + // Generic format: + // I2CDFPlayer::create(1st vPin, vPins, I2C address, xtal); + // Parameters: + // 1st vPin : First virtual pin that EX-Rail can control to play a sound, use PLAYSOUND command (alias of ANOUT) + // vPins : Total number of virtual pins allocated (1 vPin is supported currently) + // 1st vPin for UART 0 + // I2C Address : I2C address of the serial controller, in 0x format + // xtal : 0 for 1.8432Mhz, 1 for 14.7456Mhz + // + // The vPin is also a pin that can be read with the WAITFOR(vPin) command indicating if the DFPlayer has finished playing a track + // + + // I2CDFPlayer::create(10000, 1, 0x48, 1); + // + // Configuration example on a multiplexer + // I2CDFPlayer::create(10000, 1, {I2CMux_0, SubBus_0, 0x48}, 1); + + + //======================================================================= // 16-pad capacitative touch key pad based on TP229 IC. //======================================================================= @@ -285,12 +314,13 @@ void halSetup() { //======================================================================= // The parameters are: // firstVpin = First available Vpin to allocate - // numPins= Number of Vpins to allocate, can be either 1 or 2 - // i2cAddress = Available I2C address (default 0x70) + // numPins= Number of Vpins to allocate, can be either 1 to 3 + // i2cAddress = Available I2C address (default 0x67) //RotaryEncoder::create(firstVpin, numPins, i2cAddress); - //RotaryEncoder::create(700, 1, 0x70); - //RotaryEncoder::create(701, 2, 0x71); + //RotaryEncoder::create(700, 1, 0x67); + //RotaryEncoder::create(700, 2, 0x67); + //RotaryEncoder::create(700, 3, 0x67); //======================================================================= // The following directive defines an EX-FastClock instance. @@ -305,7 +335,22 @@ void halSetup() { // EXFastClock::create(0x55); + + //======================================================================= + // The following directive defines an ESP32-CAM instance. + //======================================================================= + // EXSensorCAM::create(VPIN, Number of VPINs, I2C Address) + // + // The parameters are: + // VPIN=an available Vpin as start of block of consecutive sensors (up to 80) + // #define SENSORCAM_VPIN0 #00 in config.h if not using 700. + // Number of VPINs=pin count (must not exceed 80) + // I2C address=an available I2C address (default 0x11) + // + // EXSensorCAM::create(700, 80, 0x11); //preference is now to use HAL(700 80 0x11) in myAutomation.h -} + //EXSensorCAM::create(600, 80, 0x12); //alternate or second CAM device address creation + //preference is now to use HAL(EXSensorCAM 700 80 0x11) in myAutomation.h rather than :create + } #endif From 480eb1bfde5c96119c3bf1bf9bcd9c51bb2a7a72 Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Sun, 6 Oct 2024 15:07:15 +1000 Subject: [PATCH 14/16] Delete myHal.cpp --- myHal.cpp | 363 ------------------------------------------------------ 1 file changed, 363 deletions(-) delete mode 100644 myHal.cpp diff --git a/myHal.cpp b/myHal.cpp deleted file mode 100644 index b6b9b36..0000000 --- a/myHal.cpp +++ /dev/null @@ -1,363 +0,0 @@ -// Sample myHal.cpp file. -// -// To use this file, copy it to myHal.cpp and uncomment the directives and/or -// edit them to satisfy your requirements. If you only want to use up to -// two MCP23017 GPIO Expander modules and/or up to two PCA9685 Servo modules, -// then you don't need this file as DCC++EX configures these for free! - -// Note that if the file has a .cpp extension it WILL be compiled into the build -// and the halSetup() function WILL be invoked. -// -// To prevent this, temporarily rename the file to myHal.txt or similar. -// - -// The #if directive prevent compile errors for Uno and Nano by excluding the -// HAL directives from the build. -#if !defined(IO_NO_HAL) - -// Include devices you need. -#include "IODevice.h" -//#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 -//#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments). -//#include "IO_I2CDFPlayer.h" // DFPlayer over I2C -#include "IO_EXSensorCAM.h" // sensorCAM driver - -//========================================================================== -// The function halSetup() is invoked from CS if it exists within the build. -// The setup calls are included between the open and close braces "{ ... }". -// Comments (lines preceded by "//") are optional. -//========================================================================== - -void halSetup() { - - I2CManager.setClock(100000); //to set i2c bus clock rate - - //======================================================================= - // 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::create(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. - //======================================================================= - // The parameters are: - // First Vpin=100 - // Number of VPINs=16 (numbered 100-115) - // I2C address of module=0x40 - - //PCA9685::create(100, 16, 0x40); - - - //======================================================================= - // The following directive defines an MCP23017 16-port I2C GPIO Extender module. - //======================================================================= - // The parameters are: - // First Vpin=196 - // Number of VPINs=16 (numbered 196-211) - // I2C address of module=0x22 - - //MCP23017::create(196, 16, 0x22); - - - // Alternative form, which allows the INT pin of the module to request a scan - // by pulling Arduino pin 40 to ground. Means that the I2C isn't being polled - // all the time, only when a change takes place. Multiple modules' INT pins - // may be connected to the same Arduino pin. - - //MCP23017::create(196, 16, 0x22, 40); - - - //======================================================================= - // The following directive defines an MCP23008 8-port I2C GPIO Extender module. - //======================================================================= - // The parameters are: - // First Vpin=300 - // Number of VPINs=8 (numbered 300-307) - // I2C address of module=0x22 - - //MCP23008::create(300, 8, 0x22); - - - //======================================================================= - // The following directive defines a PCF8574 8-port I2C GPIO Extender module. - //======================================================================= - // The parameters are: - // First Vpin=200 - // Number of VPINs=8 (numbered 200-207) - // I2C address of module=0x23 - - //PCF8574::create(200, 8, 0x23); - - - // Alternative form using INT pin (see above) - - //PCF8574::create(200, 8, 0x23, 40); - - - //======================================================================= - // The following directive defines a PCF8575 16-port I2C GPIO Extender module. - //======================================================================= - // The parameters are: - // First Vpin=200 - // Number of VPINs=16 (numbered 200-215) - // I2C address of module=0x23 - - //PCF8575::create(200, 16, 0x23); - - - // Alternative form using INT pin (see above) - - //PCF8575::create(200, 16, 0x23, 40); - - //======================================================================= - // The following directive defines an HCSR04 ultrasonic ranging module. - //======================================================================= - // The parameters are: - // Vpin=2000 (only one VPIN per directive) - // Number of VPINs=1 - // Arduino pin connected to TRIG=30 - // Arduino pin connected to ECHO=31 - // Minimum trigger range=20cm (VPIN goes to 1 when <20cm) - // Maximum trigger range=25cm (VPIN goes to 0 when >25cm) - // Note: Multiple devices can be configured by using a different ECHO pin - // for each one. The TRIG pin can be shared between multiple devices. - // Be aware that the 'ping' of one device may be received by another - // device and position them accordingly! - - //HCSR04::create(2000, 30, 31, 20, 25); - //HCSR04::create(2001, 30, 32, 20, 25); - - - //======================================================================= - // The following directive defines a single VL53L0X Time-of-Flight range sensor. - //======================================================================= - // The parameters are: - // VPIN=5000 - // Number of VPINs=1 - // I2C address=0x29 (default for this chip) - // Minimum trigger range=200mm (VPIN goes to 1 when <20cm) - // Maximum trigger range=250mm (VPIN goes to 0 when >25cm) - - //VL53L0X::create(5000, 1, 0x29, 200, 250); - - // For multiple VL53L0X modules, add another parameter which is a VPIN connected to the - // module's XSHUT pin. This allows the modules to be configured, at start, - // with distinct I2C addresses. In this case, the address 0x29 is only used during - // initialisation to configure each device in turn with the desired unique I2C address. - // The examples below have the modules' XSHUT pins connected to the first two pins of - // the first MCP23017 module (164 and 165), but Arduino pins may be used instead. - // The first module here is given I2C address 0x30 and the second is 0x31. - - //VL53L0X::create(5000, 1, 0x30, 200, 250, 164); - //VL53L0X::create(5001, 1, 0x31, 200, 250, 165); - - - //======================================================================= - // Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module. - //======================================================================= - // Parameters: - // 10000 = first VPIN allocated. - // 10 = number of VPINs allocated. - // Serial1 = name of serial port (usually Serial1 or Serial2). - // With these parameters, up to 10 files may be played on pins 10000-10009. - // Play is started from EX-RAIL with SET(10000) for first mp3 file, SET(10001) - // for second file, etc. Play may also be initiated by writing an analogue - // value to the first pin, e.g. ANOUT(10000,23,0,0) will play the 23rd mp3 file. - // ANOUT(10000,23,30,0) will do the same thing, as well as setting the volume to - // 30 (maximum value). - // Play is stopped by RESET(10000) (or any other allocated VPIN). - // Volume may also be set by writing an analogue value to the second pin for the player, - // e.g. ANOUT(10001,30,0,0) sets volume to maximum (30). - // The EX-RAIL script may check for completion of play by calling WAITFOR(pin), which will only proceed to the - // following line when the player is no longer busy. - // E.g. - // SEQUENCE(1) - // AT(164) // Wait for sensor attached to pin 164 to activate - // SET(10003) // Play fourth MP3 file - // LCD(4, "Playing") // Display message on LCD/OLED - // WAITFOR(10003) // Wait for playing to finish - // LCD(4, "") // Clear LCD/OLED line - // FOLLOW(1) // Go back to start - - // DFPlayer::create(10000, 10, Serial1); - - - //======================================================================= - // Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module on a SC16IS750/SC16IS752 I2C UART - //======================================================================= - // DFPlayer via NXP SC16IS752 I2C Dual UART. - // I2C address range 0x48 - 0x57 - // - // Generic format: - // I2CDFPlayer::create(1st vPin, vPins, I2C address, xtal); - // Parameters: - // 1st vPin : First virtual pin that EX-Rail can control to play a sound, use PLAYSOUND command (alias of ANOUT) - // vPins : Total number of virtual pins allocated (1 vPin is supported currently) - // 1st vPin for UART 0 - // I2C Address : I2C address of the serial controller, in 0x format - // xtal : 0 for 1.8432Mhz, 1 for 14.7456Mhz - // - // The vPin is also a pin that can be read with the WAITFOR(vPin) command indicating if the DFPlayer has finished playing a track - // - - // I2CDFPlayer::create(10000, 1, 0x48, 1); - // - // Configuration example on a multiplexer - // I2CDFPlayer::create(10000, 1, {I2CMux_0, SubBus_0, 0x48}, 1); - - - - //======================================================================= - // 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. - //======================================================================= - // EXTurntable::create(VPIN, Number of VPINs, I2C Address) - // - // The parameters are: - // VPIN=600 - // Number of VPINs=1 (Note there is no reason to change this) - // I2C address=0x60 - // - // Note that the I2C address is defined in the EX-Turntable code, and 0x60 is the default. - - //EXTurntable::create(600, 1, 0x60); - - - //======================================================================= - // The following directive defines an EX-IOExpander instance. - //======================================================================= - // EXIOExpander::create(VPIN, Number of VPINs, I2C Address) - // - // The parameters are: - // VPIN=an available Vpin - // Number of VPINs=pin count (must match device in use as per documentation) - // I2C address=an available I2C address (default 0x65) - // - // Note that the I2C address is defined in the EX-IOExpander code, and 0x65 is the default. - // The example is for an Arduino Nano. - - //EXIOExpander::create(800, 18, 0x65); - -EXIOExpander::create(800, 18, 0x65); // NanoEXIOExpander::create(820, 18, 0x75); // Nano -//EXSensorCAM::create(840, 18, 0x85); // Nano -//EXIOExpander::create(880, 18, 0x95); // Nano -//EXIOExpander::create(780, 18, 0xA5); // Nanoc -//EXIOExpander::create(600, 18, 0xB5); // Nano -//EXIOExpander::create(500, 18, 0xC5); // Nano - - - //======================================================================= - // The following directive defines a rotary encoder instance. - //======================================================================= - // The parameters are: - // firstVpin = First available Vpin to allocate - // numPins= Number of Vpins to allocate, can be either 1 to 3 - // i2cAddress = Available I2C address (default 0x67) - - //RotaryEncoder::create(firstVpin, numPins, i2cAddress); - //RotaryEncoder::create(700, 1, 0x67); - //RotaryEncoder::create(700, 2, 0x67); - //RotaryEncoder::create(700, 3, 0x67); - - //======================================================================= - // The following directive defines an EX-FastClock instance. - //======================================================================= - // EXFastCLock::create(I2C Address) - // - // The parameters are: - // - // I2C address=0x55 (decimal 85) - // - // Note that the I2C address is defined in the EX-FastClock code, and 0x55 is the default. - - - // EXFastClock::create(0x55); - - //======================================================================= - // The following directive defines an ESP32-CAM instance. - //======================================================================= - // EXSensorCAM::create(VPIN, Number of VPINs, I2C Address) - // - // The parameters are: - // VPIN=an available Vpin as start of block of consecutive sensors (up to 80) - // #define SENSORCAM_VPIN0 #00 in config.h if not using 700. - // Number of VPINs=pin count (must not exceed 80) - // I2C address=an available I2C address (default 0x11) - // - // EXSensorCAM::create(700, 80, 0x11); //preference is now to use HAL(700 80 0x11) in myAutomation.h - EXSensorCAM::create(700, 80, 0x11); - //EXSensorCAM::create(600, 80, 0x12); //alternate or second CAM device address creation - //preference is now to use HAL(EXSensorCAM 700 80 0x11) in myAutomation.h rather than :create - } - -#endif From ea2e5ab8e9487520394d5c5650bd571edc51f13c Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Sun, 6 Oct 2024 15:07:52 +1000 Subject: [PATCH 15/16] Delete CamParser.cpp --- CamParser.cpp | 95 --------------------------------------------------- 1 file changed, 95 deletions(-) delete mode 100644 CamParser.cpp diff --git a/CamParser.cpp b/CamParser.cpp deleted file mode 100644 index 27ba9a3..0000000 --- a/CamParser.cpp +++ /dev/null @@ -1,95 +0,0 @@ - -//sensorCAM parser.cpp version 3.03 Sep 2024 -#include "CamParser.h" -#include "FSH.h" -#include "IO_EXSensorCAM.h" - -#ifndef SENSORCAM_VPIN //define CAM vpin (700?) in config.h -#define SENSORCAM_VPIN 0 -#endif -#define CAM_VPIN SENSORCAM_VPIN -#ifndef SENSORCAM2_VPIN -#define SENSORCAM2_VPIN CAM_VPIN -#endif -#ifndef SENSORCAM3_VPIN -#define SENSORCAM3_VPIN 0 -#endif -const int CAMVPINS[] = {CAM_VPIN,SENSORCAM_VPIN,SENSORCAM2_VPIN,SENSORCAM3_VPIN}; -const int16_t ver=30177; -const int16_t ve =2899; - -VPIN EXSensorCAM::CAMBaseVpin = CAM_VPIN; - -bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) { - (void)stream; // probably unused parameter - VPIN vpin=EXSensorCAM::CAMBaseVpin; //use current CAM selection - - if (paramCount==0) { - DIAG(F("vpin:%d EXSensorCAMs defined at Vpins #1@ %d #2@ %d #3@ %d"),vpin,CAMVPINS[1],CAMVPINS[2],CAMVPINS[3]); - return true; - } - uint8_t camop=p[0]; // cam oprerator - int param1=0; - int16_t param3=9999; // =0 could invoke parameter changes. & -1 gives later errors - - if(camop=='C'){ - if(p[1]>=100) EXSensorCAM::CAMBaseVpin=p[1]; - if(p[1]<4) EXSensorCAM::CAMBaseVpin=CAMVPINS[p[1]]; - DIAG(F("CAM base Vpin: %c %d "),p[0],EXSensorCAM::CAMBaseVpin); - return true; - } - if (camop<100) { //switch CAM# if p[1] dictates - if(p[1]>=100 && p[1]<400) { //limits to CAM# 1 to 3 for now - vpin=CAMVPINS[p[1]/100]; - EXSensorCAM::CAMBaseVpin=vpin; - DIAG(F("switching to CAM %d baseVpin:%d"),p[1]/100,vpin); - p[1]=p[1]%100; //strip off CAM # - } - } - if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam defined - - - // send UPPER case to sensorCAM to flag binary data from a DCCEX-CS parser - switch(paramCount) { - case 1: // produces '^' - if((p[0] == ve) || (p[0] == ver) || (p[0] == 'V')) camop='^'; - if (STRCHR_P((const char *)F("EFGMQRVW^"),camop) == nullptr) return false; - if (camop=='Q') param3=10; // for activation state of all 10 banks of sensors - if (camop=='F') camop=']'; // for Reset/Finish webCAM. - break; // F Coded as ']' else conflicts with - - case 2: // - if (STRCHR_P((const char *)F("ABFILMNOPQRSTUV"),camop)==nullptr) return false; - param1=p[1]; - break; - - case 3: // or - camop=p[0]; - if (p[0]>=100) { //vpin - i.e. NOT 'A' through 'Z' - if (p[1]>236 || p[1]<0) return false; //row - if (p[2]>316 || p[2]<0) return false; //column - camop=0x80; // special 'a' case for IO_SensorCAM - vpin = p[0]; - }else if (STRCHR_P((const char *)F("IJMNT"),camop) == nullptr) return false; - param1 = p[1]; - param3 = p[2]; - break; - - case 4: // - if (camop!='A') return false; //must start with 'a' - if (p[3]>316 || p[3]<0) return false; - if (p[2]>236 || p[2]<0) return false; - if (p[1]>97 || p[1]<0) return false; //treat as bsNo. - vpin = vpin + (p[1]/10)*8 + p[1]%10; //translate p[1] - camop=0x80; // special 'a' case for IO_SensorCAM - param1=p[2]; // row - param3=p[3]; // col - break; - - default: - return false; - } - DIAG(F("CamParser: %d %c %d %d"),vpin,camop,param1,param3); - IODevice::writeAnalogue(vpin,param1,camop,param3); - return true; -} \ No newline at end of file From ece2ac3ccf29370d9feb622748aabd2942d5f3a2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 6 Oct 2024 08:00:07 +0200 Subject: [PATCH 16/16] revert last 3 commits --- IO_EXSensorCAM.h | 424 ------------------------------------------ myHal.cpp_example.txt | 65 +------ 2 files changed, 10 insertions(+), 479 deletions(-) delete mode 100644 IO_EXSensorCAM.h diff --git a/IO_EXSensorCAM.h b/IO_EXSensorCAM.h deleted file mode 100644 index fcc0bd8..0000000 --- a/IO_EXSensorCAM.h +++ /dev/null @@ -1,424 +0,0 @@ -/* 2024/08/14 - * © 2024, Barry Daniel ESP32-CAM revision - * - * 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 . -*/ -#define driverVer 305 -// v305 less debug & alpha ordered switch -// v304 static oldb0; t(##[,%%]; -// v303 zipped with CS 5.2.76 and uploaded to repo (with debug) -// v302 SEND=StringFormatter::send, remove Sp(), add 'q', memcpy( .8) -> .7); -// v301 improved 'f','p'&'q' code and driver version calc. Correct bsNo calc. for 'a' -// v300 stripped & revised without expander functionality. Needs sensorCAM.h v300 AND CamParser.cpp -// v222 uses '@'for EXIORDD read. handles and -// v216 includes 'j' command and uses CamParser rather than myFilter.h Incompatible with v203 senorCAM -// v203 added pvtThreshold to 'i' output -// v201 deleted code for compatibility with CAM pre v171. Needs CAM ver201 with o06 only -// v200 rewrite reduces need for double reads of ESP32 slave CAM. Deleted ESP32CAP. -// Inompatible with pre-v170 sensorCAM, unless set S06 to 0 and S07 to 1 (o06 & l07 say) -/* - * The IO_EXSensorCAM.h device driver can integrate with the sensorCAM device. - * It is modelled on the IO_EXIOExpander.h device driver to include specific needs of the ESP32 sensorCAM - * This device driver will configure the device on startup, along with CamParser.cpp - * interacting with the sensorCAM device for all input/output duties. - * - * #include "CamParser.h" in DCCEXParser.cpp - * #include "IO_EXSensorCAM.h" in IODevice.h - * To create EX-SensorCAM devices, define them in myHal.cpp: with - * EXSensorCAM::create(baseVpin,num_vpins,i2c_address) or -♠ * alternatively use HAL(baseVpin,numpins,i2c_address) in myAutomation.h - * also #define SENSORCAM_VPIN baseVpin in config.h - * - * void halSetup() { - * // EXSensorCAM::create(vpin, num_vpins, i2c_address); - * EXSensorCAM::create(700, 80, 0x11); - * } - * - * I2C packet size of 32 bytes (in the Wire library). -*/ -# define DIGITALREFRESH 20000UL // min uSec delay between digital reads of digitalInputStates -#ifndef IO_EX_EXSENSORCAM_H -#define IO_EX_EXSENSORCAM_H -#define SEND StringFormatter::send -#include "IODevice.h" -#include "I2CManager.h" -#include "DIAG.h" -#include "FSH.h" -#include "CamParser.h" - -///////////////////////////////////////////////////////////////////////////////////////////////////// -/* - * IODevice subclass for EX-SensorCAM. -*/ -class EXSensorCAM : public IODevice { - public: - static void create(VPIN vpin, int nPins, I2CAddress i2cAddress) { - if (checkNoOverlap(vpin, nPins, i2cAddress)) - new EXSensorCAM(vpin, nPins, i2cAddress); - } - - static VPIN CAMBaseVpin; - - private: - // Constructor - EXSensorCAM(VPIN firstVpin, int nPins, I2CAddress i2cAddress) { - _firstVpin = firstVpin; - // Number of pins cannot exceed 255 (1 byte) because of I2C message structure. - if (nPins > 80) nPins = 80; - _nPins = nPins; - _I2CAddress = i2cAddress; - addDevice(this); - } -//************************* -void _begin() { - uint8_t status; - // Initialise EX-SensorCAM device - I2CManager.begin(); - if (!I2CManager.exists(_I2CAddress)) { - DIAG(F("EX-SensorCAM I2C:%s device not found"), _I2CAddress.toString()); - _deviceState = DEVSTATE_FAILED; - return; - }else { - uint8_t commandBuffer[4]={EXIOINIT,(uint8_t)_nPins,(uint8_t)(_firstVpin & 0xFF),(uint8_t)(_firstVpin>>8)}; - status = I2CManager.read(_I2CAddress,_inputBuf,sizeof(_inputBuf),commandBuffer,sizeof(commandBuffer)); - //EXIOINIT needed to trigger and send firstVpin to CAM - - if (status == I2C_STATUS_OK) { - // Attempt to get version, non-blocking results in poor placement of response. Can be blocking here! - commandBuffer[0] = '^'; //new version code - - status = I2CManager.read(_I2CAddress, _inputBuf, sizeof(_inputBuf), commandBuffer, 1); - // for ESP32 CAM, read again for good immediate response version data - status = I2CManager.read(_I2CAddress, _inputBuf, sizeof(_inputBuf), commandBuffer, 1); - - if (status == I2C_STATUS_OK) { - _majorVer= _inputBuf[1]/10; - _minorVer= _inputBuf[1]%10; - _patchVer= _inputBuf[2]; - DIAG(F("EX-SensorCAM device found, I2C:%s, Version v%d.%d.%d"), - _I2CAddress.toString(),_majorVer, _minorVer,_patchVer); - } - } - if (status != I2C_STATUS_OK) - reportError(status); - } -} -//************************* -// 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(_verPrint) DIAG(F("_configure() driver IO_EXSensorCAM v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,vpin); - _verPrint=false; //only give driver versions once - if (paramCount != 1) return false; - return true; //at least confirm that CAM is (always) configured (no vpin check!) -} -//************************* -// Analogue input pin configuration, used to enable an EX-IOExpander device. -int _configureAnalogIn(VPIN vpin) override { - DIAG(F("_configureAnalogIn() IO_EXSensorCAM vpin %d"),vpin); - return true; // NOTE: use of EXRAIL IFGTE() etc use "analog" reads. -} -//************************* -// Main loop, collect both digital and "analog" pin states continuously (faster sensor/input reads) -void _loop(unsigned long currentMicros) override { - if (_deviceState == DEVSTATE_FAILED) return; - // Request block is used for "analogue" (cmd. data) and digital reads from the sensorCAM, which - // are performed on a cyclic basis. Writes are performed synchronously as and when requested. - if (_readState != RDS_IDLE) { //expecting a return packet - 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 - //apparently the above checks do not guarantee a good packet! error rate about 1 pkt per 1000 - //there should be a packet in _CAMresponseBuff[32] - if ((_CAMresponseBuff[0] & 0x60) >= 0x60) { //Buff[0] seems to have ascii cmd header (bit6 high) (o06) - int error = processIncomingPkt( _CAMresponseBuff, _CAMresponseBuff[0]); // '~' 'i' 'm' 'n' 't' etc - if (error>0) DIAG(F("CAM packet header(0x%x) not recognised"),_CAMresponseBuff[0]); - }else{ // Header not valid - typically replaced by bank 0 data! To avoid any bad responses set S06 to 0 - // Versions of sensorCAM.h after v300 should return header for '@' of '`'(0x60) (not 0xE6) - // followed by digitalInputStates sensor state array - } - }else reportError(status, false); // report i2c eror but don't go offline. - _readState = RDS_IDLE; - } - - // If we're not doing anything now, check to see if a new state table transfer, or for 't' repeat, is due. - if (_readState == RDS_IDLE) { //check if time for digitalRefresh - if ( currentMicros - _lastDigitalRead > _digitalRefresh) { - // Issue new read request for digital states. - - _readCommandBuffer[0] = '@'; //start new read of digitalInputStates Table // non-blocking read - I2CManager.read(_I2CAddress,_CAMresponseBuff, 32,_readCommandBuffer, 1, &_i2crb); - _lastDigitalRead = currentMicros; - _readState = RDS_DIGITAL; - - }else{ //slip in a repeat if pending - if (currentMicros - _lasttStateRead > _tStateRefresh) // Delay for "analog" command repetitions - if (_savedCmd[2]>1) { //repeat a 't' command - for (int i=0;i<7;i++) _readCommandBuffer[i] =_savedCmd[i]; - int errors = ioESP32(_I2CAddress, _CAMresponseBuff, 32, _readCommandBuffer, 7); - _lasttStateRead = currentMicros; - _savedCmd[2] -= 1; //decrement repeats - if (errors==0) return; - DIAG(F("ioESP32 error %d header 0x%x"),errors,_CAMresponseBuff[0]); - _readState = RDS_TSTATE; //this should stop further cmd requests until packet read (or timeout) - } - } //end repeat 't' - } - } -//************************* -// Obtain the bank of 8 sensors as an "analog" value -// can be used to track the position through a sequential sensor bank -int _readAnalogue(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - return _digitalInputStates[(vpin - _firstVpin) / 8]; -} -//************************* -// Obtain the correct digital sensor input value -int _read(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - int pin = vpin - _firstVpin; - return bitRead(_digitalInputStates[pin / 8], pin % 8); -} -//************************* -// Write digital value. -void _write(VPIN vpin, int value) override { - DIAG(F("**_write() vpin %d = %d"),vpin,value); - return ; -} -//************************* -// i2cAddr of ESP32 CAM -// rBuf buffer for return packet -// inbytes number of bytes to request from CAM -// outBuff holds outbytes to be sent to CAM -int ioESP32(uint8_t i2cAddr,uint8_t *rBuf,int inbytes,uint8_t *outBuff,int outbytes) { - uint8_t status = _i2crb.status; - - while( _i2crb.status != I2C_STATUS_OK){status = _i2crb.status;} //wait until bus free - - status = I2CManager.read(i2cAddr, rBuf, inbytes, outBuff, outbytes); - - if (status != I2C_STATUS_OK){ - DIAG(F("EX-SensorCAM I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status)); - reportError(status); return status; - } - return 0; // 0 for no error != 0 for error number. -} -//************************* -//function to interpret packet from sensorCAM.ino -//i2cAddr to identify CAM# (if # >1) -//rBuf contains packet of up to 32 bytes usually with (ascii) cmd header in rBuf[0] -//sensorCmd command header byte from CAM (in rBuf[0]?) -int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) { -//static uint8_t oldb0; //for debug only - int k; - int b; - char str[] = "11111111"; - // if (sensorCmd <= '~') DIAG(F("processIncomingPkt %c %d %d %d"),rBuf[0],rBuf[1],rBuf[2],rBuf[3]); - switch (sensorCmd){ - case '`': //response to request for digitalInputStates[] table '@'=>'`' - memcpy(_digitalInputStates, rBuf+1, digitalBytesNeeded); -// if ( _digitalInputStates[0]!=oldb0) { oldb0=_digitalInputStates[0]; //debug -// for (k=0;k<5;k++) {Serial.print(" ");Serial.print(_digitalInputStates[k],HEX);} -// } - break; - - case EXIORDY: //some commands give back acknowledgement only - break; - - case CAMERR: //cmd format error code from CAM - DIAG(F("CAM cmd error 0xFE 0x%x"),rBuf[1]); - break; - - case '~': //information from '^' version request - DIAG(F("EX-SensorCAM device found, I2C:%s,CAM Version v%d.%d.%d vpins %u-%u"), - _I2CAddress.toString(), rBuf[1]/10, rBuf[1]%10, rBuf[2],(int) _firstVpin, (int) _firstVpin +_nPins-1); - DIAG(F("IO_EXSensorCAM driver v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,_firstVpin); - break; - - case 'f': - DIAG(F("(f %%%%) frame header 'f' for bsNo %d/%d - showing Quarter sample (1 row) only"), rBuf[1]/8,rBuf[1]%8); - SEND(&USB_SERIAL,F(">4, rBuf[k]&15, k%3==2 ? " " : " "); - Serial.print(" latest grab: "); - for(k=16;k<28;k++) - SEND(&USB_SERIAL,F("%x%x%s"), rBuf[k]>>4, rBuf[k]&15, (k%3==0) ? " " : " "); - Serial.print(" n>\n"); - break; - - case 'i': //information from i%% - k=256*rBuf[5]+rBuf[4]; - DIAG(F("(i%%%%[,$$]) Info: Sensor 0%o(%d) enabled:%d status:%d row=%d x=%d Twin=0%o pvtThreshold=%d A~%d") - ,rBuf[1],rBuf[1],rBuf[3],rBuf[2],rBuf[6],k,rBuf[7],rBuf[9],int(rBuf[8])*16); - break; - - case 'm': - DIAG(F("(m$[,##]) Min/max: $ frames min2flip (trip) %d, maxSensors 0%o, minSensors 0%o, nLED %d," - " threshold %d, TWOIMAGE_MAXBS 0%o"),rBuf[1],rBuf[3],rBuf[2],rBuf[4],rBuf[5],rBuf[6]); - break; - - case 'n': - DIAG(F("(n$[,##]) Nominate: $ nLED %d, ## minSensors 0%o (maxSensors 0%o threshold %d)") - ,rBuf[4],rBuf[2],rBuf[3],rBuf[5]); - break; - - case 'p': - b=rBuf[1]-2; - if(b<4) { Serial.print("\n"); break; } - SEND(&USB_SERIAL,F("\n"); - break; - - case 'q': - for (int i =0; i<8; i++) str[i] = ((rBuf[2] << i) & 0x80 ? '1' : '0'); - DIAG(F("(q $) Query bank %c ENABLED sensors(S%c7-%c0): %s "), rBuf[1], rBuf[1], rBuf[1], str); - break; - - case 't': //threshold etc. from t## //bad pkt if 't' FF's - if(rBuf[1]==0xFF) {Serial.println("");_savedCmd[2] +=1; return 0;} - SEND(&USB_SERIAL,F("127) Serial.print("##* "); - else{ - if(rBuf[2]>rBuf[1]) Serial.print("-?* "); - else Serial.print("--* "); - } - for(int i=3;i<31;i+=2){ - uint8_t valu=rBuf[i]; //get bsn - if(valu==80) break; //80 = end flag - else{ - SEND(&USB_SERIAL,F("%d%d:"), (valu&0x7F)/8,(valu&0x7F)%8); - if(valu>=128) Serial.print("?-"); - else {if(rBuf[i+1]>=128) Serial.print("oo");else Serial.print("--");} - valu=rBuf[i+1]; - SEND(&USB_SERIAL,F("%d%s"),min(valu&0x7F,99),(valu<128) ? "--* ":"##* "); - } - } - Serial.print(" >\n"); - break; - - default: //header not a recognised cmd character - DIAG(F("CAM packet header not valid (0x%x) (0x%x) (0x%x)"),rBuf[0],rBuf[1],rBuf[2]); - return 1; - } - return 0; -} -//************************* -// Write (analogue) 8bit (command) values. Write the parameters to the sensorCAM -void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) override { - uint8_t outputBuffer[7]; - int errors=0; - outputBuffer[0] = camop; - int pin = vpin - _firstVpin; - - if(camop >= 0x80) { //case "a" (4p) also (3p) e.g. - camop=param1; //put row (0-236) in expected place - param1=param3; //put column in expected place - outputBuffer[0] = 'A'; - pin = (pin/8)*10 + pin%8; //restore bsNo. as integer - } - if (_deviceState == DEVSTATE_FAILED) return; - - outputBuffer[1] = pin; //vpin => bsn - outputBuffer[2] = param1 & 0xFF; - outputBuffer[3] = param1 >> 8; - outputBuffer[4] = camop; //command code - outputBuffer[5] = param3 & 0xFF; - outputBuffer[6] = param3 >> 8; - - int count=param1+1; - if(camop=='Q'){ - if(param3<=10) {count=param3; camop='B';} - //if(param1<10) outputBuffer[2] = param1*10; - } - if(camop=='B'){ //then 'b'(b%) cmd - can totally deal with that here. (but can't do b%,# (brightSF)) - if(param1>97) return; - if(param1>9) param1 = param1/10; //accept a bsNo - for(int bnk=param1;bnk%d0) %s"), bnk,b>>4,b&15,bnk,bnk,str ); - } - return; - } - if (outputBuffer[4]=='T') { //then 't' cmd - if(param1<31) { //repeated calls if param < 31 - //for (int i=0;i<7;i++) _savedCmd[i]=outputBuffer[i]; - memcpy( _savedCmd, outputBuffer, 7); - }else _savedCmd[2] = 0; //no repeats if ##>30 - }else _savedCmd[2] = 0; //no repeats unless 't' - - _lasttStateRead = micros(); //don't repeat until _tStateRefresh mSec - - errors = ioESP32(_I2CAddress, _CAMresponseBuff, 32 , outputBuffer, 7); //send to esp32-CAM - if (errors==0) return; - else { // if (_CAMresponseBuff[0] != EXIORDY) //can't be sure what is inBuff[0] ! - DIAG(F("ioESP32 i2c error %d header 0x%x"),errors,_CAMresponseBuff[0]); - } -} -//************************* - // Display device information and status. - void _display() override { - DIAG(F("EX-SensorCAM 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("")); - } -//************************* -// Helper function for error handling -void reportError(uint8_t status, bool fail=true) { - DIAG(F("EX-SensorCAM I2C:%s Error:%d (%S)"), _I2CAddress.toString(), - status, I2CManager.getErrorMessage(status)); - if (fail) _deviceState = DEVSTATE_FAILED; -} -//************************* - uint8_t _numDigitalPins = 80; - size_t digitalBytesNeeded=10; - uint8_t _CAMresponseBuff[34]; - - uint8_t _majorVer = 0; - uint8_t _minorVer = 0; - uint8_t _patchVer = 0; - - uint8_t _digitalInputStates[10]; - I2CRB _i2crb; - uint8_t _inputBuf[12]; - byte _outputBuffer[8]; - - bool _verPrint=true; - - uint8_t _readCommandBuffer[8]; - uint8_t _savedCmd[8]; //for repeat 't' command - //uint8_t _digitalPinBytes = 10; // Size of allocated memory buffer (may be longer than needed) - - enum {RDS_IDLE, RDS_DIGITAL, RDS_TSTATE}; // Read operation states - uint8_t _readState = RDS_IDLE; - //uint8_t cmdBuffer[7]={0,0,0,0,0,0,0}; - unsigned long _lastDigitalRead = 0; - unsigned long _lasttStateRead = 0; - unsigned long _digitalRefresh = DIGITALREFRESH; // Delay refreshing digital inputs for 10ms - const unsigned long _tStateRefresh = 120000UL; // Delay refreshing repeat "tState" inputs - - enum { - EXIOINIT = 0xE0, // Flag to initialise setup procedure - EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup - CAMERR = 0xFE - }; -}; -#endif \ No newline at end of file diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index c204765..5533554 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -18,15 +18,13 @@ // Include devices you need. #include "IODevice.h" //#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_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 +//#include "IO_EXTurntable.h" // Turntable-EX turntable controller +//#include "IO_EXFastClock.h" // FastClock driver //#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments). -//#include "IO_I2CDFPlayer.h" // DFPlayer over I2C -#include "IO_EXSensorCAM.h" // sensorCAM driver //========================================================================== // The function halSetup() is invoked from CS if it exists within the build. @@ -36,8 +34,6 @@ void halSetup() { - // I2CManager.setClock(100000); //to set i2c bus clock rate - //======================================================================= // The following directives define auxiliary display devices. // These can be defined in addition to the system display (display @@ -238,31 +234,6 @@ void halSetup() { // DFPlayer::create(10000, 10, Serial1); - //======================================================================= - // Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module on a SC16IS750/SC16IS752 I2C UART - //======================================================================= - // DFPlayer via NXP SC16IS752 I2C Dual UART. - // I2C address range 0x48 - 0x57 - // - // Generic format: - // I2CDFPlayer::create(1st vPin, vPins, I2C address, xtal); - // Parameters: - // 1st vPin : First virtual pin that EX-Rail can control to play a sound, use PLAYSOUND command (alias of ANOUT) - // vPins : Total number of virtual pins allocated (1 vPin is supported currently) - // 1st vPin for UART 0 - // I2C Address : I2C address of the serial controller, in 0x format - // xtal : 0 for 1.8432Mhz, 1 for 14.7456Mhz - // - // The vPin is also a pin that can be read with the WAITFOR(vPin) command indicating if the DFPlayer has finished playing a track - // - - // I2CDFPlayer::create(10000, 1, 0x48, 1); - // - // Configuration example on a multiplexer - // I2CDFPlayer::create(10000, 1, {I2CMux_0, SubBus_0, 0x48}, 1); - - - //======================================================================= // 16-pad capacitative touch key pad based on TP229 IC. //======================================================================= @@ -314,13 +285,12 @@ void halSetup() { //======================================================================= // The parameters are: // firstVpin = First available Vpin to allocate - // numPins= Number of Vpins to allocate, can be either 1 to 3 - // i2cAddress = Available I2C address (default 0x67) + // numPins= Number of Vpins to allocate, can be either 1 or 2 + // i2cAddress = Available I2C address (default 0x70) //RotaryEncoder::create(firstVpin, numPins, i2cAddress); - //RotaryEncoder::create(700, 1, 0x67); - //RotaryEncoder::create(700, 2, 0x67); - //RotaryEncoder::create(700, 3, 0x67); + //RotaryEncoder::create(700, 1, 0x70); + //RotaryEncoder::create(701, 2, 0x71); //======================================================================= // The following directive defines an EX-FastClock instance. @@ -335,22 +305,7 @@ void halSetup() { // EXFastClock::create(0x55); - - //======================================================================= - // The following directive defines an ESP32-CAM instance. - //======================================================================= - // EXSensorCAM::create(VPIN, Number of VPINs, I2C Address) - // - // The parameters are: - // VPIN=an available Vpin as start of block of consecutive sensors (up to 80) - // #define SENSORCAM_VPIN0 #00 in config.h if not using 700. - // Number of VPINs=pin count (must not exceed 80) - // I2C address=an available I2C address (default 0x11) - // - // EXSensorCAM::create(700, 80, 0x11); //preference is now to use HAL(700 80 0x11) in myAutomation.h - //EXSensorCAM::create(600, 80, 0x12); //alternate or second CAM device address creation - //preference is now to use HAL(EXSensorCAM 700 80 0x11) in myAutomation.h rather than :create - } +} #endif