1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-07-31 03:13:45 +02:00

Compare commits

..

41 Commits

Author SHA1 Message Date
Harald Barth
00e3c80b44 Committing a SHA 2021-12-21 12:28:31 +00:00
Harald Barth
7a7ca6a436 rc8 2021-12-21 13:26:16 +01:00
Asbelos
cc1cdc35ec one-off error in CIPSEND drop 2021-12-21 13:24:00 +01:00
Harald Barth
92591c8a2e Committing a SHA 2021-12-02 07:36:44 +00:00
Harald Barth
0f728c1c15 3 diffenent defines to fix RCN-213 compat 2021-12-02 08:35:42 +01:00
Harald Barth
b5af39dfc9 Merge branch 'RCN213-fixes' into master 2021-12-02 08:31:33 +01:00
Harald Barth
4924cc7779 update version 2021-11-30 20:11:45 +01:00
Harald Barth
7d665fe577 update version 2021-11-30 20:08:58 +01:00
Harald Barth
5c18f4a19d Merge branch 'short-long-addr' into master 2021-11-30 20:07:52 +01:00
Harald Barth
58afea135c Committing a SHA 2021-11-30 18:57:58 +00:00
Harald Barth
9018ec9757 DISABLE_EEPROM explanation 2021-11-30 19:56:09 +01:00
Harald Barth
aa734b25e4 Merge branch 'disable-eeprom' into master 2021-11-30 19:45:33 +01:00
Harald Barth
67e48d34f4 do not include config.h direct 2021-11-30 19:40:31 +01:00
Florian Becker
419822ef06 Committing a SHA 2021-11-30 10:12:49 +00:00
Florian Becker
d4ee215ae6 fix typo (#194)
replace "manu" with "many"
2021-11-30 10:12:29 +00:00
Harald Barth
43538d3b32 smaller code 2021-11-26 19:32:45 +01:00
Harald Barth
fd43a9b88b defines to reverse accessories and turnouts renamed 2021-11-25 23:10:03 +01:00
Harald Barth
8a17965cd2 type and correct include 2021-11-25 19:55:48 +01:00
Harald Barth
3bddeeda3e better long/short addr handling under <R>; configurable long/short border 2021-11-25 00:10:11 +01:00
Neil McKechnie
f05b3d1730 Committing a SHA 2021-11-24 13:02:35 +00:00
Neil McKechnie
a2f8a8ec91 Merge branch 'master' of https://github.com/DCC-EX/CommandStation-EX 2021-11-24 13:00:31 +00:00
Neil McKechnie
746350b846 Update version to 3.2.0 rc5 2021-11-24 12:54:02 +00:00
Neil McKechnie
97f3450621 Simplify OLED driver initialisation.
Simplify the initialisation in the SSD1306Ascii driver, by removing some of the complex structures that were inherited from the library on which it is based.  This should also allow it to compile on the ESP32 platform.
2021-11-24 12:53:03 +00:00
Asbelos
2be3e276f9 Committing a SHA 2021-11-24 12:02:40 +00:00
Asbelos
88fa5ad37c VPIN in RMFT2::doSignal 2021-11-24 12:02:16 +00:00
Harald Barth
a4f746c00c Warn for broken configs 2021-11-22 00:41:47 +01:00
Neil McKechnie
106fb612dc Committing a SHA 2021-11-21 17:56:29 +00:00
Neil McKechnie
53113e981d Update IO_PCF8574.h
Correct handling of input in immediate mode,
2021-11-21 17:56:06 +00:00
Neil McKechnie
d7fd9e1538 Committing a SHA 2021-11-15 16:16:55 +00:00
Neil McKechnie
197228c3b0 Update version to 3.2.0 rc4 2021-11-15 16:13:54 +00:00
Neil McKechnie
620dcbf925 Update myHal.cpp_example.txt
Update examples
2021-11-15 14:58:12 +00:00
Neil McKechnie
82f121c8ef Some comment changes 2021-11-15 14:45:03 +00:00
Neil McKechnie
6c98f90151 Reduce I2C interrupt time
Reduce the time spent with interrupts disabled in I2CManager response code by enabling interrupts after the state machine has finished.
Also, some comment changes.
2021-11-15 14:30:27 +00:00
Neil McKechnie
c90ea0c6df Improve validation of parameters to non-HAL digital calls.
When testing CS in minimal HAL mode but with mySetup.h and myAutomation.h files present, I experienced freezing of the arduino because the standard pinMode, digitalWrite etc don't validate the pin number passed to them.  So I've added checks on the pin number to the configure, write and read functions in the minimal HAL.
2021-11-15 13:25:11 +00:00
Neil McKechnie
d08f14be3b Rename user module mySetup.cpp to myHal.cpp, and function mySetup() to halSetup() within it. 2021-11-15 12:50:02 +00:00
Neil McKechnie
fb97ba11de Committing a SHA 2021-11-12 00:09:59 +00:00
Neil McKechnie
ee5db61349 Update version.h to 3.2.0 rc3. 2021-11-12 00:06:29 +00:00
Neil McKechnie
b384d6c14d Move call to mySetup into IODevice::begin().
Ensure that HAL devices are created before use by moving the call to mySetup into IODevice::begin().  The need for this became evident when it was noted that RMFT (EX-RAIL) interacts with HAL devices during its initialisation, by enabling pull-ups on digital inputs.
Any
2021-11-12 00:05:16 +00:00
Neil McKechnie
58fe81bf06 Update EthernetInterface.h
Remove spurious character.
2021-11-11 23:59:50 +00:00
Harald Barth
1807189183 make it possible to disable EEPROM code to save flash space 2021-11-08 02:07:21 +01:00
Harald Barth
0e78cf6e55 Committing a SHA 2021-11-07 23:20:28 +00:00
36 changed files with 508 additions and 515 deletions

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@ config.h
.vscode/extensions.json
mySetup.h
mySetup.cpp
myHal.cpp
myAutomation.h
myFilter.cpp
myAutomation.h

View File

@@ -88,16 +88,9 @@ void setup()
// Start RMFT (ignored if no automnation)
RMFT::begin();
// Link to and call mySetup() function (if defined in the build in mySetup.cpp).
// The contents will depend on the user's system hardware configuration.
// The mySetup.cpp file is a standard C++ module so has access to all of the DCC++EX APIs.
extern __attribute__((weak)) void mySetup();
if (mySetup) {
mySetup();
}
// Invoke any DCC++EX commands in the form "SETUP("xxxx");"" found in optional file mySetup.h.
// This can be used to create turnouts, outputs, sensors etc. throught the normal text commands.
// This can be used to create turnouts, outputs, sensors etc. through the normal text commands.
#if __has_include ( "mySetup.h")
#define SETUP(cmd) serialParser.parse(F(cmd))
#include "mySetup.h"

32
DCC.cpp
View File

@@ -20,7 +20,9 @@
#include "DIAG.h"
#include "DCC.h"
#include "DCCWaveform.h"
#ifndef DISABLE_EEPROM
#include "EEStore.h"
#endif
#include "GITHUB_SHA.h"
#include "version.h"
#include "FSH.h"
@@ -56,20 +58,15 @@ void DCC::begin(const FSH * motorShieldName, MotorDriver * mainDriver, MotorDriv
// Initialise HAL layer before reading EEprom.
IODevice::begin();
#ifndef DISABLE_EEPROM
// Load stuff from EEprom
(void)EEPROM; // tell compiler not to warn this is unused
EEStore::init();
#endif
DCCWaveform::begin(mainDriver,progDriver);
#ifdef DCdistrict
DCCWaveform::mainTrack.pwmSpeed(0);
#else
DCCWaveform::mainTrack.pwmSpeed(255);
#endif
DCCWaveform::progTrack.pwmSpeed(255);
}
void DCC::setJoinRelayPin(byte joinRelayPin) {
joinRelay=joinRelayPin;
if (joinRelay!=UNUSED_PIN) {
@@ -79,13 +76,6 @@ void DCC::setJoinRelayPin(byte joinRelayPin) {
}
void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) {
#ifdef DCdistrict
if (cab == DCdistrict) {
DCCWaveform::mainTrack.pwmSpeed(tSpeed, tDirection);
}
#else
#error fooar
#endif
byte speedCode = (tSpeed & 0x7F) + tDirection * 128;
setThrottle2(cab, speedCode);
// retain speed for loco reminders
@@ -98,7 +88,7 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) {
uint8_t nB = 0;
// DIAG(F("setSpeedInternal %d %x"),cab,speedCode);
if (cab > 127)
if (cab > HIGHEST_SHORT_ADDR)
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
b[nB++] = lowByte(cab);
@@ -138,7 +128,7 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) {
byte b[4];
byte nB = 0;
if (cab > 127)
if (cab > HIGHEST_SHORT_ADDR)
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
b[nB++] = lowByte(cab);
if (byte1!=0) b[nB++] = byte1;
@@ -167,7 +157,7 @@ void DCC::setFn( int cab, int16_t functionNumber, bool on) {
//non reminding advanced binary bit set
byte b[5];
byte nB = 0;
if (cab > 127)
if (cab > HIGHEST_SHORT_ADDR)
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
b[nB++] = lowByte(cab);
if (functionNumber <= 127) {
@@ -276,7 +266,7 @@ void DCC::setAccessory(int address, byte number, bool activate) {
void DCC::writeCVByteMain(int cab, int cv, byte bValue) {
byte b[5];
byte nB = 0;
if (cab > 127)
if (cab > HIGHEST_SHORT_ADDR)
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
b[nB++] = lowByte(cab);
@@ -297,7 +287,7 @@ void DCC::writeCVBitMain(int cab, int cv, byte bNum, bool bValue) {
bValue = bValue % 2;
bNum = bNum % 8;
if (cab > 127)
if (cab > HIGHEST_SHORT_ADDR)
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
b[nB++] = lowByte(cab);
@@ -562,7 +552,7 @@ void DCC::setLocoId(int id,ACK_CALLBACK callback) {
callback(-1);
return;
}
if (id<=127)
if (id<=HIGHEST_SHORT_ADDR)
ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback);
else
ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback);
@@ -920,7 +910,7 @@ void DCC::ackManagerLoop() {
case COMBINELOCOID:
// ackManagerStash is cv17, ackManagerByte is CV 18
callback( ackManagerByte + ((ackManagerStash - 192) << 8));
callback( LONG_ADDR_MARKER | ( ackManagerByte + ((ackManagerStash - 192) << 8)));
return;
case ITSKIP:

10
DCC.h
View File

@@ -23,6 +23,16 @@
#include "MotorDrivers.h"
#include "FSH.h"
#include "defines.h"
#ifndef HIGHEST_SHORT_ADDR
#define HIGHEST_SHORT_ADDR 127
#else
#if HIGHEST_SHORT_ADDR > 127
#error short addr greater than 127 does not make sense
#endif
#endif
const uint16_t LONG_ADDR_MARKER = 0x4000;
typedef void (*ACK_CALLBACK)(int16_t result);
enum ackOp : byte

View File

@@ -56,7 +56,9 @@ const int16_t HASH_KEYWORD_ON = 2657;
const int16_t HASH_KEYWORD_DCC = 6436;
const int16_t HASH_KEYWORD_SLOW = -17209;
const int16_t HASH_KEYWORD_PROGBOOST = -6353;
#ifndef DISABLE_EEPROM
const int16_t HASH_KEYWORD_EEPROM = -7168;
#endif
const int16_t HASH_KEYWORD_LIMIT = 27413;
const int16_t HASH_KEYWORD_MAX = 16244;
const int16_t HASH_KEYWORD_MIN = 15978;
@@ -278,7 +280,9 @@ void DCCEXParser::parse(const FSH * cmd) {
void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
{
#ifndef DISABLE_EEPROM
(void)EEPROM; // tell compiler not to warn this is unused
#endif
if (Diag::CMD)
DIAG(F("PARSING:%s"), com);
int16_t p[MAX_COMMAND_PARAMS];
@@ -368,7 +372,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|| ((p[activep] & 0x01) != p[activep]) // invalid activate 0|1
) break;
// Honour the configuration option (config.h) which allows the <a> command to be reversed
#ifdef DCC_ACCESSORY_RCN_213
#ifdef DCC_ACCESSORY_COMMAND_REVERSE
DCC::setAccessory(address, subaddress,p[activep]==0);
#else
DCC::setAccessory(address, subaddress,p[activep]==1);
@@ -540,6 +544,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
// TODO Send stats of speed reminders table
return;
#ifndef DISABLE_EEPROM
case 'E': // STORE EPROM <E>
EEStore::store();
StringFormatter::send(stream, F("<e %d %d %d>\n"), EEStore::eeStore->data.nTurnouts, EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs);
@@ -549,7 +554,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
EEStore::clear();
StringFormatter::send(stream, F("<O>\n"));
return;
#endif
case ' ': // < >
StringFormatter::send(stream, F("\n"));
return;
@@ -864,11 +869,13 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
delay(50); // wait for the prescaller time to expire
break; // and <X> if we didnt restart
}
#ifndef DISABLE_EEPROM
case HASH_KEYWORD_EEPROM: // <D EEPROM NumEntries>
if (params >= 2)
EEStore::dump(p[1]);
return true;
#endif
case HASH_KEYWORD_SPEED28:
DCC::setGlobalSpeedsteps(28);
@@ -958,10 +965,21 @@ void DCCEXParser::callback_R(int16_t result)
commitAsyncReplyStream();
}
void DCCEXParser::callback_Rloco(int16_t result)
{
StringFormatter::send(getAsyncReplyStream(), F("<r %d>\n"), result);
commitAsyncReplyStream();
void DCCEXParser::callback_Rloco(int16_t result) {
const FSH * detail;
if (result<=0) {
detail=F("<r ERROR %d>\n");
} else {
bool longAddr=result & LONG_ADDR_MARKER; //long addr
if (longAddr)
result = result^LONG_ADDR_MARKER;
if (longAddr && result <= HIGHEST_SHORT_ADDR)
detail=F("<r LONG %d UNSUPPORTED>\n");
else
detail=F("<r %d>\n");
}
StringFormatter::send(getAsyncReplyStream(), detail, result);
commitAsyncReplyStream();
}
void DCCEXParser::callback_Wloco(int16_t result)

View File

@@ -65,9 +65,7 @@ void DCCWaveform::interruptHandler() {
byte sigProg=progTrackSyncMain? sigMain : signalTransform[progTrack.state];
// Set the signal state for both tracks
#ifndef DCdistrict
mainTrack.motorDriver->setSignal(sigMain);
#endif
progTrack.motorDriver->setSignal(sigProg);
// Move on in the state engine

View File

@@ -107,27 +107,6 @@ class DCCWaveform {
inline void setMaxAckPulseDuration(unsigned int i) {
maxAckPulseDuration = i;
}
#ifdef DCdistrict
inline void pwmSpeed(uint8_t tSpeed) {
// DCC Speed with 0,1 stop and speed steps 2 to 127
uint8_t brake;
if (!motorDriver)
return;
if (tSpeed <= 1)
brake = 255;
else if (tSpeed >= 127)
brake = 0;
else
brake = 2 * (128-tSpeed);
motorDriver->setBrake(brake);
}
inline void pwmSpeed(uint8_t tSpeed, bool tDirection) {
if (!motorDriver)
return;
pwmSpeed(tSpeed);
motorDriver->setSignal(tDirection);
}
#endif
private:

View File

@@ -18,6 +18,9 @@
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#include "defines.h"
#ifndef DISABLE_EEPROM
#include "EEStore.h"
#include "DIAG.h"
@@ -103,3 +106,4 @@ void EEStore::dump(int num) {
EEStore *EEStore::eeStore = NULL;
int EEStore::eeAddress = 0;
#endif

View File

@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef DISABLE_EEPROM
#ifndef EEStore_h
#define EEStore_h
@@ -52,3 +53,4 @@ struct EEStore{
};
#endif
#endif // DISABLE_EEPROM

View File

@@ -23,7 +23,7 @@
#ifndef EthernetInterface_h
#define EthernetInterface_h
#include "defines.h")
#include "defines.h"
#include "DCCEXParser.h"
#include <Arduino.h>
#include <avr/pgmspace.h>

View File

@@ -1 +1 @@
#define GITHUB_SHA "EX-RAIL-DC-20211109-2306"
#define GITHUB_SHA "7a7ca6a"

View File

@@ -107,11 +107,16 @@
* the loop() function is called, and may be adequate under some circumstances.
* The advantage of NOT using interrupts is that the impact of I2C upon the DCC waveform (when accurate timing mode isn't in use)
* becomes almost zero.
* This mechanism is under evaluation and should not be relied upon as yet.
*
*/
// Uncomment following line to enable Wire library instead of native I2C drivers
//#define I2C_USE_WIRE
// Uncomment following line to disable the use of interrupts by the native I2C drivers.
//#define I2C_NO_INTERRUPTS
// Default to use interrupts within the native I2C drivers.
#ifndef I2C_NO_INTERRUPTS
#define I2C_USE_INTERRUPTS
#endif

View File

@@ -129,6 +129,10 @@ uint8_t I2CManagerClass::read(uint8_t i2cAddress, uint8_t *readBuffer, uint8_t r
/***************************************************************************
* checkForTimeout() function, called from isBusy() and wait() to cancel
* requests that are taking too long to complete.
* This function doesn't fully work as intended so is not currently called.
* Instead we check for an I2C hang-up and report an error from
* I2CRB::wait(), but we aren't able to recover from the hang-up. Such faults
* may be caused by an I2C wire short for example.
***************************************************************************/
void I2CManagerClass::checkForTimeout() {
unsigned long currentMicros = micros();
@@ -163,7 +167,10 @@ void I2CManagerClass::loop() {
#if !defined(I2C_USE_INTERRUPTS)
handleInterrupt();
#endif
checkForTimeout();
// Timeout is now reported in I2CRB::wait(), not here.
// I've left the code, commented out, as a reminder to look at this again
// in the future.
//checkForTimeout();
}
/***************************************************************************
@@ -175,6 +182,9 @@ void I2CManagerClass::handleInterrupt() {
// Update hardware state machine
I2C_handleInterrupt();
// Enable interrupts to minimise effect on other interrupt code
interrupts();
// Check if current request has completed. If there's a current request
// and state isn't active then state contains the completion status of the request.
if (state != I2C_STATE_ACTIVE && currentRequest != NULL) {

View File

@@ -28,6 +28,10 @@
#define USE_FAST_IO
#endif
// Link to halSetup function. If not defined, the function reference will be NULL.
extern __attribute__((weak)) void halSetup();
extern __attribute__((weak)) void mySetup(); // Deprecated function name, output warning if it's declared
//==================================================================================================================
// Static methods
//------------------------------------------------------------------------------------------------------------------
@@ -57,6 +61,16 @@ void IODevice::begin() {
dev->_begin();
}
_initPhase = false;
// Check for presence of deprecated mySetup() function, and output warning.
if (mySetup)
DIAG(F("WARNING: mySetup() function should be renamed to halSetup()"));
// Call user's halSetup() function (if defined in the build in myHal.cpp).
// The contents will depend on the user's system hardware configuration.
// The myHal.cpp file is a standard C++ module so has access to all of the DCC++EX APIs.
if (halSetup)
halSetup();
}
// Overarching static loop() method for the IODevice subsystem. Works through the
@@ -148,6 +162,33 @@ void IODevice::_display() {
bool IODevice::configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) {
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);
#endif
return false;
}
// Read value from virtual pin.
int IODevice::read(VPIN vpin) {
for (IODevice *dev = _firstDevice; dev != 0; dev = dev->_nextDevice) {
if (dev->owns(vpin))
return dev->_read(vpin);
}
#ifdef DIAG_IO
DIAG(F("IODevice::read(): Vpin %d not found!"), (int)vpin);
#endif
return false;
}
// Read analogue value from virtual pin.
int IODevice::readAnalogue(VPIN vpin) {
for (IODevice *dev = _firstDevice; dev != 0; dev = dev->_nextDevice) {
if (dev->owns(vpin))
return dev->_readAnalogue(vpin);
}
#ifdef DIAG_IO
DIAG(F("IODevice::readAnalogue(): Vpin %d not found!"), (int)vpin);
#endif
return false;
}
@@ -258,48 +299,22 @@ bool IODevice::owns(VPIN id) {
return (id >= _firstVpin && id < _firstVpin + _nPins);
}
// Read value from virtual pin.
int IODevice::read(VPIN vpin) {
for (IODevice *dev = _firstDevice; dev != 0; dev = dev->_nextDevice) {
if (dev->owns(vpin))
return dev->_read(vpin);
}
#ifdef DIAG_IO
DIAG(F("IODevice::read(): Vpin %d not found!"), (int)vpin);
#endif
return false;
}
// Read analogue value from virtual pin.
int IODevice::readAnalogue(VPIN vpin) {
for (IODevice *dev = _firstDevice; dev != 0; dev = dev->_nextDevice) {
if (dev->owns(vpin))
return dev->_readAnalogue(vpin);
}
#ifdef DIAG_IO
DIAG(F("IODevice::readAnalogue(): Vpin %d not found!"), (int)vpin);
#endif
return false;
}
#else // !defined(IO_NO_HAL)
// Minimal implementations of public HAL interface, to support Arduino pin I/O and nothing more.
void IODevice::begin() { DIAG(F("NO HAL CONFIGURED!")); }
bool IODevice::configure(VPIN pin, ConfigTypeEnum, int, int p[]) {
bool IODevice::configure(VPIN pin, ConfigTypeEnum configType, int nParams, int p[]) {
if (configType!=CONFIGURE_INPUT || nParams!=1 || pin >= NUM_DIGITAL_PINS) return false;
#ifdef DIAG_IO
DIAG(F("Arduino _configurePullup Pin:%d Val:%d"), pin, p[0]);
#endif
if (p[0]) {
pinMode(pin, INPUT_PULLUP);
} else {
pinMode(pin, INPUT);
}
pinMode(pin, p[0] ? INPUT_PULLUP : INPUT);
return true;
}
void IODevice::write(VPIN vpin, int value) {
if (vpin >= NUM_DIGITAL_PINS) return;
digitalWrite(vpin, value);
pinMode(vpin, OUTPUT);
}
@@ -307,6 +322,7 @@ void IODevice::writeAnalogue(VPIN, int, uint8_t, uint16_t) {}
bool IODevice::isBusy(VPIN) { return false; }
bool IODevice::hasCallback(VPIN) { return false; }
int IODevice::read(VPIN vpin) {
if (vpin >= NUM_DIGITAL_PINS) return 0;
return !digitalRead(vpin); // Return inverted state (5v=0, 0v=1)
}
int IODevice::readAnalogue(VPIN vpin) {

View File

@@ -47,11 +47,15 @@ void DCCAccessoryDecoder::_begin() {
// Device-specific write function. State 1=closed, 0=thrown. Adjust for RCN-213 compliance
void DCCAccessoryDecoder::_write(VPIN id, int state) {
int packedAddress = _packedAddress + id - _firstVpin;
#ifdef DIAG_IO
DIAG(F("DCC Write Linear Address:%d State:%d"), packedAddress, state);
#endif
#if !defined(DCC_ACCESSORY_RCN_213)
#if defined(HAL_ACCESSORY_COMMAND_REVERSE)
state = !state;
#ifdef DIAG_IO
DIAG(F("DCC Write Linear Address:%d State:%d (inverted)"), packedAddress, state);
#endif
#else
#ifdef DIAG_IO
DIAG(F("DCC Write Linear Address:%d State:%d"), packedAddress, state);
#endif
#endif
DCC::setAccessory(ADDRESS(packedAddress), SUBADDRESS(packedAddress), state);
}

View File

@@ -75,7 +75,7 @@ private:
if (immediate) {
uint8_t buffer[1];
I2CManager.read(_I2CAddress, buffer, 1);
_portInputState = ((uint16_t)buffer) & 0xff;
_portInputState = buffer[0];
} else {
requestBlock.wait(); // Wait for preceding operation to complete
// Issue new request to read GPIO register
@@ -86,7 +86,7 @@ private:
// This function is invoked when an I/O operation on the requestBlock completes.
void _processCompletion(uint8_t status) override {
if (status == I2C_STATUS_OK)
_portInputState = ((uint16_t)inputBuffer[0]) & 0xff;
_portInputState = inputBuffer[0];
else
_portInputState = 0xff;
}

View File

@@ -31,7 +31,6 @@ bool MotorDriver::commonFaultPin=false;
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
brakePWM=false;
powerPin=power_pin;
getFastPin(F("POWER"),powerPin,fastPowerPin);
pinMode(powerPin, OUTPUT);
@@ -54,7 +53,7 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8
brakePin=invertBrake ? 0-brake_pin : brake_pin;
getFastPin(F("BRAKE"),brakePin,fastBrakePin);
pinMode(brakePin, OUTPUT);
// setBrake(0); moved out to DCC::begin
setBrake(false);
}
else brakePin=UNUSED_PIN;
@@ -90,11 +89,8 @@ void MotorDriver::setPower(bool on) {
if (on) {
// toggle brake before turning power on - resets overcurrent error
// on the Pololu board if brake is wired to ^D2.
// Yes, this is an ugly special case
if (brakePin == 4 && invertBrake) {
setBrake(255);
setBrake(0);
}
setBrake(true);
setBrake(false);
setHIGH(fastPowerPin);
}
else setLOW(fastPowerPin);
@@ -108,29 +104,10 @@ void MotorDriver::setPower(bool on) {
// (HIGH == release brake) and setBrake does
// compensate for that.
//
void MotorDriver::setBrake(uint8_t intensity) {
void MotorDriver::setBrake(bool on) {
if (brakePin == UNUSED_PIN) return;
DIAG(F("Brake pin=%d val=%d"),brakePin,intensity);
if (invertBrake)
intensity = 255 - intensity;
if (intensity == 255) {
if (brakePWM) {
digitalWrite(brakePin, HIGH);
brakePWM = false;
} else
setHIGH(fastBrakePin);
return;
}
if (intensity == 0) {
if (brakePWM) {
digitalWrite(brakePin, LOW);
brakePWM = false;
} else
setLOW(fastBrakePin);
return;
}
brakePWM = true;
analogWrite(brakePin, intensity);
if (on ^ invertBrake) setHIGH(fastBrakePin);
else setLOW(fastBrakePin);
}
void MotorDriver::setSignal( bool high) {

View File

@@ -46,7 +46,7 @@ class MotorDriver {
byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin);
virtual void setPower( bool on);
virtual void setSignal( bool high);
virtual void setBrake(uint8_t);
virtual void setBrake( bool on);
virtual int getCurrentRaw();
virtual unsigned int raw2mA( int raw);
virtual int mA2raw( unsigned int mA);
@@ -69,7 +69,6 @@ class MotorDriver {
FASTPIN fastPowerPin,fastSignalPin, fastSignalPin2, fastBrakePin,fastFaultPin;
bool dualSignal; // true to use signalPin2
bool invertBrake; // brake pin passed as negative means pin is inverted
bool brakePWM; // brake is used for PWM
float senseFactor;
int senseOffset;
unsigned int tripMilliamps;

View File

@@ -48,11 +48,6 @@
new MotorDriver(3, 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 2000, UNUSED_PIN)
// TO GET THE DC district feature use this shield definition
#define STD_DCC_DC_SHIELD F("STD_DCC_DC_SHIELD"), \
new MotorDriver(3, 12, UNUSED_PIN, 9, A0, 2.99, 2000, UNUSED_PIN), \
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 2000, UNUSED_PIN)
// Pololu Motor Shield
#define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
new MotorDriver( 9, 7, UNUSED_PIN, -4, A0, 18, 3000, 12), \

View File

@@ -82,7 +82,9 @@ the state of any outputs being monitored or controlled by a separate interface o
**********************************************************************/
#include "Outputs.h"
#ifndef DISABLE_EEPROM
#include "EEStore.h"
#endif
#include "StringFormatter.h"
#include "IODevice.h"
@@ -102,10 +104,11 @@ void Output::activate(uint16_t s){
data.active = s; // if s>0, set status to active, else inactive
// set state of output pin to HIGH or LOW depending on whether bit zero of iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW)
IODevice::write(data.pin, s ^ data.invert);
#ifndef DISABLE_EEPROM
// Update EEPROM if output has been stored.
if(EEStore::eeStore->data.nOutputs > 0 && num > 0)
EEPROM.put(num, data.oStatus);
#endif
}
///////////////////////////////////////////////////////////////////////////////
@@ -141,7 +144,7 @@ bool Output::remove(uint16_t n){
///////////////////////////////////////////////////////////////////////////////
// Static function to load configuration and state of all Outputs from EEPROM
#ifndef DISABLE_EEPROM
void Output::load(){
struct OutputData data;
Output *tt;
@@ -176,6 +179,7 @@ void Output::store(){
}
}
#endif
///////////////////////////////////////////////////////////////////////////////
// Static function to create an Output object

View File

@@ -48,8 +48,10 @@ public:
bool isActive();
static Output* get(uint16_t);
static bool remove(uint16_t);
#ifndef DISABLE_EEPROM
static void load();
static void store();
#endif
static Output *create(uint16_t, VPIN, int, int=0);
static Output *firstOutput;
struct OutputData data;

View File

@@ -17,7 +17,7 @@ Both CommandStation-EX and BaseStation-Classic support much of the NMRA Digital
* Control of all cab functions F0-F28 and F29-F68
* Main Track: Write configuration variable bytes and set/clear specific configuration variable (CV) bits (aka Programming on Main or POM)
* Programming Track: Same as the main track with the addition of reading configuration variable bytes
* And manu more custom features. see [What's new in CommandStation-EX?](#whats-new-in-commandstation-ex)
* And many more custom features. see [What's new in CommandStation-EX?](#whats-new-in-commandstation-ex)
# Whats in this Repository?

View File

@@ -720,10 +720,10 @@ void RMFT2::kill(const FSH * reason, int operand) {
byte opcode=GET_OPCODE;
if (opcode==OPCODE_ENDEXRAIL) return;
if (opcode!=OPCODE_SIGNAL) continue;
byte redpin=GET_OPERAND(0);
VPIN redpin=GET_OPERAND(0);
if (redpin!=id)continue;
byte amberpin=GET_OPERAND(1);
byte greenpin=GET_OPERAND(2);
VPIN amberpin=GET_OPERAND(1);
VPIN greenpin=GET_OPERAND(2);
// If amberpin is zero, synthesise amber from red+green
IODevice::write(redpin,red || (amber && (amberpin==0)));
if (amberpin) IODevice::write(amberpin,amber);

View File

@@ -89,28 +89,93 @@ const uint8_t FLASH SSD1306AsciiWire::blankPixels[30] =
{0x40, // First byte specifies data mode
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
//==============================================================================
// this section is based on https://github.com/adafruit/Adafruit_SSD1306
/** Initialization commands for a 128x32 or 128x64 SSD1306 oled display. */
const uint8_t FLASH SSD1306AsciiWire::Adafruit128xXXinit[] = {
// Init sequence for Adafruit 128x32/64 OLED module
0x00, // Set to command mode
SSD1306_DISPLAYOFF,
SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80
SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 (initially)
SSD1306_SETDISPLAYOFFSET, 0x0, // no offset
SSD1306_SETSTARTLINE | 0x0, // line #0
SSD1306_CHARGEPUMP, 0x14, // internal vcc
SSD1306_MEMORYMODE, 0x02, // page mode
SSD1306_SEGREMAP | 0x1, // column 127 mapped to SEG0
SSD1306_COMSCANDEC, // column scan direction reversed
SSD1306_SETCOMPINS, 0X12, // set COM pins
SSD1306_SETCONTRAST, 0x7F, // contrast level 127
SSD1306_SETPRECHARGE, 0xF1, // pre-charge period (1, 15)
SSD1306_SETVCOMDETECT, 0x40, // vcomh regulator level
SSD1306_DISPLAYALLON_RESUME,
SSD1306_NORMALDISPLAY,
SSD1306_DISPLAYON
};
//------------------------------------------------------------------------------
// This section is based on https://github.com/stanleyhuangyc/MultiLCD
/** Initialization commands for a 128x64 SH1106 oled display. */
const uint8_t FLASH SSD1306AsciiWire::SH1106_132x64init[] = {
0x00, // Set to command mode
SSD1306_DISPLAYOFF,
SSD1306_SETDISPLAYCLOCKDIV, 0X80, // set osc division
SSD1306_SETMULTIPLEX, 0x3F, // ratio 64
SSD1306_SETDISPLAYOFFSET, 0X00, // set display offset
SSD1306_SETSTARTPAGE | 0X0, // set page address
SSD1306_SETSTARTLINE | 0x0, // set start line
SH1106_SET_PUMP_MODE, SH1106_PUMP_ON, // set charge pump enable
SSD1306_SEGREMAP | 0X1, // set segment remap
SSD1306_COMSCANDEC, // Com scan direction
SSD1306_SETCOMPINS, 0X12, // set COM pins
SSD1306_SETCONTRAST, 0x80, // 128
SSD1306_SETPRECHARGE, 0X1F, // set pre-charge period
SSD1306_SETVCOMDETECT, 0x40, // set vcomh
SH1106_SET_PUMP_VOLTAGE | 0X2, // 8.0 volts
SSD1306_NORMALDISPLAY, // normal / reverse
SSD1306_DISPLAYON
};
//==============================================================================
// SSD1306AsciiWire Method Definitions
//------------------------------------------------------------------------------
// Constructor
SSD1306AsciiWire::SSD1306AsciiWire(int width, int height) {
m_displayWidth = width;
m_displayHeight = height;
// Set size in characters in base class
lcdRows = height / 8;
lcdCols = width / 6;
m_col = 0;
m_row = 0;
m_colOffset = 0;
I2CManager.begin();
I2CManager.setClock(400000L); // Set max supported I2C speed
for (byte address = 0x3c; address <= 0x3d; address++) {
if (I2CManager.exists(address)) {
m_i2cAddr = address;
if (m_displayWidth==132 && m_displayHeight==64) {
// SH1106 display. This uses 128x64 centered within a 132x64 OLED.
m_colOffset = 2;
I2CManager.write_P(address, SH1106_132x64init, sizeof(SH1106_132x64init));
} else if (m_displayWidth==128 && (m_displayHeight==64 || m_displayHeight==32)) {
// SSD1306 128x64 or 128x32
I2CManager.write_P(address, Adafruit128xXXinit, sizeof(Adafruit128xXXinit));
if (m_displayHeight == 32)
I2CManager.write(address, 5, 0, // Set command mode
SSD1306_SETMULTIPLEX, 0x1F, // ratio 32
SSD1306_SETCOMPINS, 0x02); // sequential COM pins, disable remap
} else {
DIAG(F("OLED configuration option not recognised"));
return;
}
// Device found
DIAG(F("%dx%d OLED display configured on I2C:x%x"), width, height, address);
if (width == 132)
begin(&SH1106_132x64, address);
else if (height == 32)
begin(&Adafruit128x32, address);
else
begin(&Adafruit128x64, address);
// Set singleton address
lcdDisplay = this;
clear();
@@ -132,23 +197,6 @@ void SSD1306AsciiWire::clearNative() {
}
}
// Initialise device
void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) {
m_i2cAddr = i2cAddr;
m_col = 0;
m_row = 0;
const uint8_t* table = (const uint8_t*)GETFLASHW(&dev->initcmds);
uint8_t size = GETFLASH(&dev->initSize);
m_displayWidth = GETFLASH(&dev->lcdWidth);
m_displayHeight = GETFLASH(&dev->lcdHeight);
m_colOffset = GETFLASH(&dev->colOffset);
I2CManager.write_P(m_i2cAddr, table, size);
if (m_displayHeight == 32)
I2CManager.write(m_i2cAddr, 5, 0, // Set command mode
SSD1306_SETMULTIPLEX, 0x1F, // ratio 32
SSD1306_SETCOMPINS, 0x02); // sequential COM pins, disable remap
}
//------------------------------------------------------------------------------
// Set cursor position (by text line)
@@ -209,82 +257,6 @@ size_t SSD1306AsciiWire::writeNative(uint8_t ch) {
return 1;
}
//==============================================================================
// this section is based on https://github.com/adafruit/Adafruit_SSD1306
/** Initialization commands for a 128x32 or 128x64 SSD1306 oled display. */
const uint8_t FLASH SSD1306AsciiWire::Adafruit128xXXinit[] = {
// Init sequence for Adafruit 128x32/64 OLED module
0x00, // Set to command mode
SSD1306_DISPLAYOFF,
SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80
SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 (initially)
SSD1306_SETDISPLAYOFFSET, 0x0, // no offset
SSD1306_SETSTARTLINE | 0x0, // line #0
SSD1306_CHARGEPUMP, 0x14, // internal vcc
SSD1306_MEMORYMODE, 0x02, // page mode
SSD1306_SEGREMAP | 0x1, // column 127 mapped to SEG0
SSD1306_COMSCANDEC, // column scan direction reversed
SSD1306_SETCOMPINS, 0X12, // set COM pins
SSD1306_SETCONTRAST, 0x7F, // contrast level 127
SSD1306_SETPRECHARGE, 0xF1, // pre-charge period (1, 15)
SSD1306_SETVCOMDETECT, 0x40, // vcomh regulator level
SSD1306_DISPLAYALLON_RESUME,
SSD1306_NORMALDISPLAY,
SSD1306_DISPLAYON
};
/** Initialize a 128x32 SSD1306 oled display. */
const DevType FLASH SSD1306AsciiWire::Adafruit128x32 = {
Adafruit128xXXinit,
sizeof(Adafruit128xXXinit),
128,
32,
0
};
/** Initialize a 128x64 oled display. */
const DevType FLASH SSD1306AsciiWire::Adafruit128x64 = {
Adafruit128xXXinit,
sizeof(Adafruit128xXXinit),
128,
64,
0
};
//------------------------------------------------------------------------------
// This section is based on https://github.com/stanleyhuangyc/MultiLCD
/** Initialization commands for a 128x64 SH1106 oled display. */
const uint8_t FLASH SSD1306AsciiWire::SH1106_132x64init[] = {
0x00, // Set to command mode
SSD1306_DISPLAYOFF,
SSD1306_SETDISPLAYCLOCKDIV, 0X80, // set osc division
SSD1306_SETMULTIPLEX, 0x3F, // ratio 64
SSD1306_SETDISPLAYOFFSET, 0X00, // set display offset
SSD1306_SETSTARTPAGE | 0X0, // set page address
SSD1306_SETSTARTLINE | 0x0, // set start line
SH1106_SET_PUMP_MODE, SH1106_PUMP_ON, // set charge pump enable
SSD1306_SEGREMAP | 0X1, // set segment remap
SSD1306_COMSCANDEC, // Com scan direction
SSD1306_SETCOMPINS, 0X12, // set COM pins
SSD1306_SETCONTRAST, 0x80, // 128
SSD1306_SETPRECHARGE, 0X1F, // set pre-charge period
SSD1306_SETVCOMDETECT, 0x40, // set vcomh
SH1106_SET_PUMP_VOLTAGE | 0X2, // 8.0 volts
SSD1306_NORMALDISPLAY, // normal / reverse
SSD1306_DISPLAYON
};
/** Initialize a 132x64 oled SH1106 display. */
const DevType FLASH SSD1306AsciiWire::SH1106_132x64 = {
SH1106_132x64init,
sizeof(SH1106_132x64init),
128,
64,
2 // SH1106 is a 132x64 controller but most OLEDs are only attached
// to columns 2-129.
};
//------------------------------------------------------------------------------

View File

@@ -32,21 +32,6 @@
//#define NOLOWERCASE
//------------------------------------------------------------------------------
// Device initialization structure.
struct DevType {
/* Pointer to initialization command bytes. */
const uint8_t* initcmds;
/* Number of initialization bytes */
const uint8_t initSize;
/* Width of the display in pixels */
const uint8_t lcdWidth;
/** Height of the display in pixels. */
const uint8_t lcdHeight;
/* Column offset RAM to display. Used to pick start column of SH1106. */
const uint8_t colOffset;
};
// Constructor
class SSD1306AsciiWire : public LCDDisplay {
public:
@@ -55,25 +40,17 @@ class SSD1306AsciiWire : public LCDDisplay {
SSD1306AsciiWire(int width, int height);
// Initialize the display controller.
void begin(const DevType* dev, uint8_t i2cAddr);
void begin(uint8_t i2cAddr);
// Clear the display and set the cursor to (0, 0).
void clearNative() override;
// Set cursor to start of specified text line
void setRowNative(byte line) override;
// Initialize the display controller.
void init(const DevType* dev);
// Write one character to OLED
size_t writeNative(uint8_t c) override;
// Display characteristics / initialisation
static const DevType FLASH Adafruit128x32;
static const DevType FLASH Adafruit128x64;
static const DevType FLASH SH1106_132x64;
bool isBusy() override { return requestBlock.isBusy(); }
private:

View File

@@ -68,7 +68,9 @@ decide to ignore the <q ID> return and only react to <Q ID> triggers.
#include "StringFormatter.h"
#include "Sensors.h"
#ifndef DISABLE_EEPROM
#include "EEStore.h"
#endif
#include "IODevice.h"
@@ -275,7 +277,7 @@ bool Sensor::remove(int n){
}
///////////////////////////////////////////////////////////////////////////////
#ifndef DISABLE_EEPROM
void Sensor::load(){
struct SensorData data;
Sensor *tt;
@@ -303,7 +305,7 @@ void Sensor::store(){
EEStore::eeStore->data.nSensors++;
}
}
#endif
///////////////////////////////////////////////////////////////////////////////
Sensor *Sensor::firstSensor=NULL;
@@ -314,4 +316,4 @@ unsigned long Sensor::lastReadCycle=0;
Sensor *Sensor::firstPollSensor = NULL;
Sensor *Sensor::lastSensor = NULL;
bool Sensor::inputChangeCallbackRegistered = false;
#endif
#endif

View File

@@ -68,8 +68,10 @@ public:
Sensor *nextSensor;
void setState(int state);
#ifndef DISABLE_EEPROM
static void load();
static void store();
#endif
static Sensor *create(int id, VPIN vpin, int pullUp);
static Sensor* get(int id);
static bool remove(int id);

View File

@@ -22,7 +22,9 @@
#include "defines.h" // includes config.h
#ifndef DISABLE_EEPROM
#include "EEStore.h"
#endif
#include "StringFormatter.h"
#include "RMFT2.h"
#include "Turnouts.h"
@@ -141,11 +143,13 @@
if (ok) {
turnoutlistHash++; // let withrottle know something changed
#ifndef DISABLE_EEPROM
// 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, tt->_turnoutData.flags);
EEPROM.put(tt->_eepromAddress, tt->_turnoutData.flags);
#endif
#if defined(RMFT_ACTIVE)
RMFT2::turnoutEvent(id, closeFlag);
@@ -159,6 +163,7 @@
return ok;
}
#ifndef DISABLE_EEPROM
// Load all turnout objects
/* static */ void Turnout::load() {
for (uint16_t i=0; i<EEStore::eeStore->data.nTurnouts; i++) {
@@ -212,7 +217,7 @@
#endif
return tt;
}
#endif
// Display, on the specified stream, the current state of the turnout (1=thrown or 0=closed).
/* static */ void Turnout::printState(uint16_t id, Print *stream) {
Turnout *tt = get(id);
@@ -277,6 +282,7 @@
// Load a Servo turnout definition from EEPROM. The common Turnout data has already been read at this point.
Turnout *ServoTurnout::load(struct TurnoutData *turnoutData) {
#ifndef DISABLE_EEPROM
ServoTurnoutData servoTurnoutData;
// Read class-specific data from EEPROM
EEPROM.get(EEStore::pointer(), servoTurnoutData);
@@ -286,6 +292,10 @@
Turnout *tt = ServoTurnout::create(turnoutData->id, servoTurnoutData.vpin, servoTurnoutData.thrownPosition,
servoTurnoutData.closedPosition, servoTurnoutData.profile, turnoutData->closed);
return tt;
#else
(void)turnoutData;
return NULL;
#endif
}
// For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed
@@ -308,6 +318,7 @@
}
void ServoTurnout::save() {
#ifndef DISABLE_EEPROM
// Write turnout definition and current position to EEPROM
// First write common servo data, then
// write the servo-specific data
@@ -315,6 +326,7 @@
EEStore::advance(sizeof(_turnoutData));
EEPROM.put(EEStore::pointer(), _servoTurnoutData);
EEStore::advance(sizeof(_servoTurnoutData));
#endif
}
/*************************************************************************************
@@ -367,6 +379,7 @@
// Load a DCC turnout definition from EEPROM. The common Turnout data has already been read at this point.
/* static */ Turnout *DCCTurnout::load(struct TurnoutData *turnoutData) {
#ifndef DISABLE_EEPROM
DCCTurnoutData dccTurnoutData;
// Read class-specific data from EEPROM
EEPROM.get(EEStore::pointer(), dccTurnoutData);
@@ -376,6 +389,10 @@
DCCTurnout *tt = new DCCTurnout(turnoutData->id, dccTurnoutData.address, dccTurnoutData.subAddress);
return tt;
#else
(void)turnoutData;
return NULL;
#endif
}
void DCCTurnout::print(Print *stream) {
@@ -396,6 +413,7 @@
}
void DCCTurnout::save() {
#ifndef DISABLE_EEPROM
// Write turnout definition and current position to EEPROM
// First write common servo data, then
// write the servo-specific data
@@ -403,6 +421,7 @@
EEStore::advance(sizeof(_turnoutData));
EEPROM.put(EEStore::pointer(), _dccTurnoutData);
EEStore::advance(sizeof(_dccTurnoutData));
#endif
}
@@ -441,6 +460,7 @@
// Load a VPIN turnout definition from EEPROM. The common Turnout data has already been read at this point.
/* static */ Turnout *VpinTurnout::load(struct TurnoutData *turnoutData) {
#ifndef DISABLE_EEPROM
VpinTurnoutData vpinTurnoutData;
// Read class-specific data from EEPROM
EEPROM.get(EEStore::pointer(), vpinTurnoutData);
@@ -450,6 +470,10 @@
VpinTurnout *tt = new VpinTurnout(turnoutData->id, vpinTurnoutData.vpin, turnoutData->closed);
return tt;
#else
(void)turnoutData;
return NULL;
#endif
}
// Report 1 for thrown, 0 for closed.
@@ -465,6 +489,7 @@
}
void VpinTurnout::save() {
#ifndef DISABLE_EEPROM
// Write turnout definition and current position to EEPROM
// First write common servo data, then
// write the servo-specific data
@@ -472,6 +497,7 @@
EEStore::advance(sizeof(_turnoutData));
EEPROM.put(EEStore::pointer(), _vpinTurnoutData);
EEStore::advance(sizeof(_vpinTurnoutData));
#endif
}

View File

@@ -155,13 +155,14 @@ public:
inline static Turnout *first() { return _firstTurnout; }
#ifndef DISABLE_EEPROM
// Load all turnout definitions.
static void load();
// Load one turnout definition
static Turnout *loadTurnout();
// Save all turnout definitions
static void store();
#endif
static void printAll(Print *stream) {
for (Turnout *tt = _firstTurnout; tt != 0; tt = tt->_nextTurnout)
tt->printState(stream);

View File

@@ -51,6 +51,8 @@
#include "version.h"
#include "RMFT2.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define LOOPLOCOS(THROTTLECHAR, CAB) for (int loco=0;loco<MAX_MY_LOCO;loco++) \
if ((myLocos[loco].throttle==THROTTLECHAR || '*'==THROTTLECHAR) && (CAB<0 || myLocos[loco].cab==CAB))
@@ -409,7 +411,7 @@ void WiThrottle::checkHeartbeat() {
}
char WiThrottle::LorS(int cab) {
return (cab<127)?'S':'L';
return (cab<=HIGHEST_SHORT_ADDR)?'S':'L';
}
// Drive Away feature. Callback handling
@@ -421,13 +423,26 @@ char WiThrottle::stashThrottleChar;
void WiThrottle::getLocoCallback(int16_t locoid) {
stashStream->mark(stashClient);
if (locoid<0) StringFormatter::send(stashStream,F("HMNo loco found on prog track\n"));
if (locoid<=0)
StringFormatter::send(stashStream,F("HMNo loco found on prog track\n"));
else {
char addcmd[20]={'M',stashThrottleChar,'+',LorS(locoid) };
itoa(locoid,addcmd+4,10);
stashInstance->multithrottle(stashStream, (byte *)addcmd);
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
DCC::setProgTrackSyncMain(true); // <1 JOIN> so we can drive loco away
// short or long
char addrchar;
if (locoid & LONG_ADDR_MARKER) { // long addr
locoid = locoid ^ LONG_ADDR_MARKER;
addrchar = 'L';
} else
addrchar = 'S';
if (addrchar == 'L' && locoid <= HIGHEST_SHORT_ADDR )
StringFormatter::send(stashStream,F("HMLong addr <= " STR(HIGHEST_SHORT_ADDR) " not supported\n"));
else {
char addcmd[20]={'M',stashThrottleChar,'+', addrchar};
itoa(locoid,addcmd+4,10);
stashInstance->multithrottle(stashStream, (byte *)addcmd);
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
DCC::setProgTrackSyncMain(true); // <1 JOIN> so we can drive loco away
}
}
stashStream->commit();
}

View File

@@ -246,7 +246,7 @@ WifiInboundHandler::INBOUND_STATE WifiInboundHandler::loop2() {
void WifiInboundHandler::purgeCurrentCIPSEND() {
// A CIPSEND was sent but errored... or the client closed just toss it away
DIAG(F("Wifi: DROPPING CIPSEND=%d,%d"),clientPendingCIPSEND,currentReplySize);
for (int i=0;i<=currentReplySize;i++) outboundRing->read();
for (int i=0;i<currentReplySize;i++) outboundRing->read();
pendingCipsend=false;
clientPendingCIPSEND=-1;
}

View File

@@ -23,10 +23,6 @@ The configuration file for DCC-EX Command Station
**********************************************************************/
// SPECIAL CONFIG WITH MAIN AS DC (PWM) track reacting on cab #2.
//
#define DCdistrict 2
/////////////////////////////////////////////////////////////////////////////////////
// NOTE: Before connecting these boards and selecting one in this software
// check the quick install guides!!! Some of these boards require a voltage
@@ -42,11 +38,10 @@ The configuration file for DCC-EX Command Station
// FIREBOX_MK1 : The Firebox MK1
// FIREBOX_MK1S : The Firebox MK1S
// IBT_2_WITH_ARDUINO : Arduino Motor Shield for PROG and IBT-2 for MAIN
// STD_DCC_DC_SHIELD : as STANDARD but with brake so MAIN can be run as DC (PWM)
// |
// +-----------------------v
//
#define MOTOR_SHIELD_TYPE STD_DCC_DC_SHIELD
#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD
/////////////////////////////////////////////////////////////////////////////////////
//
// The IP port to talk to a WIFI or Ethernet shield.
@@ -133,6 +128,27 @@ The configuration file for DCC-EX Command Station
// Define scroll mode as 0, 1 or 2
#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 works.
//
// #define DISABLE_EEPROM
/////////////////////////////////////////////////////////////////////////////////////
// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address
// is 127 and the first long address is 128. There are manufacturers which have
// another view. Lenz CS for example have considered addresses long from 100. If
// you want to change to that mode, do
//#define HIGHEST_SHORT_ADDR 99
// If you want to run all your locos addressed long format, you could even do a
//#define HIGHEST_SHORT_ADDR 0
// We do not support to use the same address, for example 100(long) and 100(short)
// at the same time, there must be a border.
/////////////////////////////////////////////////////////////////////////////////////
//
// DEFINE TURNOUTS/ACCESSORIES FOLLOW NORM RCN-213
@@ -146,10 +162,20 @@ The configuration file for DCC-EX Command Station
// don't add it to your config.h.
//#define DCC_TURNOUTS_RCN_213
// The following #define likewise inverts the behaviour of the <a> command
// for triggering DCC Accessory Decoders, so that <a addr subaddr 0> generates a
// By default, the driver which defines a DCC accessory decoder
// does send out the same state change on the DCC packet as it
// receives. This means a VPIN state=1 sends D=1 (close turnout
// or signal green) in the DCC packet. This can be reversed if
// necessary.
//#define HAL_ACCESSORY_COMMAND_REVERSE
// If you have issues with that the direction of the accessory commands is
// reversed (for example when converting from another CS to DCC-EX) then
// you can use this to revese the sense of all accessory commmands sent
// over DCC++. This #define likewise inverts the behaviour of the <a> command
// for triggering DCC Accessory Decoders, so that <a addr subaddr 0> generates a
// DCC packet with D=1 (close turnout) and <a addr subaddr 1> generates D=0
// (throw turnout).
//#define DCC_ACCESSORY_RCN_213
//#define DCC_ACCESSORY_COMMAND_REVERSE
/////////////////////////////////////////////////////////////////////////////////////

View File

@@ -39,19 +39,29 @@
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO))
#define BIG_RAM
#endif
#if ENABLE_WIFI && defined(BIG_RAM)
#define WIFI_ON true
#ifndef WIFI_CHANNEL
#define WIFI_CHANNEL 1
#endif
#if ENABLE_WIFI
#if defined(BIG_RAM)
#define WIFI_ON true
#ifndef WIFI_CHANNEL
#define WIFI_CHANNEL 1
#endif
#else
#warning You have defined that you want WIFI but your hardware has not enough memory to do that, so WIFI DISABLED
#define WIFI_ON false
#endif
#else
#define WIFI_ON false
#define WIFI_ON false
#endif
#if ENABLE_ETHERNET && defined(BIG_RAM)
#define ETHERNET_ON true
#if ENABLE_ETHERNET
#if defined(BIG_RAM)
#define ETHERNET_ON true
#else
#warning You have defined that you want ETHERNET but your hardware has not enough memory to do that, so ETHERNET DISABLED
#define ETHERNET_ON false
#endif
#else
#define ETHERNET_ON false
#define ETHERNET_ON false
#endif
#if WIFI_ON && ETHERNET_ON
@@ -65,8 +75,12 @@
//
#define WIFI_SERIAL_LINK_SPEED 115200
#if __has_include ( "myAutomation.h") && defined(BIG_RAM)
#define RMFT_ACTIVE
#if __has_include ( "myAutomation.h")
#if defined(BIG_RAM) || defined(DISABLE_EEPROM)
#define RMFT_ACTIVE
#else
#warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED
#endif
#endif
#endif
#endif

161
myHal.cpp_example.txt Normal file
View File

@@ -0,0 +1,161 @@
// Sample myHal.cpp file.
//
// To use this file, copy it to myHal.cpp and uncomment the directives and/or
// edit them to satisfy your requirements. If you only want to use up to
// two MCP23017 GPIO Expander modules and/or up to two PCA9685 Servo modules,
// then you don't need this file as DCC++EX configures these for free!
// Note that if the file has a .cpp extension it WILL be compiled into the build
// and the halSetup() function WILL be invoked.
//
// To prevent this, temporarily rename the file to myHal.txt or similar.
//
// Include devices you need.
#include "IODevice.h"
#include "IO_HCSR04.h" // Ultrasonic range sensor
#include "IO_VL53L0X.h" // Laser time-of-flight sensor
#include "IO_DFPlayer.h" // MP3 sound player
//==========================================================================
// The function halSetup() is invoked from CS if it exists within the build.
// The setup calls are included between the open and close braces "{ ... }".
// Comments (lines preceded by "//") are optional.
//==========================================================================
void halSetup() {
//=======================================================================
// 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::create(100, 16, 0x40);
//=======================================================================
// The following directive defines an MCP23017 16-port I2C GPIO Extender module.
//=======================================================================
// The parameters are:
// First Vpin=196
// Number of VPINs=16 (numbered 196-211)
// I2C address of module=0x22
//MCP23017::create(196, 16, 0x22);
// 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::create(196, 16, 0x22, 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
//MCP23008::create(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::create(200, 8, 0x23);
// Alternative form using INT pin (see above)
//PCF8574::create(200, 8, 0x23, 40);
//=======================================================================
// The following directive defines an HCSR04 ultrasonic ranging 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::create(2000, 30, 31, 20, 25);
//HCSR04::create(2001, 30, 32, 20, 25);
//=======================================================================
// The following directive defines a single VL53L0X Time-of-Flight range sensor.
//=======================================================================
// The parameters are:
// VPIN=5000
// Number of VPINs=1
// I2C address=0x29 (default for this chip)
// Minimum trigger range=200mm (VPIN goes to 1 when <20cm)
// Maximum trigger range=250mm (VPIN goes to 0 when >25cm)
//VL53L0X::create(5000, 1, 0x29, 200, 250);
// For multiple VL53L0X modules, add another parameter which is a VPIN connected to the
// module's XSHUT pin. This allows the modules to be configured, at start,
// with distinct I2C addresses. In this case, the address 0x29 is only used during
// initialisation to configure each device in turn with the desired unique I2C address.
// The examples below have the modules' XSHUT pins connected to the first two pins of
// the first MCP23017 module (164 and 165), but Arduino pins may be used instead.
// The first module here is given I2C address 0x30 and the second is 0x31.
//VL53L0X::create(5000, 1, 0x30, 200, 250, 164);
//VL53L0X::create(5001, 1, 0x31, 200, 250, 165);
//=======================================================================
// Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module.
//=======================================================================
// Parameters:
// 10000 = first VPIN allocated.
// 10 = number of VPINs allocated.
// Serial1 = name of serial port (usually Serial1 or Serial2).
// With these parameters, up to 10 files may be played on pins 10000-10009.
// Play is started from EX-RAIL with SET(10000) for first mp3 file, SET(10001)
// for second file, etc. Play may also be initiated by writing an analogue
// value to the first pin, e.g. SERVO(10000,23,0) will play the 23rd mp3 file.
// SERVO(10000,23,30) will do the same thing, as well as setting the volume to
// 30 (maximum value).
// Play is stopped by RESET(10000) (or any other allocated VPIN).
// Volume may also be set by writing an analogue value to the second pin for the player,
// e.g. SERVO(10001,30,0) sets volume to maximum (30).
// The EX-RAIL script may check for completion of play by calling WAITFOR(pin), which will only proceed to the
// following line when the player is no longer busy.
// E.g.
// SEQUENCE(1)
// AT(164) // Wait for sensor attached to pin 164 to activate
// SET(10003) // Play fourth MP3 file
// LCD(4, "Playing") // Display message on LCD/OLED
// WAITFOR(10003) // Wait for playing to finish
// LCD(4, " ") // Clear LCD/OLED line
// FOLLOW(1) // Go back to start
// DFPlayer::create(10000, 10, Serial1);
}
#endif

View File

@@ -1,214 +0,0 @@
// 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"
#include "IO_VL53L0X.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=196
// Number of VPINs=16 (numbered 196-211)
// I2C address of module=0x22
//MCP23017 gpioModule2(196, 16, 0x22);
// 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(196, 16, 0x22, 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
//MCP23008 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 ranging 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 following directive defines a single VL53L0X Time-of-Flight range sensor.
//=======================================================================
// The parameters are:
// VPIN=5000
// Number of VPINs=1
// I2C address=0x29 (default for this chip)
// Minimum trigger range=200mm (VPIN goes to 1 when <20cm)
// Maximum trigger range=250mm (VPIN goes to 0 when >25cm)
//VL53L0X tofModule1(5000, 1, 0x29, 200, 250);
// For multiple VL53L0X modules, add another parameter which is a VPIN connected to the
// module's XSHUT pin. This allows the modules to be configured, at start,
// with distinct I2C addresses. In this case, the address 0x29 is only used during
// initialisation to configure each device in turn with the desired unique I2C address.
// The examples below have the modules' XSHUT pins connected to the first two pins of
// the first MCP23017 module (164 and 165), but Arduino pins may be used instead.
// The first module here is given I2C address 0x30 and the second is 0x31.
//VL53L0X tofModule1(5000, 1, 0x30, 200, 250, 164);
//VL53L0X tofModule2(5001, 1, 0x31, 200, 250, 165);
//=======================================================================
// 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 a module driver, 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(196, 16, 0x22);
//=======================================================================
// Creating a Turnout
//=======================================================================
// Parameters: same as <T> 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 <T> 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 <S> 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);
//=======================================================================
// Play mp3 files from a Micro-SD card, using a DFPlayer MP3 Module.
//=======================================================================
// Parameters:
// 10000 = first VPIN allocated.
// 10 = number of VPINs allocated.
// Serial1 = name of serial port (usually Serial1 or Serial2).
// With these parameters, up to 10 files may be played on pins 10000-10009.
// Play is started from EX-RAIL with SET(10000) for first mp3 file, SET(10001)
// for second file, etc. Play may also be initiated by writing an analogue
// value to the first pin, e.g. SERVO(10000,23,0) will play the 23rd mp3 file.
// SERVO(10000,23,30) will do the same thing, as well as setting the volume to
// 30 (maximum value).
// Play is stopped by RESET(10000) (or any other allocated VPIN).
// Volume may also be set by writing an analogue value to the second pin for the player,
// e.g. SERVO(10001,30,0) sets volume to maximum (30).
// The EX-RAIL script may check for completion of play by calling WAITFOR(pin), which will only proceed to the
// following line when the player is no longer busy.
// E.g.
// SEQUENCE(1)
// AT(164) // Wait for sensor attached to pin 164 to activate
// SET(10003) // Play fourth MP3 file
// LCD(4, "Playing") // Display message on LCD/OLED
// WAITFOR(10003) // Wait for playing to finish
// LCD(4, " ") // Clear LCD/OLED line
// FOLLOW(1) // Go back to start
// DFPlayer::create(10000, 10, Serial1);
}
#endif

View File

@@ -3,7 +3,7 @@
#include "StringFormatter.h"
#define VERSION "3.2.0 DCrc2"
#define VERSION "3.2.0 rc8"
// 3.2.0 Major functional and non-functional changes.
// New HAL added for I/O (digital and analogue inputs and outputs, servos etc).
// Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules.
@@ -21,6 +21,10 @@
// Configuration options to globally flip polarity of DCC Accessory states when driven
// from <a> command and <T> command.
// Increased use of display for showing loco decoder programming information.
// Can disable EEPROM code
// Can define border between long and short addresses
// Turnout and accessory states (thrown/closed = 0/1 or 1/0) can be set to match RCN-213
// Bugfix: one-off error in CIPSEND drop
// ...
// 3.1.7 Bugfix: Unknown locos should have speed forward
// 3.1.6 Make output ID two bytes and guess format/size of registered outputs found in EEPROM