diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 27be7f3..429e1dc 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -700,22 +700,17 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[]) { bool state = false; switch (p[1]) { - // By default turnout command uses 0=throw, 1=close, - // but legacy DCC++ behaviour is 1=throw, 0=close. + // Turnout messages use 1=throw, 0=close. case 0: - state = Turnout::useLegacyTurnoutBehaviour; - break; - case 1: - state = !Turnout::useLegacyTurnoutBehaviour; - break; case HASH_KEYWORD_C: state = true; break; + case 1: case HASH_KEYWORD_T: state= false; break; default: - return false; + return false; // Invalid parameter } if (!Turnout::setClosed(p[0], state)) return false; diff --git a/I2CManager_NonBlocking.h b/I2CManager_NonBlocking.h index 920cecd..41e9283 100644 --- a/I2CManager_NonBlocking.h +++ b/I2CManager_NonBlocking.h @@ -164,8 +164,6 @@ void I2CManagerClass::loop() { #if !defined(I2C_USE_INTERRUPTS) handleInterrupt(); #endif - // If free, initiate next transaction - startTransaction(); checkForTimeout(); } diff --git a/IODevice.cpp b/IODevice.cpp index 43db2f6..e7b9dbd 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -292,7 +292,7 @@ ArduinoPins::ArduinoPins(VPIN firstVpin, int nPins) { _pinPullups = (uint8_t *)calloc(2, arrayLen); _pinModes = (&_pinPullups[0]) + arrayLen; for (int i=0; i> 4) & 0x0f) << BACKPACK_DATA_BITS) | mode; uint8_t lownib = ((value & 0x0f) << BACKPACK_DATA_BITS) | mode; + // Wait for previous request to complete before writing to outputbuffer. + requestBlock.wait(); // Send both nibbles uint8_t len = 0; outputBuffer[len++] = highnib|En; outputBuffer[len++] = highnib; outputBuffer[len++] = lownib|En; outputBuffer[len++] = lownib; - I2CManager.write(_Addr, outputBuffer, len, &requestBlock); + I2CManager.write(_Addr, outputBuffer, len); // Write command synchronously } // write 4 data bits to the HD44780 LCD controller. @@ -210,7 +214,7 @@ void LiquidCrystal_I2C::write4bits(uint8_t value) { uint8_t len = 0; outputBuffer[len++] = _data|En; outputBuffer[len++] = _data; - I2CManager.write(_Addr, outputBuffer, len, &requestBlock); + I2CManager.write(_Addr, outputBuffer, len); // Write command synchronously } // write a byte to the PCF8574 I2C interface. We don't need to set @@ -219,5 +223,5 @@ void LiquidCrystal_I2C::expanderWrite(uint8_t value) { // Wait for previous request to complete before writing to outputbuffer. requestBlock.wait(); outputBuffer[0] = value | _backlightval; - I2CManager.write(_Addr, outputBuffer, 1, &requestBlock); + I2CManager.write(_Addr, outputBuffer, 1); // Write command synchronously } \ No newline at end of file diff --git a/LiquidCrystal_I2C.h b/LiquidCrystal_I2C.h index 6d65541..6cd4384 100644 --- a/LiquidCrystal_I2C.h +++ b/LiquidCrystal_I2C.h @@ -90,7 +90,8 @@ private: I2CRB requestBlock; uint8_t outputBuffer[4]; - bool isBusy() { return requestBlock.isBusy(); } + // I/O is synchronous, so if this is called we're not busy! + bool isBusy() { return false; } }; #endif diff --git a/Turnouts.cpp b/Turnouts.cpp index 7b87153..552cd9d 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -20,13 +20,8 @@ * along with CommandStation. If not, see . */ -// Set the following definition to true for = throw and = close -// or to false for = close and = throw (the original way). -#ifndef USE_LEGACY_TURNOUT_BEHAVIOUR -#define USE_LEGACY_TURNOUT_BEHAVIOUR false -#endif -#include "defines.h" +#include "defines.h" // includes config.h #include "EEStore.h" #include "StringFormatter.h" #include "RMFT2.h" @@ -47,7 +42,6 @@ * Public static data */ int Turnout::turnoutlistHash = 0; - bool Turnout::useLegacyTurnoutBehaviour = USE_LEGACY_TURNOUT_BEHAVIOUR; /* * Protected static functions @@ -74,9 +68,10 @@ turnoutlistHash++; } + // For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed; void Turnout::printState(Print *stream) { StringFormatter::send(stream, F("\n"), - _turnoutData.id, _turnoutData.closed ^ useLegacyTurnoutBehaviour); + _turnoutData.id, !_turnoutData.closed); } // Remove nominated turnout from turnout linked list and delete the object. @@ -137,7 +132,7 @@ // Write byte containing new closed/thrown state to EEPROM if required. Note that eepromAddress // is always zero for LCN turnouts. if (EEStore::eeStore->data.nTurnouts > 0 && tt->_eepromAddress > 0) - EEPROM.put(tt->_eepromAddress, *((uint8_t *) &tt->_turnoutData)); + EEPROM.put(tt->_eepromAddress, tt->_turnoutData.flags); #if defined(RMFT_ACTIVE) RMFT2::turnoutEvent(id, closeFlag); @@ -172,7 +167,7 @@ Turnout *tt = 0; // Read turnout type from EEPROM struct TurnoutData turnoutData; - int eepromAddress = EEStore::pointer(); // Address of byte containing the closed flag. + int eepromAddress = EEStore::pointer() + offsetof(struct TurnoutData, flags); // Address of byte containing the closed flag. EEPROM.get(EEStore::pointer(), turnoutData); EEStore::advance(sizeof(turnoutData)); @@ -196,7 +191,7 @@ } if (tt) { // Save EEPROM address in object. Note that LCN turnouts always have eepromAddress of zero. - tt->_eepromAddress = eepromAddress; + tt->_eepromAddress = eepromAddress + offsetof(struct TurnoutData, flags); } #ifdef EESTOREDEBUG @@ -205,7 +200,7 @@ return tt; } - // Display, on the specified stream, the current state of the turnout (1 or 0). + // Display, on the specified stream, the current state of the turnout (1=thrown or 0=closed). void Turnout::printState(uint16_t id, Print *stream) { Turnout *tt = get(id); if (!tt) tt->printState(stream); @@ -279,10 +274,11 @@ return tt; } + // For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed void ServoTurnout::print(Print *stream) { StringFormatter::send(stream, F("\n"), _turnoutData.id, _servoTurnoutData.vpin, _servoTurnoutData.thrownPosition, _servoTurnoutData.closedPosition, _servoTurnoutData.profile, - _turnoutData.closed ^ useLegacyTurnoutBehaviour); + !_turnoutData.closed); } // ServoTurnout-specific code for throwing or closing a servo turnout. @@ -312,6 +308,12 @@ * *************************************************************************************/ +#if defined(DCC_TURNOUTS_RCN_213) + const bool DCCTurnout::rcn213Compliant = true; +#else + const bool DCCTurnout::rcn213Compliant = false; +#endif + // DCCTurnoutData contains data specific to this subclass that is // written to EEPROM when the turnout is saved. struct DCCTurnoutData { @@ -363,19 +365,19 @@ void DCCTurnout::print(Print *stream) { StringFormatter::send(stream, F("\n"), _turnoutData.id, (((_dccTurnoutData.address-1) >> 2)+1), ((_dccTurnoutData.address-1) & 3), - _turnoutData.closed ^ useLegacyTurnoutBehaviour); - // Also report using classic DCC++ syntax for DCC accessory turnouts + !_turnoutData.closed); + // Also report using classic DCC++ syntax for DCC accessory turnouts, since JMRI expects this. StringFormatter::send(stream, F("\n"), _turnoutData.id, (((_dccTurnoutData.address-1) >> 2)+1), ((_dccTurnoutData.address-1) & 3), - _turnoutData.closed ^ useLegacyTurnoutBehaviour); + !_turnoutData.closed); } bool DCCTurnout::setClosedInternal(bool close) { // DCC++ Classic behaviour is that Throw writes a 1 in the packet, // and Close writes a 0. - // RCN-214 specifies that Throw is 0 and Close is 1. + // RCN-213 specifies that Throw is 0 and Close is 1. DCC::setAccessory((((_dccTurnoutData.address-1) >> 2) + 1), - ((_dccTurnoutData.address-1) & 3), close ^ useLegacyTurnoutBehaviour); + ((_dccTurnoutData.address-1) & 3), close ^ !rcn213Compliant); _turnoutData.closed = close; return true; } @@ -437,9 +439,10 @@ return tt; } + // Report 1 for thrown, 0 for closed. void VpinTurnout::print(Print *stream) { StringFormatter::send(stream, F("\n"), _turnoutData.id, _vpinTurnoutData.vpin, - _turnoutData.closed ^ useLegacyTurnoutBehaviour); + !_turnoutData.closed); } bool VpinTurnout::setClosedInternal(bool close) { @@ -501,8 +504,9 @@ //void save() override { } //static Turnout *load(struct TurnoutData *turnoutData) { + // Report 1 for thrown, 0 for closed. void LCNTurnout::print(Print *stream) { StringFormatter::send(stream, F("\n"), _turnoutData.id, - _turnoutData.closed ^ useLegacyTurnoutBehaviour); + !_turnoutData.closed); } diff --git a/Turnouts.h b/Turnouts.h index 45f60a6..9c14089 100644 --- a/Turnouts.h +++ b/Turnouts.h @@ -53,9 +53,14 @@ protected: // vice versa. If the turnout has been saved, then this byte is rewritten // when changed in RAM. The 'closed' flag must be located in the first byte. struct TurnoutData { - bool closed : 1; - bool _rfu: 2; - uint8_t turnoutType : 5; + union { + struct { + bool closed : 1; + bool _rfu: 2; + uint8_t turnoutType : 5; + }; + uint8_t flags; + }; uint16_t id; } _turnoutData; // 3 bytes @@ -104,8 +109,8 @@ public: * Static data */ static int turnoutlistHash; - static bool useLegacyTurnoutBehaviour; - + static const bool useClassicTurnoutCommands; + /* * Public base class functions */ @@ -182,11 +187,11 @@ private: } _servoTurnoutData; // 6 bytes // Constructor - ServoTurnout(uint16_t id, VPIN vpin, uint16_t thrownPosition, uint16_t closedPosition, uint8_t profile, bool closed = true); + ServoTurnout(uint16_t id, VPIN vpin, uint16_t thrownPosition, uint16_t closedPosition, uint8_t profile, bool closed); public: // Create function - static Turnout *create(uint16_t id, VPIN vpin, uint16_t thrownPosition, uint16_t closedPosition, uint8_t profile, bool closed = true); + static Turnout *create(uint16_t id, VPIN vpin, uint16_t thrownPosition, uint16_t closedPosition, uint8_t profile, bool closed=true); // Load a Servo turnout definition from EEPROM. The common Turnout data has already been read at this point. static Turnout *load(struct TurnoutData *turnoutData); @@ -222,6 +227,8 @@ public: // Load a VPIN turnout definition from EEPROM. The common Turnout data has already been read at this point. static Turnout *load(struct TurnoutData *turnoutData); void print(Print *stream) override; + // Flag whether DCC Accessory packets are to contain 1=close/0=throw(RCN-213) or 1=throw/0-close (DCC++ Classic) + static const bool rcn213Compliant; protected: bool setClosedInternal(bool close) override; @@ -243,7 +250,7 @@ private: } _vpinTurnoutData; // 2 bytes // Constructor - VpinTurnout(uint16_t id, VPIN vpin, bool closed=true); + VpinTurnout(uint16_t id, VPIN vpin, bool closed); public: // Create function @@ -270,8 +277,8 @@ private: // struct LCNTurnoutData { // } _lcnTurnoutData; // 0 bytes - // Constructor - LCNTurnout(uint16_t id, bool closed=true); + // Constructor + LCNTurnout(uint16_t id, bool closed); public: // Create function diff --git a/config.example.h b/config.example.h index 0debbc2..6c5c69a 100644 --- a/config.example.h +++ b/config.example.h @@ -129,4 +129,16 @@ The configuration file for DCC-EX Command Station #define SCROLLMODE 1 ///////////////////////////////////////////////////////////////////////////////////// +// +// 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 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 +///////////////////////////////////////////////////////////////////////////////////// diff --git a/mySetup.cpp_example.txt b/mySetup.cpp_example.txt new file mode 100644 index 0000000..949088a --- /dev/null +++ b/mySetup.cpp_example.txt @@ -0,0 +1,139 @@ +// Sample mySetup.cpp file. +// +// To use this file, copy it to mySetup.cpp and uncomment the directives and/or +// edit them to satisfy your requirements. + +// Note that if the file has a .cpp extension it WILL be compiled into the build +// and the mySetup() function WILL be invoked. +// +// To prevent this, temporarily rename it to mySetup.txt or similar. +// + +#include "IODevice.h" +#include "Turnouts.h" +#include "Sensors.h" +#include "IO_HCSR04.h" + + +// The #if directive prevent compile errors for Uno and Nano by excluding the +// HAL directives from the build. +#if !defined(IO_NO_HAL) + + +// Examples of statically defined HAL directives (alternative to the create() call). +// These have to be outside of the mySetup() function. + + +// The following directive defines a PCA9685 PWM Servo driver module. +// The parameters are: +// First Vpin=100 +// Number of VPINs=16 (numbered 100-115) +// I2C address of module=0x40 + +//PCA9685 pwmModule1(100, 16, 0x40); + + +// The following directive defines an MCP23017 16-port I2C GPIO Extender module. +// The parameters are: +// First Vpin=164 +// Number of VPINs=16 (numbered 164-179) +// I2C address of module=0x20 + +//MCP23017 gpioModule2(164, 16, 0x20); + + +// Alternative form, which allows the INT pin of the module to request a scan +// by pulling Arduino pin 40 to ground. Means that the I2C isn't being polled +// all the time, only when a change takes place. Multiple modules' INT pins +// may be connected to the same Arduino pin. + +//MCP23017 gpioModule2(164, 16, 0x20, 40); + + +// The following directive defines an MCP23008 8-port I2C GPIO Extender module. +// The parameters are: +// First Vpin=300 +// Number of VPINs=8 (numbered 300-307) +// I2C address of module=0x22 + +//MCP23017 gpioModule3(300, 8, 0x22); + + +// The following directive defines a PCF8574 8-port I2C GPIO Extender module. +// The parameters are: +// First Vpin=200 +// Number of VPINs=8 (numbered 200-207) +// I2C address of module=0x23 + +//PCF8574 gpioModule4(200, 8, 0x23); + + +// Alternative form using INT pin (see above) + +//PCF8574 gpioModule4(200, 8, 0x23, 40); + + +// The following directive defines an HCSR04 ultrasonic module. +// The parameters are: +// Vpin=2000 (only one VPIN per directive) +// Number of VPINs=1 +// Arduino pin connected to TRIG=30 +// Arduino pin connected to ECHO=31 +// Minimum trigger range=20cm (VPIN goes to 1 when <20cm) +// Maximum trigger range=25cm (VPIN goes to 0 when >25cm) +// Note: Multiple devices can be configured by using a different ECHO pin +// for each one. The TRIG pin can be shared between multiple devices. +// Be aware that the 'ping' of one device may be received by another +// device and position them accordingly! + +//HCSR04 sonarModule1(2000, 30, 31, 20, 25); +//HCSR04 sonarModule2(2001, 30, 32, 20, 25); + + +// The function mySetup() is invoked from CS if it exists within the build. +// It is called just before mysetup.h is executed, so things set up within here can be +// referenced by commands in mySetup.h. + +void mySetup() { + + // Alternative way of creating MCP23017, which has to be within the mySetup() function + // The other devices can also be created in this way. The parameter lists for the + // create() function are identical to the parameter lists for the declarations. + + //MCP23017::create(180, 16, 0x21); + + + // Creating a Turnout + // Parameters: same as command for Servo turnouts + // ID and VPIN are 100, sonar moves between positions 102 and 490 with slow profile. + // Profile may be Instant, Fast, Medium, Slow or Bounce. + + //ServoTurnout::create(100, 100, 490, 102, PCA9685::Slow); + + + // DCC Accessory turnout + // Parameters: same as command for DCC Accessory turnouts + // ID=3000 + // Decoder address=23 + // Decoder subaddress = 1 + + //DCCTurnout::create(3000, 23, 1); + + + // Creating a Sensor + // Parameters: As for the command, + // id = 164, + // Vpin = 164 (configured above as pin 0 of an MCP23017) + // Pullup enable = 1 (enabled) + + //Sensor::create(164, 164, 1); + + + // Way of creating lots of identical sensors in a range + + //for (int i=165; i<180; i++) + // Sensor::create(i, i, 1); + +} + +#endif