mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-30 03:26:13 +01:00
CAM first compile
This commit is contained in:
parent
449a5f1670
commit
0c62d27be2
70
CamParser.cpp
Normal file
70
CamParser.cpp
Normal 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
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"
|
||||||
|
|
||||||
// This macro can't be created easily as a portable function because the
|
// This macro can't be created easily as a portable function because the
|
||||||
// flashlist requires a far pointer for high flash access.
|
// 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);
|
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;
|
||||||
|
@ -799,7 +800,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
|
||||||
|
|
|
@ -547,6 +547,7 @@ protected:
|
||||||
#include "IO_duinoNodes.h"
|
#include "IO_duinoNodes.h"
|
||||||
#include "IO_EXIOExpander.h"
|
#include "IO_EXIOExpander.h"
|
||||||
#include "IO_trainbrains.h"
|
#include "IO_trainbrains.h"
|
||||||
|
#include "IO_EXSensorCAM.h"
|
||||||
|
|
||||||
|
|
||||||
#endif // iodevice_h
|
#endif // iodevice_h
|
||||||
|
|
208
IO_EXSensorCAM.h
Normal file
208
IO_EXSensorCAM.h
Normal 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
|
Loading…
Reference in New Issue
Block a user