1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-26 17:46:14 +01:00

CAM first compile

This commit is contained in:
Asbelos 2024-05-23 08:35:03 +01:00
parent 449a5f1670
commit 0c62d27be2
5 changed files with 298 additions and 2 deletions

70
CamParser.cpp Normal file
View File

@ -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: //<N code val>
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: //<N vpin rowY colx >
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: //<N a id row col>
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;
}

12
CamParser.h Normal file
View 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

View File

@ -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) { // <z vpin ana;og profile duration>
if (params>=2 && params<=4) { // <z vpin analog profile duration>
// 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': // <N commands for SensorCam
if (CamParser::parseN(stream,params,p)) return;
break;
#endif
case '/': // implemented in EXRAIL parser
case 'L': // LCC interface implemented in EXRAIL parser
break; // Will <X> if not intercepted by EXRAIL

View File

@ -547,6 +547,7 @@ protected:
#include "IO_duinoNodes.h"
#include "IO_EXIOExpander.h"
#include "IO_trainbrains.h"
#include "IO_EXSensorCAM.h"
#endif // iodevice_h

208
IO_EXSensorCAM.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
/*
* 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)
// <N a bsno row col> is passed in here as opcode=128+bsNo, p1=row, p2=col
// so p0 is reverse engineered from the opcode
// <N vpin row col> 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) { // <N a bsNo row col>
p0=opcode & 0x7f; // get bSno from opcode
opcode='a'; // and revert to correct code
}
else if (opcode=='A') { // <N vpin row col>
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