mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-26 17:46:14 +01:00
Sensorcam
This commit is contained in:
parent
c50f3e016c
commit
5efe385f2e
95
CamParser.cpp
Normal file
95
CamParser.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
|
||||||
|
//sensorCAM parser.cpp version 3.03 Sep 2024
|
||||||
|
#include "CamParser.h"
|
||||||
|
#include "FSH.h"
|
||||||
|
#include "IO_EXSensorCAM.h"
|
||||||
|
|
||||||
|
#ifndef SENSORCAM_VPIN //define CAM vpin (700?) in config.h
|
||||||
|
#define SENSORCAM_VPIN 0
|
||||||
|
#endif
|
||||||
|
#define CAM_VPIN SENSORCAM_VPIN
|
||||||
|
#ifndef SENSORCAM2_VPIN
|
||||||
|
#define SENSORCAM2_VPIN CAM_VPIN
|
||||||
|
#endif
|
||||||
|
#ifndef SENSORCAM3_VPIN
|
||||||
|
#define SENSORCAM3_VPIN 0
|
||||||
|
#endif
|
||||||
|
const int CAMVPINS[] = {CAM_VPIN,SENSORCAM_VPIN,SENSORCAM2_VPIN,SENSORCAM3_VPIN};
|
||||||
|
const int16_t ver=30177;
|
||||||
|
const int16_t ve =2899;
|
||||||
|
|
||||||
|
VPIN EXSensorCAM::CAMBaseVpin = CAM_VPIN;
|
||||||
|
|
||||||
|
bool CamParser::parseN(Print * stream, byte paramCount, int16_t p[]) {
|
||||||
|
(void)stream; // probably unused parameter
|
||||||
|
VPIN vpin=EXSensorCAM::CAMBaseVpin; //use current CAM selection
|
||||||
|
|
||||||
|
if (paramCount==0) {
|
||||||
|
DIAG(F("vpin:%d EXSensorCAMs defined at Vpins #1@ %d #2@ %d #3@ %d"),vpin,CAMVPINS[1],CAMVPINS[2],CAMVPINS[3]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint8_t camop=p[0]; // cam oprerator
|
||||||
|
int param1=0;
|
||||||
|
int16_t param3=9999; // =0 could invoke parameter changes. & -1 gives later errors
|
||||||
|
|
||||||
|
if(camop=='C'){
|
||||||
|
if(p[1]>=100) EXSensorCAM::CAMBaseVpin=p[1];
|
||||||
|
if(p[1]<4) EXSensorCAM::CAMBaseVpin=CAMVPINS[p[1]];
|
||||||
|
DIAG(F("CAM base Vpin: %c %d "),p[0],EXSensorCAM::CAMBaseVpin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (camop<100) { //switch CAM# if p[1] dictates
|
||||||
|
if(p[1]>=100 && p[1]<400) { //limits to CAM# 1 to 3 for now
|
||||||
|
vpin=CAMVPINS[p[1]/100];
|
||||||
|
EXSensorCAM::CAMBaseVpin=vpin;
|
||||||
|
DIAG(F("switching to CAM %d baseVpin:%d"),p[1]/100,vpin);
|
||||||
|
p[1]=p[1]%100; //strip off CAM #
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (EXSensorCAM::CAMBaseVpin==0) return false; // no cam defined
|
||||||
|
|
||||||
|
|
||||||
|
// send UPPER case to sensorCAM to flag binary data from a DCCEX-CS parser
|
||||||
|
switch(paramCount) {
|
||||||
|
case 1: //<N ver> produces '^'
|
||||||
|
if((p[0] == ve) || (p[0] == ver) || (p[0] == 'V')) camop='^';
|
||||||
|
if (STRCHR_P((const char *)F("EFGMQRVW^"),camop) == nullptr) return false;
|
||||||
|
if (camop=='Q') param3=10; //<NQ> for activation state of all 10 banks of sensors
|
||||||
|
if (camop=='F') camop=']'; //<NF> for Reset/Finish webCAM.
|
||||||
|
break; // F Coded as ']' else conflicts with <Nf %%>
|
||||||
|
|
||||||
|
case 2: //<N camop p1>
|
||||||
|
if (STRCHR_P((const char *)F("ABFILMNOPQRSTUV"),camop)==nullptr) return false;
|
||||||
|
param1=p[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: //<N vpin rowY colx > or <N cmd p1 p2>
|
||||||
|
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: //<N a id row col>
|
||||||
|
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;
|
||||||
|
}
|
12
CamParser.h
Normal file
12
CamParser.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef CamParser_H
|
||||||
|
#define CamParser_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "IODevice.h"
|
||||||
|
|
||||||
|
class CamParser {
|
||||||
|
public:
|
||||||
|
static bool parseN(Print * stream, byte paramCount, int16_t p[]);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -117,6 +117,7 @@ Once a new OPCODE is decided upon, update this list.
|
||||||
#include "Turntables.h"
|
#include "Turntables.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "KeywordHasher.h"
|
#include "KeywordHasher.h"
|
||||||
|
#include "CamParser.h"
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#include "WifiESP32.h"
|
#include "WifiESP32.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -457,7 +458,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
||||||
else IODevice::write(-p[0],LOW);
|
else IODevice::write(-p[0],LOW);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (params>=2 && params<=4) { // <z vpin ana;og profile duration>
|
if (params>=2 && params<=4) { // <z vpin analog profile duration>
|
||||||
// unused params default to 0
|
// unused params default to 0
|
||||||
IODevice::writeAnalogue(p[0],p[1],p[2],p[3]);
|
IODevice::writeAnalogue(p[0],p[1],p[2],p[3]);
|
||||||
return;
|
return;
|
||||||
|
@ -876,7 +877,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case 'N': // <N commands for SensorCam
|
||||||
|
if (CamParser::parseN(stream,params,p)) return;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case '/': // implemented in EXRAIL parser
|
case '/': // implemented in EXRAIL parser
|
||||||
case 'L': // LCC interface implemented in EXRAIL parser
|
case 'L': // LCC interface implemented in EXRAIL parser
|
||||||
break; // Will <X> if not intercepted by EXRAIL
|
break; // Will <X> if not intercepted by EXRAIL
|
||||||
|
|
|
@ -571,5 +571,6 @@ protected:
|
||||||
#include "IO_TCA8418.h"
|
#include "IO_TCA8418.h"
|
||||||
#include "IO_NeoPixel.h"
|
#include "IO_NeoPixel.h"
|
||||||
#include "IO_TM1638.h"
|
#include "IO_TM1638.h"
|
||||||
|
#include "IO_EXSensorCAM.h"
|
||||||
|
|
||||||
#endif // iodevice_h
|
#endif // iodevice_h
|
||||||
|
|
424
IO_EXSensorCAM.h
Normal file
424
IO_EXSensorCAM.h
Normal file
|
@ -0,0 +1,424 @@
|
||||||
|
/* 2024/08/14
|
||||||
|
* © 2024, Barry Daniel ESP32-CAM revision
|
||||||
|
*
|
||||||
|
* This file is part of EX-CommandStation
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#define driverVer 305
|
||||||
|
// v305 less debug & alpha ordered switch
|
||||||
|
// v304 static oldb0; t(##[,%%];
|
||||||
|
// v303 zipped with CS 5.2.76 and uploaded to repo (with debug)
|
||||||
|
// v302 SEND=StringFormatter::send, remove Sp(), add 'q', memcpy( .8) -> .7);
|
||||||
|
// v301 improved 'f','p'&'q' code and driver version calc. Correct bsNo calc. for 'a'
|
||||||
|
// v300 stripped & revised without expander functionality. Needs sensorCAM.h v300 AND CamParser.cpp
|
||||||
|
// v222 uses '@'for EXIORDD read. handles <NB $> and <NN $ ##>
|
||||||
|
// 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(EXSensorCAM baseVpin numpins i2c_address) in myAutomation.h
|
||||||
|
* also #define SENSORCAM_VPIN baseVpin in config.h
|
||||||
|
*
|
||||||
|
* void halSetup() {
|
||||||
|
* // EXSensorCAM::create(vpin, num_vpins, i2c_address);
|
||||||
|
* EXSensorCAM::create(700, 80, 0x11);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* I2C packet size of 32 bytes (in the Wire library).
|
||||||
|
*/
|
||||||
|
# define DIGITALREFRESH 20000UL // min uSec delay between digital reads of digitalInputStates
|
||||||
|
#ifndef IO_EX_EXSENSORCAM_H
|
||||||
|
#define IO_EX_EXSENSORCAM_H
|
||||||
|
#define SEND StringFormatter::send
|
||||||
|
#include "IODevice.h"
|
||||||
|
#include "I2CManager.h"
|
||||||
|
#include "DIAG.h"
|
||||||
|
#include "FSH.h"
|
||||||
|
#include "CamParser.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
* IODevice subclass for EX-SensorCAM.
|
||||||
|
*/
|
||||||
|
class EXSensorCAM : public IODevice {
|
||||||
|
public:
|
||||||
|
static void create(VPIN vpin, int nPins, I2CAddress i2cAddress) {
|
||||||
|
if (checkNoOverlap(vpin, nPins, i2cAddress))
|
||||||
|
new EXSensorCAM(vpin, nPins, i2cAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VPIN CAMBaseVpin;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Constructor
|
||||||
|
EXSensorCAM(VPIN firstVpin, int nPins, I2CAddress i2cAddress) {
|
||||||
|
_firstVpin = firstVpin;
|
||||||
|
// Number of pins cannot exceed 255 (1 byte) because of I2C message structure.
|
||||||
|
if (nPins > 80) nPins = 80;
|
||||||
|
_nPins = nPins;
|
||||||
|
_I2CAddress = i2cAddress;
|
||||||
|
addDevice(this);
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
void _begin() {
|
||||||
|
uint8_t status;
|
||||||
|
// Initialise EX-SensorCAM device
|
||||||
|
I2CManager.begin();
|
||||||
|
if (!I2CManager.exists(_I2CAddress)) {
|
||||||
|
DIAG(F("EX-SensorCAM I2C:%s device not found"), _I2CAddress.toString());
|
||||||
|
_deviceState = DEVSTATE_FAILED;
|
||||||
|
return;
|
||||||
|
}else {
|
||||||
|
uint8_t commandBuffer[4]={EXIOINIT,(uint8_t)_nPins,(uint8_t)(_firstVpin & 0xFF),(uint8_t)(_firstVpin>>8)};
|
||||||
|
status = I2CManager.read(_I2CAddress,_inputBuf,sizeof(_inputBuf),commandBuffer,sizeof(commandBuffer));
|
||||||
|
//EXIOINIT needed to trigger and send firstVpin to CAM
|
||||||
|
|
||||||
|
if (status == I2C_STATUS_OK) {
|
||||||
|
// Attempt to get version, non-blocking results in poor placement of response. Can be blocking here!
|
||||||
|
commandBuffer[0] = '^'; //new version code
|
||||||
|
|
||||||
|
status = I2CManager.read(_I2CAddress, _inputBuf, sizeof(_inputBuf), commandBuffer, 1);
|
||||||
|
// for ESP32 CAM, read again for good immediate response version data
|
||||||
|
status = I2CManager.read(_I2CAddress, _inputBuf, sizeof(_inputBuf), commandBuffer, 1);
|
||||||
|
|
||||||
|
if (status == I2C_STATUS_OK) {
|
||||||
|
_majorVer= _inputBuf[1]/10;
|
||||||
|
_minorVer= _inputBuf[1]%10;
|
||||||
|
_patchVer= _inputBuf[2];
|
||||||
|
DIAG(F("EX-SensorCAM device found, I2C:%s, Version v%d.%d.%d"),
|
||||||
|
_I2CAddress.toString(),_majorVer, _minorVer,_patchVer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (status != I2C_STATUS_OK)
|
||||||
|
reportError(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Digital input pin configuration, used to enable on EX-IOExpander device and set pullups if requested.
|
||||||
|
// Configuration isn't done frequently so we can use blocking I2C calls here, and so buffers can
|
||||||
|
// be allocated from the stack to reduce RAM allocation.
|
||||||
|
bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override {
|
||||||
|
if(_verPrint) DIAG(F("_configure() driver IO_EXSensorCAM v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,vpin);
|
||||||
|
_verPrint=false; //only give driver versions once
|
||||||
|
if (paramCount != 1) return false;
|
||||||
|
return true; //at least confirm that CAM is (always) configured (no vpin check!)
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Analogue input pin configuration, used to enable an EX-IOExpander device.
|
||||||
|
int _configureAnalogIn(VPIN vpin) override {
|
||||||
|
DIAG(F("_configureAnalogIn() IO_EXSensorCAM vpin %d"),vpin);
|
||||||
|
return true; // NOTE: use of EXRAIL IFGTE() etc use "analog" reads.
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Main loop, collect both digital and "analog" pin states continuously (faster sensor/input reads)
|
||||||
|
void _loop(unsigned long currentMicros) override {
|
||||||
|
if (_deviceState == DEVSTATE_FAILED) return;
|
||||||
|
// Request block is used for "analogue" (cmd. data) and digital reads from the sensorCAM, which
|
||||||
|
// are performed on a cyclic basis. Writes are performed synchronously as and when requested.
|
||||||
|
if (_readState != RDS_IDLE) { //expecting a return packet
|
||||||
|
if (_i2crb.isBusy()) return; // If I2C operation still in progress, return
|
||||||
|
uint8_t status = _i2crb.status;
|
||||||
|
if (status == I2C_STATUS_OK) { // If device request ok, read input data
|
||||||
|
//apparently the above checks do not guarantee a good packet! error rate about 1 pkt per 1000
|
||||||
|
//there should be a packet in _CAMresponseBuff[32]
|
||||||
|
if ((_CAMresponseBuff[0] & 0x60) >= 0x60) { //Buff[0] seems to have ascii cmd header (bit6 high) (o06)
|
||||||
|
int error = processIncomingPkt( _CAMresponseBuff, _CAMresponseBuff[0]); // '~' 'i' 'm' 'n' 't' etc
|
||||||
|
if (error>0) DIAG(F("CAM packet header(0x%x) not recognised"),_CAMresponseBuff[0]);
|
||||||
|
}else{ // Header not valid - typically replaced by bank 0 data! To avoid any bad responses set S06 to 0
|
||||||
|
// Versions of sensorCAM.h after v300 should return header for '@' of '`'(0x60) (not 0xE6)
|
||||||
|
// followed by digitalInputStates sensor state array
|
||||||
|
}
|
||||||
|
}else reportError(status, false); // report i2c eror but don't go offline.
|
||||||
|
_readState = RDS_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're not doing anything now, check to see if a new state table transfer, or for 't' repeat, is due.
|
||||||
|
if (_readState == RDS_IDLE) { //check if time for digitalRefresh
|
||||||
|
if ( currentMicros - _lastDigitalRead > _digitalRefresh) {
|
||||||
|
// Issue new read request for digital states.
|
||||||
|
|
||||||
|
_readCommandBuffer[0] = '@'; //start new read of digitalInputStates Table // non-blocking read
|
||||||
|
I2CManager.read(_I2CAddress,_CAMresponseBuff, 32,_readCommandBuffer, 1, &_i2crb);
|
||||||
|
_lastDigitalRead = currentMicros;
|
||||||
|
_readState = RDS_DIGITAL;
|
||||||
|
|
||||||
|
}else{ //slip in a repeat <NT n> if pending
|
||||||
|
if (currentMicros - _lasttStateRead > _tStateRefresh) // Delay for "analog" command repetitions
|
||||||
|
if (_savedCmd[2]>1) { //repeat a 't' command
|
||||||
|
for (int i=0;i<7;i++) _readCommandBuffer[i] =_savedCmd[i];
|
||||||
|
int errors = ioESP32(_I2CAddress, _CAMresponseBuff, 32, _readCommandBuffer, 7);
|
||||||
|
_lasttStateRead = currentMicros;
|
||||||
|
_savedCmd[2] -= 1; //decrement repeats
|
||||||
|
if (errors==0) return;
|
||||||
|
DIAG(F("ioESP32 error %d header 0x%x"),errors,_CAMresponseBuff[0]);
|
||||||
|
_readState = RDS_TSTATE; //this should stop further cmd requests until packet read (or timeout)
|
||||||
|
}
|
||||||
|
} //end repeat 't'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Obtain the bank of 8 sensors as an "analog" value
|
||||||
|
// can be used to track the position through a sequential sensor bank
|
||||||
|
int _readAnalogue(VPIN vpin) override {
|
||||||
|
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||||
|
return _digitalInputStates[(vpin - _firstVpin) / 8];
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Obtain the correct digital sensor input value
|
||||||
|
int _read(VPIN vpin) override {
|
||||||
|
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||||
|
int pin = vpin - _firstVpin;
|
||||||
|
return bitRead(_digitalInputStates[pin / 8], pin % 8);
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Write digital value.
|
||||||
|
void _write(VPIN vpin, int value) override {
|
||||||
|
DIAG(F("**_write() vpin %d = %d"),vpin,value);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// i2cAddr of ESP32 CAM
|
||||||
|
// rBuf buffer for return packet
|
||||||
|
// inbytes number of bytes to request from CAM
|
||||||
|
// outBuff holds outbytes to be sent to CAM
|
||||||
|
int ioESP32(uint8_t i2cAddr,uint8_t *rBuf,int inbytes,uint8_t *outBuff,int outbytes) {
|
||||||
|
uint8_t status = _i2crb.status;
|
||||||
|
|
||||||
|
while( _i2crb.status != I2C_STATUS_OK){status = _i2crb.status;} //wait until bus free
|
||||||
|
|
||||||
|
status = I2CManager.read(i2cAddr, rBuf, inbytes, outBuff, outbytes);
|
||||||
|
|
||||||
|
if (status != I2C_STATUS_OK){
|
||||||
|
DIAG(F("EX-SensorCAM I2C:%s Error:%d %S"), _I2CAddress.toString(), status, I2CManager.getErrorMessage(status));
|
||||||
|
reportError(status); return status;
|
||||||
|
}
|
||||||
|
return 0; // 0 for no error != 0 for error number.
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
//function to interpret packet from sensorCAM.ino
|
||||||
|
//i2cAddr to identify CAM# (if # >1)
|
||||||
|
//rBuf contains packet of up to 32 bytes usually with (ascii) cmd header in rBuf[0]
|
||||||
|
//sensorCmd command header byte from CAM (in rBuf[0]?)
|
||||||
|
int processIncomingPkt(uint8_t *rBuf,uint8_t sensorCmd) {
|
||||||
|
//static uint8_t oldb0; //for debug only
|
||||||
|
int k;
|
||||||
|
int b;
|
||||||
|
char str[] = "11111111";
|
||||||
|
// if (sensorCmd <= '~') DIAG(F("processIncomingPkt %c %d %d %d"),rBuf[0],rBuf[1],rBuf[2],rBuf[3]);
|
||||||
|
switch (sensorCmd){
|
||||||
|
case '`': //response to request for digitalInputStates[] table '@'=>'`'
|
||||||
|
memcpy(_digitalInputStates, rBuf+1, digitalBytesNeeded);
|
||||||
|
// if ( _digitalInputStates[0]!=oldb0) { oldb0=_digitalInputStates[0]; //debug
|
||||||
|
// for (k=0;k<5;k++) {Serial.print(" ");Serial.print(_digitalInputStates[k],HEX);}
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXIORDY: //some commands give back acknowledgement only
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CAMERR: //cmd format error code from CAM
|
||||||
|
DIAG(F("CAM cmd error 0xFE 0x%x"),rBuf[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '~': //information from '^' version request <N v[er]>
|
||||||
|
DIAG(F("EX-SensorCAM device found, I2C:%s,CAM Version v%d.%d.%d vpins %u-%u"),
|
||||||
|
_I2CAddress.toString(), rBuf[1]/10, rBuf[1]%10, rBuf[2],(int) _firstVpin, (int) _firstVpin +_nPins-1);
|
||||||
|
DIAG(F("IO_EXSensorCAM driver v0.%d.%d vpin: %d "), driverVer/100,driverVer%100,_firstVpin);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
DIAG(F("(f %%%%) frame header 'f' for bsNo %d/%d - showing Quarter sample (1 row) only"), rBuf[1]/8,rBuf[1]%8);
|
||||||
|
SEND(&USB_SERIAL,F("<n row: %d Ref bytes: "),rBuf[2]);
|
||||||
|
for(k=3;k<15;k++)
|
||||||
|
SEND(&USB_SERIAL,F("%x%x%s"), rBuf[k]>>4, rBuf[k]&15, k%3==2 ? " " : " ");
|
||||||
|
Serial.print(" latest grab: ");
|
||||||
|
for(k=16;k<28;k++)
|
||||||
|
SEND(&USB_SERIAL,F("%x%x%s"), rBuf[k]>>4, rBuf[k]&15, (k%3==0) ? " " : " ");
|
||||||
|
Serial.print(" n>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i': //information from i%%
|
||||||
|
k=256*rBuf[5]+rBuf[4];
|
||||||
|
DIAG(F("(i%%%%[,$$]) Info: Sensor 0%o(%d) enabled:%d status:%d row=%d x=%d Twin=0%o pvtThreshold=%d A~%d")
|
||||||
|
,rBuf[1],rBuf[1],rBuf[3],rBuf[2],rBuf[6],k,rBuf[7],rBuf[9],int(rBuf[8])*16);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
DIAG(F("(m$[,##]) Min/max: $ frames min2flip (trip) %d, maxSensors 0%o, minSensors 0%o, nLED %d,"
|
||||||
|
" threshold %d, TWOIMAGE_MAXBS 0%o"),rBuf[1],rBuf[3],rBuf[2],rBuf[4],rBuf[5],rBuf[6]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
DIAG(F("(n$[,##]) Nominate: $ nLED %d, ## minSensors 0%o (maxSensors 0%o threshold %d)")
|
||||||
|
,rBuf[4],rBuf[2],rBuf[3],rBuf[5]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
b=rBuf[1]-2;
|
||||||
|
if(b<4) { Serial.print("<n (p%%) Bank empty n>\n"); break; }
|
||||||
|
SEND(&USB_SERIAL,F("<n (p%%) Bank: %d "),(0x7F&rBuf[2])/8);
|
||||||
|
for (int j=2; j<b; j+=3)
|
||||||
|
SEND(&USB_SERIAL,F(" S[%d%d]: r=%d x=%d"),0x7F&rBuf[j]/8,0x7F&rBuf[j]%8,rBuf[j+1],rBuf[j+2]+2*(rBuf[j]&0x80));
|
||||||
|
Serial.print(" n>\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
for (int i =0; i<8; i++) str[i] = ((rBuf[2] << i) & 0x80 ? '1' : '0');
|
||||||
|
DIAG(F("(q $) Query bank %c ENABLED sensors(S%c7-%c0): %s "), rBuf[1], rBuf[1], rBuf[1], str);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't': //threshold etc. from t## //bad pkt if 't' FF's
|
||||||
|
if(rBuf[1]==0xFF) {Serial.println("<n bad CAM 't' packet: 74 FF n>");_savedCmd[2] +=1; return 0;}
|
||||||
|
SEND(&USB_SERIAL,F("<n (t[##[,%%%%]]) Threshold:%d sensor S00:-%d"),rBuf[1],min(rBuf[2]&0x7F,99));
|
||||||
|
if(rBuf[2]>127) Serial.print("##* ");
|
||||||
|
else{
|
||||||
|
if(rBuf[2]>rBuf[1]) Serial.print("-?* ");
|
||||||
|
else Serial.print("--* ");
|
||||||
|
}
|
||||||
|
for(int i=3;i<31;i+=2){
|
||||||
|
uint8_t valu=rBuf[i]; //get bsn
|
||||||
|
if(valu==80) break; //80 = end flag
|
||||||
|
else{
|
||||||
|
SEND(&USB_SERIAL,F("%d%d:"), (valu&0x7F)/8,(valu&0x7F)%8);
|
||||||
|
if(valu>=128) Serial.print("?-");
|
||||||
|
else {if(rBuf[i+1]>=128) Serial.print("oo");else Serial.print("--");}
|
||||||
|
valu=rBuf[i+1];
|
||||||
|
SEND(&USB_SERIAL,F("%d%s"),min(valu&0x7F,99),(valu<128) ? "--* ":"##* ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.print(" >\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: //header not a recognised cmd character
|
||||||
|
DIAG(F("CAM packet header not valid (0x%x) (0x%x) (0x%x)"),rBuf[0],rBuf[1],rBuf[2]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Write (analogue) 8bit (command) values. Write the parameters to the sensorCAM
|
||||||
|
void _writeAnalogue(VPIN vpin, int param1, uint8_t camop, uint16_t param3) override {
|
||||||
|
uint8_t outputBuffer[7];
|
||||||
|
int errors=0;
|
||||||
|
outputBuffer[0] = camop;
|
||||||
|
int pin = vpin - _firstVpin;
|
||||||
|
|
||||||
|
if(camop >= 0x80) { //case "a" (4p) also (3p) e.g. <N 713 210 310>
|
||||||
|
camop=param1; //put row (0-236) in expected place
|
||||||
|
param1=param3; //put column in expected place
|
||||||
|
outputBuffer[0] = 'A';
|
||||||
|
pin = (pin/8)*10 + pin%8; //restore bsNo. as integer
|
||||||
|
}
|
||||||
|
if (_deviceState == DEVSTATE_FAILED) return;
|
||||||
|
|
||||||
|
outputBuffer[1] = pin; //vpin => bsn
|
||||||
|
outputBuffer[2] = param1 & 0xFF;
|
||||||
|
outputBuffer[3] = param1 >> 8;
|
||||||
|
outputBuffer[4] = camop; //command code
|
||||||
|
outputBuffer[5] = param3 & 0xFF;
|
||||||
|
outputBuffer[6] = param3 >> 8;
|
||||||
|
|
||||||
|
int count=param1+1;
|
||||||
|
if(camop=='Q'){
|
||||||
|
if(param3<=10) {count=param3; camop='B';}
|
||||||
|
//if(param1<10) outputBuffer[2] = param1*10;
|
||||||
|
}
|
||||||
|
if(camop=='B'){ //then 'b'(b%) cmd - can totally deal with that here. (but can't do b%,# (brightSF))
|
||||||
|
if(param1>97) return;
|
||||||
|
if(param1>9) param1 = param1/10; //accept a bsNo
|
||||||
|
for(int bnk=param1;bnk<count;bnk++) {
|
||||||
|
uint8_t b=_digitalInputStates[bnk];
|
||||||
|
char str[] = "11111111";
|
||||||
|
for (int i=0;i<8;i++) if(((b<<i)&0x80) == 0) str[i]='0';
|
||||||
|
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, 7);
|
||||||
|
}else _savedCmd[2] = 0; //no repeats if ##>30
|
||||||
|
}else _savedCmd[2] = 0; //no repeats unless 't'
|
||||||
|
|
||||||
|
_lasttStateRead = micros(); //don't repeat until _tStateRefresh mSec
|
||||||
|
|
||||||
|
errors = ioESP32(_I2CAddress, _CAMresponseBuff, 32 , outputBuffer, 7); //send to esp32-CAM
|
||||||
|
if (errors==0) return;
|
||||||
|
else { // if (_CAMresponseBuff[0] != EXIORDY) //can't be sure what is inBuff[0] !
|
||||||
|
DIAG(F("ioESP32 i2c error %d header 0x%x"),errors,_CAMresponseBuff[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Display device information and status.
|
||||||
|
void _display() override {
|
||||||
|
DIAG(F("EX-SensorCAM I2C:%s v%d.%d.%d Vpins %u-%u %S"),
|
||||||
|
_I2CAddress.toString(), _majorVer, _minorVer, _patchVer,
|
||||||
|
(int)_firstVpin, (int)_firstVpin+_nPins-1,
|
||||||
|
_deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F(""));
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
// Helper function for error handling
|
||||||
|
void reportError(uint8_t status, bool fail=true) {
|
||||||
|
DIAG(F("EX-SensorCAM I2C:%s Error:%d (%S)"), _I2CAddress.toString(),
|
||||||
|
status, I2CManager.getErrorMessage(status));
|
||||||
|
if (fail) _deviceState = DEVSTATE_FAILED;
|
||||||
|
}
|
||||||
|
//*************************
|
||||||
|
uint8_t _numDigitalPins = 80;
|
||||||
|
size_t digitalBytesNeeded=10;
|
||||||
|
uint8_t _CAMresponseBuff[34];
|
||||||
|
|
||||||
|
uint8_t _majorVer = 0;
|
||||||
|
uint8_t _minorVer = 0;
|
||||||
|
uint8_t _patchVer = 0;
|
||||||
|
|
||||||
|
uint8_t _digitalInputStates[10];
|
||||||
|
I2CRB _i2crb;
|
||||||
|
uint8_t _inputBuf[12];
|
||||||
|
byte _outputBuffer[8];
|
||||||
|
|
||||||
|
bool _verPrint=true;
|
||||||
|
|
||||||
|
uint8_t _readCommandBuffer[8];
|
||||||
|
uint8_t _savedCmd[8]; //for repeat 't' command
|
||||||
|
//uint8_t _digitalPinBytes = 10; // Size of allocated memory buffer (may be longer than needed)
|
||||||
|
|
||||||
|
enum {RDS_IDLE, RDS_DIGITAL, RDS_TSTATE}; // Read operation states
|
||||||
|
uint8_t _readState = RDS_IDLE;
|
||||||
|
//uint8_t cmdBuffer[7]={0,0,0,0,0,0,0};
|
||||||
|
unsigned long _lastDigitalRead = 0;
|
||||||
|
unsigned long _lasttStateRead = 0;
|
||||||
|
unsigned long _digitalRefresh = DIGITALREFRESH; // Delay refreshing digital inputs for 10ms
|
||||||
|
const unsigned long _tStateRefresh = 120000UL; // Delay refreshing repeat "tState" inputs
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EXIOINIT = 0xE0, // Flag to initialise setup procedure
|
||||||
|
EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup
|
||||||
|
CAMERR = 0xFE
|
||||||
|
};
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -333,5 +333,19 @@ The configuration file for DCC-EX Command Station
|
||||||
// to the sabertooth controller _as_well_. Default: Undefined.
|
// to the sabertooth controller _as_well_. Default: Undefined.
|
||||||
//
|
//
|
||||||
//#define SABERTOOTH 1
|
//#define SABERTOOTH 1
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// SENSORCAM
|
||||||
|
// ESP32-CAM based video sensors require #define to use appropriate base vpin number.
|
||||||
|
//#define SENSORCAM_VPIN 700
|
||||||
|
// To bypass 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)
|
||||||
|
//
|
||||||
|
// For smoother power-up, define a STARTUP_DELAY to allow CAM to initialise ref images
|
||||||
|
//#define STARTUP_DELAY 5000 // up to 20sec. CS delay
|
||||||
|
//
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
#define VERSION "5.2.86"
|
#define VERSION "5.2.87"
|
||||||
|
// 5.2.87 - CamParser and IO_EXSensorCam driver
|
||||||
// 5.2.86 - IO_TCA8418 driver for keypad matrix input now fully functioning, including being able to use an interrupt pin
|
// 5.2.86 - IO_TCA8418 driver for keypad matrix input now fully functioning, including being able to use an interrupt pin
|
||||||
// 5.2.85 - IO_TM1638 driver, SEG7 Exrail macro and _s7 segment pattern generator.
|
// 5.2.85 - IO_TM1638 driver, SEG7 Exrail macro and _s7 segment pattern generator.
|
||||||
// 5.2.84 - Fix TrackManager setDCCSignal and setPROGSignal for STM32 shadowing of PORTG/PORTH - this time it really is correct!
|
// 5.2.84 - Fix TrackManager setDCCSignal and setPROGSignal for STM32 shadowing of PORTG/PORTH - this time it really is correct!
|
||||||
|
|
Loading…
Reference in New Issue
Block a user