#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