From 0c62d27be2fcdd16f429784e1b451ce923a17385 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 23 May 2024 08:35:03 +0100 Subject: [PATCH 1/7] CAM first compile --- CamParser.cpp | 70 ++++++++++++++++ CamParser.h | 12 +++ DCCEXParser.cpp | 9 +- IODevice.h | 1 + IO_EXSensorCAM.h | 208 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 CamParser.cpp create mode 100644 CamParser.h create mode 100644 IO_EXSensorCAM.h diff --git a/CamParser.cpp b/CamParser.cpp new file mode 100644 index 00000000..4ec8142d --- /dev/null +++ b/CamParser.cpp @@ -0,0 +1,70 @@ +#include "CamParser.h" +#include "FSH.h" +#include "IO_EXSensorCAM.h" + + +VPIN EXSensorCAM::CAMBaseVpin = 0; + +bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) +{ + (void)stream; // probably unused parameter + + if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam found + if (paramCount == 0) return false; + VPIN vpin=EXSensorCAM::CAMBaseVpin; + byte camop=p[0]; // cam oprerator (F is special) + if (camop!='F') camop=p[0]-0x20; // lower case the oprerator + int16_t param1; + int16_t param2; + + switch(paramCount) { + case 0: + return false; + + case 1: + if (strchr_P((const char *)F("egrvwxFimt"),camop) == nullptr) return false; + param1=0; + param2=0; + break; + + case 2: // + if(camop=='c'){ + EXSensorCAM::CAMBaseVpin=p[1]; + DIAG(F("CAM base vpin: %c %d "),p[0],p[1]); + return true; + } + if (strchr_P((const char *)F("oalnrsuvimt"),camop) == nullptr) return false; + + param1 = p[1]; + param2 = 0; + break; + + case 3: // + if (p[1]>236 || p[1]<0) return false; + if (p[2]>316 || p[2]<0) return false; + + camop='A'; // sepcial case in IO_SensorCAM + vpin = p[0]; + param1 = p[1]; + param2 = p[2]; + break; + + case 4: // + if (camop!='a') return false;//must start with 'a' + + if (p[1]>80 || p[1]<0) return false; + if (p[2]>236 || p[2]<0) return false; + if (p[3]>316 || p[3]<0) return false; + + camop=128+p[1]; // sensor id in camop + param1=p[2]; // row + param2=p[3]; // col + break; + + default: + return false; + } + DIAG(F("Cam: %d %d %c %d"),vpin,param1,camop,param2); + IODevice::writeAnalogue(vpin,param1,camop,param2); + return true; +} diff --git a/CamParser.h b/CamParser.h new file mode 100644 index 00000000..ad1b7e64 --- /dev/null +++ b/CamParser.h @@ -0,0 +1,12 @@ +#ifndef CamParser_H +#define CamParser_H +#include +#include "IODevice.h" + +class CamParser { + public: + static bool parseN(Print * stream, byte paramCount, int16_t p[]); +}; + + +#endif \ No newline at end of file diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 431093ff..cdbd71aa 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -117,6 +117,7 @@ Once a new OPCODE is decided upon, update this list. #include "Turntables.h" #include "version.h" #include "KeywordHasher.h" +#include "CamParser.h" // This macro can't be created easily as a portable function because the // flashlist requires a far pointer for high flash access. @@ -401,7 +402,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) else IODevice::write(-p[0],LOW); return; } - if (params>=2 && params<=4) { // + if (params>=2 && params<=4) { // // unused params default to 0 IODevice::writeAnalogue(p[0],p[1],p[2],p[3]); return; @@ -799,7 +800,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) return; break; #endif - +#ifndef IO_NO_HAL + case 'N': // if not intercepted by EXRAIL diff --git a/IODevice.h b/IODevice.h index 6c70f5f2..2ba03553 100644 --- a/IODevice.h +++ b/IODevice.h @@ -547,6 +547,7 @@ class FLAGS : IODevice { #include "IO_duinoNodes.h" #include "IO_EXIOExpander.h" #include "IO_trainbrains.h" +#include "IO_EXSensorCAM.h" #endif // iodevice_h diff --git a/IO_EXSensorCAM.h b/IO_EXSensorCAM.h new file mode 100644 index 00000000..81a03efb --- /dev/null +++ b/IO_EXSensorCAM.h @@ -0,0 +1,208 @@ +/* 12/MAY/24 + * © 2022, Peter Cole. All rights reserved. + * © 2023, Barry Daniel ESP32 revision + * © 2024, Harald Barth. All rights reserved. + * + * 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 . + */ + +/* + * The IO_EXSensorCAM.h device driver can integrate with the sensorCAM device. + * This device driver will configure the device on startup, along with + * interacting with the device for all input/output duties. + * + * To create EX-SensorCAM devices, define them in myAutomation.h: + * + * // HAL(EXSensorCAM,vpin, num_vpins, i2c_address); + * HAL(EXSensorCAM,700, 80, 0x11) + * + * The total number of pins cannot exceed 80 because of the communications + * packet format. The number of analogue inputs cannot exceed 16 because of a + * limit on the maximum I2C packet size of 32 bytes (in the Wire library). + */ + +#ifndef IO_EX_EXSENSORCAM_H +#define IO_EX_EXSENSORCAM_H + +#include "DIAG.h" +#include "FSH.h" +#include "I2CManager.h" +#include "IODevice.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; + if (CAMBaseVpin==0) CAMBaseVpin=_firstVpin; + // Number of pins cannot exceed 80. + if (nPins > 80) nPins = 80; + _nPins = nPins; + _I2CAddress = i2cAddress; + addDevice(this); + } + + void _begin() { + // 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; + } + + _i2crb.setRequestParams(_I2CAddress, _inputBuffer, sizeof(_inputBuffer), + _outputBuffer, sizeof(_outputBuffer)); + _outputBuffer[0]='V'; + _i2crb.writeLen=1; + I2CManager.queueRequest(&_i2crb); + _deviceState = DEVSTATE_SCANNING; + } + + + // Main loop, collect any input and reissue poll cmd + void _loop(unsigned long currentMicros) override { + if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return + + if (_i2crb.isBusy()) return; // If I2C operation still in progress, return + + processInput(); + if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return + + + // is it time to request another set of pins? + if (currentMicros - _lastDigitalRead > _digitalRefresh) return; + + // Issue new read request for digital states. + _lastDigitalRead = currentMicros; + _outputBuffer[0] = 'Q'; // query sensor states + _i2crb.writeLen = 1; + I2CManager.queueRequest(&_i2crb); + _deviceState = DEVSTATE_SCANNING; + } + + // Obtain the a block of 8 sensors as an analog value. + // can be used to track poisition in sequention pin blocks + int _readAnalogue(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + return _digitalInputStates[(vpin - _firstVpin) / 8]; + } + + // Obtain a sensor state + int _read(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + return bitRead(_digitalInputStates[pin / 8], pin % 8); + } + + // Used to write a command from the parseN function. + // note parameter names changed to suit use in this scenario + // but the function signature remians as per IO_DEVICE + // _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { + void _writeAnalogue(VPIN vpin, int p1, uint8_t opcode, uint16_t p2) override { + if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return + _i2crb.wait(); + // opcodes are lower case as-entered by user through CamParser. + // except as follows: (to avoid adding more base functions to IO_DEVICE) + // is passed in here as opcode=128+bsNo, p1=row, p2=col + // so p0 is reverse engineered from the opcode + // is passed with opcode 'A' p1=row, p2=col + // and is converted to 'a' and the P0 bsNo obtained from the VPIN. + uint16_t p0 =0; + + if (opcode>=128) { // + p0=opcode & 0x7f; // get bSno from opcode + opcode='a'; // and revert to correct code + } + else if (opcode=='A') { // + p0=vpin - _firstVpin; + opcode='a'; + } + + _outputBuffer[0]=opcode; + _outputBuffer[1]=p0 & 0xFF; + _outputBuffer[2]=p0 >> 8; + _outputBuffer[3]=p1 & 0xFF; + _outputBuffer[4]=(p1 >> 8) & 0xFF; + _outputBuffer[5]=p2 & 0xFF; + _outputBuffer[6]=p2 >> 8; + I2CManager.queueRequest(&_i2crb); + } + + // This function is invoked when an I/O operation on the requestBlock + // completes. + void processInput() { + if (_deviceState != DEVSTATE_SCANNING) return; + // some input was pending + uint8_t status = _i2crb.status; + if (status != I2C_STATUS_OK) { + reportError(status); + return; + } + + _deviceState = DEVSTATE_NORMAL; + + if (_inputBuffer[0] == 'Q') { // all sensors + memcpy(_digitalInputStates, _inputBuffer + 1, + sizeof(_digitalInputStates)); + return; + } + if (_inputBuffer[0] == 'V') { // version response + memcpy(_version, _inputBuffer + 1, sizeof(_version)); + _display(); + return; + } + } + + // Display device information and status. + void _display() override { + DIAG(F("EX-SensorCAM I2C:%s v%d.%d.%d Vpins %u-%u %S"), + _I2CAddress.toString(), _version[0], _version[1], _version[2], + (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; + uint8_t _version[3]; + uint8_t _digitalInputStates[10]; + I2CRB _i2crb; + byte _inputBuffer[12]; + byte _outputBuffer[8]; + + unsigned long _lastDigitalRead = 0; + const unsigned long _digitalRefresh = + 10000UL; // Delay refreshing digital inputs for 10ms +}; + +#endif From 77e6e48d7834ff22caa989f01e2d65fadb99ccb6 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 23 May 2024 08:50:37 +0100 Subject: [PATCH 2/7] ccrrect caps. --- CamParser.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CamParser.cpp b/CamParser.cpp index 4ec8142d..1de58570 100644 --- a/CamParser.cpp +++ b/CamParser.cpp @@ -8,12 +8,13 @@ VPIN EXSensorCAM::CAMBaseVpin = 0; bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) { (void)stream; // probably unused parameter - - if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam found + // DIAG(F("cam (%d) %c %d %d %d"),paramCount,p[0],p[1],p[2],p[3]); + + //if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam found if (paramCount == 0) return false; VPIN vpin=EXSensorCAM::CAMBaseVpin; byte camop=p[0]; // cam oprerator (F is special) - if (camop!='F') camop=p[0]-0x20; // lower case the oprerator + if (camop!='F') camop=p[0]+0x20; // lower case the oprerator int16_t param1; int16_t param2; From d06d3ec77e22156bb6d25c77579c2d36c8a9e829 Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Wed, 21 Aug 2024 14:33:21 +1000 Subject: [PATCH 3/7] Add files via upload --- CamParser.cpp | 165 ++++++----- IO_EXSensorCAM.h | 619 +++++++++++++++++++++++++++-------------- config.example.h | 622 ++++++++++++++++++++---------------------- myHal.cpp | 362 ++++++++++++++++++++++++ myHal.cpp_example.txt | 28 +- mySetup.h | 26 ++ 6 files changed, 1215 insertions(+), 607 deletions(-) create mode 100644 myHal.cpp create mode 100644 mySetup.h diff --git a/CamParser.cpp b/CamParser.cpp index 1de58570..8d4ab2f7 100644 --- a/CamParser.cpp +++ b/CamParser.cpp @@ -1,71 +1,94 @@ -#include "CamParser.h" -#include "FSH.h" -#include "IO_EXSensorCAM.h" - - -VPIN EXSensorCAM::CAMBaseVpin = 0; - -bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) -{ - (void)stream; // probably unused parameter - // DIAG(F("cam (%d) %c %d %d %d"),paramCount,p[0],p[1],p[2],p[3]); - - //if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam found - if (paramCount == 0) return false; - VPIN vpin=EXSensorCAM::CAMBaseVpin; - byte camop=p[0]; // cam oprerator (F is special) - if (camop!='F') camop=p[0]+0x20; // lower case the oprerator - int16_t param1; - int16_t param2; - - switch(paramCount) { - case 0: - return false; - - case 1: - if (strchr_P((const char *)F("egrvwxFimt"),camop) == nullptr) return false; - param1=0; - param2=0; - break; - - case 2: // - if(camop=='c'){ - EXSensorCAM::CAMBaseVpin=p[1]; - DIAG(F("CAM base vpin: %c %d "),p[0],p[1]); - return true; - } - if (strchr_P((const char *)F("oalnrsuvimt"),camop) == nullptr) return false; - - param1 = p[1]; - param2 = 0; - break; - - case 3: // - if (p[1]>236 || p[1]<0) return false; - if (p[2]>316 || p[2]<0) return false; - - camop='A'; // sepcial case in IO_SensorCAM - vpin = p[0]; - param1 = p[1]; - param2 = p[2]; - break; - - case 4: // - if (camop!='a') return false;//must start with 'a' - - if (p[1]>80 || p[1]<0) return false; - if (p[2]>236 || p[2]<0) return false; - if (p[3]>316 || p[3]<0) return false; - - camop=128+p[1]; // sensor id in camop - param1=p[2]; // row - param2=p[3]; // col - break; - - default: - return false; - } - DIAG(F("Cam: %d %d %c %d"),vpin,param1,camop,param2); - IODevice::writeAnalogue(vpin,param1,camop,param2); - return true; -} + +//sensorCAM parser.cpp version 3.01 Aug 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 version=9914; +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 + int16_t 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 + + if((p[0] == ve) || (p[0] == ver) || (p[0] == version)) camop='^'; + // send UPPER case to sensorCAM to flag binary data from a DCCEX-CS parser + switch(paramCount) { + case 1: // produces '^' + if (strchr_P((const char *)F("EFGMRVW^"),camop) == nullptr) return false; + if (camop=='F') camop=']'; // for Reset/Finish webCAM. + break; // Coded as ']' else conflicts with + + case 2: // + if (strchr_P((const char *)F("ABFILMNOPRSTUV^"),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 index 81a03efb..40420c37 100644 --- a/IO_EXSensorCAM.h +++ b/IO_EXSensorCAM.h @@ -1,208 +1,411 @@ -/* 12/MAY/24 - * © 2022, Peter Cole. All rights reserved. - * © 2023, Barry Daniel ESP32 revision - * © 2024, Harald Barth. All rights reserved. - * - * 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 . - */ - -/* - * The IO_EXSensorCAM.h device driver can integrate with the sensorCAM device. - * This device driver will configure the device on startup, along with - * interacting with the device for all input/output duties. - * - * To create EX-SensorCAM devices, define them in myAutomation.h: - * - * // HAL(EXSensorCAM,vpin, num_vpins, i2c_address); - * HAL(EXSensorCAM,700, 80, 0x11) - * - * The total number of pins cannot exceed 80 because of the communications - * packet format. The number of analogue inputs cannot exceed 16 because of a - * limit on the maximum I2C packet size of 32 bytes (in the Wire library). - */ - -#ifndef IO_EX_EXSENSORCAM_H -#define IO_EX_EXSENSORCAM_H - -#include "DIAG.h" -#include "FSH.h" -#include "I2CManager.h" -#include "IODevice.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; - if (CAMBaseVpin==0) CAMBaseVpin=_firstVpin; - // Number of pins cannot exceed 80. - if (nPins > 80) nPins = 80; - _nPins = nPins; - _I2CAddress = i2cAddress; - addDevice(this); - } - - void _begin() { - // 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; - } - - _i2crb.setRequestParams(_I2CAddress, _inputBuffer, sizeof(_inputBuffer), - _outputBuffer, sizeof(_outputBuffer)); - _outputBuffer[0]='V'; - _i2crb.writeLen=1; - I2CManager.queueRequest(&_i2crb); - _deviceState = DEVSTATE_SCANNING; - } - - - // Main loop, collect any input and reissue poll cmd - void _loop(unsigned long currentMicros) override { - if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return - - if (_i2crb.isBusy()) return; // If I2C operation still in progress, return - - processInput(); - if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return - - - // is it time to request another set of pins? - if (currentMicros - _lastDigitalRead > _digitalRefresh) return; - - // Issue new read request for digital states. - _lastDigitalRead = currentMicros; - _outputBuffer[0] = 'Q'; // query sensor states - _i2crb.writeLen = 1; - I2CManager.queueRequest(&_i2crb); - _deviceState = DEVSTATE_SCANNING; - } - - // Obtain the a block of 8 sensors as an analog value. - // can be used to track poisition in sequention pin blocks - int _readAnalogue(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - return _digitalInputStates[(vpin - _firstVpin) / 8]; - } - - // Obtain a sensor state - int _read(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - int pin = vpin - _firstVpin; - return bitRead(_digitalInputStates[pin / 8], pin % 8); - } - - // Used to write a command from the parseN function. - // note parameter names changed to suit use in this scenario - // but the function signature remians as per IO_DEVICE - // _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { - void _writeAnalogue(VPIN vpin, int p1, uint8_t opcode, uint16_t p2) override { - if (_deviceState == DEVSTATE_FAILED) return; // If device failed, return - _i2crb.wait(); - // opcodes are lower case as-entered by user through CamParser. - // except as follows: (to avoid adding more base functions to IO_DEVICE) - // is passed in here as opcode=128+bsNo, p1=row, p2=col - // so p0 is reverse engineered from the opcode - // is passed with opcode 'A' p1=row, p2=col - // and is converted to 'a' and the P0 bsNo obtained from the VPIN. - uint16_t p0 =0; - - if (opcode>=128) { // - p0=opcode & 0x7f; // get bSno from opcode - opcode='a'; // and revert to correct code - } - else if (opcode=='A') { // - p0=vpin - _firstVpin; - opcode='a'; - } - - _outputBuffer[0]=opcode; - _outputBuffer[1]=p0 & 0xFF; - _outputBuffer[2]=p0 >> 8; - _outputBuffer[3]=p1 & 0xFF; - _outputBuffer[4]=(p1 >> 8) & 0xFF; - _outputBuffer[5]=p2 & 0xFF; - _outputBuffer[6]=p2 >> 8; - I2CManager.queueRequest(&_i2crb); - } - - // This function is invoked when an I/O operation on the requestBlock - // completes. - void processInput() { - if (_deviceState != DEVSTATE_SCANNING) return; - // some input was pending - uint8_t status = _i2crb.status; - if (status != I2C_STATUS_OK) { - reportError(status); - return; - } - - _deviceState = DEVSTATE_NORMAL; - - if (_inputBuffer[0] == 'Q') { // all sensors - memcpy(_digitalInputStates, _inputBuffer + 1, - sizeof(_digitalInputStates)); - return; - } - if (_inputBuffer[0] == 'V') { // version response - memcpy(_version, _inputBuffer + 1, sizeof(_version)); - _display(); - return; - } - } - - // Display device information and status. - void _display() override { - DIAG(F("EX-SensorCAM I2C:%s v%d.%d.%d Vpins %u-%u %S"), - _I2CAddress.toString(), _version[0], _version[1], _version[2], - (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; - uint8_t _version[3]; - uint8_t _digitalInputStates[10]; - I2CRB _i2crb; - byte _inputBuffer[12]; - byte _outputBuffer[8]; - - unsigned long _lastDigitalRead = 0; - const unsigned long _digitalRefresh = - 10000UL; // Delay refreshing digital inputs for 10ms -}; - -#endif +/* 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 301 +// v301 improved 'f' and 'p' 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 Sp Serial.print + +#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 response set S06 to 0 + // versions of sensorCAM.h after v300 should return header of '@'(0x40) (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 +// rBuff 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) { + int k; + int b; + int x; + // 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); + 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 '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 'f': + DIAG(F("(f %%%%) frame header 'f' for 0%o - showing Quarter sample (1 row) only"), rBuf[1]); + /*if(rBuf[2]==0)*/ { Sp(">4,HEX); Sp(rBuf[k]&15,HEX); if(k%3==2)Sp(" "); } + Sp(" latest grab ->"); + for(k=16;k<28;k++){ Sp(" "); Sp(rBuf[k]>>4,HEX); Sp(rBuf[k]&15,HEX); if(k%3==0)Sp(" "); } + Sp(" n>\n"); + break; + + case 'p': + b=rBuf[1]-2; + if(b<4) { Sp("\n"); break; } + Sp("\n"); + break; + + case 't': //threshold etc. from t## //bad pkt if 't' FF's + if(rBuf[1]==0xFF) {Serial.println("");_savedCmd[2] +=1; return 0;} + Sp("99)k=99;Sp(k); + if(rBuf[2]>127) Sp("##* "); + else{ + if(rBuf[2]>rBuf[1]) Sp("-?* "); + else Sp("--* "); + } + for(int i=3;i<31;i+=2){ + uint8_t valu=rBuf[i]; //get bsn + if(valu==80) break; //80 = end flag + else{ + if((valu&0x7F)<8) Sp("0"); Sp(valu&0x7F,OCT);Sp(':'); + if(valu>=128) Sp("?-"); + else {if(rBuf[i+1]>=128) Sp("oo");else Sp("--");} + valu=rBuf[i+1]; k=valu&0x7F; + if(k>99) k=99; Sp(k); + if(valu<128) Sp("--* "); else Sp("##* "); + } + } + 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, int16_t 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. + } + 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; + + 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 + uint8_t b=_digitalInputStates[param1]; + char str[] = "11111111"; + for (int i=0;i<8;i++) if(((b<0) %s"), param1,b>>4,b&15,param1,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, 8); + }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/config.example.h b/config.example.h index 3fc86c34..8d63583b 100644 --- a/config.example.h +++ b/config.example.h @@ -1,327 +1,295 @@ -/* - * © 2022 Paul M. Antoine - * © 2021 Neil McKechnie - * © 2020-2023 Harald Barth - * © 2020-2021 Fred Decker - * © 2020-2021 Chris Harlow - * © 2023 Nathan Kellenicki - * - * This file is part of CommandStation-EX - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * It is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CommandStation. If not, see . - */ - -/********************************************************************** - -The configuration file for DCC-EX Command Station - -**********************************************************************/ - -///////////////////////////////////////////////////////////////////////////////////// -// If you want to add your own motor driver definition(s), add them here -// For example MY_SHIELD with display name "MINE": -// (remove comment start and end marker if you want to edit and use that) -/* -#define MY_SHIELD F("MINE"), \ - new MotorDriver( 3, 12, UNUSED_PIN, 9, A0, 5.08, 3000, A4), \ - new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 5.08, 1500, A5) -*/ - -///////////////////////////////////////////////////////////////////////////////////// -// NOTE: Before connecting these boards and selecting one in this software -// check the quick install guides!!! Some of these boards require a voltage -// generating resistor on the current sense pin of the device. Failure to select -// the correct resistor could damage the sense pin on your Arduino or destroy -// the device. -// -// DEFINE MOTOR_SHIELD_TYPE BELOW. THESE ARE EXAMPLES. FULL LIST IN MotorDrivers.h -// -// STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel -// POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track) -// FUNDUMOTO_SHIELD : Fundumoto Shield, no current sensing (not recommended, no short protection) -// FIREBOX_MK1 : The Firebox MK1 -// FIREBOX_MK1S : The Firebox MK1S -// IBT_2_WITH_ARDUINO : Arduino Motor Shield for PROG and IBT-2 for MAIN -// EX8874_SHIELD : DCC-EX TI DRV8874 based motor shield -// | -// +-----------------------v -// -#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD -// -///////////////////////////////////////////////////////////////////////////////////// -// -// If you want to restrict the maximum current LOWER than what your -// motor shield can provide, you can do that here. For example if you -// have a motor shield that can provide 5A and your power supply can -// only provide 2.5A then you should restict the maximum current to -// 2.25A (90% of 2.5A) so that DCC-EX does shut off the track before -// your PS does shut DCC-EX. MAX_CURRENT is in mA so for this example -// it would be 2250, adjust the number according to your PS. If your -// PS has a higher rating than your motor shield you do not need this. -// You can use this as well if you are cautious and your trains do not -// need full current. -// #define MAX_CURRENT 2250 -// -///////////////////////////////////////////////////////////////////////////////////// -// -// The IP port to talk to a WIFI or Ethernet shield. -// -#define IP_PORT 2560 - -///////////////////////////////////////////////////////////////////////////////////// -// -// NOTE: Only supported on Arduino Mega -// Set to false if you not even want it on the Arduino Mega -// -#define ENABLE_WIFI true - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE WiFi Parameters (only in effect if WIFI is on) -// -// If DONT_TOUCH_WIFI_CONF is set, all WIFI config will be done with -// the <+> commands and this sketch will not change anything over -// AT commands and the other WIFI_* defines below do not have any effect. -//#define DONT_TOUCH_WIFI_CONF -// -// WIFI_SSID is the network name IF you want to use your existing home network. -// Do NOT change this if you want to use the WiFi in Access Point (AP) mode. -// -// If you do NOT set the WIFI_SSID and do NOT set the WIFI_PASSWORD, -// then the WiFi chip will first try to connect to the previously -// configured network and if that fails fall back to Access Point mode. -// The SSID of the AP will be automatically set to DCCEX_*. -// If you DO set the WIFI_SSID then the WiFi chip will try to connect -// to that (home) network in station (client) mode. If a WIFI_PASSWORD -// is set (recommended), that password will be used for AP mode. -// The AP mode password must be at least 8 characters long. -// -// Your SSID may not contain ``"'' (double quote, ASCII 0x22). -#define WIFI_SSID "Your network name" -// -// WIFI_PASSWORD is the network password for your home network or if -// you want to change the password from default AP mode password -// to the AP password you want. -// Your password may not contain ``"'' (double quote, ASCII 0x22). -#define WIFI_PASSWORD "Your network passwd" -// -// WIFI_HOSTNAME: You probably don't need to change this -#define WIFI_HOSTNAME "dccex" -// -// WIFI_CHANNEL: If the line "#define ENABLE_WIFI true" is uncommented, -// WiFi will be enabled (Mega only). The default channel is set to "1" whether -// this line exists or not. If you need to use an alternate channel (we recommend -// using only 1,6, or 11) you may change it here. -#define WIFI_CHANNEL 1 -// -// WIFI_FORCE_AP: If you'd like to specify your own WIFI_SSID in AP mode, set this -// true. Otherwise it is assumed that you'd like to connect to an existing network -// with that SSID. -#define WIFI_FORCE_AP false - -///////////////////////////////////////////////////////////////////////////////////// -// -// ENABLE_ETHERNET: Set to true if you have an Arduino Ethernet card (wired). This -// is not for Wifi. You will then need the Arduino Ethernet library as well -// -//#define ENABLE_ETHERNET true - - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP -// -//#define IP_ADDRESS { 192, 168, 1, 200 } - - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE LCD SCREEN USAGE BY THE BASE STATION -// -// Note: This feature requires an I2C enabled LCD screen using a Hitachi HD44780 -// controller and a commonly available PCF8574 based I2C 'backpack'. -// To enable, uncomment one of the #define lines below - -// define LCD_DRIVER for I2C address 0x27, 16 cols, 2 rows -// #define LCD_DRIVER 0x27,16,2 - -//OR define OLED_DRIVER width,height[,address] in pixels (address auto detected if not supplied) -// 128x32 or 128x64 I2C SSD1306-based devices are supported. -// Use 132,64 for a SH1106-based I2C device with a 128x64 display. -// #define OLED_DRIVER 0x3c,128,32 - -// Define scroll mode as 0, 1 or 2 -// * #define SCROLLMODE 0 is scroll continuous (fill screen if poss), -// * #define SCROLLMODE 1 is by page (alternate between pages), -// * #define SCROLLMODE 2 is by row (move up 1 row at a time). -#define SCROLLMODE 1 - -// In order to avoid wasting memory the current scroll buffer is limited -// to 8 lines. Some users wishing to display additional information -// such as TrackManager power states have requested additional rows aware -// of the warning that this will take extra RAM. if you wish to include additional rows -// uncomment the following #define and set the number of lines you need. -//#define MAX_CHARACTER_ROWS 12 - - -///////////////////////////////////////////////////////////////////////////////////// -// DISABLE EEPROM -// -// If you do not need the EEPROM at all, you can disable all the code that saves -// data in the EEPROM. You might want to do that if you are in a Arduino UNO -// and want to use the EXRAIL automation. Otherwise you do not have enough RAM -// to do that. Of course, then none of the EEPROM related commands work. -// -// EEPROM does not work on ESP32. So on ESP32, EEPROM will always be disabled, -// at least until it works. -// -// #define DISABLE_EEPROM - -///////////////////////////////////////////////////////////////////////////////////// -// DISABLE PROG -// -// If you do not need programming capability, you can disable all programming related -// commands. You might want to do that if you are using an Arduino UNO and still want -// to use EXRAIL automation, as the Uno is lacking in RAM and Flash to run both. -// -// Note this disables all programming functionality, including EXRAIL. -// -// #define DISABLE_PROG - -///////////////////////////////////////////////////////////////////////////////////// -// DISABLE / ENABLE VDPY -// -// The Virtual display "VDPY" feature is by default enabled everywhere -// but on Uno and Nano. If you think you can fit it (for example -// having disabled some of the features above) you can enable it with -// ENABLE_VDPY. You can even disable it on all other CPUs with -// DISABLE_VDPY -// -// #define DISABLE_VDPY -// #define ENABLE_VDPY - -///////////////////////////////////////////////////////////////////////////////////// -// DISABLE / ENABLE DIAG -// -// To diagose different errors, you can turn on differnet messages. This costs -// program memory which we do not have enough on the Uno and Nano, so it is -// by default DISABLED on those. If you think you can fit it (for example -// having disabled some of the features above) you can enable it with -// ENABLE_DIAG. You can even disable it on all other CPUs with -// DISABLE_DIAG -// -// #define DISABLE_DIAG -// #define ENABLE_DIAG - -///////////////////////////////////////////////////////////////////////////////////// -// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address -// is 127 and the first long address is 128. There are manufacturers which have -// another view. Lenz CS for example have considered addresses long from 100. If -// you want to change to that mode, do -//#define HIGHEST_SHORT_ADDR 99 -// If you want to run all your locos addressed long format, you could even do a -//#define HIGHEST_SHORT_ADDR 0 -// We do not support to use the same address, for example 100(long) and 100(short) -// at the same time, there must be a border. - -///////////////////////////////////////////////////////////////////////////////////// -// Some newer 32bit microcontrollers boot very quickly, so powering on I2C and other -// peripheral devices at the same time may result in the CommandStation booting too -// quickly to detect them. -// To work around this, uncomment the STARTUP_DELAY line below and set a value in -// milliseconds that works for your environment, default is 3000 (3 seconds). -// #define STARTUP_DELAY 3000 - -///////////////////////////////////////////////////////////////////////////////////// -// -// DEFINE TURNOUTS/ACCESSORIES FOLLOW NORM RCN-213 -// -// According to norm RCN-213 a DCC packet with a 1 is closed/straight -// and one with a 0 is thrown/diverging. In DCC++ Classic, and in previous -// versions of DCC++EX, a turnout throw command was implemented in the packet as -// '1' and a close command as '0'. The #define below makes the states -// match with the norm. But we don't want to cause havoc on existent layouts, -// so we define this only for new installations. If you don't want this, -// don't add it to your config.h. -//#define DCC_TURNOUTS_RCN_213 - -// By default, the driver which defines a DCC accessory decoder -// does send out the same state change on the DCC packet as it -// receives. This means a VPIN state=1 sends D=1 (close turnout -// or signal green) in the DCC packet. This can be reversed if -// necessary. -//#define HAL_ACCESSORY_COMMAND_REVERSE - -// If you have issues with that the direction of the accessory commands is -// reversed (for example when converting from another CS to DCC-EX) then -// you can use this to reverse the sense of all accessory commmands sent -// over DCC++. This #define likewise inverts the behaviour of the command -// for triggering DCC Accessory Decoders, so that generates a -// DCC packet with D=1 (close turnout) and generates D=0 -// (throw turnout). -//#define DCC_ACCESSORY_RCN_213 -// -// HANDLING MULTIPLE SERIAL THROTTLES -// The command station always operates with the default Serial port. -// Diagnostics are only emitted on the default serial port and not broadcast. -// Other serial throttles may be added to the Serial1, Serial2, Serial3, Serial4, -// Serial5, and Serial6 ports which may or may not exist on your CPU. (Mega has 3, -// SAMD/SAMC and STM32 have up to 6.) -// To monitor a throttle on one or more serial ports, uncomment the defines below. -// NOTE: do not define here the WiFi shield serial port or your wifi will not work. -// -//#define SERIAL1_COMMANDS -//#define SERIAL2_COMMANDS -//#define SERIAL3_COMMANDS -//#define SERIAL4_COMMANDS -//#define SERIAL5_COMMANDS -//#define SERIAL6_COMMANDS -// -// BLUETOOTH SERIAL ON ESP32 -// On ESP32 you have the possibility to use the builtin BT serial to connect to -// the CS. -// -// The CS shows up as a pairable BT Clasic device. Name is "DCCEX-hexnumber". -// BT is as an additional serial port, debug messages are still sent over USB, -// not BT serial. -// -// If you enable this there are some implications: -// 1. WiFi will sleep more (as WiFi and BT share the radio. So WiFi performance -// may suffer -// 2. The app will be bigger that 1.2MB, so the default partition scheme will not -// work any more. You need to choose a partition scheme with 2MB (or bigger). -// For example "NO OTA (2MB APP, 2MB SPIFFS)" in the Arduino IDE. -// 3. There is no securuity (PIN) implemented. Everyone in radio range can pair -// with your CS. -// -//#define SERIAL_BT_COMMANDS - -// BOOSTER PIN INPUT ON ESP32 -// On ESP32 you have the possibility to define a pin as booster input -// Arduio pin D2 is GPIO 26 on ESPDuino32 -// -//#define BOOSTER_INPUT 26 - -// SABERTOOTH -// -// This is a very special option and only useful if you happen to have a -// sabertooth motor controller from dimension engineering configured to -// take commands from and ESP32 via serial at 9600 baud from GPIO17 (TX) -// and GPIO16 (RX, currently unused). -// The number defined is the DCC address for which speed controls are sent -// to the sabertooth controller _as_well_. Default: Undefined. -// -//#define SABERTOOTH 1 - -///////////////////////////////////////////////////////////////////////////////////// +/* + * © 2022 Paul M. Antoine + * © 2021 Neil McKechnie + * © 2020-2023 Harald Barth + * © 2020-2021 Fred Decker + * © 2020-2021 Chris Harlow + * © 2023 Nathan Kellenicki + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +/********************************************************************** + +The configuration file for DCC-EX Command Station + +**********************************************************************/ + +///////////////////////////////////////////////////////////////////////////////////// +// If you want to add your own motor driver definition(s), add them here +// For example MY_SHIELD with display name "MINE": +// (remove comment start and end marker if you want to edit and use that) +/* +#define MY_SHIELD F("MINE"), \ + new MotorDriver( 3, 12, UNUSED_PIN, 9, A0, 5.08, 3000, A4), \ + new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 5.08, 1500, A5) +*/ + +///////////////////////////////////////////////////////////////////////////////////// +// NOTE: Before connecting these boards and selecting one in this software +// check the quick install guides!!! Some of these boards require a voltage +// generating resistor on the current sense pin of the device. Failure to select +// the correct resistor could damage the sense pin on your Arduino or destroy +// the device. +// +// DEFINE MOTOR_SHIELD_TYPE BELOW. THESE ARE EXAMPLES. FULL LIST IN MotorDrivers.h +// +// STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel +// POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track) +// FUNDUMOTO_SHIELD : Fundumoto Shield, no current sensing (not recommended, no short protection) +// FIREBOX_MK1 : The Firebox MK1 +// FIREBOX_MK1S : The Firebox MK1S +// IBT_2_WITH_ARDUINO : Arduino Motor Shield for PROG and IBT-2 for MAIN +// EX8874_SHIELD : DCC-EX TI DRV8874 based motor shield +// | +// +-----------------------v +// +#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD +// +///////////////////////////////////////////////////////////////////////////////////// +// +// If you want to restrict the maximum current LOWER than what your +// motor shield can provide, you can do that here. For example if you +// have a motor shield that can provide 5A and your power supply can +// only provide 2.5A then you should restict the maximum current to +// 2.25A (90% of 2.5A) so that DCC-EX does shut off the track before +// your PS does shut DCC-EX. MAX_CURRENT is in mA so for this example +// it would be 2250, adjust the number according to your PS. If your +// PS has a higher rating than your motor shield you do not need this. +// You can use this as well if you are cautious and your trains do not +// need full current. +// #define MAX_CURRENT 2250 +// +///////////////////////////////////////////////////////////////////////////////////// +// +// The IP port to talk to a WIFI or Ethernet shield. +// +#define IP_PORT 2560 + +///////////////////////////////////////////////////////////////////////////////////// +// +// NOTE: Only supported on Arduino Mega +// Set to false if you not even want it on the Arduino Mega +// +#define ENABLE_WIFI false //true + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE WiFi Parameters (only in effect if WIFI is on) +// +// If DONT_TOUCH_WIFI_CONF is set, all WIFI config will be done with +// the <+> commands and this sketch will not change anything over +// AT commands and the other WIFI_* defines below do not have any effect. +//#define DONT_TOUCH_WIFI_CONF +// +// WIFI_SSID is the network name IF you want to use your existing home network. +// Do NOT change this if you want to use the WiFi in Access Point (AP) mode. +// +// If you do NOT set the WIFI_SSID and do NOT set the WIFI_PASSWORD, +// then the WiFi chip will first try to connect to the previously +// configured network and if that fails fall back to Access Point mode. +// The SSID of the AP will be automatically set to DCCEX_*. +// If you DO set the WIFI_SSID then the WiFi chip will try to connect +// to that (home) network in station (client) mode. If a WIFI_PASSWORD +// is set (recommended), that password will be used for AP mode. +// The AP mode password must be at least 8 characters long. +// +// Your SSID may not contain ``"'' (double quote, ASCII 0x22). +#define WIFI_SSID "Your network name" +// +// WIFI_PASSWORD is the network password for your home network or if +// you want to change the password from default AP mode password +// to the AP password you want. +// Your password may not contain ``"'' (double quote, ASCII 0x22). +#define WIFI_PASSWORD "Your network passwd" +// +// WIFI_HOSTNAME: You probably don't need to change this +#define WIFI_HOSTNAME "dccex" +// +// WIFI_CHANNEL: If the line "#define ENABLE_WIFI true" is uncommented, +// WiFi will be enabled (Mega only). The default channel is set to "1" whether +// this line exists or not. If you need to use an alternate channel (we recommend +// using only 1,6, or 11) you may change it here. +#define WIFI_CHANNEL 1 +// +// WIFI_FORCE_AP: If you'd like to specify your own WIFI_SSID in AP mode, set this +// true. Otherwise it is assumed that you'd like to connect to an existing network +// with that SSID. +#define WIFI_FORCE_AP false + +///////////////////////////////////////////////////////////////////////////////////// +// +// ENABLE_ETHERNET: Set to true if you have an Arduino Ethernet card (wired). This +// is not for Wifi. You will then need the Arduino Ethernet library as well +// +//#define ENABLE_ETHERNET true + + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP +// +//#define IP_ADDRESS { 192, 168, 1, 200 } + + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE LCD SCREEN USAGE BY THE BASE STATION +// +// Note: This feature requires an I2C enabled LCD screen using a Hitachi HD44780 +// controller and a commonly available PCF8574 based I2C 'backpack'. +// To enable, uncomment one of the #define lines below + +// define LCD_DRIVER for I2C address 0x27, 16 cols, 2 rows +// #define LCD_DRIVER 0x27,16,2 + +//OR define OLED_DRIVER width,height[,address] in pixels (address auto detected if not supplied) +// 128x32 or 128x64 I2C SSD1306-based devices are supported. +// Use 132,64 for a SH1106-based I2C device with a 128x64 display. +// #define OLED_DRIVER 0x3c,128,32 + +// Define scroll mode as 0, 1 or 2 +// * #define SCROLLMODE 0 is scroll continuous (fill screen if poss), +// * #define SCROLLMODE 1 is by page (alternate between pages), +// * #define SCROLLMODE 2 is by row (move up 1 row at a time). +#define SCROLLMODE 1 + +///////////////////////////////////////////////////////////////////////////////////// +// DISABLE EEPROM +// +// If you do not need the EEPROM at all, you can disable all the code that saves +// data in the EEPROM. You might want to do that if you are in a Arduino UNO +// and want to use the EXRAIL automation. Otherwise you do not have enough RAM +// to do that. Of course, then none of the EEPROM related commands work. +// +// EEPROM does not work on ESP32. So on ESP32, EEPROM will always be disabled, +// at least until it works. +// +// #define DISABLE_EEPROM + +///////////////////////////////////////////////////////////////////////////////////// +// DISABLE PROG +// +// If you do not need programming capability, you can disable all programming related +// commands. You might want to do that if you are using an Arduino UNO and still want +// to use EXRAIL automation, as the Uno is lacking in RAM and Flash to run both. +// +// Note this disables all programming functionality, including EXRAIL. +// +// #define DISABLE_PROG + +///////////////////////////////////////////////////////////////////////////////////// +// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address +// is 127 and the first long address is 128. There are manufacturers which have +// another view. Lenz CS for example have considered addresses long from 100. If +// you want to change to that mode, do +//#define HIGHEST_SHORT_ADDR 99 +// If you want to run all your locos addressed long format, you could even do a +//#define HIGHEST_SHORT_ADDR 0 +// We do not support to use the same address, for example 100(long) and 100(short) +// at the same time, there must be a border. + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE TURNOUTS/ACCESSORIES FOLLOW NORM RCN-213 +// +// According to norm RCN-213 a DCC packet with a 1 is closed/straight +// and one with a 0 is thrown/diverging. In DCC++ Classic, and in previous +// versions of DCC++EX, a turnout throw command was implemented in the packet as +// '1' and a close command as '0'. The #define below makes the states +// match with the norm. But we don't want to cause havoc on existent layouts, +// so we define this only for new installations. If you don't want this, +// don't add it to your config.h. +//#define DCC_TURNOUTS_RCN_213 + +// By default, the driver which defines a DCC accessory decoder +// does send out the same state change on the DCC packet as it +// receives. This means a VPIN state=1 sends D=1 (close turnout +// or signal green) in the DCC packet. This can be reversed if +// necessary. +//#define HAL_ACCESSORY_COMMAND_REVERSE + +// If you have issues with that the direction of the accessory commands is +// reversed (for example when converting from another CS to DCC-EX) then +// you can use this to reverse the sense of all accessory commmands sent +// over DCC++. This #define likewise inverts the behaviour of the command +// for triggering DCC Accessory Decoders, so that generates a +// DCC packet with D=1 (close turnout) and generates D=0 +// (throw turnout). +//#define DCC_ACCESSORY_RCN_213 +// +// HANDLING MULTIPLE SERIAL THROTTLES +// The command station always operates with the default Serial port. +// Diagnostics are only emitted on the default serial port and not broadcast. +// Other serial throttles may be added to the Serial1, Serial2, Serial3, Serial4, +// Serial5, and Serial6 ports which may or may not exist on your CPU. (Mega has 3, +// SAMD/SAMC and STM32 have up to 6.) +// To monitor a throttle on one or more serial ports, uncomment the defines below. +// NOTE: do not define here the WiFi shield serial port or your wifi will not work. +// +//#define SERIAL1_COMMANDS +//#define SERIAL2_COMMANDS +//#define SERIAL3_COMMANDS +//#define SERIAL4_COMMANDS +//#define SERIAL5_COMMANDS +//#define SERIAL6_COMMANDS +// +// BLUETOOTH SERIAL ON ESP32 +// On ESP32 you have the possibility to use the builtin BT serial to connect to +// the CS. +// +// The CS shows up as a pairable BT Clasic device. Name is "DCCEX-hexnumber". +// BT is as an additional serial port, debug messages are still sent over USB, +// not BT serial. +// +// If you enable this there are some implications: +// 1. WiFi will sleep more (as WiFi and BT share the radio. So WiFi performance +// may suffer +// 2. The app will be bigger that 1.2MB, so the default partition scheme will not +// work any more. You need to choose a partition scheme with 2MB (or bigger). +// For example "NO OTA (2MB APP, 2MB SPIFFS)" in the Arduino IDE. +// 3. There is no securuity (PIN) implemented. Everyone in radio range can pair +// with your CS. +// +//#define SERIAL_BT_COMMANDS + +// SABERTOOTH +// +// This is a very special option and only useful if you happen to have a +// sabertooth motor controller from dimension engineering configured to +// take commands from and ESP32 via serial at 9600 baud from GPIO17 (TX) +// and GPIO16 (RX, currently unused). +// The number defined is the DCC address for which speed controls are sent +// to the sabertooth controller _as_well_. Default: Undefined. +// +//#define SABERTOOTH 1 +// +///////////////////////////////////////////////////////////////////////////////////// +// +// SENSORCAM +// ESP32-CAM based video sensors require #define to use appropriate base vpin number. +#define SENSORCAM_VPIN 700 +// For shortcut to vPin number, define CAM for ex-rail use e.g. AT(CAM 012) for S12 etc. +#define CAM SENSORCAM_VPIN+ + +//#define SENSORCAM2_VPIN 600 //define other CAM's if installed. +//#define CAM2 SENSORCAM2_VPIN+ //for EX-RAIL commands e.g. IFLT(CAM2 020,1)1 +// +// For smoother power-up, define a STARTUP_DELAY to allow CAM to initialise ref images +#define STARTUP_DELAY 5000 // up to 20sec. CS delay + + +///////////////////////////////////////////////////////////////////////////////////// diff --git a/myHal.cpp b/myHal.cpp new file mode 100644 index 00000000..752bc216 --- /dev/null +++ b/myHal.cpp @@ -0,0 +1,362 @@ +// 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_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); + + //======================================================================= + // The following directives define auxiliary display devices. + // These can be defined in addition to the system display (display + // number 0) that is defined in config.h. + // A write to a line which is beyond the length of the screen will overwrite + // the bottom line, unless the line number is 255 in which case the + // screen contents will scroll up before the text is written to the + // bottom line. + //======================================================================= + // + // Create a 128x32 OLED display device as display number 1 + // (line 0 is written by EX-RAIL 'SCREEN(1, 0, "text")'). + + //HALDisplay::create(1, 0x3d, 128, 32); + + // Create a 20x4 LCD display device as display number 2 + // (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")'). + + // HALDisplay(2, 0x27, 20, 4); + + + //======================================================================= + // User Add-ins + //======================================================================= + // User add-ins can be created when you want to do something that + // can't be done in EX-RAIL but does not merit a HAL driver. The + // user add-in is a C++ function that is executed periodically by the + // HAL subsystem. + + // Example: The function will be executed once per second and will display, + // on screen #3, the first eight entries (assuming an 8-line display) + // from the loco speed table. + + // Put the following block of code in myHal.cpp OUTSIDE of the + // halSetup() function: + // + // void updateLocoScreen() { + // for (int i=0; i<8; i++) { + // if (DCC::speedTable[i].loco > 0) { + // int speed = DCC::speedTable[i].speedCode; + // char direction = (speed & 0x80) ? 'R' : 'F'; + // speed = speed & 0x7f; + // if (speed > 0) speed = speed - 1; + // SCREEN(3, i, F("Loco:%4d %3d %c"), DCC::speedTable[i].loco, + // speed, direction); + // } + // } + // } + // + // Put the following line INSIDE the halSetup() function: + // + // UserAddin::create(updateLocoScreen, 1000); + // + + + //======================================================================= + // The following directive defines a PCA9685 PWM Servo driver module. + //======================================================================= + // 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); + + + //======================================================================= + + + + + + + + + + + + + + + + + + + + + + + + + + // 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 or 2 + // i2cAddress = Available I2C address (default 0x70) + + //RotaryEncoder::create(firstVpin, numPins, i2cAddress); + //RotaryEncoder::create(700, 1, 0x70); + //RotaryEncoder::create(701, 2, 0x71); + + + //======================================================================= + // 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) + // #define ESP32CAP 0x13 in config.h to raise allowable ESP32 range of addresses + // Note that the I2C address (0x11) is the default in the sensorCAM code + // + // EXSensorCAM::create(700, 80, 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 +} + +#endif diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index f715c632..ef49f603 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -26,6 +26,7 @@ //#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. @@ -35,6 +36,8 @@ void halSetup() { + //I2CManager.setClock(100000); + //======================================================================= // The following directives define auxiliary display devices. // These can be defined in addition to the system display (display @@ -303,7 +306,14 @@ void halSetup() { // 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); + + + + + //======================================================================= @@ -332,7 +342,23 @@ 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) + // #define ESP32CAP 0x13 in config.h to raise allowable ESP32 range of addresses + // Note that the I2C address (0x11) is the default in the sensorCAM code + // + // EXSensorCAM::create(700, 80, 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 } #endif diff --git a/mySetup.h b/mySetup.h new file mode 100644 index 00000000..2dc2360a --- /dev/null +++ b/mySetup.h @@ -0,0 +1,26 @@ +// setup for sensorCAM on an ESP32-CAM NUMDigitalPins <= 80 +// assume 700 is first vpin (set with ...CREATE(700,80,0x11) +// the optional SETUP operations below initiate jmri monitoring of sensors for any change of state +// Mostly only useful during debug of initial system but load up CS with extra work. Use judiciously +// id vPin +SETUP(""); // set up for control OUTPUT on vpin #00 +// start of up to 80 sensors numbered bsNo's 100 to 197 (OCT) (0/0 to 9/7) +SETUP(""); // first sensor (S00) (reference) +SETUP(""); +SETUP(""); +// as many as you want. You can add later manually with CS native commands +SETUP(""); +SETUP(""); // Note: suggested id is b/s format (~OCT); vpin is DEC. +SETUP(""); // myFilter.cpp REQUIRES this relationship for bsNo to vPin conversion +SETUP(""); +SETUP(""); +SETUP(""); +//etc. // can create a bulk set of sensors with c++ code so: +//for(uint16_t b=2; b<=9;b++) for(uint16_t s=0;s<8;s++) Sensor::create(100+b*10+s,700+b*8+s,1); +//SETUP(""); +SETUP(""); +SETUP(""); +//SETUP(""); +// +SETUP(""); +SETUP(""); From 3b0c634423f1859475cb8d7f914f84c478e3182c Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Thu, 22 Aug 2024 11:11:04 +1000 Subject: [PATCH 4/7] Add files via upload --- CamParser.cpp | 2 +- IO_EXSensorCAM.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CamParser.cpp b/CamParser.cpp index 8d4ab2f7..c117b162 100644 --- a/CamParser.cpp +++ b/CamParser.cpp @@ -30,7 +30,7 @@ bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) { return true; } uint8_t camop=p[0]; // cam oprerator - int16_t param1=0; + int param1=0; int16_t param3=9999; // =0 could invoke parameter changes. & -1 gives later errors if(camop=='C'){ diff --git a/IO_EXSensorCAM.h b/IO_EXSensorCAM.h index 40420c37..b7c6a664 100644 --- a/IO_EXSensorCAM.h +++ b/IO_EXSensorCAM.h @@ -226,7 +226,7 @@ int ioESP32(uint8_t i2cAddr,uint8_t *rBuf,int inbytes,uint8_t *outBuff,int outby int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) { int k; int b; - int x; + // 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 '@'=>'`' @@ -314,7 +314,7 @@ int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) { } //************************* // Write (analogue) 8bit (command) values. Write the parameters to the sensorCAM -void _writeAnalogue(VPIN vpin, int16_t param1, uint8_t camop, uint16_t param3) override { +void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) override { uint8_t outputBuffer[7]; int errors=0; outputBuffer[0] = camop; From 3620a87869288a9d53782991cbd6e4100fc0eaf9 Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Fri, 6 Sep 2024 12:45:31 +1000 Subject: [PATCH 5/7] Add files via upload --- CamParser.cpp | 7 ++-- IO_EXSensorCAM.h | 97 ++++++++++++++++++++++++------------------- myHal.cpp_example.txt | 2 +- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/CamParser.cpp b/CamParser.cpp index c117b162..fa9eee58 100644 --- a/CamParser.cpp +++ b/CamParser.cpp @@ -53,12 +53,13 @@ bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) { // send UPPER case to sensorCAM to flag binary data from a DCCEX-CS parser switch(paramCount) { case 1: // produces '^' - if (strchr_P((const char *)F("EFGMRVW^"),camop) == nullptr) return false; + if (STRCHR_P((const char *)F("EFGMQRVW^"),camop) == nullptr) return false; if (camop=='F') camop=']'; // for Reset/Finish webCAM. + if (camop=='Q') param3=10; // for activation state of all 10 banks of sensors break; // Coded as ']' else conflicts with case 2: // - if (strchr_P((const char *)F("ABFILMNOPRSTUV^"),camop)==nullptr) return false; + if (STRCHR_P((const char *)F("ABFILMNOPQRSTUV^"),camop)==nullptr) return false; param1=p[1]; break; @@ -69,7 +70,7 @@ bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) { 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; + }else if (STRCHR_P((const char *)F("IJMNT"),camop) == nullptr) return false; param1 = p[1]; param3 = p[2]; break; diff --git a/IO_EXSensorCAM.h b/IO_EXSensorCAM.h index b7c6a664..23a5c2bd 100644 --- a/IO_EXSensorCAM.h +++ b/IO_EXSensorCAM.h @@ -17,8 +17,8 @@ * along with CommandStation. If not, see . */ -#define driverVer 301 -// v301 improved 'f' and 'p' code and driver version calc. Correct bsNo calc. for 'a' +#define driverVer 303 +// 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 @@ -49,8 +49,7 @@ # define DIGITALREFRESH 20000UL // min uSec delay between digital reads of digitalInputStates #ifndef IO_EX_EXSENSORCAM_H #define IO_EX_EXSENSORCAM_H - -#define Sp Serial.print +#define SEND StringFormatter::send #include "IODevice.h" #include "I2CManager.h" @@ -83,7 +82,7 @@ class EXSensorCAM : public IODevice { addDevice(this); } //************************* - + uint8_t oldb0; void _begin() { uint8_t status; // Initialise EX-SensorCAM device @@ -148,9 +147,9 @@ void _loop(unsigned long currentMicros) override { 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 response set S06 to 0 - // versions of sensorCAM.h after v300 should return header of '@'(0x40) (not 0xE6) followed by - // digitalInputStates sensor state array + }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; @@ -202,7 +201,7 @@ void _write(VPIN vpin, int value) override { } //************************* // i2cAddr of ESP32 CAM -// rBuff buffer for return packet +// 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) { @@ -226,12 +225,14 @@ int ioESP32(uint8_t i2cAddr,uint8_t *rBuf,int inbytes,uint8_t *outBuff,int outby int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) { 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); - break; + 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; @@ -262,45 +263,48 @@ int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) { ,rBuf[4],rBuf[2],rBuf[3],rBuf[5]); 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 'f': - DIAG(F("(f %%%%) frame header 'f' for 0%o - showing Quarter sample (1 row) only"), rBuf[1]); - /*if(rBuf[2]==0)*/ { Sp(">4,HEX); Sp(rBuf[k]&15,HEX); if(k%3==2)Sp(" "); } - Sp(" latest grab ->"); - for(k=16;k<28;k++){ Sp(" "); Sp(rBuf[k]>>4,HEX); Sp(rBuf[k]&15,HEX); if(k%3==0)Sp(" "); } - Sp(" n>\n"); + 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 'p': - b=rBuf[1]-2; - if(b<4) { Sp("\n"); break; } - Sp("\n"); + b=rBuf[1]-2; + if(b<4) { Serial.print("\n"); break; } + SEND(&USB_SERIAL,F("\n"); break; case 't': //threshold etc. from t## //bad pkt if 't' FF's - if(rBuf[1]==0xFF) {Serial.println("");_savedCmd[2] +=1; return 0;} - Sp("99)k=99;Sp(k); - if(rBuf[2]>127) Sp("##* "); + if(rBuf[1]==0xFF) {Serial.println("");_savedCmd[2] +=1; return 0;} + SEND(&USB_SERIAL,F("127) Serial.print("##* "); else{ - if(rBuf[2]>rBuf[1]) Sp("-?* "); - else Sp("--* "); + 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{ - if((valu&0x7F)<8) Sp("0"); Sp(valu&0x7F,OCT);Sp(':'); - if(valu>=128) Sp("?-"); - else {if(rBuf[i+1]>=128) Sp("oo");else Sp("--");} - valu=rBuf[i+1]; k=valu&0x7F; - if(k>99) k=99; Sp(k); - if(valu<128) Sp("--* "); else Sp("##* "); + 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"); @@ -324,7 +328,7 @@ void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) overr 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. + pin = (pin/8)*10 + pin%8; //restore bsNo. as integer } if (_deviceState == DEVSTATE_FAILED) return; @@ -335,13 +339,20 @@ void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) overr 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 - uint8_t b=_digitalInputStates[param1]; - char str[] = "11111111"; - for (int i=0;i<8;i++) if(((b<0) %s"), param1,b>>4,b&15,param1,str ); + for(param1;param1%d0) %s"), param1,b>>4,b&15,param1,param1,str ); + } return; } if (outputBuffer[4]=='T') { //then 't' cmd diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index ef49f603..e42138ce 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -36,7 +36,7 @@ void halSetup() { - //I2CManager.setClock(100000); + I2CManager.setClock(100000); //======================================================================= // The following directives define auxiliary display devices. From 99df0a39d72380877cb027d26beb6f27b4b0b1eb Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Fri, 6 Sep 2024 13:18:06 +1000 Subject: [PATCH 6/7] Add files via upload --- FSH.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FSH.h b/FSH.h index 280d37e9..58974ed7 100644 --- a/FSH.h +++ b/FSH.h @@ -52,6 +52,7 @@ typedef __FlashStringHelper FSH; #define STRNCPY_P strncpy_P #define STRNCMP_P strncmp_P #define STRLEN_P strlen_P +#define STRCHR_P strchr_P #if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) // AVR_MEGA memory deliberately placed at end of link may need _far functions @@ -92,5 +93,6 @@ typedef char FSH; #define STRNCPY_P strncpy #define STRNCMP_P strncmp #define STRLEN_P strlen +#define STRCHR_P strchr #endif #endif From 3e03ff61613254e302f5589a9a87d64c00933bb5 Mon Sep 17 00:00:00 2001 From: Barry Daniel Date: Fri, 6 Sep 2024 15:44:52 +1000 Subject: [PATCH 7/7] Add files via upload --- IO_EXSensorCAM.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IO_EXSensorCAM.h b/IO_EXSensorCAM.h index 23a5c2bd..7fdfdc34 100644 --- a/IO_EXSensorCAM.h +++ b/IO_EXSensorCAM.h @@ -347,18 +347,18 @@ void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) overr 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(param1;param1%d0) %s"), param1,b>>4,b&15,param1,param1,str ); + DIAG(F("(b $) Bank: %d activated byte: 0x%x%x (sensors S%d7->%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, 8); + memcpy( _savedCmd, outputBuffer, 7); }else _savedCmd[2] = 0; //no repeats if ##>30 }else _savedCmd[2] = 0; //no repeats unless 't'