mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-07-30 02:43:45 +02:00
Compare commits
53 Commits
v3.2.0rc9
...
amuzzing1-
Author | SHA1 | Date | |
---|---|---|---|
|
ac550e81c8 | ||
|
2a16e7d710 | ||
|
4be8d58f76 | ||
|
0ab3fe07c5 | ||
|
a37ca6b6b6 | ||
|
5b12c2864d | ||
|
90ca262cd9 | ||
|
b29eedf772 | ||
|
71cd3fc292 | ||
|
331538549f | ||
|
931e348c3d | ||
|
ac3ffd2a36 | ||
|
c15ea6e083 | ||
|
90092b4ad5 | ||
|
fa4f5f08ef | ||
|
3038f29dac | ||
|
e57cd1e2aa | ||
|
c47dd101a5 | ||
|
cf905ce2f3 | ||
|
4bfcaa605a | ||
|
557b71c036 | ||
|
d0b01e3f03 | ||
|
bd9a04572d | ||
|
a26d988acc | ||
|
22bf2b69f1 | ||
|
58ef7d25db | ||
|
1ec3b173fd | ||
|
07c0004996 | ||
|
99a62883c7 | ||
|
b7af6be1f2 | ||
|
eb6b14b848 | ||
|
fa261ec39f | ||
|
4ebf7978d8 | ||
|
a0a635c167 | ||
|
ca0c34f9fa | ||
|
b6501c7e3e | ||
|
8af74f7082 | ||
|
75598b2b8b | ||
|
bd7c8bf78e | ||
|
05545321a9 | ||
|
03f7014c02 | ||
|
306f100085 | ||
|
509014bb6a | ||
|
f577c11eb7 | ||
|
73ea7a1479 | ||
|
f807339eec | ||
|
b4aa47a451 | ||
|
1416f83f8c | ||
|
d5955a36bf | ||
|
35f7ac3d77 | ||
|
41cda58bef | ||
|
0b8c455594 | ||
|
e0a7c4d155 |
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
github: DCC-EX
|
||||
patreon: dccex
|
14
.github/workflows/label-sponsors.yml
vendored
Normal file
14
.github/workflows/label-sponsors.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: Label sponsors
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
build:
|
||||
name: is-sponsor-label
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: JasonEtco/is-sponsor-label-action@v1.2.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
3
.github/workflows/sha.yml
vendored
3
.github/workflows/sha.yml
vendored
@@ -24,10 +24,11 @@ jobs:
|
||||
sha=$(git rev-parse --short "$GITHUB_SHA")
|
||||
echo "#define GITHUB_SHA \"$sha\"" > GITHUB_SHA.h
|
||||
|
||||
- uses: EndBug/add-and-commit@v4 # You can change this to use a specific version
|
||||
- uses: EndBug/add-and-commit@v8 # You can change this to use a specific version
|
||||
with:
|
||||
add: 'GITHUB_SHA.h'
|
||||
message: 'Committing a SHA'
|
||||
commit: --amend
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this line unchanged
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,7 +7,7 @@ Release/*
|
||||
.pio/
|
||||
.vscode/
|
||||
config.h
|
||||
.vscode/extensions.json
|
||||
.vscode/*
|
||||
mySetup.h
|
||||
mySetup.cpp
|
||||
myHal.cpp
|
||||
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -3,5 +3,8 @@
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
|
@@ -135,3 +135,8 @@ void CommandDistributor::broadcastPower() {
|
||||
LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason);
|
||||
broadcast(true);
|
||||
}
|
||||
|
||||
void CommandDistributor::broadcastText(const FSH * msg) {
|
||||
StringFormatter::send(broadcastBufferWriter,F("%S"),msg);
|
||||
broadcast(false);
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@ public :
|
||||
static void broadcastSensor(int16_t id, bool value);
|
||||
static void broadcastTurnout(int16_t id, bool isClosed);
|
||||
static void broadcastPower();
|
||||
static void broadcastText(const FSH * msg);
|
||||
static void forget(byte clientId);
|
||||
private:
|
||||
static void broadcast(bool includeWithrottleClients);
|
||||
|
@@ -91,7 +91,7 @@ void setup()
|
||||
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h
|
||||
DCC::begin(MOTOR_SHIELD_TYPE);
|
||||
|
||||
// Start RMFT (ignored if no automnation)
|
||||
// Start RMFT aka EX-RAIL (ignored if no automnation)
|
||||
RMFT::begin();
|
||||
|
||||
|
||||
|
8
DCC.cpp
8
DCC.cpp
@@ -33,7 +33,7 @@
|
||||
#include "version.h"
|
||||
#include "FSH.h"
|
||||
#include "IODevice.h"
|
||||
#include "RMFT2.h"
|
||||
#include "EXRAIL2.h"
|
||||
#include "CommandDistributor.h"
|
||||
|
||||
// This module is responsible for converting API calls into
|
||||
@@ -252,7 +252,7 @@ void DCC::setAccessory(int address, byte number, bool activate) {
|
||||
b[1] = ((((address / 64) % 8) << 4) + (number % 4 << 1) + activate % 2) ^ 0xF8; // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
|
||||
|
||||
DCCWaveform::mainTrack.schedulePacket(b, 2, 4); // Repeat the packet four times
|
||||
#if defined(RMFT_ACTIVE)
|
||||
#if defined(EXRAIL_ACTIVE)
|
||||
RMFT2::activateEvent(address<<2|number,activate);
|
||||
#endif
|
||||
}
|
||||
@@ -733,6 +733,8 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[]
|
||||
DCCWaveform::progTrack.autoPowerOff=true; // power off afterwards
|
||||
if (Diag::ACK) DIAG(F("Auto Prog power on"));
|
||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||
if (MotorDriver::commonFaultPin)
|
||||
DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
||||
DCCWaveform::progTrack.sentResetsSincePacket = 0;
|
||||
}
|
||||
|
||||
@@ -992,6 +994,8 @@ void DCC::callback(int value) {
|
||||
if (DCCWaveform::progTrack.autoPowerOff) {
|
||||
if (Diag::ACK) DIAG(F("Auto Prog power off"));
|
||||
DCCWaveform::progTrack.doAutoPowerOff();
|
||||
if (MotorDriver::commonFaultPin)
|
||||
DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
|
||||
}
|
||||
// Restore <1 JOIN> to state before BASELINE
|
||||
if (ackManagerRejoin) {
|
||||
|
2
DCCEX.h
2
DCCEX.h
@@ -43,7 +43,7 @@
|
||||
#include "Turnouts.h"
|
||||
#include "Sensors.h"
|
||||
#include "Outputs.h"
|
||||
#include "RMFT.h"
|
||||
#include "CommandDistributor.h"
|
||||
#include "EXRAIL.h"
|
||||
|
||||
#endif
|
||||
|
@@ -383,23 +383,24 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||
bool prog=false;
|
||||
bool join=false;
|
||||
if (params > 1) break;
|
||||
if (params==0) { // <1>
|
||||
if (params==0 || MotorDriver::commonFaultPin) { // <1> or tracks can not be handled individually
|
||||
main=true;
|
||||
prog=true;
|
||||
}
|
||||
else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN>
|
||||
if (params==1) {
|
||||
if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN>
|
||||
main=true;
|
||||
prog=true;
|
||||
join=!MotorDriver::commonFaultPin;
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN>
|
||||
join=true;
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN>
|
||||
main=true;
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG>
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG>
|
||||
prog=true;
|
||||
}
|
||||
else break; // will reply <X>
|
||||
|
||||
}
|
||||
else break; // will reply <X>
|
||||
}
|
||||
if (main) DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
||||
if (prog) DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||
DCC::setProgTrackSyncMain(join);
|
||||
@@ -413,17 +414,19 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||
bool main=false;
|
||||
bool prog=false;
|
||||
if (params > 1) break;
|
||||
if (params==0) { // <0>
|
||||
main=true;
|
||||
prog=true;
|
||||
if (params==0 || MotorDriver::commonFaultPin) { // <0> or tracks can not be handled individually
|
||||
main=true;
|
||||
prog=true;
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN>
|
||||
main=true;
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG>
|
||||
prog=true;
|
||||
}
|
||||
else break; // will reply <X>
|
||||
if (params==1) {
|
||||
if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN>
|
||||
main=true;
|
||||
}
|
||||
else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG>
|
||||
prog=true;
|
||||
}
|
||||
else break; // will reply <X>
|
||||
}
|
||||
|
||||
if (main) DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
|
||||
if (prog) {
|
||||
@@ -738,10 +741,10 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
|
||||
LCD(1, F("Ack Limit=%dmA"), p[2]); // <D ACK LIMIT 42>
|
||||
} else if (p[1] == HASH_KEYWORD_MIN) {
|
||||
DCCWaveform::progTrack.setMinAckPulseDuration(p[2]);
|
||||
LCD(0, F("Ack Min=%dus"), p[2]); // <D ACK MIN 1500>
|
||||
LCD(0, F("Ack Min=%uus"), p[2]); // <D ACK MIN 1500>
|
||||
} else if (p[1] == HASH_KEYWORD_MAX) {
|
||||
DCCWaveform::progTrack.setMaxAckPulseDuration(p[2]);
|
||||
LCD(0, F("Ack Max=%dus"), p[2]); // <D ACK MAX 9000>
|
||||
LCD(0, F("Ack Max=%uus"), p[2]); // <D ACK MAX 9000>
|
||||
} else if (p[1] == HASH_KEYWORD_RETRY) {
|
||||
if (p[2] >255) p[2]=3;
|
||||
LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCC::setAckRetry(p[2])); // <D ACK RETRY 2>
|
||||
@@ -883,7 +886,7 @@ void DCCEXParser::callback_R(int16_t result)
|
||||
void DCCEXParser::callback_Rloco(int16_t result) {
|
||||
const FSH * detail;
|
||||
if (result<=0) {
|
||||
detail=F("<r ERROR %d>\n");
|
||||
detail=F("<r %d>\n");
|
||||
} else {
|
||||
bool longAddr=result & LONG_ADDR_MARKER; //long addr
|
||||
if (longAddr)
|
||||
|
@@ -289,7 +289,7 @@ void DCCWaveform::setAckBaseline() {
|
||||
if (isMainTrack) return;
|
||||
int baseline=motorDriver->getCurrentRaw();
|
||||
ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA);
|
||||
if (Diag::ACK) DIAG(F("ACK baseline=%d/%dmA Threshold=%d/%dmA Duration between %dus and %dus"),
|
||||
if (Diag::ACK) DIAG(F("ACK baseline=%d/%dmA Threshold=%d/%dmA Duration between %uus and %uus"),
|
||||
baseline,motorDriver->raw2mA(baseline),
|
||||
ackThreshold,motorDriver->raw2mA(ackThreshold),
|
||||
minAckPulseDuration, maxAckPulseDuration);
|
||||
@@ -309,7 +309,7 @@ void DCCWaveform::setAckPending() {
|
||||
|
||||
byte DCCWaveform::getAck() {
|
||||
if (ackPending) return (2); // still waiting
|
||||
if (Diag::ACK) DIAG(F("%S after %dmS max=%d/%dmA pulse=%duS samples=%d gaps=%d"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration,
|
||||
if (Diag::ACK) DIAG(F("%S after %dmS max=%d/%dmA pulse=%uuS samples=%d gaps=%d"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration,
|
||||
ackMaxCurrent,motorDriver->raw2mA(ackMaxCurrent), ackPulseDuration, numAckSamples, numAckGaps);
|
||||
if (ackDetected) return (1); // Yes we had an ack
|
||||
return(0); // pending set off but not detected means no ACK.
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#ifndef RMFT_H
|
||||
#define RMFT_H
|
||||
#ifndef EXRAIL_H
|
||||
#define EXRAIL_H
|
||||
|
||||
#if defined(RMFT_ACTIVE)
|
||||
#include "RMFT2.h"
|
||||
#if defined(EXRAIL_ACTIVE)
|
||||
#include "EXRAIL2.h"
|
||||
|
||||
class RMFT {
|
||||
public:
|
||||
@@ -10,7 +10,7 @@
|
||||
static void inline loop() {RMFT2::loop();}
|
||||
};
|
||||
|
||||
#include "RMFTMacros.h"
|
||||
#include "EXRAILMacros.h"
|
||||
|
||||
#else
|
||||
// Dummy RMFT
|
@@ -42,7 +42,7 @@
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "RMFT2.h"
|
||||
#include "EXRAIL2.h"
|
||||
#include "DCC.h"
|
||||
#include "DCCWaveform.h"
|
||||
#include "DIAG.h"
|
||||
@@ -67,11 +67,11 @@ const int16_t HASH_KEYWORD_ROUTES=-3702;
|
||||
|
||||
// One instance of RMFT clas is used for each "thread" in the automation.
|
||||
// Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation.
|
||||
// The thrrads exist in a ring, each time through loop() the next thread in the ring is serviced.
|
||||
// The threads exist in a ring, each time through loop() the next thread in the ring is serviced.
|
||||
|
||||
// Statics
|
||||
const int16_t LOCO_ID_WAITING=-99; // waiting for loco id from prog track
|
||||
int16_t RMFT2::progtrackLocoId; // used for callback when detecting a loco on prograck
|
||||
int16_t RMFT2::progtrackLocoId; // used for callback when detecting a loco on prog track
|
||||
bool RMFT2::diag=false; // <D EXRAIL ON>
|
||||
RMFT2 * RMFT2::loopTask=NULL; // loopTask contains the address of ONE of the tasks in a ring.
|
||||
RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE.
|
||||
@@ -681,7 +681,7 @@ void RMFT2::loop2() {
|
||||
break;
|
||||
|
||||
case OPCODE_IFRANDOM: // do block on random percentage
|
||||
if (random(100)>=operand) if (!skipIfBlock()) return;
|
||||
if ((int16_t)random(100)>=operand) if (!skipIfBlock()) return;
|
||||
break;
|
||||
|
||||
case OPCODE_IFRESERVE: // do block if we successfully RERSERVE
|
||||
@@ -794,6 +794,12 @@ void RMFT2::loop2() {
|
||||
DCC::setProgTrackSyncMain(true);
|
||||
CommandDistributor::broadcastPower();
|
||||
break;
|
||||
|
||||
case OPCODE_POWERON:
|
||||
DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
||||
DCC::setProgTrackSyncMain(false);
|
||||
CommandDistributor::broadcastPower();
|
||||
break;
|
||||
|
||||
case OPCODE_UNJOIN:
|
||||
DCC::setProgTrackSyncMain(false);
|
||||
@@ -870,13 +876,13 @@ void RMFT2::loop2() {
|
||||
break;
|
||||
|
||||
case OPCODE_AUTOSTART: // Handled only during begin process
|
||||
case OPCODE_PAD: // Just a padding for previous opcode needing >1 operad byte.
|
||||
case OPCODE_PAD: // Just a padding for previous opcode needing >1 operand byte.
|
||||
case OPCODE_TURNOUT: // Turnout definition ignored at runtime
|
||||
case OPCODE_SERVOTURNOUT: // Turnout definition ignored at runtime
|
||||
case OPCODE_PINTURNOUT: // Turnout definition ignored at runtime
|
||||
case OPCODE_ONCLOSE: // Turnout event catcers ignored here
|
||||
case OPCODE_ONCLOSE: // Turnout event catchers ignored here
|
||||
case OPCODE_ONTHROW:
|
||||
case OPCODE_ONACTIVATE: // Activate event catcers ignored here
|
||||
case OPCODE_ONACTIVATE: // Activate event catchers ignored here
|
||||
case OPCODE_ONDEACTIVATE:
|
||||
break;
|
||||
|
@@ -18,8 +18,8 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef RMFT2_H
|
||||
#define RMFT2_H
|
||||
#ifndef EXRAIL2_H
|
||||
#define EXRAIL2_H
|
||||
#include "FSH.h"
|
||||
#include "IODevice.h"
|
||||
|
||||
@@ -44,7 +44,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
||||
OPCODE_PAD,OPCODE_FOLLOW,OPCODE_CALL,OPCODE_RETURN,
|
||||
OPCODE_JOIN,OPCODE_UNJOIN,OPCODE_READ_LOCO1,OPCODE_READ_LOCO2,OPCODE_POM,
|
||||
OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO,
|
||||
OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,
|
||||
OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON,
|
||||
OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT,
|
||||
OPCODE_PRINT,OPCODE_DCCACTIVATE,
|
||||
OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE,OPCODE_IFGTE,OPCODE_IFLT,
|
@@ -32,6 +32,7 @@
|
||||
#undef ATTIMEOUT
|
||||
#undef AUTOMATION
|
||||
#undef AUTOSTART
|
||||
#undef BROADCAST
|
||||
#undef CALL
|
||||
#undef CLOSE
|
||||
#undef DEACTIVATE
|
||||
@@ -79,6 +80,7 @@
|
||||
#undef PRINT
|
||||
#undef POM
|
||||
#undef POWEROFF
|
||||
#undef POWERON
|
||||
#undef READ_LOCO
|
||||
#undef RED
|
||||
#undef RESERVE
|
||||
@@ -121,6 +123,7 @@
|
||||
#define ATTIMEOUT(sensor_id,timeout_ms)
|
||||
#define AUTOMATION(id, description)
|
||||
#define AUTOSTART
|
||||
#define BROADCAST(msg)
|
||||
#define CALL(route)
|
||||
#define CLOSE(id)
|
||||
#define DEACTIVATE(addr,subaddr)
|
||||
@@ -168,6 +171,7 @@
|
||||
#define PRINT(msg)
|
||||
#define POM(cv,value)
|
||||
#define POWEROFF
|
||||
#define POWERON
|
||||
#define READ_LOCO
|
||||
#define RED(signal_id)
|
||||
#define RESERVE(blockid)
|
@@ -19,8 +19,8 @@
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RMFTMacros_H
|
||||
#define RMFTMacros_H
|
||||
#ifndef EXRAILMacros_H
|
||||
#define EXRAILMacros_H
|
||||
|
||||
// remove normal code LCD & SERIAL macros (will be restored later)
|
||||
#undef LCD
|
||||
@@ -50,13 +50,13 @@
|
||||
// CAUTION: The macros below are multiple passed over myAutomation.h
|
||||
|
||||
// Pass 1 Implements aliases
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef ALIAS
|
||||
#define ALIAS(name,value) const int name=value;
|
||||
#include "myAutomation.h"
|
||||
|
||||
// Pass 2 convert descriptions to withrottle format emitter function
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef ROUTE
|
||||
#define ROUTE(id, description) emitRouteDescription(stream,'R',id,F(description));
|
||||
#undef AUTOMATION
|
||||
@@ -67,8 +67,10 @@ void RMFT2::emitWithrottleDescriptions(Print * stream) {
|
||||
}
|
||||
|
||||
// Pass 3... Create Text sending functions
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
const int StringMacroTracker1=__COUNTER__;
|
||||
#undef BROADCAST
|
||||
#define BROADCAST(msg) case (__COUNTER__ - StringMacroTracker1) : CommandDistributor::broadcastText(F(msg));break;
|
||||
#undef PRINT
|
||||
#define PRINT(msg) case (__COUNTER__ - StringMacroTracker1) : printMessage2(F(msg));break;
|
||||
#undef LCN
|
||||
@@ -93,7 +95,7 @@ void RMFT2::printMessage(uint16_t id) {
|
||||
|
||||
|
||||
// Pass 4: Turnout descriptions (optional)
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef TURNOUT
|
||||
#define TURNOUT(id,addr,subaddr,description...) case id: desc=F("" description); break;
|
||||
#undef PIN_TURNOUT
|
||||
@@ -112,7 +114,7 @@ void RMFT2::emitTurnoutDescription(Print* stream,int16_t turnoutid) {
|
||||
}
|
||||
|
||||
// Pass 5: Roster names (count)
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef ROSTER
|
||||
#define ROSTER(cabid,name,funcmap...) +1
|
||||
|
||||
@@ -121,7 +123,7 @@ const byte RMFT2::rosterNameCount=0
|
||||
;
|
||||
|
||||
// Pass 6: Roster names emitter
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef ROSTER
|
||||
#define ROSTER(cabid,name,funcmap...) StringFormatter::send(stream,(FSH *)format,F(name),cabid,cabid<128?'S':'L');
|
||||
void RMFT2::emitWithrottleRoster(Print * stream) {
|
||||
@@ -133,7 +135,7 @@ void RMFT2::emitWithrottleRoster(Print * stream) {
|
||||
}
|
||||
|
||||
// Pass 7: functions getter
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef ROSTER
|
||||
#define ROSTER(cabid,name,funcmap...) case cabid: return F("" funcmap);
|
||||
const FSH * RMFT2::getRosterFunctions(int16_t cabid) {
|
||||
@@ -144,7 +146,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t cabid) {
|
||||
}
|
||||
|
||||
// Pass 8 Signal definitions
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
#undef SIGNAL
|
||||
#define SIGNAL(redpin,amberpin,greenpin) redpin,amberpin,greenpin,
|
||||
const FLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||
@@ -154,7 +156,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||
// Last Pass : create main routes table
|
||||
// Only undef the macros, not dummy them.
|
||||
#define RMFT2_UNDEF_ONLY
|
||||
#include "RMFT2MacroReset.h"
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
// Define internal helper macros.
|
||||
// Everything we generate here has to be compile-time evaluated to
|
||||
// a constant.
|
||||
@@ -170,6 +172,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||
#define ATTIMEOUT(sensor_id,timeout) OPCODE_ATTIMEOUT1,0,0,OPCODE_ATTIMEOUT2,V(sensor_id),OPCODE_PAD,V(timeout/100L),
|
||||
#define AUTOMATION(id, description) OPCODE_AUTOMATION, V(id),
|
||||
#define AUTOSTART OPCODE_AUTOSTART,0,0,
|
||||
#define BROADCAST(msg) PRINT(msg)
|
||||
#define CALL(route) OPCODE_CALL,V(route),
|
||||
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
||||
#define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1),
|
||||
@@ -216,6 +219,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
||||
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
||||
#define POWEROFF OPCODE_POWEROFF,0,0,
|
||||
#define POWERON OPCODE_POWERON,0,0,
|
||||
#define PRINT(msg) OPCODE_PRINT,V(__COUNTER__ - StringMacroTracker2),
|
||||
#define READ_LOCO OPCODE_READ_LOCO1,0,0,OPCODE_READ_LOCO2,0,0,
|
||||
#define RED(signal_id) OPCODE_RED,V(signal_id),
|
@@ -1 +1 @@
|
||||
#define GITHUB_SHA "61390cb"
|
||||
#define GITHUB_SHA "a26d988"
|
||||
|
@@ -72,7 +72,7 @@ void I2CManagerClass::I2C_sendStart() {
|
||||
bytesToReceive = currentRequest->readLen;
|
||||
|
||||
// If anything to send, initiate write. Otherwise initiate read.
|
||||
if (operation == OPERATION_READ || (operation == OPERATION_REQUEST & !bytesToSend))
|
||||
if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) & !bytesToSend))
|
||||
TWI0.MADDR = (currentRequest->i2cAddress << 1) | 1;
|
||||
else
|
||||
TWI0.MADDR = (currentRequest->i2cAddress << 1) | 0;
|
||||
@@ -157,4 +157,4 @@ ISR(TWI0_TWIM_vect) {
|
||||
I2CManagerClass::handleInterrupt();
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -98,22 +98,20 @@ uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t rea
|
||||
* returned in the I2CRB as for the asynchronous version.
|
||||
***************************************************************************/
|
||||
void I2CManagerClass::queueRequest(I2CRB *req) {
|
||||
uint8_t status;
|
||||
switch (req->operation) {
|
||||
case OPERATION_READ:
|
||||
status = read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req);
|
||||
req->status = read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req);
|
||||
break;
|
||||
case OPERATION_SEND:
|
||||
status = write(req->i2cAddress, req->writeBuffer, req->writeLen, req);
|
||||
req->status = write(req->i2cAddress, req->writeBuffer, req->writeLen, req);
|
||||
break;
|
||||
case OPERATION_SEND_P:
|
||||
status = write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req);
|
||||
req->status = write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req);
|
||||
break;
|
||||
case OPERATION_REQUEST:
|
||||
status = read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req);
|
||||
req->status = read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req);
|
||||
break;
|
||||
}
|
||||
req->status = status;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
|
@@ -152,16 +152,16 @@ int MotorDriver::getCurrentRaw() {
|
||||
bool irq = disableInterrupts();
|
||||
current = analogRead(currentPin)-senseOffset;
|
||||
enableInterrupts(irq);
|
||||
#elif defined(ARDUINO_TEENSY32) || defined(ARDUINO_TEENSY35)|| defined(ARDUINO_TEENSY36)
|
||||
#else // Uno, Mega and all the TEENSY3* but not TEENSY4*
|
||||
unsigned char sreg_backup;
|
||||
sreg_backup = SREG; /* save interrupt enable/disable state */
|
||||
cli();
|
||||
current = analogRead(currentPin)-senseOffset;
|
||||
#if defined(ARDUINO_TEENSY32) || defined(ARDUINO_TEENSY35)|| defined(ARDUINO_TEENSY36)
|
||||
overflow_count = 0;
|
||||
SREG = sreg_backup; /* restore interrupt state */
|
||||
#else
|
||||
current = analogRead(currentPin)-senseOffset;
|
||||
#endif
|
||||
if (sreg_backup & 128) sei(); /* restore interrupt state */
|
||||
#endif // outer #
|
||||
if (current<0) current=0-current;
|
||||
if ((faultPin != UNUSED_PIN) && isLOW(fastFaultPin) && isHIGH(fastPowerPin))
|
||||
return (current == 0 ? -1 : -current);
|
||||
|
@@ -100,4 +100,11 @@
|
||||
new MotorDriver(6, 7, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 1.0, 1100, UNUSED_PIN), \
|
||||
new MotorDriver(5, 4, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 1.0, 1100, UNUSED_PIN)
|
||||
|
||||
// This is an example how to setup a motor shield definition for a motor shield connected
|
||||
// to an NANO EVERY board. You have to make the connectons from the shield to the board
|
||||
// as in this example or adjust the values yourself.
|
||||
#define NANOEVERY_EXAMPLE F("NANOEVERY_EXAMPLE"), \
|
||||
new MotorDriver(5, 6, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN),\
|
||||
new MotorDriver(9, 10, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 2000, UNUSED_PIN)
|
||||
|
||||
#endif
|
||||
|
@@ -22,7 +22,7 @@ Both CommandStation-EX and BaseStation-Classic support much of the NMRA Digital
|
||||
|
||||
# What’s in this Repository?
|
||||
|
||||
This repository, CommandStation-EX, contains a complete DCC++ EX Commmand Station sketch designed for compiling and uploading into an Arduino Uno, Mega, or Nano. All sketch files are in the folder named CommandStation-EX and its subforlders.
|
||||
This repository, CommandStation-EX, contains a complete DCC++ EX Commmand Station sketch designed for compiling and uploading into an Arduino Uno, Mega, or Nano.
|
||||
|
||||
To utilize this sketch, you can use the following:
|
||||
|
||||
@@ -69,7 +69,7 @@ in config.h.
|
||||
* Automatic slot (register) management
|
||||
* Automation (coming soon)
|
||||
|
||||
NOTE: DCC-EX is a major rewrite to the code. We started over and rebuilt it from the ground up! For what that means to you, click [HERE](notes/rewrite.md).
|
||||
NOTE: DCC-EX is a major rewrite to the code. We started over and rebuilt it from the ground up! For what that means, you can read [HERE](https://dcc-ex.com/about/rewrite.html).
|
||||
|
||||
# More information
|
||||
You can learn more at the [DCC++ EX website](https://dcc-ex.com/)
|
||||
|
284
Release_Notes/release_notes_v4.0.0.md
Normal file
284
Release_Notes/release_notes_v4.0.0.md
Normal file
@@ -0,0 +1,284 @@
|
||||
Version 4.0 Release Notes
|
||||
*************************
|
||||
|
||||
The DCC-EX Team is pleased to release CommandStation-EX-v4.0.0 as a Production Release. Release v4.0.0 is a Major release that adds significant new product design, plus Automation features and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v3.2.0 to v3.2.0 rc13.
|
||||
|
||||
**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.**
|
||||
|
||||
[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip)
|
||||
|
||||
|
||||
[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz)
|
||||
|
||||
**Known Issues**
|
||||
|
||||
- **Wi-Fi** - Requires sending `<AT>` commands from a serial monitor if you want to switch between AP mode and STA station mode after initial setup
|
||||
- **Pololu Motor Shield** - is supported with this release, but the user may have to adjust timings to enable programming mode due to limitations in its current sensing circuitry
|
||||
|
||||
**All New Major DCC++EX 4.0.0 features**
|
||||
|
||||
- **New HAL Hardware Abstraction Layer API** that automatically detects and greatly simplifies interfacing to many predefined accessory boards for servos, signals & sensors and added I/O (digital and analog inputs and outputs, servos etc).
|
||||
- HAL Support for;
|
||||
- MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules.
|
||||
- PCA9685 PWM (servo & signal) control modules.
|
||||
- Analogue inputs on Arduino pins and on ADS111x I2C modules.
|
||||
- MP3 sound playback via DFPlayer module.
|
||||
- HC-SR04 Ultrasonic range sensor module.
|
||||
- VL53L0X Laser range sensor module (Time-Of-Flight).
|
||||
- A new `<D HAL SHOW>` command to list the HAL devices attached to the command station
|
||||
|
||||
**New Command Station Broadcast throttle logic**
|
||||
|
||||
- Synchronizes multiple WiThrottles and PC based JMRI Throttles for direction, speed and F-key updates
|
||||
|
||||
**New ‘Discovered Servers’ on WiFi Throttles**
|
||||
|
||||
- Our New multicast Dynamic Network Server (mDNS) enhancement allows us to display the available WiFi server connections to a DCC++EX Command Station. Selecting it allows your WiThrottle App to connect to and load Server Rosters and function keys to your throttle from the new DCC++EX Command Station Server Roster.
|
||||
|
||||
**New DCC++EX 4.0.0 with EX-RAIL Extended Railroad Automation Instruction Language**
|
||||
|
||||
- Use to control your entire layout or as a separate accessory/animation controller
|
||||
- Awesome, cleverly powerful yet simple user friendly scripting language for user built Automation & Routing scripts.
|
||||
- You can control Engines, Sensors, Turnouts, Signals, Outputs and Accessories that are entered into your new myAutomation.h file, then uploaded into the DCC++EX Command Station.
|
||||
- EX-RAIL scripts are automatically displayed as Automation {Handoff} and Route {Set} buttons on supported WiFi Throttle Apps.
|
||||
|
||||
**New EX-RAIL ‘Roster’ Feature**
|
||||
|
||||
- List and store user defined engine roster & function keys inside the command station, and automatically load them in WiFi Throttle Apps.
|
||||
- When choosing “DCC++EX” from discovered servers an Engine Driver or WiThrottle is directly connected to the Command Station.
|
||||
- The EX-RAIL ’ROSTER’ command allows all the engine numbers, names and function keys you’ve listed in your myAutomation.h file to automatically upload the Command Station's ‘Server Roster’ into your Engine Driver and WiThrottle Apps.
|
||||
|
||||
**New JMRI 4.99.2 and above specific DCC++EX 4.0 features**
|
||||
|
||||
- Enhanced JMRI DCC++ Configure Base Station pane for building and maintaining Sensor, Turnout and Output devices, or these can automatically be populated from the DCC++EX Command Station's mySetup.h file into JMRI.
|
||||
|
||||
- JMRI now supports multiple serial connected DCC++EX Command Stations, to display and track separate "Send DCC++ Command" and "DCC++ Traffic" Monitors for each Command Station at the same time.
|
||||
For example: Use an Uno DCC++EX DecoderPro Programming Station {DCC++Prg} on a desktop programming track and a second Mega DCC++EX EX-RAIL Command Station for Operations {DCC++Ops} on the layout with an additional `<JOINED>` programming spur or siding track for acquiring an engine and ‘Drive Away’ onto the mainline (see the DriveAway feature for more information).
|
||||
|
||||
**DCC++EX 4.0.0 additional product enhancements**
|
||||
|
||||
- Additional Motor Shields and Motor Board {boosters) supported
|
||||
- Additional Accessory boards supported for GPIO expansion, Sensors, Servos & Signals
|
||||
- Additional diagnostic commands like ‘D ACK RETRY’ and ‘D EXRAIL ON’ events, ‘D HAL SHOW’ devices and ‘D SERVO’ positions, and ‘D RESET’ the command station while maintaining the serial connection with JMRI
|
||||
- Automatic retry on failed ACK detection to give decoders another chance
|
||||
- New EX-RAIL ’/’ slash command allows JMRI to directly communicate with many EX-RAIL scripts
|
||||
- Turnout class revised to expand turnout capabilities and allow turnout names/descriptors to display in WiThrottle Apps.
|
||||
- Build turnouts through either or both mySetup.h and myAutomation.h files, and have them automatically passed to, and populate, JMRI Turnout Tables
|
||||
- Turnout user names display in Engine Driver & WiThrottles
|
||||
- Output class now allows ID > 255.
|
||||
- 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 memory code to allow room for DCC++EX 4.0 to fit on a Uno Command Station
|
||||
- Can define border between long and short addresses
|
||||
- Native non-blocking I2C drivers for AVR and Nano architectures (fallback to blocking Wire library for other platforms).
|
||||
- EEPROM layout change - deletes EEPROM contents on first start following upgrade.
|
||||
|
||||
**4.0.0 Bug Fixes**
|
||||
|
||||
- Compiles on Nano Every
|
||||
- Diagnostic display of ack pulses >32ms
|
||||
- Current read from wrong ADC during interrupt
|
||||
- AT(+) Command Pass Through
|
||||
- CiDAP WiFi Drop out and the WiThrottle F-key looping error corrected
|
||||
- One-off error in CIPSEND drop
|
||||
- Common Fault Pin Error
|
||||
- Uno Memory Utilization optimized
|
||||
|
||||
#### Summary of Release 3.1.0 key features and/or bug fixes by Point Release
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.16**
|
||||
|
||||
- Ignore CV1 bit 7 read if rejected by a non NMRA compliant decoder when identifying loco id
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.15**
|
||||
|
||||
- Send function commands just once instead of repeating them 4 times
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.14**
|
||||
|
||||
- Add feature to tolerate decoders that incorrectly have gaps in their ACK pulse
|
||||
- Provide proper track power management when joining and unjoining tracks with <1 JOIN>
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.13**
|
||||
|
||||
- Fix for CAB Functions greater than 127
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.12**
|
||||
|
||||
- Fixed clear screen issue for nanoEvery and nanoWifi
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.11**
|
||||
|
||||
- Reorganized files for support of 128 speed steps
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.10**
|
||||
|
||||
- Added Support for the Teensy 3.2, 3.5, 3.6, 4.0 and 4.1 MCUs
|
||||
- No functional change just changes to avoid complier warnings for Teensy/nanoEvery
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.9**
|
||||
|
||||
- Rearranges serial newlines for the benefit of JMRI
|
||||
- Major update for efficiencies in displays (LCD, OLED)
|
||||
- Add I2C Support functions
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.8**
|
||||
|
||||
- Wraps <* *> around DIAGS for the benefit of JMRI
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.7**
|
||||
|
||||
- Implemented support for older 28 apeed step decoders - Option to turn on 28 step speed decoders in addition to 128. If set, all locos will use 28 steps.
|
||||
- Improved overload messages with raw values (relative to offset)
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.6**
|
||||
|
||||
- Prevent compiler warning about deprecated B constants
|
||||
- Fix Bug that did not let us transmit 5 byte sized packets - 5 Byte commands like PoM (programming on main) were not being sent correctly
|
||||
- Support for Huge function numbers (DCC BinaryStateControl) - Support Functions beyond F28
|
||||
- <!> ESTOP all - New command to emergency stop all locos on the main track
|
||||
- <- [cab]> estop and forget cab/all cabs - Stop and remove loco from the CS. Stops the repeating throttle messages
|
||||
- `<D RESET>` command to reboot Arduino
|
||||
- Automatic sensor offset detect
|
||||
- Improved startup msgs from Motor Drivers (accuracy and auto sense factors)
|
||||
- Drop post-write verify - No need to double check CV writes. Writes are now even faster.
|
||||
- Allow current sense pin set to UNUSED_PIN - No need to ground an unused analog current pin. Produce startup warning and callback -2 for prog track cmds.
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.5**
|
||||
|
||||
- Fix Fn Key startup with loco ID and fix state change for F16-28
|
||||
- Removed ethernet mac config and made it automatic
|
||||
- Show wifi ip and port on lcd
|
||||
- Auto load config.example.h with warning
|
||||
- Dropped example .ino files
|
||||
- Corrected .ino comments
|
||||
- Add Pololu fault pin handling
|
||||
- Waveform speed/simplicity improvements
|
||||
- Improved pin speed in waveform
|
||||
- Portability to nanoEvery and UnoWifiRev2 CPUs
|
||||
- Analog read speed improvements
|
||||
- Drop need for DIO2 library
|
||||
- Improved current check code
|
||||
- Linear command
|
||||
- Removed need for ArduinoTimers files
|
||||
- Removed option to choose different timer
|
||||
- Added EX-RAIL hooks for automation in future version
|
||||
- Fixed Turnout list
|
||||
- Allow command keywords in mixed case
|
||||
- Dropped unused memstream
|
||||
- PWM pin accuracy if requirements met
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.4**
|
||||
|
||||
- "Drive-Away" Feature - added so that throttles like Engine Driver can allow a loco to be programmed on a usable, electrically isolated programming track and then drive off onto the main track
|
||||
- WiFi Startup Fixes
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.3**
|
||||
|
||||
- Command to write loco address and clear consist
|
||||
- Command will allow for consist address
|
||||
- Startup commands implemented
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.2:**
|
||||
|
||||
- Create new output for current in mA for `<c>` command - New current response outputs current in mA, overlimit current, and maximum board capable current
|
||||
- Simultaneously update JMRI to handle new current meter
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.1:**
|
||||
|
||||
- Add back fix for jitter
|
||||
- Add Turnouts, Outputs and Sensors to `<s>` command output
|
||||
|
||||
**CommandStation-EX V3.0.0:**
|
||||
|
||||
**Release v3.0.0 was a major rewrite if earlier versions of DCC++. The code base was re-architeced and core changes were made to the Waveform generator to reduce overhead and make better use of Arduino.** **Summary of the key new features added in Release v3.0.0 include:**
|
||||
|
||||
- **New USB Browser Based Throttle** - WebThrottle-EX is a full front-end to controller to control the CS to run trains.
|
||||
- **WiFi Support** - AP and station modes supported. Auto-detection of an ESP8266 WiFi module with AT firmware on a Mega's serial port. Connection to JMRI and WiThrottle clients.
|
||||
- **Withrottle Integrations** - Act as a host for up to four WiThrottle clients concurrently.
|
||||
- **Add LCD/OLED support** - OLED supported on Mega only
|
||||
- **Improved CV programming routines** - checks for length of CV pulse, and breaks out of the wait state once it has received an ACK, now reading one CV per second.
|
||||
- **Improved current sensing** - rewrote current sensing routines for safer operation. Current thresholds based on milliamps, not magic numbers
|
||||
- **Individual track power control** - Ability to toggle power on either or both tracks, and to "JOIN" the tracks and make them output the same waveform for multiple power districts.
|
||||
- **Single or Dual-Pin PWM output** - Allows control of H-bridges with PH/EN or dual PWM inputs
|
||||
- **New, simpler function command** - `<F>` command allows setting functions based on their number, not based on a code as in `<f>`
|
||||
- **Function reminders** - Function reminders are sent in addition to speed reminders
|
||||
- **Functions to F28** - All NMRA functions are now supported
|
||||
- **Filters and user functions** - Ability to filter commands in the parser and execute custom code based on them. (ex: Redirect Turnout commands via NRF24)
|
||||
- **Diagnostic `<D>` commands** - See documentation for a full list of new diagnostic commands
|
||||
- **Rewrote DCC++ Parser** - more efficient operation, accepts multi-char input and uses less RAM
|
||||
- **Rewritten waveform generator** - capable of using any pin for DCC waveform out, eliminating the need for jumpers
|
||||
- **Rewritten packet generator** - Simplify and make smaller, remove idea of "registers" from original code
|
||||
- **Add free RAM messages** - Free RAM messages are now printed whenever there is a decerase in available RAM
|
||||
- **Fix EEPROM bugs**
|
||||
- **Number of locos discovery command** - `<#>` command
|
||||
- **Support for more locomotives** - 20 locomotives on an UNO and 50 an a Mega.
|
||||
- **Automatic slot management** - slot variable in throttle/function commands are ignored and slot management is taken care of automatically. `<->` and `<- CAB>` commands added to release locos from memory and stop packets to the track.
|
||||
|
||||
**Key Contributors**
|
||||
|
||||
**Project Lead**
|
||||
|
||||
- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk)
|
||||
|
||||
**CommandStation-EX Developers**
|
||||
|
||||
- Chris Harlow - Bournemouth, UK (UKBloke)
|
||||
- Harald Barth - Stockholm, Sweden (Haba)
|
||||
- Neil McKechnie - Worcestershire, UK (NeilMck)
|
||||
- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk)
|
||||
- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting)
|
||||
- M Steve Todd - Oregon, USA (MSteveTodd)
|
||||
- Scott Catalano - Pennsylvania
|
||||
- Gregor Baues - Île-de-France, France (grbba)
|
||||
|
||||
**Engine Driver and JMRI Interface**
|
||||
|
||||
- M Steve Todd
|
||||
|
||||
**exInstaller Software**
|
||||
|
||||
- Anthony W - Dayton, Ohio, USA (Dex, Dex++)
|
||||
|
||||
**Website and Documentation**
|
||||
|
||||
- Mani Kumar - Bangalor, India (Mani / Mani Kumar)
|
||||
- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk)
|
||||
- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting)
|
||||
- Roger Beschizza - Dorset, UK (Roger Beschizza)
|
||||
- Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter)
|
||||
- Kevin Smith - Rochester Hills, Michigan USA (KC Smith)
|
||||
- Colin Grabham - Central NSW, Australia (Kebbin)
|
||||
|
||||
**WebThrotle-EX**
|
||||
|
||||
- Fred Decker - Holly Springs, NC (FlightRisk/FrightRisk)
|
||||
- Mani Kumar - Bangalor, India (Mani /Mani Kumar)
|
||||
- Matt H - Somewhere in Europe
|
||||
|
||||
**Beta Testing / Release Management / Support**
|
||||
|
||||
- Larry Dribin - Release Management
|
||||
- Kevin Smith - Rochester Hills, Michigan USA (KC Smith)
|
||||
- Herb Morton - Kingwood Texas, USA (Ash++)
|
||||
- Keith Ledbetter
|
||||
- Brad Van der Elst
|
||||
- Andrew Pye
|
||||
- Mike Bowers
|
||||
- Randy McKenzie
|
||||
- Roberto Bravin
|
||||
- Sam Brigden
|
||||
- Alan Lautenslager
|
||||
- Martin Bafver
|
||||
- Mário André Silva
|
||||
- Anthony Kochevar
|
||||
- Gajanatha Kobbekaduwe
|
||||
- Sumner Patterson
|
||||
- Paul - Virginia, USA
|
||||
|
||||
**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.**
|
||||
|
||||
[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip)
|
||||
|
||||
|
||||
[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.tar.gz)
|
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* © 2021 Chris Harlow
|
||||
* © 2022 Harald Barth
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is part of DCC++EX
|
||||
@@ -19,29 +20,33 @@
|
||||
*/
|
||||
|
||||
#include "SerialManager.h"
|
||||
#include "DCCEXParser.h"
|
||||
SerialManager * SerialManager::first=NULL;
|
||||
#include "DCCEXParser.h"
|
||||
SerialManager * SerialManager::first=NULL;
|
||||
|
||||
SerialManager::SerialManager(HardwareSerial * myserial) {
|
||||
serial=myserial;
|
||||
next=first;
|
||||
first=this;
|
||||
bufferLength=0;
|
||||
myserial->begin(115200);
|
||||
inCommandPayload=false;
|
||||
}
|
||||
SerialManager::SerialManager(Stream * myserial) {
|
||||
serial=myserial;
|
||||
next=first;
|
||||
first=this;
|
||||
bufferLength=0;
|
||||
inCommandPayload=false;
|
||||
}
|
||||
|
||||
void SerialManager::init() {
|
||||
while (!Serial && millis() < 5000); // wait max 5s for Serial to start
|
||||
Serial.begin(115200);
|
||||
new SerialManager(&Serial);
|
||||
#ifdef SERIAL3_COMMANDS
|
||||
new SerialManager(&Serial3);
|
||||
Serial3.begin(115200);
|
||||
new SerialManager(&Serial3);
|
||||
#endif
|
||||
#ifdef SERIAL2_COMMANDS
|
||||
new SerialManager(&Serial2);
|
||||
Serial2.begin(115200);
|
||||
new SerialManager(&Serial2);
|
||||
#endif
|
||||
#ifdef SERIAL1_COMMANDS
|
||||
new SerialManager(&Serial1);
|
||||
Serial1.begin(115200);
|
||||
new SerialManager(&Serial1);
|
||||
#endif
|
||||
new SerialManager(&Serial);
|
||||
}
|
||||
|
||||
void SerialManager::broadcast(RingStream * ring) {
|
||||
|
@@ -37,10 +37,10 @@ public:
|
||||
|
||||
private:
|
||||
static SerialManager * first;
|
||||
SerialManager(HardwareSerial * myserial);
|
||||
SerialManager(Stream * myserial);
|
||||
void loop2();
|
||||
void broadcast2(RingStream * ring);
|
||||
HardwareSerial * serial;
|
||||
Stream * serial;
|
||||
SerialManager * next;
|
||||
byte bufferLength;
|
||||
byte buffer[COMMAND_BUFFER_SIZE];
|
||||
|
@@ -99,6 +99,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
||||
case 'E': printEscapes(stream,(const FSH*)va_arg(args, char*)); break;
|
||||
case 'S': stream->print((const FSH*)va_arg(args, char*)); break;
|
||||
case 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break;
|
||||
case 'u': printPadded(stream,va_arg(args, unsigned int), formatWidth, formatLeft); break;
|
||||
case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break;
|
||||
case 'b': stream->print(va_arg(args, int), BIN); break;
|
||||
case 'o': stream->print(va_arg(args, int), OCT); break;
|
||||
|
@@ -30,7 +30,7 @@
|
||||
#endif
|
||||
#include "StringFormatter.h"
|
||||
#include "CommandDistributor.h"
|
||||
#include "RMFT2.h"
|
||||
#include "EXRAIL2.h"
|
||||
#include "Turnouts.h"
|
||||
#include "DCC.h"
|
||||
#include "LCN.h"
|
||||
@@ -114,7 +114,7 @@
|
||||
|
||||
// I know it says setClosedStateOnly, but we need to tell others
|
||||
// that the state has changed too.
|
||||
#if defined(RMFT_ACTIVE)
|
||||
#if defined(EXRAIL_ACTIVE)
|
||||
RMFT2::turnoutEvent(id, closeFlag);
|
||||
#endif
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
EEPROM.put(tt->_eepromAddress, tt->_turnoutData.flags);
|
||||
#endif
|
||||
|
||||
#if defined(RMFT_ACTIVE)
|
||||
#if defined(EXRAIL_ACTIVE)
|
||||
RMFT2::turnoutEvent(id, closeFlag);
|
||||
#endif
|
||||
|
||||
|
@@ -53,7 +53,7 @@
|
||||
#include "DIAG.h"
|
||||
#include "GITHUB_SHA.h"
|
||||
#include "version.h"
|
||||
#include "RMFT2.h"
|
||||
#include "EXRAIL2.h"
|
||||
#include "CommandDistributor.h"
|
||||
|
||||
#define LOOPLOCOS(THROTTLECHAR, CAB) for (int loco=0;loco<MAX_MY_LOCO;loco++) \
|
||||
@@ -121,7 +121,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||
for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){
|
||||
int id=tt->getId();
|
||||
StringFormatter::send(stream,F("]\\[%d}|{"), id);
|
||||
#ifdef RMFT_ACTIVE
|
||||
#ifdef EXRAIL_ACTIVE
|
||||
RMFT2::emitTurnoutDescription(stream,id);
|
||||
#else
|
||||
StringFormatter::send(stream,F("%d"), id);
|
||||
@@ -133,9 +133,9 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||
}
|
||||
|
||||
else if (!exRailSent) {
|
||||
// Send ExRail routes list if not already sent (but not at same time as turnouts above)
|
||||
// Send EX-RAIL routes list if not already sent (but not at same time as turnouts above)
|
||||
exRailSent=true;
|
||||
#ifdef RMFT_ACTIVE
|
||||
#ifdef EXRAIL_ACTIVE
|
||||
RMFT2::emitWithrottleRouteList(stream);
|
||||
#endif
|
||||
// allow heartbeat to slow down once all metadata sent
|
||||
@@ -156,7 +156,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||
DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
||||
CommandDistributor::broadcastPower();
|
||||
}
|
||||
#if defined(RMFT_ACTIVE)
|
||||
#if defined(EXRAIL_ACTIVE)
|
||||
else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate
|
||||
// exrail routes are RA2Rn , Animations are RA2An
|
||||
int route=getInt(cmd+5);
|
||||
@@ -205,7 +205,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||
StringFormatter::send(stream,F("HtDCC-EX v%S, %S, %S, %S\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA));
|
||||
StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n"));
|
||||
StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON);
|
||||
#ifdef RMFT_ACTIVE
|
||||
#ifdef EXRAIL_ACTIVE
|
||||
RMFT2::emitWithrottleRoster(stream);
|
||||
#endif
|
||||
// set heartbeat to 1 second because we need to sync the metadata
|
||||
@@ -287,7 +287,7 @@ void WiThrottle::multithrottle(RingStream * stream, byte * cmd){
|
||||
int fkeys=29;
|
||||
myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle
|
||||
|
||||
#ifdef RMFT_ACTIVE
|
||||
#ifdef EXRAIL_ACTIVE
|
||||
const char * functionNames=(char *) RMFT2::getRosterFunctions(locoid);
|
||||
if (!functionNames) {
|
||||
// no roster, use presets as above
|
||||
@@ -514,25 +514,32 @@ char WiThrottle::stashThrottleChar;
|
||||
void WiThrottle::getLocoCallback(int16_t locoid) {
|
||||
stashStream->mark(stashClient);
|
||||
|
||||
if (locoid<=0)
|
||||
if (locoid<=0) {
|
||||
StringFormatter::send(stashStream,F("HMNo loco found on prog track\n"));
|
||||
else {
|
||||
// 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 %d <= %d not supported\n"), locoid,HIGHEST_SHORT_ADDR);
|
||||
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(); // done here, commit and return
|
||||
return;
|
||||
}
|
||||
|
||||
// short or long
|
||||
char addrchar;
|
||||
if (locoid & LONG_ADDR_MARKER) { // maker bit indicates long addr
|
||||
locoid = locoid ^ LONG_ADDR_MARKER; // remove marker bit to get real long addr
|
||||
if (locoid <= HIGHEST_SHORT_ADDR ) { // out of range for long addr
|
||||
StringFormatter::send(stashStream,F("HMLong addr %d <= %d unsupported\n"), locoid, HIGHEST_SHORT_ADDR);
|
||||
stashStream->commit(); // done here, commit and return
|
||||
return;
|
||||
}
|
||||
addrchar = 'L';
|
||||
} else {
|
||||
addrchar = 'S';
|
||||
}
|
||||
|
||||
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();
|
||||
CommandDistributor::broadcastPower();
|
||||
|
||||
}
|
||||
|
@@ -1,25 +1,27 @@
|
||||
/*
|
||||
* © 2021 Fred Decker
|
||||
* © 2021 Fred Decker
|
||||
* © 2020-2021 Chris Harlow
|
||||
* © 2020, Chris Harlow. All rights reserved.
|
||||
* © 2020, Harald Barth.
|
||||
*
|
||||
* This file is part of Asbelos DCC API
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
© 2022 Mark Muzzin
|
||||
© 2021 Fred Decker
|
||||
© 2021 Fred Decker
|
||||
© 2020-2021 Chris Harlow
|
||||
© 2020, Chris Harlow. All rights reserved.
|
||||
© 2020, Harald Barth.
|
||||
|
||||
This file is part of Asbelos DCC API
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO_AVR_UNO_WIFI_REV2
|
||||
#include <Arduino.h>
|
||||
#include "WifiInboundHandler.h"
|
||||
@@ -30,7 +32,7 @@
|
||||
WifiInboundHandler * WifiInboundHandler::singleton;
|
||||
|
||||
void WifiInboundHandler::setup(Stream * ESStream) {
|
||||
singleton=new WifiInboundHandler(ESStream);
|
||||
singleton = new WifiInboundHandler(ESStream);
|
||||
}
|
||||
|
||||
void WifiInboundHandler::loop() {
|
||||
@@ -39,221 +41,328 @@ void WifiInboundHandler::loop() {
|
||||
|
||||
|
||||
WifiInboundHandler::WifiInboundHandler(Stream * ESStream) {
|
||||
wifiStream=ESStream;
|
||||
clientPendingCIPSEND=-1;
|
||||
inboundRing=new RingStream(INBOUND_RING);
|
||||
outboundRing=new RingStream(OUTBOUND_RING);
|
||||
pendingCipsend=false;
|
||||
}
|
||||
wifiStream = ESStream;
|
||||
clientPendingCIPSEND = -1;
|
||||
inboundRing = new RingStream(INBOUND_RING);
|
||||
outboundRing = new RingStream(OUTBOUND_RING);
|
||||
cipSendStatus = CIP_SEND_NONE;
|
||||
}
|
||||
|
||||
|
||||
// Handle any inbound transmission
|
||||
// +IPD,x,lll:data is stored in streamer[x]
|
||||
// Other input returns
|
||||
void WifiInboundHandler::loop1() {
|
||||
// First handle all inbound traffic events because they will block the sending
|
||||
if (loop2()!=INBOUND_IDLE) return;
|
||||
// Other input returns
|
||||
void WifiInboundHandler::loop1()
|
||||
{
|
||||
// First handle all inbound traffic events because they will block the sending
|
||||
if (loop2() != INBOUND_IDLE) return;
|
||||
|
||||
WiThrottle::loop(outboundRing);
|
||||
|
||||
// if nothing is already CIPSEND pending, we can CIPSEND one reply
|
||||
if (clientPendingCIPSEND<0) {
|
||||
clientPendingCIPSEND=outboundRing->read();
|
||||
if (clientPendingCIPSEND>=0) {
|
||||
currentReplySize=outboundRing->count();
|
||||
pendingCipsend=true;
|
||||
}
|
||||
}
|
||||
|
||||
WiThrottle::loop(outboundRing);
|
||||
|
||||
if (pendingCipsend) {
|
||||
if (Diag::WIFI) DIAG( F("WiFi: [[CIPSEND=%d,%d]]"), clientPendingCIPSEND, currentReplySize);
|
||||
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), clientPendingCIPSEND, currentReplySize);
|
||||
pendingCipsend=false;
|
||||
return;
|
||||
// If no sends are pending we can check if there is any more data to send
|
||||
if (cipSendStatus == CIP_SEND_NONE) {
|
||||
|
||||
// If nothing is already CIPSEND pending, we can CIPSEND one reply
|
||||
if (clientPendingCIPSEND < 0) {
|
||||
|
||||
//Read the next character in the outbound ring. If its -1, no data in the ring
|
||||
clientPendingCIPSEND = outboundRing->read();
|
||||
|
||||
//If there is data in the ring
|
||||
if (clientPendingCIPSEND >= 0 ) {
|
||||
if (Diag::WIFI) { ts.cipSendStart = millis(); DIAG( F("cipSendStatus = CIP_SEND_PENDING")); }
|
||||
//Get the length of the reply to send
|
||||
currentReplySize = outboundRing->count();
|
||||
//Set the pending CIPSEND flag
|
||||
cipSendStatus = CIP_SEND_PENDING;
|
||||
}
|
||||
|
||||
|
||||
// if something waiting to execute, we can call it
|
||||
int clientId=inboundRing->read();
|
||||
if (clientId>=0) {
|
||||
int count=inboundRing->count();
|
||||
if (Diag::WIFI) DIAG(F("Wifi EXEC: %d %d:"),clientId,count);
|
||||
byte cmd[count+1];
|
||||
for (int i=0;i<count;i++) cmd[i]=inboundRing->read();
|
||||
cmd[count]=0;
|
||||
if (Diag::WIFI) DIAG(F("%e"),cmd);
|
||||
|
||||
outboundRing->mark(clientId); // remember start of outbound data
|
||||
CommandDistributor::parse(clientId,cmd,outboundRing);
|
||||
// The commit call will either write the lenbgth bytes
|
||||
// OR rollback to the mark because the reply is empty or commend generated more than fits the buffer
|
||||
if (!outboundRing->commit()) {
|
||||
DIAG(F("OUTBOUND FULL processing cmd:%s"),cmd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cipSendStatus == CIP_SEND_PENDING) {
|
||||
if (Diag::WIFI) DIAG( F("WiFi: [[CIPSEND=%d,%d]]"), clientPendingCIPSEND, currentReplySize);
|
||||
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), clientPendingCIPSEND, currentReplySize);
|
||||
if (Diag::WIFI) DIAG( F("cipSendStatus = CIP_SEND_SENT [%lms]"),millis()-ts.cipSendStart);
|
||||
cipSendStatus = CIP_SEND_SENT;
|
||||
return;
|
||||
}
|
||||
|
||||
// if something waiting to execute, we can call it
|
||||
int clientId = inboundRing->read();
|
||||
if (clientId >= 0) {
|
||||
int count = inboundRing->count();
|
||||
if (Diag::WIFI) DIAG(F("Wifi EXEC: %d %d:"), clientId, count);
|
||||
byte cmd[count + 1];
|
||||
for (int i = 0; i < count; i++) cmd[i] = inboundRing->read();
|
||||
cmd[count] = 0;
|
||||
if (Diag::WIFI) DIAG(F("%e"), cmd);
|
||||
|
||||
outboundRing->mark(clientId); // remember start of outbound data
|
||||
CommandDistributor::parse(clientId, cmd, outboundRing);
|
||||
|
||||
// The commit call will either write the length bytes
|
||||
// OR rollback to the mark because the reply is empty or commend generated more than fits the buffer
|
||||
if (outboundRing->commit() == false) {
|
||||
DIAG(F("OUTBOUND FULL processing cmd:%s"), cmd);
|
||||
outboundRing->flush();
|
||||
inboundRing->flush();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed length atoi
|
||||
int WifiInboundHandler::antoi(char* str, int len) {
|
||||
int res = 0;
|
||||
|
||||
for (int i = 0; i != len; ++i)
|
||||
res = res * 10 + str[i] - '0';
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This is a Finite State Automation (FSA) handling the inbound bytes from an ES AT command processor
|
||||
|
||||
// This loop processes the inbound bytes from an ES AT command processor
|
||||
WifiInboundHandler::INBOUND_STATE WifiInboundHandler::loop2() {
|
||||
while (wifiStream->available()) {
|
||||
int ch = wifiStream->read();
|
||||
|
||||
// echo the char to the diagnostic stream in escaped format
|
||||
if (Diag::WIFI) {
|
||||
// DIAG(F(" %d/"), loopState);
|
||||
StringFormatter::printEscape(ch); // DIAG in disguise
|
||||
INBOUND_STATE retVal = INBOUND_IDLE;
|
||||
char *stringStartPos = NULL;
|
||||
char *stringEndPos = NULL;
|
||||
char ch = 0;
|
||||
int i;
|
||||
bool exitLoop = false;
|
||||
|
||||
//Main message input loop
|
||||
//This loop fills the small receive buffer one message at a time
|
||||
//The execption is the +IPD message. Only the data up to the ':' character (end of length)
|
||||
//is put in this buffer. The actual +IPD data is written directly into the inbound buffer
|
||||
while (!exitLoop)
|
||||
{
|
||||
//We exit this loop either if there is no data available, or we have a complete message to process
|
||||
while (wifiStream->available() && messageToProcess == RECV_MSG_NONE)
|
||||
{
|
||||
//Read next character
|
||||
ch = wifiStream->read();
|
||||
|
||||
//Add to receive buffer if there is space
|
||||
if (recBufferPos < INBOUND_CMD_BUFFER) {
|
||||
recBuffer[recBufferPos++] = ch;
|
||||
}
|
||||
else {
|
||||
if (Diag::WIFI) DIAG(F("WARN: Message too long for INBOUND BUFFER, purging"));
|
||||
memset(recBuffer, 0, INBOUND_CMD_BUFFER);
|
||||
recBufferPos = 0;
|
||||
}
|
||||
|
||||
if (Diag::WIFI) StringFormatter::printEscape(ch);
|
||||
|
||||
//Process data ending in \r\n
|
||||
if ((recBuffer[recBufferPos - 2] == '\r') && (recBuffer[recBufferPos - 1] == '\n')) {
|
||||
messageToProcess = RECV_MSG_CRLF;
|
||||
}
|
||||
|
||||
//Ready to send data, will always be the first char in the message
|
||||
else if (recBuffer[0] == '>') {
|
||||
messageToProcess = RECV_MSG_SND_DATA;
|
||||
}
|
||||
|
||||
//IPD message
|
||||
else if ((stringStartPos = strstr(recBuffer, "+IPD,")) != NULL && (stringEndPos = strstr(recBuffer, ":")) != NULL) {
|
||||
|
||||
if (Diag::WIFI) { ts.ipdStart = millis(); DIAG( F("+IPD Message START")); }
|
||||
|
||||
//Advance the start string to the beginning of the length
|
||||
stringStartPos += strlen("+IPD,");
|
||||
|
||||
//Get the end of the client ID
|
||||
stringEndPos = strstr(stringStartPos, ",");
|
||||
|
||||
//Get the client Id
|
||||
runningClientId = antoi(stringStartPos, stringEndPos - stringStartPos);
|
||||
|
||||
//Get the data length
|
||||
stringStartPos = stringEndPos + 1;
|
||||
stringEndPos = strstr(stringStartPos, ":");
|
||||
dataLength = antoi(stringStartPos, stringEndPos - stringStartPos);
|
||||
|
||||
if(dataLength > 0) {
|
||||
//Set the remaining data length
|
||||
dataRemaining = dataLength;
|
||||
messageToProcess = RECV_MSG_IPD_MSG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (loopState) {
|
||||
case ANYTHING: // looking for +IPD, > , busy , n,CONNECTED, n,CLOSED, ERROR, SEND OK
|
||||
|
||||
if (ch == '+') {
|
||||
loopState = IPD;
|
||||
break;
|
||||
//Process messages
|
||||
switch (messageToProcess) {
|
||||
case RECV_MSG_NONE:
|
||||
//Check if the buffer position is not zero
|
||||
if (recBufferPos != 0) {
|
||||
//No message to process, but buffer has some data already
|
||||
retVal = INBOUND_BUSY;
|
||||
}
|
||||
|
||||
if (ch=='>') {
|
||||
if (Diag::WIFI) DIAG(F("[XMIT %d]"),currentReplySize);
|
||||
for (int i=0;i<currentReplySize;i++) {
|
||||
int cout=outboundRing->read();
|
||||
wifiStream->write(cout);
|
||||
if (Diag::WIFI) StringFormatter::printEscape(cout); // DIAG in disguise
|
||||
}
|
||||
clientPendingCIPSEND=-1;
|
||||
pendingCipsend=false;
|
||||
loopState=SKIPTOEND;
|
||||
break;
|
||||
else {
|
||||
//No message to process, no data in buffer
|
||||
retVal = INBOUND_IDLE;
|
||||
}
|
||||
|
||||
if (ch=='R') { // Received ... bytes
|
||||
loopState=SKIPTOEND;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch=='S') { // SEND OK probably
|
||||
loopState=SKIPTOEND;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch=='b') { // This is a busy indicator... probabaly must restart a CIPSEND
|
||||
pendingCipsend=(clientPendingCIPSEND>=0);
|
||||
loopState=SKIPTOEND;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch>='0' && ch<='9') {
|
||||
runningClientId=ch-'0';
|
||||
loopState=GOT_CLIENT_ID;
|
||||
break;
|
||||
exitLoop = true;
|
||||
break;
|
||||
|
||||
case RECV_MSG_CRLF:
|
||||
|
||||
//Check for a SEND FAIL message
|
||||
if (strstr(recBuffer, "SEND FAIL") != NULL) {
|
||||
if (Diag::WIFI) DIAG(F("[SEND FAIL - detected"));
|
||||
if (cipSendStatus == CIP_SEND_DATA_SENT) {
|
||||
if (Diag::WIFI) DIAG( F("cipSendStatus = CIP_SEND_NONE [%lms]"),millis()-ts.cipSendStart);
|
||||
cipSendStatus = CIP_SEND_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch=='E' || ch=='l') { // ERROR or "link is not valid"
|
||||
if (clientPendingCIPSEND>=0) {
|
||||
//Check for a SEND OK message
|
||||
else if (strstr(recBuffer, "SEND OK") != NULL) {
|
||||
if (Diag::WIFI) DIAG(F("[SEND OK - detected [%lms]"),millis()-ts.cipSendStart);
|
||||
if (cipSendStatus == CIP_SEND_DATA_SENT) {
|
||||
if (Diag::WIFI) DIAG( F("cipSendStatus = CIP_SEND_NONE [%lms]"),millis()-ts.cipSendStart);
|
||||
cipSendStatus = CIP_SEND_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
//Check for an error
|
||||
else if (strstr(recBuffer, "ERROR") != NULL) {
|
||||
if (clientPendingCIPSEND >= 0) {
|
||||
if (Diag::WIFI) DIAG(F("[ERROR detected - purging CIPSEND"));
|
||||
// A CIPSEND was errored... just toss it away
|
||||
purgeCurrentCIPSEND();
|
||||
purgeCurrentCIPSEND();
|
||||
}
|
||||
loopState=SKIPTOEND;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPD: // Looking for I in +IPD
|
||||
loopState = (ch == 'I') ? IPD1 : SKIPTOEND;
|
||||
break;
|
||||
|
||||
case IPD1: // Looking for P in +IPD
|
||||
loopState = (ch == 'P') ? IPD2 : SKIPTOEND;
|
||||
break;
|
||||
|
||||
case IPD2: // Looking for D in +IPD
|
||||
loopState = (ch == 'D') ? IPD3 : SKIPTOEND;
|
||||
break;
|
||||
|
||||
case IPD3: // Looking for , After +IPD
|
||||
loopState = (ch == ',') ? IPD4_CLIENT : SKIPTOEND;
|
||||
break;
|
||||
|
||||
case IPD4_CLIENT: // reading connection id
|
||||
if (ch >= '0' || ch <='9'){
|
||||
runningClientId=ch-'0';
|
||||
loopState=IPD5;
|
||||
|
||||
//Check for an tcp connection closed
|
||||
else if (strstr(recBuffer, "link is not valid") != NULL) {
|
||||
if (clientPendingCIPSEND >= 0) {
|
||||
if (Diag::WIFI) DIAG(F("['link is not valid' detected - purging CIPSEND]"));
|
||||
purgeCurrentCIPSEND();
|
||||
//Clear out all data from esp8266 receive buffer
|
||||
while (wifiStream->available()) {
|
||||
wifiStream->read();
|
||||
}
|
||||
}
|
||||
}
|
||||
else loopState=SKIPTOEND;
|
||||
break;
|
||||
|
||||
case IPD5: // Looking for , After +IPD,client
|
||||
loopState = (ch == ',') ? IPD6_LENGTH : SKIPTOEND;
|
||||
dataLength=0; // ready to start collecting the length
|
||||
|
||||
case RECV_MSG_SND_DATA:
|
||||
if (Diag::WIFI) DIAG( F("cipSendStatus = CIP_SEND_DATA_SENT [XMIT START %d] [%lms]"),currentReplySize, millis()-ts.cipSendStart);
|
||||
for (i = 0; i < currentReplySize; i++)
|
||||
{
|
||||
//Read data from the outboundRing and write to to Wifi
|
||||
int cout = outboundRing->read();
|
||||
wifiStream->write(cout);
|
||||
if (Diag::WIFI) StringFormatter::printEscape(cout); // DIAG in disguise
|
||||
}
|
||||
|
||||
//Set CIPSEND flag to -1
|
||||
clientPendingCIPSEND = -1;
|
||||
if (Diag::WIFI) DIAG( F("cipSendStatus = CIP_SEND_DATA_SENT [XMIT END] [%lms]"),millis()-ts.cipSendStart);
|
||||
cipSendStatus = CIP_SEND_DATA_SENT;
|
||||
break;
|
||||
|
||||
case IPD6_LENGTH: // reading for length
|
||||
if (ch == ':') {
|
||||
if (dataLength==0) {
|
||||
loopState=ANYTHING;
|
||||
break;
|
||||
}
|
||||
if (Diag::WIFI) DIAG(F("Wifi inbound data(%d:%d):"),runningClientId,dataLength);
|
||||
if (inboundRing->freeSpace()<=(dataLength+1)) {
|
||||
// This input would overflow the inbound ring, ignore it
|
||||
loopState=IPD_IGNORE_DATA;
|
||||
if (Diag::WIFI) DIAG(F("Wifi OVERFLOW IGNORING:"));
|
||||
break;
|
||||
}
|
||||
|
||||
case RECV_MSG_IPD_MSG:
|
||||
|
||||
//Mark the location with the client ID
|
||||
if (dataRemaining == dataLength) {
|
||||
if (Diag::WIFI) DIAG(F("Wifi inbound data(%d:%d) [%lms]:"), runningClientId, dataLength, millis()-ts.ipdStart);
|
||||
inboundRing->mark(runningClientId);
|
||||
loopState=IPD_DATA;
|
||||
break;
|
||||
}
|
||||
dataLength = dataLength * 10 + (ch - '0');
|
||||
break;
|
||||
|
||||
case IPD_DATA: // reading data
|
||||
inboundRing->write(ch);
|
||||
dataLength--;
|
||||
if (dataLength == 0) {
|
||||
inboundRing->commit();
|
||||
loopState = ANYTHING;
|
||||
|
||||
while (wifiStream->available()) {
|
||||
//Read next character (data byte)
|
||||
ch = wifiStream->read();
|
||||
if (Diag::WIFI) StringFormatter::printEscape(ch);
|
||||
if (Diag::WIFI) DIAG(F("[+IPD %d]"), dataRemaining);
|
||||
dataRemaining--;
|
||||
|
||||
//Check if we would overflow the inbound ring
|
||||
if (inboundRing->freeSpace() <= (dataLength + 1))
|
||||
{
|
||||
DIAG(F("Wifi OVERFLOW IGNORING:"));
|
||||
|
||||
//If we have an overflow, flush the remaining bytes
|
||||
do{
|
||||
ch = wifiStream->read();
|
||||
}while(dataRemaining--);
|
||||
|
||||
outboundRing->flush();
|
||||
inboundRing->flush();
|
||||
|
||||
messageToProcess = RECV_MSG_IPD_DONE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write data to inbound ring
|
||||
inboundRing->write(ch);
|
||||
|
||||
// Commit inbound ring data
|
||||
if (dataRemaining == 0) {
|
||||
if (Diag::WIFI) DIAG(F("COMMIT [%lms]:"),millis()-ts.ipdStart);
|
||||
if(inboundRing->commit() == false) {
|
||||
outboundRing->flush();
|
||||
inboundRing->flush();
|
||||
}
|
||||
messageToProcess = RECV_MSG_IPD_DONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IPD_IGNORE_DATA: // ignoring data that would not fit in inbound ring
|
||||
dataLength--;
|
||||
if (dataLength == 0) loopState = ANYTHING;
|
||||
break;
|
||||
|
||||
case GOT_CLIENT_ID: // got x before CLOSE or CONNECTED
|
||||
loopState=(ch==',') ? GOT_CLIENT_ID2: SKIPTOEND;
|
||||
break;
|
||||
|
||||
case GOT_CLIENT_ID2: // got "x,"
|
||||
if (ch=='C') {
|
||||
// got "x C" before CLOSE or CONNECTED, or CONNECT FAILED
|
||||
if (runningClientId==clientPendingCIPSEND) purgeCurrentCIPSEND();
|
||||
else CommandDistributor::forget(runningClientId);
|
||||
//Catch-all for messages we don't care about
|
||||
default:
|
||||
//Check if the buffer position is not zero
|
||||
if (recBufferPos != 0)
|
||||
{
|
||||
//In process of receiving, return with busy so we can wait for the remaining part of the message
|
||||
retVal = INBOUND_BUSY;
|
||||
}
|
||||
else {
|
||||
// Not receiving a message so this message was not important
|
||||
messageToProcess = RECV_MSG_GENERIC;
|
||||
}
|
||||
loopState=SKIPTOEND;
|
||||
break;
|
||||
|
||||
case SKIPTOEND: // skipping for /n
|
||||
if (ch=='\n') loopState=ANYTHING;
|
||||
break;
|
||||
} // switch
|
||||
} // available
|
||||
return (loopState==ANYTHING) ? INBOUND_IDLE: INBOUND_BUSY;
|
||||
}
|
||||
|
||||
//If we processed a message, clear and get ready for the next message
|
||||
if (messageToProcess != RECV_MSG_NONE && messageToProcess != RECV_MSG_IPD_MSG) {
|
||||
//Reset buffer to start processing next message
|
||||
memset(recBuffer, 0, INBOUND_CMD_BUFFER);
|
||||
//Reset receive buffer position
|
||||
recBufferPos = 0;
|
||||
//Reset message to process
|
||||
messageToProcess = RECV_MSG_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void WifiInboundHandler::purgeCurrentCIPSEND() {
|
||||
// A CIPSEND was sent but errored... or the client closed just toss it away
|
||||
CommandDistributor::forget(clientPendingCIPSEND);
|
||||
DIAG(F("Wifi: DROPPING CIPSEND=%d,%d"),clientPendingCIPSEND,currentReplySize);
|
||||
for (int i=0;i<currentReplySize;i++) outboundRing->read();
|
||||
pendingCipsend=false;
|
||||
clientPendingCIPSEND=-1;
|
||||
// A CIPSEND was sent but errored... or the client closed just toss it away
|
||||
//CommandDistributor::forget(clientPendingCIPSEND);
|
||||
DIAG(F("Wifi: DROPPING CIPSEND=%d,%d"), clientPendingCIPSEND, currentReplySize);
|
||||
for (int i = 0; i < currentReplySize; i++) {
|
||||
outboundRing->read();
|
||||
}
|
||||
|
||||
//If CIP SEND was sent, flush out by writing 0s */
|
||||
if (cipSendStatus == CIP_SEND_SENT) {
|
||||
for (int i = 0; i < currentReplySize; i++) {
|
||||
int c = 0;
|
||||
wifiStream->write(c);
|
||||
}
|
||||
}
|
||||
// Clear CIP send status
|
||||
cipSendStatus = CIP_SEND_NONE;
|
||||
clientPendingCIPSEND = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,24 +1,25 @@
|
||||
/*
|
||||
* © 2021 Harald Barth
|
||||
* © 2021 Fred Decker
|
||||
* (c) 2021 Fred Decker. All rights reserved.
|
||||
* (c) 2020 Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This file is part of CommandStation-EX
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* It is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
© 2022 Mark Muzzin
|
||||
© 2021 Harald Barth
|
||||
© 2021 Fred Decker
|
||||
(c) 2021 Fred Decker. All rights reserved.
|
||||
(c) 2020 Chris Harlow. All rights reserved.
|
||||
|
||||
This file is part of CommandStation-EX
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef WifiInboundHandler_h
|
||||
#define WifiInboundHandler_h
|
||||
|
||||
@@ -27,57 +28,65 @@
|
||||
#include "DIAG.h"
|
||||
|
||||
class WifiInboundHandler {
|
||||
public:
|
||||
static void setup(Stream * ESStream);
|
||||
static void loop();
|
||||
|
||||
private:
|
||||
public:
|
||||
static void setup(Stream * ESStream);
|
||||
static void loop();
|
||||
|
||||
static WifiInboundHandler * singleton;
|
||||
|
||||
|
||||
enum INBOUND_STATE : byte {
|
||||
INBOUND_BUSY, // keep calling in loop()
|
||||
INBOUND_IDLE // Nothing happening, outbound may xcall CIPSEND
|
||||
};
|
||||
private:
|
||||
|
||||
enum LOOP_STATE : byte {
|
||||
ANYTHING, // ready for +IPD, n CLOSED, n CONNECTED, busy etc...
|
||||
SKIPTOEND, // skip to newline
|
||||
|
||||
// +IPD,client,length:data
|
||||
IPD, // got +
|
||||
IPD1, // got +I
|
||||
IPD2, // got +IP
|
||||
IPD3, // got +IPD
|
||||
IPD4_CLIENT, // got +IPD, reading cient id
|
||||
IPD5, // got +IPD,c
|
||||
IPD6_LENGTH, // got +IPD,c, reading length
|
||||
IPD_DATA, // got +IPD,c,ll,: collecting data
|
||||
IPD_IGNORE_DATA, // got +IPD,c,ll,: ignoring the data that won't fit inblound Ring
|
||||
static WifiInboundHandler * singleton;
|
||||
|
||||
GOT_CLIENT_ID, // clientid prefix to CONNECTED / CLOSED
|
||||
GOT_CLIENT_ID2 // clientid prefix to CONNECTED / CLOSED
|
||||
};
|
||||
enum INBOUND_STATE : byte {
|
||||
INBOUND_BUSY, // Keep calling in loop()
|
||||
INBOUND_IDLE // Nothing happening, outbound may xcall CIPSEND
|
||||
};
|
||||
|
||||
|
||||
WifiInboundHandler(Stream * ESStream);
|
||||
void loop1();
|
||||
INBOUND_STATE loop2();
|
||||
void purgeCurrentCIPSEND();
|
||||
Stream * wifiStream;
|
||||
|
||||
static const int INBOUND_RING = 512;
|
||||
static const int OUTBOUND_RING = 2048;
|
||||
|
||||
RingStream * inboundRing;
|
||||
RingStream * outboundRing;
|
||||
|
||||
LOOP_STATE loopState=ANYTHING;
|
||||
int runningClientId; // latest client inbound processing data or CLOSE
|
||||
int dataLength; // dataLength of +IPD
|
||||
int clientPendingCIPSEND=-1;
|
||||
int currentReplySize;
|
||||
bool pendingCipsend;
|
||||
enum RECV_MSG_STATE : byte {
|
||||
RECV_MSG_NONE, // No message to process
|
||||
RECV_MSG_CRLF, // Message ending in CRLF
|
||||
RECV_MSG_SND_DATA, // Send data message
|
||||
RECV_MSG_IPD_MSG, // +IPD Message in progress
|
||||
RECV_MSG_IPD_DONE, // +IPD Message done
|
||||
RECV_MSG_GENERIC // Catchall for incoming messages not used
|
||||
};
|
||||
|
||||
enum CIP_SEND_STATUS : byte {
|
||||
CIP_SEND_NONE,
|
||||
CIP_SEND_PENDING,
|
||||
CIP_SEND_SENT,
|
||||
CIP_SEND_DATA_SENT
|
||||
};
|
||||
|
||||
typedef struct _timeStamps {
|
||||
long cipSendStart;
|
||||
long ipdStart;
|
||||
} timeStamps_t;
|
||||
|
||||
WifiInboundHandler(Stream * ESStream);
|
||||
void loop1();
|
||||
INBOUND_STATE loop2();
|
||||
void purgeCurrentCIPSEND();
|
||||
int antoi(char* str, int len);
|
||||
Stream * wifiStream;
|
||||
|
||||
static const int INBOUND_CMD_BUFFER = 32;
|
||||
static const int INBOUND_RING = 512;
|
||||
static const int OUTBOUND_RING = 2048;
|
||||
|
||||
RingStream * inboundRing;
|
||||
RingStream * outboundRing;
|
||||
|
||||
RECV_MSG_STATE messageToProcess = RECV_MSG_NONE; // Track the current message to process
|
||||
|
||||
int runningClientId; // latest client inbound processing data or CLOSE
|
||||
int dataLength; // dataLength of +IPD
|
||||
int dataRemaining = 0; // remaining +IPD data to receive
|
||||
int clientPendingCIPSEND = -1;
|
||||
int currentReplySize;
|
||||
CIP_SEND_STATUS cipSendStatus;
|
||||
char recBuffer[INBOUND_CMD_BUFFER] = {'\0'};
|
||||
int recBufferPos = 0;
|
||||
int recBufferWatermark = 0;
|
||||
timeStamps_t ts = {0};
|
||||
};
|
||||
#endif
|
||||
|
@@ -29,7 +29,7 @@ The configuration file for DCC-EX Command Station
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Before connecting these boards and selecting one in this software
|
||||
// check the quick install guides!!! Some of these boards require a voltage
|
||||
// generating resitor on the current sense pin of the device. Failure to select
|
||||
// generating resistor on the current sense pin of the device. Failure to select
|
||||
// the correct resistor could damage the sense pin on your Arduino or destroy
|
||||
// the device.
|
||||
//
|
||||
@@ -85,7 +85,7 @@ The configuration file for DCC-EX Command Station
|
||||
// WIFI_PASSWORD is the network password for your home network or if
|
||||
// you want to change the password from default AP mode password
|
||||
// to the AP password you want.
|
||||
// Your password may not conain ``"'' (double quote, ASCII 0x22).
|
||||
// Your password may not contain ``"'' (double quote, ASCII 0x22).
|
||||
#define WIFI_PASSWORD "Your network passwd"
|
||||
//
|
||||
// WIFI_HOSTNAME: You probably don't need to change this
|
||||
@@ -125,10 +125,13 @@ The configuration file for DCC-EX Command Station
|
||||
|
||||
//OR define OLED_DRIVER width,height in pixels (address auto detected)
|
||||
// 128x32 or 128x64 I2C SSD1306-based devices are supported.
|
||||
// Also 132x64 I2C SH1106 devices
|
||||
// Use 132,64 for a SH1106-based I2C device with a 128x64 display.
|
||||
// #define OLED_DRIVER 128,32
|
||||
|
||||
// Define scroll mode as 0, 1 or 2
|
||||
// * #define SCROLLMODE 0 is scroll continuous (fill screen if poss),
|
||||
// * #define SCROLLMODE 1 is by page (alternate between pages),
|
||||
// * #define SCROLLMODE 2 is by row (move up 1 row at a time).
|
||||
#define SCROLLMODE 1
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -137,7 +140,7 @@ The configuration file for DCC-EX Command Station
|
||||
// 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.
|
||||
// to do that. Of course, then none of the EEPROM related commands work.
|
||||
//
|
||||
// #define DISABLE_EEPROM
|
||||
|
||||
@@ -174,7 +177,7 @@ The configuration file for DCC-EX Command Station
|
||||
|
||||
// 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
|
||||
// you can use this to reverse 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
|
||||
@@ -189,8 +192,8 @@ The configuration file for DCC-EX Command Station
|
||||
// To monitor a throttle on one or more serial ports, uncomment the defines below.
|
||||
// NOTE: do not define here the WiFi shield serial port or your wifi will not work.
|
||||
//
|
||||
//#define SERIAL1_COMMAND
|
||||
//#define SERIAL2_COMMAND
|
||||
//#define SERIAL3_COMMAND
|
||||
//#define SERIAL1_COMMANDS
|
||||
//#define SERIAL2_COMMANDS
|
||||
//#define SERIAL3_COMMANDS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@@ -40,7 +40,7 @@
|
||||
// WIFI_ON: All prereqs for running with WIFI are met
|
||||
// Note: WIFI_CHANNEL may not exist in early config.h files so is added here if needed.
|
||||
|
||||
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO))
|
||||
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO)) || defined(ARDUINO_AVR_NANO_EVERY)
|
||||
#define BIG_RAM
|
||||
#endif
|
||||
#if ENABLE_WIFI
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
#if __has_include ( "myAutomation.h")
|
||||
#if defined(BIG_RAM) || defined(DISABLE_EEPROM)
|
||||
#define RMFT_ACTIVE
|
||||
#define EXRAIL_ACTIVE
|
||||
#else
|
||||
#define EXRAIL_WARNING
|
||||
#endif
|
||||
|
270
installer.json
Normal file
270
installer.json
Normal file
@@ -0,0 +1,270 @@
|
||||
[
|
||||
{
|
||||
"Name": "BaseStationClassic",
|
||||
"Git": "DCC-EX/BaseStation-Classic",
|
||||
"Libraries": [
|
||||
{
|
||||
"Name": "Ethernet",
|
||||
"Repo": "arduino-libraries/Ethernet",
|
||||
"Location": "libraries/Ethernet",
|
||||
"LibraryDownloadAvailable": true
|
||||
}
|
||||
],
|
||||
"SupportedBoards": [
|
||||
{
|
||||
"Name": "Uno",
|
||||
"FQBN": "arduino:avr:uno",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Mega",
|
||||
"FQBN": "arduino:avr:mega",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
}
|
||||
],
|
||||
"DisplayName": "Base Station Classic",
|
||||
"InputFileLocation": "DCCpp",
|
||||
"AllowAdvanced": false,
|
||||
"ConfigFile": "DCCpp/Config.h"
|
||||
},
|
||||
{
|
||||
"Name": "CommandStation-EX",
|
||||
"Git": "DCC-EX/CommandStation-EX",
|
||||
"Libraries": [
|
||||
{
|
||||
"Name": "Ethernet",
|
||||
"Repo": "arduino-libraries/Ethernet",
|
||||
"Location": "libraries/Ethernet",
|
||||
"LibraryDownloadAvailable": true
|
||||
},
|
||||
{
|
||||
"Name": "DIO2",
|
||||
"Repo": "",
|
||||
"Location": "libraries/DIO2",
|
||||
"LibraryDownloadAvailable": true
|
||||
}
|
||||
],
|
||||
"SupportedBoards": [
|
||||
{
|
||||
"Name": "Uno",
|
||||
"FQBN": "arduino:avr:uno",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Mega",
|
||||
"FQBN": "arduino:avr:mega",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Mega Wifi",
|
||||
"FQBN": "arduino:avr:mega",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Nano",
|
||||
"FQBN": "arduino:avr:nano",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Nano Every",
|
||||
"FQBN": "arduino:megaavr:nanoevery",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "megaavr",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Teensy 3.x",
|
||||
"FQBN": "teensy:avr:teensy31",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "teensy",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "Teensy 4.x",
|
||||
"FQBN": "teensy:avr:teensy41",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "teensy",
|
||||
"Package": "arduino"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
},
|
||||
{
|
||||
"Name": "SAMD21",
|
||||
"FQBN": "SparkFun:samd",
|
||||
"Platforms": [
|
||||
{
|
||||
"Architecture": "avr",
|
||||
"Package": "arduino"
|
||||
},
|
||||
{
|
||||
"Architecture": "samd",
|
||||
"Package": "arduino"
|
||||
},
|
||||
{
|
||||
"Architecture": "samd",
|
||||
"Package": "SparkFun"
|
||||
}
|
||||
],
|
||||
"SupportedMotoShields": [
|
||||
{
|
||||
"Name": "Arduino Motor Shield",
|
||||
"Declaration": "STANDARD_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "Pololu MC33926 Motor Shield",
|
||||
"Declaration": "POLOLU_MOTOR_SHIELD"
|
||||
},
|
||||
{
|
||||
"Name": "FireBox MK1",
|
||||
"Declaration": "FIREBOX_MK1"
|
||||
},
|
||||
{
|
||||
"Name": "FireBox MK1S",
|
||||
"Declaration": "FIREBOX_MK1S"
|
||||
}
|
||||
],
|
||||
"ExtraLibraries": []
|
||||
}
|
||||
],
|
||||
"DisplayName": "CommandStation EX",
|
||||
"InputFileLocation": "",
|
||||
"AllowAdvanced": true,
|
||||
"ConfigFile": "config.h"
|
||||
}
|
||||
]
|
@@ -1,19 +1,18 @@
|
||||
/* This is an automation example file.
|
||||
* The presence of a file calle "myAutomation.h" brings EX-RAIL code into
|
||||
* The presence of a file called "myAutomation.h" brings EX-RAIL code into
|
||||
* the command station.
|
||||
* The auotomation may have multiple concurrent tasks.
|
||||
* The automation may have multiple concurrent tasks.
|
||||
* A task may
|
||||
* - Act as a ROUTE setup macro for a user to drive over
|
||||
* - drive a loco through an AUTOMATION
|
||||
* - automate some cosmetic part of the layout without any loco.
|
||||
*
|
||||
* At startup, a single task is created to execute the first
|
||||
* instruction after E$XRAIL.
|
||||
* At startup, a single task is created to execute the startup sequence.
|
||||
* This task may simply follow a route, or may START
|
||||
* further tasks (thats is.. send a loco out along a route).
|
||||
* further tasks (that is.. send a loco out along a route).
|
||||
*
|
||||
* Where the loco id is not known at compile time, a new task
|
||||
* can be creatd with the command:
|
||||
* can be created with the command:
|
||||
* </ START [cab] route>
|
||||
*
|
||||
* A ROUTE, AUTOMATION or SEQUENCE are internally identical in ExRail terms
|
||||
@@ -24,11 +23,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
EXRAIL // myAutomation must start with the EXRAIL instruction
|
||||
// This is the default starting route, AKA SEQUENCE(0)
|
||||
SENDLOCO(3,1) // send loco 3 off along route 1
|
||||
SENDLOCO(10,2) // send loco 10 off along route 2
|
||||
DONE // This just ends the startup thread, leaving 2 others running.
|
||||
// This is the startup sequence, AKA SEQUENCE(0)
|
||||
SENDLOCO(3,1) // send loco 3 off along route 1
|
||||
SENDLOCO(10,2) // send loco 10 off along route 2
|
||||
DONE // This just ends the startup thread, leaving 2 others running.
|
||||
|
||||
/* SEQUENCE(1) is a simple shuttle between 2 sensors
|
||||
* S20 and S21 are sensors on arduino pins 20 and 21
|
||||
@@ -78,7 +76,3 @@ EXRAIL // myAutomation must start with the EXRAIL instruction
|
||||
AT(33) STOP
|
||||
DELAY(20000) // wait 20 seconds
|
||||
FOLLOW(2) // follow sequence 2... ie repeat the process
|
||||
|
||||
ENDEXRAIL // marks the end of the EXRAIL program.
|
||||
|
||||
|
||||
|
192
myAutomation2.h
192
myAutomation2.h
@@ -1,192 +0,0 @@
|
||||
|
||||
/* This is an automation example file.
|
||||
* The presence of a file calle "myAutomation.h" brings EX-RAIL code into
|
||||
* the command station.
|
||||
* The auotomation may have multiple concurrent tasks.
|
||||
* A task may drive one loco through a ROUTE or may simply
|
||||
* automate some other part of the layout without any loco.
|
||||
*
|
||||
* At startup, a single task is created to execute the first
|
||||
* instruction after ROUTES.
|
||||
* This task may simply follow a route, or may SCHEDULE
|
||||
* further tasks (thats is.. send a loco out along a route).
|
||||
*
|
||||
* Where the loco id is not known at compile time, a new task
|
||||
* can be creatd with the command:
|
||||
* </ SCHEDULE [cab] route>
|
||||
*
|
||||
*/
|
||||
|
||||
// Include the name to pin mappings for my layout
|
||||
#include "myLayout.h"
|
||||
|
||||
ALIAS(ROUTE_1,1)
|
||||
ALIAS(UP_MOUNTAIN,8)
|
||||
ALIAS(UP_MOUNTAIN_FROM_PROG,88)
|
||||
ALIAS(INNER_LOOP,7)
|
||||
ALIAS(INNER_FROM_PROG,77)
|
||||
|
||||
//EXRAIL // myAutomation must start with the EXRAIL instruction
|
||||
// This is the default starting route, AKA ROUTE(0)
|
||||
// START(999) // this is just a diagnostic test cycle
|
||||
PRINT("started")
|
||||
LCD(0,"EXRAIL RULES")
|
||||
SERIAL("I had one of them but the leg fell off!")
|
||||
DONE // This just ends the startup thread
|
||||
|
||||
|
||||
/*AUTOSTART*/ ROUTE(ROUTE_1,"Close All")
|
||||
LCD(1,"Bingo")
|
||||
CLOSE(TOP_TURNOUT) DELAY(10)
|
||||
CLOSE(Y_TURNOUT) DELAY(10)
|
||||
CLOSE(MIDDLE_TURNOUT) DELAY(10)
|
||||
CLOSE(JOIN_TURNOUT) DELAY(10)
|
||||
CLOSE(LOWER_TURNOUT) DELAY(10)
|
||||
CLOSE(CROSSOVER_TURNOUT) DELAY(10)
|
||||
CLOSE(PROG_TURNOUT) DELAY(10)
|
||||
PRINT("Close All completed")
|
||||
|
||||
ENDTASK
|
||||
|
||||
|
||||
SEQUENCE(UP_MOUNTAIN) // starting at the lower closed turnout siding and going up the mountain
|
||||
PRINT("Up Mountain started")
|
||||
DELAY(10000) // wait 10 seconds
|
||||
RESERVE(BLOCK_LOWER_MOUNTAIN)
|
||||
CLOSE(LOWER_TURNOUT) CLOSE(JOIN_TURNOUT)
|
||||
FWD(60) AT(Y_LOWER)
|
||||
RESERVE(BLOCK_X_MOUNTAIN)
|
||||
CLOSE(Y_TURNOUT) CLOSE(MIDDLE_TURNOUT)
|
||||
FWD(40) AT(MIDDLE_C_BUFFER) STOP
|
||||
FREE(BLOCK_X_MOUNTAIN) FREE(BLOCK_LOWER_MOUNTAIN)
|
||||
DELAY(10000)
|
||||
RESERVE(BLOCK_UPPER_MOUNTAIN) RESERVE(BLOCK_X_MOUNTAIN)
|
||||
CLOSE(MIDDLE_TURNOUT) THROW(Y_TURNOUT) THROW(TOP_TURNOUT)
|
||||
REV(55)
|
||||
AFTER(Y_UPPER) FREE(BLOCK_X_MOUNTAIN)
|
||||
REV(55) AT(TOP_T_BUFFER) STOP // At top of mountain
|
||||
FREE(BLOCK_UPPER_MOUNTAIN)
|
||||
DELAY(5000)
|
||||
RESERVE(BLOCK_UPPER_MOUNTAIN)
|
||||
THROW(TOP_TURNOUT)
|
||||
FWD(60) AT(Y_UPPER)
|
||||
RESERVE(BLOCK_X_MOUNTAIN)
|
||||
THROW(Y_TURNOUT) CLOSE(MIDDLE_TURNOUT)
|
||||
FWD(40) AT(MIDDLE_C_BUFFER) STOP
|
||||
FREE(BLOCK_UPPER_MOUNTAIN) FREE(BLOCK_X_MOUNTAIN)
|
||||
DELAY(6000)
|
||||
RESERVE(BLOCK_LOWER_MOUNTAIN) RESERVE(BLOCK_X_MOUNTAIN)
|
||||
CLOSE(MIDDLE_TURNOUT) CLOSE(Y_TURNOUT) CLOSE(JOIN_TURNOUT) CLOSE(LOWER_TURNOUT)
|
||||
REV(60)
|
||||
AFTER(Y_LOWER) FREE(BLOCK_X_MOUNTAIN)
|
||||
AT(LOWER_C_BUFFER) STOP
|
||||
FREE(BLOCK_LOWER_MOUNTAIN)
|
||||
FOLLOW(UP_MOUNTAIN)
|
||||
|
||||
AUTOMATION(UP_MOUNTAIN_FROM_PROG,"Send up mountain from prog")
|
||||
JOIN
|
||||
RESERVE(BLOCK_LOWER_MOUNTAIN)
|
||||
RESERVE(BLOCK_X_INNER)
|
||||
RESERVE(BLOCK_X_OUTER)
|
||||
// safe to cross
|
||||
THROW(PROG_TURNOUT) THROW(CROSSOVER_TURNOUT) THROW(JOIN_TURNOUT)
|
||||
FWD(45)
|
||||
AFTER(JOIN_AFTER) STOP
|
||||
CLOSE(PROG_TURNOUT) CLOSE(CROSSOVER_TURNOUT) CLOSE(JOIN_TURNOUT)
|
||||
FREE(BLOCK_X_OUTER) FREE(BLOCK_X_INNER)
|
||||
CLOSE(LOWER_TURNOUT)
|
||||
REV(40) AT(LOWER_C_BUFFER) STOP
|
||||
FREE(BLOCK_LOWER_MOUNTAIN)
|
||||
FOLLOW(UP_MOUNTAIN)
|
||||
|
||||
SEQUENCE(INNER_LOOP)
|
||||
FWD(50)
|
||||
AT(CROSSOVER_INNER_BEFORE)
|
||||
RESERVE(BLOCK_X_INNER)
|
||||
CLOSE(CROSSOVER_TURNOUT)
|
||||
FWD(50)
|
||||
AFTER(CROSSOVER_INNER_AFTER)
|
||||
FREE(BLOCK_X_INNER)
|
||||
FOLLOW(INNER_LOOP)
|
||||
|
||||
|
||||
// Turnout definitions
|
||||
TURNOUT(TOP_TURNOUT, TOP_TURNOUT,0,"Top Station")
|
||||
TURNOUT(Y_TURNOUT, Y_TURNOUT,0,"Mountain join")
|
||||
TURNOUT(MIDDLE_TURNOUT, MIDDLE_TURNOUT,0,"Middle Station")
|
||||
TURNOUT(JOIN_TURNOUT,JOIN_TURNOUT,0)
|
||||
TURNOUT(LOWER_TURNOUT,LOWER_TURNOUT,0)
|
||||
TURNOUT(CROSSOVER_TURNOUT,CROSSOVER_TURNOUT,0)
|
||||
TURNOUT(PROG_TURNOUT,PROG_TURNOUT,0)
|
||||
|
||||
// Single slip protection
|
||||
ONTHROW(2)
|
||||
THROW(1)
|
||||
DONE
|
||||
ONCLOSE(1)
|
||||
CLOSE(2)
|
||||
DONE
|
||||
|
||||
|
||||
ROUTE(61,"Call return test")
|
||||
PRINT("In 61 test 1")
|
||||
CALL(62)
|
||||
PRINT("In 61 test 2")
|
||||
CALL(62)
|
||||
PRINT("In 61 test 3")
|
||||
ACTIVATE(100,2)
|
||||
DEACTIVATE(100,2)
|
||||
DONE
|
||||
|
||||
SEQUENCE(62)
|
||||
PRINT("In seq 62")
|
||||
RETURN
|
||||
|
||||
ROUTE(63,"Signal test 40,41,42")
|
||||
SIGNAL(40,41,42)
|
||||
DELAY(2000)
|
||||
RED(40)
|
||||
DELAY(2000)
|
||||
AMBER(40)
|
||||
DELAY(2000)
|
||||
GREEN(40)
|
||||
FOLLOW(63)
|
||||
|
||||
|
||||
ROUTE(64,"Func test 6772")
|
||||
XFON(6772,1)
|
||||
DELAY(5000)
|
||||
XFOFF(6772,1)
|
||||
DELAY(5000)
|
||||
FOLLOW(64)
|
||||
|
||||
ROUTE(65,"Negative sensor test")
|
||||
PRINT(" WAIT for -176")
|
||||
AT(-176)
|
||||
PRINT(" WAIT for 176")
|
||||
AT(176)
|
||||
PRINT("done")
|
||||
DONE
|
||||
|
||||
ROUTE(123,"Activate stuff")
|
||||
ACTIVATEL(5)
|
||||
ACTIVATE(7,2)
|
||||
DEACTIVATE(3,2)
|
||||
DEACTIVATEL(6)
|
||||
DONE
|
||||
|
||||
ONACTIVATEL(5)
|
||||
PRINT("ACT 5")
|
||||
DONE
|
||||
ONACTIVATE(7,2)
|
||||
PRINT("ACT 7,2")
|
||||
DONE
|
||||
ONDEACTIVATE(7,2)
|
||||
PRINT("DEACT 7,2")
|
||||
DONE
|
||||
ONDEACTIVATEL(5)
|
||||
PRINT("DEACT 5")
|
||||
DONE
|
||||
|
||||
|
||||
|
@@ -1,16 +1,88 @@
|
||||
The DCC-EX Team is pleased to release CommandStation-EX-v3.1.0 as a Production Release. Release v3.1.0 is a minor release that adds additional features and fixes a number of bugs. With the number of new features, this could have easily been a major release. The team is continually improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v3.0.1 to v3.0.16.
|
||||
The DCC-EX Team is pleased to release CommandStation-EX-v4.0.0 as a Production Release. Release v4.0.0 is a Major release that adds significant new product design, plus Automation features and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v3.2.0 to v3.2.0 rc13.
|
||||
|
||||
**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.**
|
||||
|
||||
[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v3.1.0-Prod/CommandStation-EX.zip)
|
||||
[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v3.1.0-Prod/CommandStation-EX.tar.gz)
|
||||
[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip)
|
||||
|
||||
|
||||
|
||||
[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz)
|
||||
|
||||
**Known Issues**
|
||||
|
||||
- **Wi-Fi** - works, but requires sending <AT> commands from a serial monitor if you want to switch between AP mode and STA station mode after initial setup
|
||||
- **Pololu Motor Shield** - is supported with this release, but the user may have to adjust timings to enable programming mode due to limitation in its current sensing circuitry
|
||||
- **Wi-Fi** - Requires sending `<AT>` commands from a serial monitor if you want to switch between AP mode and STA station mode after initial setup
|
||||
- **Pololu Motor Shield** - is supported with this release, but the user may have to adjust timings to enable programming mode due to limitations in its current sensing circuitry
|
||||
|
||||
#### Summary of key features and/or bug fixes by Point Release
|
||||
**All New Major DCC++EX 4.0.0 features**
|
||||
|
||||
- **New HAL Hardware Abstraction Layer API** that automatically detects and greatly simplifies interfacing to many predefined accessory boards for servos, signals & sensors and added I/O (digital and analog inputs and outputs, servos etc).
|
||||
- HAL Support for;
|
||||
- MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules.
|
||||
- PCA9685 PWM (servo & signal) control modules.
|
||||
- Analogue inputs on Arduino pins and on ADS111x I2C modules.
|
||||
- MP3 sound playback via DFPlayer module.
|
||||
- HC-SR04 Ultrasonic range sensor module.
|
||||
- VL53L0X Laser range sensor module (Time-Of-Flight).
|
||||
- A new `<D HAL SHOW>` command to list the HAL devices attached to the command station
|
||||
|
||||
**New Command Station Broadcast throttle logic**
|
||||
|
||||
- Synchronizes multiple WiThrottles and PC based JMRI Throttles for direction, speed and F-key updates
|
||||
|
||||
**New ‘Discovered Servers’ on WiFi Throttles**
|
||||
|
||||
- Our New multicast Dynamic Network Server (mDNS) enhancement allows us to display the available WiFi server connections to a DCC++EX Command Station. Selecting it allows your WiThrottle App to connect to and load Server Rosters and function keys to your throttle from the new DCC++EX Command Station Server Roster.
|
||||
|
||||
**New DCC++EX 4.0.0 with EX-RAIL Extended Railroad Automation Instruction Language**
|
||||
|
||||
- Use to control your entire layout or as a separate accessory/animation controller
|
||||
- Awesome, cleverly powerful yet simple user friendly scripting language for user built Automation & Routing scripts.
|
||||
- You can control Engines, Sensors, Turnouts, Signals, Outputs and Accessories that are entered into your new myAutomation.h file, then uploaded into the DCC++EX Command Station.
|
||||
- EX-RAIL scripts are automatically displayed as Automation {Handoff} and Route {Set} buttons on supported WiFi Throttle Apps.
|
||||
|
||||
**New EX-RAIL ‘Roster’ Feature**
|
||||
|
||||
- List and store user defined engine roster & function keys inside the command station, and automatically load them in WiFi Throttle Apps.
|
||||
- When choosing “DCC++EX” from discovered servers an Engine Driver or WiThrottle is directly connected to the Command Station.
|
||||
- The EX-RAIL ’ROSTER’ command allows all the engine numbers, names and function keys you’ve listed in your myAutomation.h file to automatically upload the Command Station's ‘Server Roster’ into your Engine Driver and WiThrottle Apps.
|
||||
|
||||
**New JMRI 4.99.2 and above specific DCC++EX 4.0 features**
|
||||
|
||||
- Enhanced JMRI DCC++ Configure Base Station pane for building and maintaining Sensor, Turnout and Output devices, or these can automatically be populated from the DCC++EX Command Station's mySetup.h file into JMRI.
|
||||
|
||||
- JMRI now supports multiple serial connected DCC++EX Command Stations, to display and track separate "Send DCC++ Command" and "DCC++ Traffic" Monitors for each Command Station at the same time.
|
||||
For example: Use an Uno DCC++EX DecoderPro Programming Station {DCC++Prg} on a desktop programming track and a second Mega DCC++EX EX-RAIL Command Station for Operations {DCC++Ops} on the layout with an additional `<JOINED>` programming spur or siding track for acquiring an engine and ‘Drive Away’ onto the mainline (see the DriveAway feature for more information).
|
||||
|
||||
**DCC++EX 4.0.0 additional product enhancements**
|
||||
|
||||
- Additional Motor Shields and Motor Board {boosters) supported
|
||||
- Additional Accessory boards supported for GPIO expansion, Sensors, Servos & Signals
|
||||
- Additional diagnostic commands like ‘D ACK RETRY’ and ‘D EXRAIL ON’ events, ‘D HAL SHOW’ devices and ‘D SERVO’ positions, and ‘D RESET’ the command station while maintaining the serial connection with JMRI
|
||||
- Automatic retry on failed ACK detection to give decoders another chance
|
||||
- New EX-RAIL ’/’ slash command allows JMRI to directly communicate with many EX-RAIL scripts
|
||||
- Turnout class revised to expand turnout capabilities and allow turnout names/descriptors to display in WiThrottle Apps.
|
||||
- Build turnouts through either or both mySetup.h and myAutomation.h files, and have them automatically passed to, and populate, JMRI Turnout Tables
|
||||
- Turnout user names display in Engine Driver & WiThrottles
|
||||
- Output class now allows ID > 255.
|
||||
- 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 memory code to allow room for DCC++EX 4.0 to fit on a Uno Command Station
|
||||
- Can define border between long and short addresses
|
||||
- Native non-blocking I2C drivers for AVR and Nano architectures (fallback to blocking Wire library for other platforms).
|
||||
- EEPROM layout change - deletes EEPROM contents on first start following upgrade.
|
||||
|
||||
**4.0.0 Bug Fixes**
|
||||
|
||||
- Compiles on Nano Every
|
||||
- Diagnostic display of ack pulses >32ms
|
||||
- Current read from wrong ADC during interrupt
|
||||
- AT(+) Command Pass Through
|
||||
- CiDAP WiFi Drop out and the WiThrottle F-key looping error corrected
|
||||
- One-off error in CIPSEND drop
|
||||
- Common Fault Pin Error
|
||||
- Uno Memory Utilization optimized
|
||||
|
||||
#### Summary of Release 3.1.0 key features and/or bug fixes by Point Release
|
||||
|
||||
**Summary of the key new features added to CommandStation-EX V3.0.16**
|
||||
|
||||
@@ -154,7 +226,7 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.1.0 as a Production R
|
||||
- Neil McKechnie - Worcestershire, UK (NeilMck)
|
||||
- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk)
|
||||
- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting)
|
||||
- M Steve Todd -
|
||||
- M Steve Todd - Oregon, USA (MSteveTodd)
|
||||
- Scott Catalano - Pennsylvania
|
||||
- Gregor Baues - Île-de-France, France (grbba)
|
||||
|
||||
@@ -174,6 +246,7 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.1.0 as a Production R
|
||||
- Roger Beschizza - Dorset, UK (Roger Beschizza)
|
||||
- Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter)
|
||||
- Kevin Smith - Rochester Hills, Michigan USA (KC Smith)
|
||||
- Colin Grabham - Central NSW, Australia (Kebbin)
|
||||
|
||||
**WebThrotle-EX**
|
||||
|
||||
@@ -185,13 +258,14 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.1.0 as a Production R
|
||||
|
||||
- Larry Dribin - Release Management
|
||||
- Kevin Smith - Rochester Hills, Michigan USA (KC Smith)
|
||||
- Herb Morton - Kingwood Texas, USA (Ash++)
|
||||
- Keith Ledbetter
|
||||
- BradVan der Elst
|
||||
- Brad Van der Elst
|
||||
- Andrew Pye
|
||||
- Mike Bowers
|
||||
- Randy McKenzie
|
||||
- Roberto Bravin
|
||||
- Sim Brigden
|
||||
- Sam Brigden
|
||||
- Alan Lautenslager
|
||||
- Martin Bafver
|
||||
- Mário André Silva
|
||||
@@ -202,5 +276,7 @@ The DCC-EX Team is pleased to release CommandStation-EX-v3.1.0 as a Production R
|
||||
|
||||
**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.**
|
||||
|
||||
[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v3.1.0-Prod/CommandStation-EX.zip)
|
||||
[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v3.1.0-Prod/CommandStation-EX.tar.gz)
|
||||
[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip)
|
||||
|
||||
|
||||
[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.tar.gz)
|
||||
|
41
version.h
41
version.h
@@ -3,29 +3,44 @@
|
||||
|
||||
#include "StringFormatter.h"
|
||||
|
||||
#define VERSION "3.2.0 rc9"
|
||||
// 3.2.0 Major functional and non-functional changes.
|
||||
// New HAL added for I/O (digital and analogue inputs and outputs, servos etc).
|
||||
#define VERSION "4.0.1"
|
||||
// 4.0.1 EXRAIL BROADCAST("msg")
|
||||
// EXRAIL POWERON
|
||||
// 4.0.0 Major functional and non-functional changes.
|
||||
// Engine Driver "DriveAway" feature enhancement
|
||||
// 'Discovered Server' multicast Dynamic Network Server (mDNS) displays available WiFi connections to a DCC++EX Command Station
|
||||
// New EX-RAIL "Extended Railroad Automation Instruction Language" automation capability.
|
||||
// EX-Rail Function commands for creating Automation, Route & Sequence Scripts
|
||||
// EX-RAIL “ROSTER” Engines Id & Function key layout on Engine Driver or WiThrottle
|
||||
// EX-RAIL DCC++EX Commands to Control EX-RAIL via JMRI Send pane and IDE Serial monitors
|
||||
// New JMRI feature enhancements;
|
||||
// Reads DCC++EX EEPROM & automatically uploades any Signals, DCC Turnouts, Servo Turnouts, Vpin Turnouts , & Output pane
|
||||
// Turnout class revised to expand turnout capabilities, new commands added.
|
||||
// Provides for multiple additional DCC++EX WiFi connections as accessory controllers or CS for a programming track when Motor Shields are added
|
||||
// Supports Multiple Command Station connections and individual tracking of Send DCC++ Command panes and DCC++ Traffic Monitor panes
|
||||
// New HAL added for I/O (digital and analogue inputs and outputs, servos etc)
|
||||
// Automatically detects & connects to supported devices included in your config.h file
|
||||
// Support for MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules.
|
||||
// Support for PCA9685 PWM (servo) control modules.
|
||||
// Support for analogue inputs on Arduino pins and on ADS111x I2C modules.
|
||||
// Support for MP3 sound playback via DFPlayer module.
|
||||
// Support for HC-SR04 Ultrasonic range sensor module.
|
||||
// Support for VL53L0X Laser range sensor module (Time-Of-Flight).
|
||||
// Native non-blocking I2C drivers for AVR and Nano architectures (fallback
|
||||
// to blocking Wire library for other platforms).
|
||||
// EEPROM layout change - deletes EEPROM contents on first start following upgrade.
|
||||
// New EX-RAIL automation capability.
|
||||
// Turnout class revised to expand turnout capabilities, new commands added.
|
||||
// Output class now allows ID > 255.
|
||||
// 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.
|
||||
// Added <D HAL SHOW> diagnostic command to show configured devices
|
||||
// New Processor Support added
|
||||
// Compiles on Nano Every and Teensy
|
||||
// Native non-blocking I2C drivers for AVR and Nano architectures (fallback to blocking Wire library for other platforms).
|
||||
// Can disable EEPROM code
|
||||
// EEPROM layout change - deletes EEPROM contents on first start following upgrade.
|
||||
// Output class now allows ID > 255.
|
||||
// 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 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
|
||||
// ...
|
||||
// Bugfix: disgnostic display of ack pulses >32kus
|
||||
// Bugfix: Current read from wrong ADC during interrupt
|
||||
// 3.2.0 Development Release Includes all of 3.1.1 thru 3.1.7 enhancements
|
||||
// 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
|
||||
// 3.1.5 Fix LCD corruption on power-up
|
||||
|
Reference in New Issue
Block a user