From f348857ddba2ab8897517846a1191680019c8887 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Mon, 27 Mar 2023 12:39:11 +0100 Subject: [PATCH] Add FLAGS device for EX-RAIL state communications. Improve VPIN display in messages. FLAGS HAL device added to IODevice.h, which allows use of SET/RESET// to set and reset a VPIN state, and to allow /IF/IFNOT/AT/WAITFOR/etc. to monitor the VPIN state. Also, correct handling of VPINs above 32767 in DIAG calls within IODevice.cpp and IODevice.h. --- IODevice.cpp | 24 +-- IODevice.h | 69 ++++++++ config.h.txt | 169 ++++++++++++++++++ myHal.cpp.txt | 465 ++++++++++++++++++++++++++++++++++++++++++++++++++ version.h | 4 +- 5 files changed, 719 insertions(+), 12 deletions(-) create mode 100644 config.h.txt create mode 100644 myHal.cpp.txt diff --git a/IODevice.cpp b/IODevice.cpp index e907c23..03ccf85 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -169,7 +169,7 @@ bool IODevice::hasCallback(VPIN vpin) { // Display (to diagnostics) details of the device. void IODevice::_display() { - DIAG(F("Unknown device Vpins:%d-%d %S"), + DIAG(F("Unknown device Vpins:%u-%u %S"), (int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState==DEVSTATE_FAILED ? F("OFFLINE") : F("")); } @@ -179,7 +179,7 @@ bool IODevice::configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, i IODevice *dev = findDevice(vpin); if (dev) return dev->_configure(vpin, configType, paramCount, params); #ifdef DIAG_IO - DIAG(F("IODevice::configure(): Vpin ID %d not found!"), (int)vpin); + DIAG(F("IODevice::configure(): VPIN %u not found!"), (int)vpin); #endif return false; } @@ -191,7 +191,7 @@ int IODevice::read(VPIN vpin) { return dev->_read(vpin); } #ifdef DIAG_IO - DIAG(F("IODevice::read(): Vpin %d not found!"), (int)vpin); + DIAG(F("IODevice::read(): VPIN %u not found!"), (int)vpin); #endif return false; } @@ -203,7 +203,7 @@ int IODevice::readAnalogue(VPIN vpin) { return dev->_readAnalogue(vpin); } #ifdef DIAG_IO - DIAG(F("IODevice::readAnalogue(): Vpin %d not found!"), (int)vpin); + DIAG(F("IODevice::readAnalogue(): VPIN %u not found!"), (int)vpin); #endif return -1023; } @@ -213,7 +213,7 @@ int IODevice::configureAnalogIn(VPIN vpin) { return dev->_configureAnalogIn(vpin); } #ifdef DIAG_IO - DIAG(F("IODevice::configureAnalogIn(): Vpin %d not found!"), (int)vpin); + DIAG(F("IODevice::configureAnalogIn(): VPIN %u not found!"), (int)vpin); #endif return -1023; } @@ -227,7 +227,7 @@ void IODevice::write(VPIN vpin, int value) { return; } #ifdef DIAG_IO - DIAG(F("IODevice::write(): Vpin ID %d not found!"), (int)vpin); + DIAG(F("IODevice::write(): VPIN %u not found!"), (int)vpin); #endif } @@ -246,7 +246,7 @@ void IODevice::writeAnalogue(VPIN vpin, int value, uint8_t param1, uint16_t para return; } #ifdef DIAG_IO - DIAG(F("IODevice::writeAnalogue(): Vpin ID %d not found!"), (int)vpin); + DIAG(F("IODevice::writeAnalogue(): VPIN %u not found!"), (int)vpin); #endif } @@ -314,9 +314,11 @@ IODevice *IODevice::findDeviceFollowing(VPIN vpin) { // Private helper function to check for vpin overlap. Run during setup only. // returns true if pins DONT overlap with existing device +// TODO: Move the I2C address reservation and checks into the I2CManager code. +// That will enable non-HAL devices to reserve I2C addresses too. bool IODevice::checkNoOverlap(VPIN firstPin, uint8_t nPins, I2CAddress i2cAddress) { #ifdef DIAG_IO - DIAG(F("Check no overlap %d %d %s"), firstPin,nPins,i2cAddress.toString()); + DIAG(F("Check no overlap %u %u %s"), firstPin,nPins,i2cAddress.toString()); #endif VPIN lastPin=firstPin+nPins-1; for (IODevice *dev = _firstDevice; dev != 0; dev = dev->_nextDevice) { @@ -327,7 +329,7 @@ bool IODevice::checkNoOverlap(VPIN firstPin, uint8_t nPins, I2CAddress i2cAddres VPIN lastDevPin=firstDevPin+dev->_nPins-1; bool noOverlap= firstPin>lastDevPin || lastPin= NUM_DIGITAL_PINS) return false; #ifdef DIAG_IO - DIAG(F("Arduino _configurePullup Pin:%d Val:%d"), pin, p[0]); + DIAG(F("Arduino _configurePullup pin:%d Val:%d"), pin, p[0]); #endif pinMode(pin, p[0] ? INPUT_PULLUP : INPUT); return true; @@ -528,7 +530,7 @@ int ArduinoPins::_configureAnalogIn(VPIN vpin) { } void ArduinoPins::_display() { - DIAG(F("Arduino Vpins:%d-%d"), (int)_firstVpin, (int)_firstVpin+_nPins-1); + DIAG(F("Arduino Vpins:%u-%u"), (int)_firstVpin, (int)_firstVpin+_nPins-1); } ///////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/IODevice.h b/IODevice.h index 51b5aa0..769e111 100644 --- a/IODevice.h +++ b/IODevice.h @@ -467,6 +467,75 @@ protected: } }; +///////////////////////////////////////////////////////////////////////////////////////////////////// +// +// This HAL device driver is intended for communication in automation +// sequences. A VPIN can be SET or RESET within a sequence, and its +// current state checked elsewhere using IF, IFNOT, AT etc. or monitored +// from JMRI using a Sensor object (DCC-EX command). +// Alternatively, the flag can be set from JMRI and other interfaces +// using the command, to enable or disable actions within a sequence. +// +// Example of configuration in halSetup.h: +// +// FLAGS::create(32000, 128); +// +// or in myAutomation.h: +// +// HAL(FLAGS, 32000, 128); +// +// Both create 128 flags numbered with VPINs 32000-32127. +// +// + +class FLAGS : IODevice { +private: + uint8_t *_states = NULL; + +public: + static void create(VPIN firstVpin, unsigned int nPins) { + if (checkNoOverlap(firstVpin, nPins)) + new FLAGS(firstVpin, nPins); + } + +protected: + // Constructor performs static initialisation of the device object + FLAGS (VPIN firstVpin, int nPins) { + _firstVpin = firstVpin; + _nPins = nPins; + _states = (uint8_t *)calloc(1, (_nPins+7)/8); + if (!_states) { + DIAG(F("FLAGS: ERROR Memory Allocation Failure")); + return; + } + + addDevice(this); + } + + int _read(VPIN vpin) override { + int pin = vpin - _firstVpin; + if (pin >= _nPins || pin < 0) return 0; + uint8_t mask = 1 << (pin & 7); + return (_states[pin>>3] & mask) ? 1 : 0; + } + + void _write(VPIN vpin, int value) override { + int pin = vpin - _firstVpin; + if (pin >= _nPins || pin < 0) return; + uint8_t mask = 1 << (pin & 7); + if (value) + _states[pin>>3] |= mask; + else + _states[pin>>3] &= ~mask; + } + + void _display() override { + DIAG(F("FLAGS configured on VPINs %u-%u"), + _firstVpin, _firstVpin+_nPins-1); + } + +}; + #include "IO_MCP23008.h" #include "IO_MCP23017.h" #include "IO_PCF8574.h" diff --git a/config.h.txt b/config.h.txt new file mode 100644 index 0000000..98cc3fc --- /dev/null +++ b/config.h.txt @@ -0,0 +1,169 @@ +/********************************************************************** + +Config.h +COPYRIGHT (c) 2013-2016 Gregg E. Berman +COPYRIGHT (c) 2020 Fred Decker + +The configuration file for DCC++ EX Command Station + +**********************************************************************/ +///////////////////////////////////////////////////////////////////////////////////// +// NOTE: Before connecting these boards and selecting one in this software +// check the quick install guides!!! Some of these boards require a voltage +// generating resitor 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 ACCORDING TO THE FOLLOWING TABLE: +// +// 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 +// | +// +-----------------------v +// +// #define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), +// new MotorDriver(3, 12, UNUSED_PIN, 9, A0, 0.488, 1500, UNUSED_PIN), +// new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 0.488, 1500, UNUSED_PIN) + +#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD + +///////////////////////////////////////////////////////////////////////////////////// +// +// 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, 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_*. +// +// Your SSID may not conain ``"'' (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 conain ``"'' (double quote, ASCII 0x22). +#define WIFI_PASSWORD "deadcafe" +// +// WIFI_HOSTNAME: You probably don't need to change this +#define WIFI_HOSTNAME "dccex" +// +///////////////////////////////////////////////////////////////////////////////////// +// +// Wifi connect timeout in milliseconds. Default is 14000 (14 seconds). You only need +// to set this if you have an extremely slow Wifi router. +// +#define WIFI_CONNECT_TIMEOUT 14000 + +///////////////////////////////////////////////////////////////////////////////////// +// +// 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, 31 } + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE +// +// Uncomment to use with Ethernet Shields +// +// Ethernet Shields do not have have a MAC address in hardware. There may be one on +// a sticker on the Shield that you should use. Otherwise choose one of the ones below +// Be certain that no other device on your network has this same MAC address! +// +// 52:b8:8a:8e:ce:21 +// e3:e9:73:e1:db:0d +// 54:2b:13:52:ac:0c + +// NOTE: This is not used with ESP8266 WiFi modules. + +//#define MAC_ADDRESS { 0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 } // MAC address of your networking card found on the sticker on your card or take one from above + +// +// #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF } + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE LCD SCREEN USAGE BY THE BASE STATION +// +// Note: This feature requires an I2C enabled LCD screen using a Hitachi HD44780 +// controller and a PCF8574 based I2C 'backpack', +// OR an I2C Oled screen based on SSD1306 (128x64 or 128x32) controller, +// OR an I2C Oled screen based on SH1106 (132x64) controller. +// To enable, uncomment one of the lines below + +// define LCD_DRIVER for I2C LCD address 0x3f,16 cols, 2 rows +//#define LCD_DRIVER {SubBus_4,0x27},20,4 + +//OR define OLED_DRIVER width,height in pixels (address auto detected) +#if defined(ARDUINO_ARCH_STM32) +#define OLED_DRIVER 0x3c, 128, 64 +#else +#define OLED_DRIVER {SubBus_0,0x3c}, 128, 32 +#endif + +#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 EX-RAIL automation. Otherwise you do not have enough RAM +// to do that. Of course, then none of the EEPROM related commands work. +// +#define DISABLE_EEPROM + + +///////////////////////////////////////////////////////////////////////////////////// +// +// 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 + +// The following #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 + +///////////////////////////////////////////////////////////////////////////////////// diff --git a/myHal.cpp.txt b/myHal.cpp.txt new file mode 100644 index 0000000..b5b612e --- /dev/null +++ b/myHal.cpp.txt @@ -0,0 +1,465 @@ +#include "defines.h" +#include "IODevice.h" + +#ifndef IO_NO_HAL + +#include "IO_VL53L0X.h" +#include "IO_HCSR04.h" +#include "Sensors.h" +#include "Turnouts.h" +#include "IO_DFPlayer.h" +//#include "IO_Wire.h" +#include "IO_AnalogueInputs.h" +#if __has_include("IO_Servo.h") +#include "IO_Servo.h" +#include "IO_PCA9685pwm.h" +#endif + +#include "IO_HALDisplay.h" +#include "LiquidCrystal_I2C.h" + +#if __has_include("IO_CMRI.h") +#include "IO_CMRI.h" +#endif + +//#include "IO_ExampleSerial.h" + +//#include "IO_EXFastclock.h" +//#include "IO_EXTurntable.h" + +#if __has_include("IO_ExternalEEPROM.h") +#include "IO_ExternalEEPROM.h" +#endif + +#if __has_include("IO_Network.h") +#include "IO_Network.h" +#include "Net_RF24.h" +#include "Net_ENC28J60.h" +#include "Net_Ethernet.h" +#define NETWORK_PRESENT +#endif + +#include "IO_TouchKeypad.h" + +#define WIRE_TEST 0 +#define TESTHARNESS 1 +#define I2C_STRESS_TEST 0 +#define I2C_SETCLOCK 0 + +#include "DCC.h" + + +#if 0 // Long Strings +#define s10 "0123456789" +#define s100 s10 s10 s10 s10 s10 s10 s10 s10 s10 s10 +#define s1k s100 s100 s100 s100 s100 s100 s100 s100 s100 s100 +#define s10k s1k s1k s1k s1k s1k s1k s1k s1k s1k s1k +#define s32k s10k s10k s10k s1k s1k +volatile const char PROGMEM ss1[] = s32k; +#endif + + +#if TESTHARNESS + +// Function to be invoked by test harness +void myTest() { + // DIAG(F("VL53L0X #1 Test: dist=%d signal=%d ambient=%d value=%d"), + // IODevice::readAnalogue(5000), + // IODevice::readAnalogue(5001), + // IODevice::readAnalogue(5002), + // IODevice::read(5000)); + // DIAG(F("VL53L0X #2 Test: dist=%d signal=%d ambient=%d value=%d"), + // IODevice::readAnalogue(5003), + // IODevice::readAnalogue(5004), + // IODevice::readAnalogue(5005), + // IODevice::read(5003)); + // DIAG(F("HCSR04 Test: dist=%d value=%d"), + // IODevice::readAnalogue(2000), + // IODevice::read(2000)); + // DIAG(F("ADS111x Test: %d %d %d %d %d"), + // IODevice::readAnalogue(4500), + // IODevice::readAnalogue(4501), + // IODevice::readAnalogue(4502), + // IODevice::readAnalogue(4503), + // IODevice::readAnalogue(A5) + // ); + // DIAG(F("RF24 Test: 4000:%d 4002:%d"), + // IODevice::read(4000), + // IODevice::read(4002) + // ); + DIAG(F("EXPANDER: 2212:%d 2213:%d 2214:%d"), + IODevice::readAnalogue(2212), + IODevice::readAnalogue(2213), + IODevice::readAnalogue(2214)); +} +#endif + +#if I2C_STRESS_TEST +static bool initialised = false; +static uint8_t lastStatus = 0; +static const int nRBs = 3; // request blocks concurrently +static const int I2cTestPeriod = 1; // milliseconds +static I2CAddress testDevice = {SubBus_6, 0x27}; +static I2CRB rb[nRBs]; +static uint8_t readBuffer[nRBs*32]; // nRB x 32-byte input buffer +static uint8_t writeBuffer[nRBs]; // nRB x 1-byte output buffer +static unsigned long count = 0; +static unsigned long errors = 0; +static unsigned long lastOutput = millis(); + +void I2CTest() { + if (!initialised) { + // I2C Loading for stress test. + // Write value then read back 32 times + for (int i=0; i 60000) { // 1 minute + DIAG(F("I2CTest: Count=%l Errors=%l"), count, errors); + count = errors = 0; + lastOutput = millis(); + } +} +#endif + +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); + } + } +} + +void updateTime() { + uint8_t buffer[20]; + I2CAddress rtc = {SubBus_1, 0x68}; // Real-time clock I2C address + buffer[0] = 0; + + // Set time - only needs to be done once if battery is ok. + static bool timeSet = false; + if (!timeSet) { + // I2CManager.read(rtc, buffer+1, sizeof(buffer)-1); + // uint8_t year = 23; // 2023 + // uint8_t day = 2; // tuesday + // uint8_t date = 21; // 21st + // uint8_t month = 2; // feb + // uint8_t hours = 23; // xx: + // uint8_t minutes = 25; // :xx + // buffer[1] = 0; // seconds + // buffer[2] = ((minutes / 10) << 4) | (minutes % 10); + // buffer[3] = ((hours / 10) << 4) | (hours % 10); + // buffer[4] = day; + // buffer[5] = ((date/10) << 4) + date%10; // 24th + // buffer[6] = ((month/10) << 4) + month%10; // feb + // buffer[7] = ((year/10) << 4) + year%10; // xx23 + // for (uint8_t i=8; i> 4; + uint8_t seconds1 = buffer[1] & 0xf; + uint8_t minutes10 = buffer[2] >> 4; + uint8_t minutes1 = buffer[2] & 0xf; + uint8_t hours10 = buffer[3] >> 4; + uint8_t hours1 = buffer[3] & 0xf; + SCREEN(10, 0, F("Departures %d%d:%d%d:%d%d"), + hours10, hours1, minutes10, minutes1, seconds10, seconds1); + } +} + +void showCharacterSet() { + if (millis() < 3000) return; + const uint8_t lineLen = 20; + char buffer[lineLen+1]; + static uint8_t nextChar = 0x20; + for (uint8_t row=0; row<8; row+=1) { + for (uint8_t col=0; col::create(10, {SubBus_5, 0x3c}, 132, 64); // SH1106 + // UserAddin::create(updateLocoScreen, 1000); + // UserAddin::create(showCharacterSet, 5000); + // UserAddin::create(updateTime, 1000); + + HALDisplay::create(10, {SubBus_4, 0x3c}, 128, 32); + HALDisplay::create(10, {SubBus_7, 0x3c}, 128, 32); + + //HALDisplay::create(10, {SubBus_4, 0x27}, 20, 4); + + // Draw double boxes with X O O X inside. + // SCREEN(3, 2, F("\xc9\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xcb\xcd\xcd\xcd\xbb")); + // SCREEN(3, 3, F("\xba X \xba O \xba O \xba O \xba X \xba")); + // SCREEN(3, 4, F("\xcc\xcd\xcd\xcd\xce\xcd\xcd\xcd\xce\xcd\xcd\xcd\xce\xcd\xcd\xcd\xce\xcd\xcd\xcd\xb9")); + // SCREEN(3, 5, F("\xba X \xba O \xba O \xba O \xba X \xba")); + // SCREEN(3, 6, F("\xc8\xcd\xcd\xcd\xca\xcd\xcd\xcd\xca\xcd\xcd\xcd\xca\xcd\xcd\xcd\xca\xcd\xcd\xcd\xbc")); + + // Draw single boxes with X O O X inside. + // SCREEN(3, 0, F("Summary Data:")); + // SCREEN(3, 1, F("\xda\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xc2\xc4\xc4\xc4\xbf")); + // SCREEN(3, 2, F("\xb3 X \xb3 O \xb3 O \xb3 O \xb3 X \xb3")); + // SCREEN(3, 3, F("\xc3\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xb4")); + // SCREEN(3, 4, F("\xb3 X \xb3 O \xb3 O \xb3 O \xb3 X \xb3")); + // SCREEN(3, 5, F("\xc3\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xc5\xc4\xc4\xc4\xb4")); + // SCREEN(3, 6, F("\xb3 X \xb3 O \xb3 O \xb3 O \xb3 X \xb3")); + // SCREEN(3, 7, F("\xc0\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xc1\xc4\xc4\xc4\xd9")); + + // Blocks of different greyness + // SCREEN(3, 0, F("\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xdb\xdb\xdb\xdb")); + // SCREEN(3, 1, F("\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xdb\xdb\xdb\xdb")); + // SCREEN(3, 2, F("\xb0\xb0\xb0\xb0\xb1\xb1\xb1\xb1\xb2\xb2\xb2\xb2\xdb\xdb\xdb\xdb")); + + // DCCEX logo + // SCREEN(3, 1, F("\xb0\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\x20\xb0\xb0\xb0\x20\xb0\x20\xb0")); + // SCREEN(3, 2, F("\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\xb0")); + // SCREEN(3, 3, F("\xb0\x20\xb0\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\xb0\xb0\x20\x20\x20\xb0\x20")); + // SCREEN(3, 4, F("\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\xb0")); + // SCREEN(3, 5, F("\xb0\xb0\x20\x20\x20\xb0\x20\x20\x20\xb0\x20\x20\x20\x20\xb0\xb0\xb0\x20\xb0\x20\xb0")); + // SCREEN(3, 7, F("\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1")); + +#if 0 + // List versions of devices that respond to the version request + for (uint8_t address = 8; address<0x78; address++) { + uint8_t buffer[3]; + uint8_t status = I2CManager.read(0x7c, buffer, sizeof(buffer), 1, address); + if (status == I2C_STATUS_OK) { + uint16_t manufacturer = ((uint16_t)buffer[0] << 4 ) | (buffer[1] >> 4); + uint16_t deviceID = ((uint16_t)(buffer[1] & 0x0f) << 5) | (buffer[2] >> 3); + uint16_t dieRevision = buffer[2] & 0x1f; + DIAG(F("Addr %s version: %x %x %x"), address.toString(), manufacturer, deviceID, dieRevision); + } + } +#endif + +#if I2C_STRESS_TEST + UserAddin::create(I2CTest, I2cTestPeriod); +#endif + +#if WIRE_TEST + // Test of Wire-I2CManager interface + Wire.begin(); + Wire.setClock(400000); + Wire.beginTransmission(0x23); + Wire.print("Hello"); + uint8_t status = Wire.endTransmission(); + if (status==0) DIAG(F("Wire: device Found on 0x23")); + + Wire.beginTransmission(0x23); + Wire.write(0xde); + Wire.endTransmission(false); // don't send stop + Wire.requestFrom(0x23, 1); + if (Wire.available()) { + DIAG(F("Wire: value=x%x"), Wire.read()); + } + uint8_t st = I2CManager.write(0x33, 0, 0); + DIAG(F("I2CManager 0x33 st=%d \"%S\""), st, + I2CManager.getErrorMessage(st)); +#endif + +#if I2C_SETCLOCK + // Test I2C clock changes + // Set up two I2C request blocks + I2CRB rb1, rb2; + uint8_t readBuff[32]; + rb1.setRequestParams(0x23, readBuff, sizeof(readBuff), readBuff, sizeof(readBuff)); + rb2.setRequestParams(0x23, readBuff, sizeof(readBuff), readBuff, sizeof(readBuff)); + // First set clock to 400kHz and then issue requests + I2CManager.forceClock(400000); + I2CManager.queueRequest(&rb1); + I2CManager.queueRequest(&rb2); + // Wait a little to allow the first transaction to start + delayMicroseconds(2); + // ... then request a clock speed change + I2CManager.forceClock(100000); + DIAG(F("I2CClock: rb1 status=%d"), rb1.wait()); + DIAG(F("I2CClock: rb2 status=%d"), rb2.wait()); + // Reset clock speed + I2CManager.forceClock(400000); +#endif + + EXIOExpander::create(2200, 18, {SubBus_0, 0x65}); + //UserAddin::create(myTest, 1000); + // ServoTurnout::create(2200, 2200, 400, 200, 0); + // ServoTurnout::create(2200, 2200, 400, 200, 0); + + TouchKeypad::create(2300, 16, 25, 24); + + // GPIO + PCF8574::create(800, 8, {SubBus_1, 0x23}); + //PCF8574::create(808, 8, {SubBus_2, 0x27}); + PCF8574::create(65000, 8, 0x27); + + MCP23017::create(164,16,{SubBus_3, 0x20}); + //MCP23017::create(180,16,{SubBus_0, 0x27}); + Sensor::create(170, 170, 1); // Hall effect, enable pullup. + Sensor::create(171, 171, 1); + + // PWM (LEDs and Servos) + // For servos, use default 50Hz pulses. + PCA9685::create(100, 16, {SubBus_1, 0x41}); + // For LEDs, use 1kHz pulses. + PCA9685::create(116, 16, {SubBus_1, 0x40}, 1000); + + // 4-pin Analogue Input Module + //ADS111x::create(4500, 4, 0x48); + + // Laser Time-Of-Flight Sensors + VL53L0X::create(5000, 3, {SubBus_0, 0x60}, 300, 310, 46); + //VL53L0X::create(5003, 3, {SubBus_6, 0x61}, 300, 310, 47); + Sensor::create(5000, 5000, 0); + Sensor::create(5003, 5003, 0); + // Monitor reset digital on first TOF + //Sensor::create(46,46,0); + + // // External 24C256 EEPROM (256kBytes) on I2C address 0x50. + // ExternalEEPROM::create({SubBus_0, 0x50}, 256); + + // Play up to 10 sounds on pins 10000-10009. Player is connected to Serial1 or Serial2. + #if defined(HAVE_HWSERIAL1) && !defined(ARDUINO_ARCH_STM32) + DFPlayer::create(10000, 14, Serial1); + #elif defined(ARDUINO_ARCH_STM32) + DFPlayer::create(10000, 10, Serial3); // Pins PC11 (RX) and PC10 (TX) + #endif + + // Ultrasound echo device + HCSR04::create(2000, 32, 33, 80, 85 /*, HCSR04::LOOP */); + Sensor::create(2000, 2000, 0); + +#if __has_include("IO_CMRI.h") + CMRIbus::create(0, Serial2, 115200, 50, 40); // 50ms cycle, pin 40 for DE/!RE pins + CMRInode::create(25000, 72, 0, 0, 'M'); // SMINI address 0 + for (int pin=0; pin<24; pin++) { + Sensor::create(25000+pin, 25000+pin, 0); + } +#endif + + //CMRInode::create(25072, 72, 0, 13, 'M'); // SMINI address 13 + //CMRInode::create(25144, 288, 0, 14, 'C', 144, 144); // CPNODE address 14 + +#ifdef NETWORK_PRESENT + // Define remote pins to be used. The range of remote pins is like a common data area shared + // between all nodes. + // For outputs, a write to a remote VPIN causes a message to be sent to another node, which then performs + // the write operation on the device VPIN that is local to that node. + // For inputs, the state of remote input VPIN is read on the node where it is connected, and then + // sent to other nodes in the system where the state is saved and processed. Updates are sent on change, and + // also periodically if no changes. + // + // Each definition is a triple of remote node, remote pin, indexed by relative pin. Up to 224 rpins can + // be configured (per node). This is to fit into a 32-byte packet. + REMOTEPINS rpins[] = { + {30,164,RPIN_IN} , //4000 Node 30, first MCP23017 pin, input + {30,165,RPIN_IN}, //4001 Node 30, second MCP23017 pin, input + {30,166,RPIN_OUT}, //4002 Node 30, third MCP23017 pin, output + {30,166,RPIN_OUT}, //4003 Node 30, fourth MCP23017 pin, output + {30,100,RPIN_INOUT}, //4004 Node 30, first PCA9685 servo pin + {30,101,RPIN_INOUT}, //4005 Node 30, second PCA9685 servo pin + {30,102,RPIN_INOUT}, //4006 Node 30, third PCA9685 servo pin + {30,103,RPIN_INOUT}, //4007 Node 30, fourth PCA9685 servo pin + {30,24,RPIN_IN}, //4008 Node 30, Arduino pin D24 + {30,25,RPIN_IN}, //4009 Node 30, Arduino pin D25 + {30,26,RPIN_IN}, //4010 Node 30, Arduino pin D26 + {30,27,RPIN_IN}, //4011 Node 30, Arduino pin D27 + {30,1000,RPIN_OUT}, //4012 Node 30, DFPlayer playing flag (when read) / Song selector (when written) + {30,5000,RPIN_IN}, //4013 Node 30, VL53L0X detect pin + {30,VPIN_NONE,0}, //4014 Node 30, spare + {30,VPIN_NONE,0}, //4015 Node 30, spare + + {31,164,RPIN_IN} , //4016 Node 31, first MCP23017 pin, input + {31,165,RPIN_IN}, //4017 Node 31, second MCP23017 pin, input + {31,166,RPIN_OUT}, //4018 Node 31, third MCP23017 pin, output + {31,166,RPIN_OUT}, //4019 Node 31, fourth MCP23017 pin, output + {31,100,RPIN_INOUT}, //4020 Node 31, first PCA9685 servo pin + {31,101,RPIN_INOUT}, //4021 Node 31, second PCA9685 servo pin + {31,102,RPIN_INOUT}, //4022 Node 31, third PCA9685 servo pin + {31,103,RPIN_INOUT}, //4023 Node 31, fourth PCA9685 servo pin + {31,24,RPIN_IN}, //4024 Node 31, Arduino pin D24 + {31,25,RPIN_IN}, //4025 Node 31, Arduino pin D25 + {31,26,RPIN_IN}, //4026 Node 31, Arduino pin D26 + {31,27,RPIN_IN}, //4027 Node 31, Arduino pin D27 + {31,3,RPIN_IN}, //4028 Node 31, Arduino pin D3 + {31,VPIN_NONE,0}, //4029 Node 31, spare + {31,VPIN_NONE,0}, //4030 Node 31, spare + {31,VPIN_NONE,0} //4031 Node 31, spare + }; + // FirstVPIN, nPins, thisNode, pinDefs, CEPin, CSNPin + // Net_RF24 *rf24Driver = new Net_RF24(48, 49); + // Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, rf24Driver); + #if NODE==30 + //Net_ENC28J60 *encDriver = new Net_ENC28J60(49); + //Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, encDriver); + #elif NODE==31 + Net_ENC28J60 *encDriver = new Net_ENC28J60(53); + Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, encDriver); + #else + Net_Ethernet *etherDriver = new Net_Ethernet(); + Network::create(4000, NUMREMOTEPINS(rpins), NODE, rpins, etherDriver); + #endif + for (int i=0; i<=32; i++) + Sensor::create(4000+i, 4000+i, 0); +#endif + +#ifdef ARDUINO_ARCH_STM32 +//PCF8574::create(1900, 8, 0x27); +Sensor::create(1900,100,1); +Sensor::create(1901,101,1); +#endif + +} +#endif // IO_NO_HAL diff --git a/version.h b/version.h index f9484a1..47d82db 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.36" +#define VERSION "4.2.37" +// 4.2.37 - Add new FLAGS HAL device for communications to/from EX-RAIL; +// - Fix diag display of high VPINs within IODevice class. // 4.2.36 - do not broadcast a turnout state that has not changed // 4.2.35 - add direct pin manipulation command // 4.2.34 - Completely fix EX-IOExpander analogue inputs