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

Compare commits

...

53 Commits

Author SHA1 Message Date
Mark Muzzin
ac550e81c8 - Added esp8266 revceive buffer clear when a link drop is detected
- Added timestamps for CIPSEND and +IPD tracing
2022-05-04 07:38:42 -04:00
Mark Muzzin
2a16e7d710 Swapped IPD DIAG messages to better align the message with the byte recieved 2022-05-03 22:48:13 -04:00
Mark Muzzin
4be8d58f76 Updated code for inbound wifi data buffering 2022-05-02 23:18:05 -04:00
Fred
0ab3fe07c5 Update FUNDING.yml 2022-03-29 11:59:40 -04:00
Fred
a37ca6b6b6 Update FUNDING.yml 2022-03-28 19:43:06 -04:00
Fred
5b12c2864d Create FUNDING.yml
Add patreon as our first funding site
2022-03-28 19:40:18 -04:00
Fred
90ca262cd9 Update label-sponsors.yml
Fix actions
2022-03-28 17:48:48 -04:00
Fred
b29eedf772 Create label-sponsors.yml file
When users who are sponsors submit a PR or an issue, a "sponsors" label will appear next to their name
2022-03-28 17:44:16 -04:00
Helmut Fischer
71cd3fc292 README.md: dead link to rewrite (#217)
Corrected dead link to `notes/rewrite.md' with appropriat text.
2022-03-24 11:37:29 -04:00
Helmut Fischer
331538549f README.md: removed misleading "folder/subforlders" (#218)
Removed misleading mention of "folder named CommandStation-EX and its subforlders"
2022-03-24 11:36:34 -04:00
Kcsmith0708
931e348c3d Update version.h (#222)
Cleaned up & Updated 4.0.0 Section for public release
2022-03-24 11:32:24 -04:00
Asbelos
ac3ffd2a36 Exrail BROADCAST and POWERON version 4.0.1 (#216)
* EXRAIL BROADCAST("msg") UNTESTED

* Add POWERON to EXRAIL

* POWERON only powers main, join will do both

* Update Version 4.0.1

* Broadcast jopin after driveaway

* rollback of previous edit  line 535 WiThrottle.cpp

* restructure GetLocoCallback() for better readability and put broadcastPower() at right place

Co-authored-by: Ash-4 <81280775+Ash-4@users.noreply.github.com>
Co-authored-by: Harald Barth <haba@kth.se>
2022-03-07 11:30:47 -05:00
Fred
c15ea6e083 Update config.example.h
Add description of display scroll modes
2022-03-07 08:05:02 -05:00
Chris Mayo
90092b4ad5 Remove EXRAIL/ENDEXRAIL from myAutomation.example.h (#215)
Use "startup sequence" to describe the initial instructions.
2022-03-06 10:07:44 -05:00
Chris Mayo
fa4f5f08ef Comment Typos (#214)
* Example config comment typos

* Code comment typos
2022-03-06 10:05:35 -05:00
Fred
3038f29dac Update release_notes_v4.0.0.md 2022-02-17 15:02:49 -05:00
Fred
e57cd1e2aa Update release_notes.md 2022-02-17 15:00:59 -05:00
Fred
c47dd101a5 Update release_notes_v4.0.0.md 2022-02-17 12:29:59 -05:00
Chris Mayo
cf905ce2f3 Clarify config.h entry for an OLED with a SH1106 (#210)
See:
79763a3 ("SH1106 OLED Display Offset Fix (#169)", 2021-06-10)
97f3450 ("Simplify OLED driver initialisation.", 2021-11-24)
2022-02-17 12:21:24 -05:00
Kebbin
4bfcaa605a update release_notes.md (#212) 2022-02-17 08:00:11 -05:00
Fred
557b71c036 Frightrisk update v4 (#209)
* Add Haba's bugfix for analog current reading with interrupts

* Update verson and release notes for 4.0

* remove unwanted changes to newer files
2022-02-16 12:18:43 -05:00
Fred
d0b01e3f03 Frightrisk ver4 (#205)
* Add release notes for verion 4.0

* Add archive release notes

* fix for SHA to modify previous commit instead of using new SHA

* new format for supported boards to allow easy addition of new boards

* Update release_notes.md (#206)

Formatting lines
Server Roster clarification and release 4,0 bug fix  vs 3.1 edit.
added support members

* Some duplication removal and rewording (#207)

WiThrottle and JMRI-related items

Co-authored-by: Fred <fndecker@gmail.com>

* rename config.json to installer.json, fixes for boards(may still be broken)

Co-authored-by: dexslab <dex35803@gmail.com>
Co-authored-by: Kcsmith0708 <kcsmith0708@wowway.com>
Co-authored-by: mstevetodd <mstevetodd@mstevetodd.com>
2022-02-16 12:02:11 -05:00
Dex
bd9a04572d Committing a SHA 2022-02-11 14:53:16 +00:00
Dex
a26d988acc add config.json to master repo for installer (#204) 2022-02-11 09:52:56 -05:00
Asbelos
22bf2b69f1 Committing a SHA 2022-02-04 15:33:26 +00:00
Asbelos
58ef7d25db Remove myAutomation2.h
This file was never intended for release.
2022-02-04 15:33:04 +00:00
Harald Barth
1ec3b173fd Committing a SHA 2022-01-31 22:25:59 +00:00
Harald Barth
07c0004996 Merge branch 'master' of https://github.com/DCC-EX/CommandStation-EX 2022-01-31 22:54:38 +01:00
FrightRisk
99a62883c7 Committing a SHA 2022-01-31 02:51:10 +00:00
FrightRisk
b7af6be1f2 Fix remaining rmft remnants 2022-01-30 21:50:43 -05:00
Fred
eb6b14b848 Committing a SHA 2022-01-31 02:39:51 +00:00
Fred
fa261ec39f Update version.h 2022-01-30 21:39:37 -05:00
Harald Barth
4ebf7978d8 rename all RMFT_ACTIVE to EXTAIL_ACTVE 2022-01-31 03:00:29 +01:00
Harald Barth
a0a635c167 version 2022-01-31 00:05:13 +01:00
Harald Barth
ca0c34f9fa Merge branch 'currentsenseirq' 2022-01-31 00:03:35 +01:00
Harald Barth
b6501c7e3e revert to write ERROR 2022-01-30 23:58:34 +01:00
Harald Barth
8af74f7082 protect analog read with cli() 2022-01-30 23:56:16 +01:00
Fred
75598b2b8b Committing a SHA 2022-01-30 17:31:41 +00:00
Fred
bd7c8bf78e Rename RMFT files and references to EXRAIL (#201)
Make naming consistent with our marketing of ex-rail for files and defines

* Rename RMFT.h to EXRAIL.h

* Rename RMFT2.cpp to EXRAIL2.cpp

* Rename RMFT2.h to EXRAIL2.h

* Rename RMFT2MacroReset.h to EXRAIL2MacroReset.h

* Rename RMFTMacros.h to EXRAILMacros.h

* Rename RMFT references to EXRAIL
2022-01-30 12:31:26 -05:00
Harald Barth
05545321a9 Committing a SHA 2022-01-23 17:55:49 +00:00
Harald Barth
03f7014c02 spell example right 2022-01-23 18:55:00 +01:00
Harald Barth
306f100085 Merge branch 'master' of https://github.com/DCC-EX/CommandStation-EX 2022-01-17 19:01:58 +01:00
Asbelos
509014bb6a Committing a SHA 2022-01-17 15:56:37 +00:00
Asbelos
f577c11eb7 Correct ack diag msgs
Bug caused by unsigned ints >32k being displayed as negative int.
2022-01-17 15:56:16 +00:00
Harald Barth
73ea7a1479 compiles on Nano Every 2022-01-15 20:23:58 +01:00
Harald Barth
f807339eec suggested code readability improvement by compiler warning 2022-01-15 20:21:28 +01:00
Harald Barth
b4aa47a451 Committing a SHA 2022-01-15 16:24:52 +00:00
Harald Barth
1416f83f8c Serial check, mostly for Teensy, does not impact startup if Serial is available 2022-01-15 17:24:18 +01:00
Asbelos
d5955a36bf Committing a SHA 2022-01-11 11:22:42 +00:00
Asbelos
35f7ac3d77 Teensy compatibility issues 2022-01-11 11:22:20 +00:00
Harald Barth
41cda58bef Committing a SHA 2022-01-08 20:47:13 +00:00
Harald Barth
0b8c455594 update version 2022-01-08 21:45:46 +01:00
Harald Barth
e0a7c4d155 Fixed regression: Shields with common fault pin works again 2022-01-08 21:44:32 +01:00
38 changed files with 1262 additions and 629 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: DCC-EX
patreon: dccex

14
.github/workflows/label-sponsors.yml vendored Normal file
View 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 }}

View File

@@ -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
View File

@@ -7,7 +7,7 @@ Release/*
.pio/
.vscode/
config.h
.vscode/extensions.json
.vscode/*
mySetup.h
mySetup.cpp
myHal.cpp

View File

@@ -3,5 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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) {

View File

@@ -43,7 +43,7 @@
#include "Turnouts.h"
#include "Sensors.h"
#include "Outputs.h"
#include "RMFT.h"
#include "CommandDistributor.h"
#include "EXRAIL.h"
#endif

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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)

View File

@@ -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),

View File

@@ -1 +1 @@
#define GITHUB_SHA "61390cb"
#define GITHUB_SHA "a26d988"

View File

@@ -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

View File

@@ -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;
}
/***************************************************************************

View File

@@ -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);

View File

@@ -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

View File

@@ -22,7 +22,7 @@ Both CommandStation-EX and BaseStation-Classic support much of the NMRA Digital
# Whats 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/)

View 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 youve 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)

View File

@@ -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) {

View File

@@ -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];

View File

@@ -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;

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
/////////////////////////////////////////////////////////////////////////////////////

View File

@@ -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
View 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"
}
]

View File

@@ -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.

View File

@@ -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

View File

@@ -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 youve 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)

View File

@@ -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