mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-26 17:46:14 +01:00
format/indentation change only
This commit is contained in:
parent
1934fdd0e1
commit
b0915e8332
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
@ -28,96 +28,95 @@
|
||||||
|
|
||||||
#if defined(BIG_MEMORY) | defined(WIFI_ON) | defined(ETHERNET_ON)
|
#if defined(BIG_MEMORY) | defined(WIFI_ON) | defined(ETHERNET_ON)
|
||||||
// This section of CommandDistributor is simply not relevant on a uno or similar
|
// This section of CommandDistributor is simply not relevant on a uno or similar
|
||||||
const byte NO_CLIENT=255;
|
const byte NO_CLIENT=255;
|
||||||
|
|
||||||
RingStream * CommandDistributor::ring=0;
|
RingStream * CommandDistributor::ring=0;
|
||||||
byte CommandDistributor::ringClient=NO_CLIENT;
|
byte CommandDistributor::ringClient=NO_CLIENT;
|
||||||
CommandDistributor::clientType CommandDistributor::clients[8]={
|
CommandDistributor::clientType CommandDistributor::clients[8]={
|
||||||
NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE};
|
NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE};
|
||||||
RingStream * CommandDistributor::broadcastBufferWriter=new RingStream(100);
|
RingStream * CommandDistributor::broadcastBufferWriter=new RingStream(100);
|
||||||
|
|
||||||
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) {
|
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) {
|
||||||
ring=stream;
|
ring=stream;
|
||||||
ringClient=stream->peekTargetMark();
|
ringClient=stream->peekTargetMark();
|
||||||
if (buffer[0] == '<') {
|
if (buffer[0] == '<') {
|
||||||
clients[clientId]=COMMAND_TYPE;
|
clients[clientId]=COMMAND_TYPE;
|
||||||
DCCEXParser::parse(stream, buffer, ring);
|
DCCEXParser::parse(stream, buffer, ring);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
clients[clientId]=WITHROTTLE_TYPE;
|
clients[clientId]=WITHROTTLE_TYPE;
|
||||||
WiThrottle::getThrottle(clientId)->parse(ring, buffer);
|
WiThrottle::getThrottle(clientId)->parse(ring, buffer);
|
||||||
}
|
}
|
||||||
ringClient=NO_CLIENT;
|
ringClient=NO_CLIENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::forget(byte clientId) {
|
void CommandDistributor::forget(byte clientId) {
|
||||||
clients[clientId]=NONE_TYPE;
|
clients[clientId]=NONE_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CommandDistributor::broadcast(bool includeWithrottleClients) {
|
|
||||||
broadcastBufferWriter->write((byte)'\0');
|
|
||||||
|
|
||||||
/* Boadcast to Serials */
|
void CommandDistributor::broadcast(bool includeWithrottleClients) {
|
||||||
SerialManager::broadcast(broadcastBufferWriter);
|
broadcastBufferWriter->write((byte)'\0');
|
||||||
|
|
||||||
|
/* Boadcast to Serials */
|
||||||
|
SerialManager::broadcast(broadcastBufferWriter);
|
||||||
|
|
||||||
#if defined(WIFI_ON) | defined(ETHERNET_ON)
|
#if defined(WIFI_ON) | defined(ETHERNET_ON)
|
||||||
// If we are broadcasting from a wifi/eth process we need to complete its output
|
// If we are broadcasting from a wifi/eth process we need to complete its output
|
||||||
// before merging broadcasts in the ring, then reinstate it in case
|
// before merging broadcasts in the ring, then reinstate it in case
|
||||||
// the process continues to output to its client.
|
// the process continues to output to its client.
|
||||||
if (ringClient!=NO_CLIENT) ring->commit();
|
if (ringClient!=NO_CLIENT) ring->commit();
|
||||||
|
|
||||||
/* loop through ring clients */
|
/* loop through ring clients */
|
||||||
for (byte clientId=0; clientId<sizeof(clients); clientId++) {
|
for (byte clientId=0; clientId<sizeof(clients); clientId++) {
|
||||||
if (clients[clientId]==NONE_TYPE) continue;
|
if (clients[clientId]==NONE_TYPE) continue;
|
||||||
if ( clients[clientId]==WITHROTTLE_TYPE && !includeWithrottleClients) continue;
|
if ( clients[clientId]==WITHROTTLE_TYPE && !includeWithrottleClients) continue;
|
||||||
ring->mark(clientId);
|
ring->mark(clientId);
|
||||||
broadcastBufferWriter->printBuffer(ring);
|
broadcastBufferWriter->printBuffer(ring);
|
||||||
ring->commit();
|
ring->commit();
|
||||||
}
|
}
|
||||||
if (ringClient!=NO_CLIENT) ring->mark(ringClient);
|
if (ringClient!=NO_CLIENT) ring->mark(ringClient);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
broadcastBufferWriter->flush();
|
broadcastBufferWriter->flush();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// For a UNO/NANO we can broadcast direct to just one Serial instead of the ring
|
// For a UNO/NANO we can broadcast direct to just one Serial instead of the ring
|
||||||
// Redirect ring output ditrect to Serial
|
// Redirect ring output ditrect to Serial
|
||||||
#define broadcastBufferWriter &Serial
|
#define broadcastBufferWriter &Serial
|
||||||
// and ignore the internal broadcast call.
|
// and ignore the internal broadcast call.
|
||||||
void CommandDistributor::broadcast(bool includeWithrottleClients) {
|
void CommandDistributor::broadcast(bool includeWithrottleClients) {
|
||||||
(void)includeWithrottleClients;
|
(void)includeWithrottleClients;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void CommandDistributor::broadcastSensor(int16_t id, bool on ) {
|
void CommandDistributor::broadcastSensor(int16_t id, bool on ) {
|
||||||
StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id);
|
StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id);
|
||||||
broadcast(false);
|
broadcast(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
|
void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
|
||||||
// For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed;
|
// For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed;
|
||||||
// The string below contains serial and Withrottle protocols which should
|
// The string below contains serial and Withrottle protocols which should
|
||||||
// be safe for both types.
|
// be safe for both types.
|
||||||
StringFormatter::send(broadcastBufferWriter,F("<H %d %d>\n"),id, !isClosed);
|
StringFormatter::send(broadcastBufferWriter,F("<H %d %d>\n"),id, !isClosed);
|
||||||
#if defined(WIFI_ON) | defined(ETHERNET_ON)
|
#if defined(WIFI_ON) | defined(ETHERNET_ON)
|
||||||
StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id);
|
StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id);
|
||||||
#endif
|
#endif
|
||||||
broadcast(true);
|
broadcast(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastLoco(byte slot) {
|
void CommandDistributor::broadcastLoco(byte slot) {
|
||||||
DCC::LOCO * sp=&DCC::speedTable[slot];
|
DCC::LOCO * sp=&DCC::speedTable[slot];
|
||||||
StringFormatter::send(broadcastBufferWriter,F("<l %d %d %d %l>\n"),
|
StringFormatter::send(broadcastBufferWriter,F("<l %d %d %d %l>\n"),
|
||||||
sp->loco,slot,sp->speedCode,sp->functions);
|
sp->loco,slot,sp->speedCode,sp->functions);
|
||||||
broadcast(false);
|
broadcast(false);
|
||||||
#if defined(WIFI_ON) | defined(ETHERNET_ON)
|
#if defined(WIFI_ON) | defined(ETHERNET_ON)
|
||||||
WiThrottle::markForBroadcast(sp->loco);
|
WiThrottle::markForBroadcast(sp->loco);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastPower() {
|
void CommandDistributor::broadcastPower() {
|
||||||
bool main=DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON;
|
bool main=DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON;
|
||||||
bool prog=DCCWaveform::progTrack.getPowerMode()==POWERMODE::ON;
|
bool prog=DCCWaveform::progTrack.getPowerMode()==POWERMODE::ON;
|
||||||
bool join=DCCWaveform::progTrackSyncMain;
|
bool join=DCCWaveform::progTrackSyncMain;
|
||||||
const FSH * reason=F("");
|
const FSH * reason=F("");
|
||||||
|
@ -127,11 +126,9 @@ void CommandDistributor::broadcastPower() {
|
||||||
else if (main) reason=F(" MAIN");
|
else if (main) reason=F(" MAIN");
|
||||||
else if (prog) reason=F(" PROG");
|
else if (prog) reason=F(" PROG");
|
||||||
else state='0';
|
else state='0';
|
||||||
|
|
||||||
StringFormatter::send(broadcastBufferWriter,
|
StringFormatter::send(broadcastBufferWriter,
|
||||||
F("<p%c%S>\nPPA%c\n"),state,reason, main?'1':'0');
|
F("<p%c%S>\nPPA%c\n"),state,reason, main?'1':'0');
|
||||||
LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason);
|
LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason);
|
||||||
broadcast(true);
|
broadcast(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
@ -35,7 +35,7 @@ private:
|
||||||
static RingStream * ring;
|
static RingStream * ring;
|
||||||
static RingStream * broadcastBufferWriter;
|
static RingStream * broadcastBufferWriter;
|
||||||
static byte ringClient;
|
static byte ringClient;
|
||||||
|
|
||||||
// each bit in broadcastlist = 1<<clientid
|
// each bit in broadcastlist = 1<<clientid
|
||||||
enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE};
|
enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE};
|
||||||
static clientType clients[8];
|
static clientType clients[8];
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
// DCC-EX CommandStation-EX Please see https://DCC-EX.com
|
// DCC-EX CommandStation-EX Please see https://DCC-EX.com
|
||||||
//
|
//
|
||||||
// This file is the main sketch for the Command Station.
|
// This file is the main sketch for the Command Station.
|
||||||
//
|
//
|
||||||
// CONFIGURATION:
|
// CONFIGURATION:
|
||||||
// Configuration is normally performed by editing a file called config.h.
|
// Configuration is normally performed by editing a file called config.h.
|
||||||
// This file is NOT shipped with the code so that if you pull a later version
|
// This file is NOT shipped with the code so that if you pull a later version
|
||||||
// of the code, your configuration will not be overwritten.
|
// of the code, your configuration will not be overwritten.
|
||||||
//
|
//
|
||||||
// If you used the automatic installer program, config.h will have been created automatically.
|
// If you used the automatic installer program, config.h will have been created automatically.
|
||||||
//
|
//
|
||||||
// To obtain a starting copy of config.h please copy the file config.example.h which is
|
// To obtain a starting copy of config.h please copy the file config.example.h which is
|
||||||
// shipped with the code and may be updated as new features are added.
|
// shipped with the code and may be updated as new features are added.
|
||||||
//
|
//
|
||||||
// If config.h is not found, config.example.h will be used with all defaults.
|
// If config.h is not found, config.example.h will be used with all defaults.
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#if __has_include ( "config.h")
|
#if __has_include ( "config.h")
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#else
|
#else
|
||||||
#warning config.h not found. Using defaults from config.example.h
|
#warning config.h not found. Using defaults from config.example.h
|
||||||
#include "config.example.h"
|
#include "config.example.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* © 2020,2021 Chris Harlow, Harald Barth, David Cutting,
|
* © 2020,2021 Chris Harlow, Harald Barth, David Cutting,
|
||||||
* Fred Decker, Gregor Baues, Anthony W - Dayton All rights reserved.
|
* Fred Decker, Gregor Baues, Anthony W - Dayton All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
@ -47,10 +47,10 @@
|
||||||
#include "DCCEX.h"
|
#include "DCCEX.h"
|
||||||
#ifdef WIFI_WARNING
|
#ifdef WIFI_WARNING
|
||||||
#warning You have defined that you want WiFi but your hardware has not enough memory to do that, so WiFi DISABLED
|
#warning You have defined that you want WiFi but your hardware has not enough memory to do that, so WiFi DISABLED
|
||||||
#endif
|
#endif
|
||||||
#ifdef ETHERNET_WARNING
|
#ifdef ETHERNET_WARNING
|
||||||
#warning You have defined that you want Ethernet but your hardware has not enough memory to do that, so Ethernet DISABLED
|
#warning You have defined that you want Ethernet but your hardware has not enough memory to do that, so Ethernet DISABLED
|
||||||
#endif
|
#endif
|
||||||
#ifdef EXRAIL_WARNING
|
#ifdef EXRAIL_WARNING
|
||||||
#warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED
|
#warning You have myAutomation.h but your hardware has not enough memory to do that, so EX-RAIL DISABLED
|
||||||
#endif
|
#endif
|
||||||
|
@ -66,10 +66,10 @@ void setup()
|
||||||
DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com"));
|
DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com"));
|
||||||
|
|
||||||
CONDITIONAL_LCD_START {
|
CONDITIONAL_LCD_START {
|
||||||
// This block is still executed for DIAGS if LCD not in use
|
// This block is still executed for DIAGS if LCD not in use
|
||||||
LCD(0,F("DCC++ EX v%S"),F(VERSION));
|
LCD(0,F("DCC++ EX v%S"),F(VERSION));
|
||||||
LCD(1,F("Lic GPLv3"));
|
LCD(1,F("Lic GPLv3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Responsibility 2: Start all the communications before the DCC engine
|
// Responsibility 2: Start all the communications before the DCC engine
|
||||||
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
|
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
|
||||||
|
@ -88,25 +88,25 @@ void setup()
|
||||||
// detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic.
|
// detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic.
|
||||||
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h
|
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h
|
||||||
DCC::begin(MOTOR_SHIELD_TYPE);
|
DCC::begin(MOTOR_SHIELD_TYPE);
|
||||||
|
|
||||||
// Start RMFT (ignored if no automnation)
|
// Start RMFT (ignored if no automnation)
|
||||||
RMFT::begin();
|
RMFT::begin();
|
||||||
|
|
||||||
|
|
||||||
// Invoke any DCC++EX commands in the form "SETUP("xxxx");"" found in optional file mySetup.h.
|
|
||||||
|
// Invoke any DCC++EX commands in the form "SETUP("xxxx");"" found in optional file mySetup.h.
|
||||||
// This can be used to create turnouts, outputs, sensors etc. through the normal text commands.
|
// This can be used to create turnouts, outputs, sensors etc. through the normal text commands.
|
||||||
#if __has_include ( "mySetup.h")
|
#if __has_include ( "mySetup.h")
|
||||||
#define SETUP(cmd) DCCEXParser::parse(F(cmd))
|
#define SETUP(cmd) DCCEXParser::parse(F(cmd))
|
||||||
#include "mySetup.h"
|
#include "mySetup.h"
|
||||||
#undef SETUP
|
#undef SETUP
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(LCN_SERIAL)
|
|
||||||
LCN_SERIAL.begin(115200);
|
|
||||||
LCN::init(LCN_SERIAL);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LCD(3,F("Ready"));
|
#if defined(LCN_SERIAL)
|
||||||
|
LCN_SERIAL.begin(115200);
|
||||||
|
LCN::init(LCN_SERIAL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LCD(3,F("Ready"));
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ void loop()
|
||||||
// Responsibility 2: handle any incoming commands on USB connection
|
// Responsibility 2: handle any incoming commands on USB connection
|
||||||
SerialManager::loop();
|
SerialManager::loop();
|
||||||
|
|
||||||
// Responsibility 3: Optionally handle any incoming WiFi traffic
|
// Responsibility 3: Optionally handle any incoming WiFi traffic
|
||||||
#if WIFI_ON
|
#if WIFI_ON
|
||||||
WifiInterface::loop();
|
WifiInterface::loop();
|
||||||
#endif
|
#endif
|
||||||
|
@ -131,23 +131,22 @@ void loop()
|
||||||
|
|
||||||
RMFT::loop(); // ignored if no automation
|
RMFT::loop(); // ignored if no automation
|
||||||
|
|
||||||
#if defined(LCN_SERIAL)
|
#if defined(LCN_SERIAL)
|
||||||
LCN::loop();
|
LCN::loop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LCDDisplay::loop(); // ignored if LCD not in use
|
LCDDisplay::loop(); // ignored if LCD not in use
|
||||||
|
|
||||||
// Handle/update IO devices.
|
// Handle/update IO devices.
|
||||||
IODevice::loop();
|
IODevice::loop();
|
||||||
|
|
||||||
Sensor::checkAll(); // Update and print changes
|
Sensor::checkAll(); // Update and print changes
|
||||||
|
|
||||||
// Report any decrease in memory (will automatically trigger on first call)
|
// Report any decrease in memory (will automatically trigger on first call)
|
||||||
static int ramLowWatermark = __INT_MAX__; // replaced on first loop
|
static int ramLowWatermark = __INT_MAX__; // replaced on first loop
|
||||||
|
|
||||||
int freeNow = minimumFreeMemory();
|
int freeNow = minimumFreeMemory();
|
||||||
if (freeNow < ramLowWatermark)
|
if (freeNow < ramLowWatermark) {
|
||||||
{
|
|
||||||
ramLowWatermark = freeNow;
|
ramLowWatermark = freeNow;
|
||||||
LCD(3,F("Free RAM=%5db"), ramLowWatermark);
|
LCD(3,F("Free RAM=%5db"), ramLowWatermark);
|
||||||
}
|
}
|
||||||
|
|
296
DCC.cpp
296
DCC.cpp
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* © 2020, Chris Harlow. All rights reserved.
|
* © 2020, Chris Harlow. All rights reserved.
|
||||||
* © 2020, Harald Barth
|
* © 2020, Harald Barth
|
||||||
*
|
*
|
||||||
* This file is part of Asbelos DCC API
|
* This file is part of Asbelos DCC API
|
||||||
*
|
*
|
||||||
* This is free software: you can redistribute it and/or modify
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
@ -43,11 +43,11 @@
|
||||||
// Obtaining ACKs from the prog track using a function
|
// Obtaining ACKs from the prog track using a function
|
||||||
// There are no volatiles here.
|
// There are no volatiles here.
|
||||||
|
|
||||||
const byte FN_GROUP_1=0x01;
|
const byte FN_GROUP_1=0x01;
|
||||||
const byte FN_GROUP_2=0x02;
|
const byte FN_GROUP_2=0x02;
|
||||||
const byte FN_GROUP_3=0x04;
|
const byte FN_GROUP_3=0x04;
|
||||||
const byte FN_GROUP_4=0x08;
|
const byte FN_GROUP_4=0x08;
|
||||||
const byte FN_GROUP_5=0x10;
|
const byte FN_GROUP_5=0x10;
|
||||||
|
|
||||||
FSH* DCC::shieldName=NULL;
|
FSH* DCC::shieldName=NULL;
|
||||||
byte DCC::joinRelay=UNUSED_PIN;
|
byte DCC::joinRelay=UNUSED_PIN;
|
||||||
|
@ -66,7 +66,7 @@ void DCC::begin(const FSH * motorShieldName, MotorDriver * mainDriver, MotorDriv
|
||||||
EEStore::init();
|
EEStore::init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DCCWaveform::begin(mainDriver,progDriver);
|
DCCWaveform::begin(mainDriver,progDriver);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::setJoinRelayPin(byte joinRelayPin) {
|
void DCC::setJoinRelayPin(byte joinRelayPin) {
|
||||||
|
@ -78,7 +78,7 @@ void DCC::setJoinRelayPin(byte joinRelayPin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) {
|
void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) {
|
||||||
byte speedCode = (tSpeed & 0x7F) + tDirection * 128;
|
byte speedCode = (tSpeed & 0x7F) + tDirection * 128;
|
||||||
setThrottle2(cab, speedCode);
|
setThrottle2(cab, speedCode);
|
||||||
// retain speed for loco reminders
|
// retain speed for loco reminders
|
||||||
updateLocoReminder(cab, speedCode );
|
updateLocoReminder(cab, speedCode );
|
||||||
|
@ -89,7 +89,7 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) {
|
||||||
uint8_t b[4];
|
uint8_t b[4];
|
||||||
uint8_t nB = 0;
|
uint8_t nB = 0;
|
||||||
// DIAG(F("setSpeedInternal %d %x"),cab,speedCode);
|
// DIAG(F("setSpeedInternal %d %x"),cab,speedCode);
|
||||||
|
|
||||||
if (cab > HIGHEST_SHORT_ADDR)
|
if (cab > HIGHEST_SHORT_ADDR)
|
||||||
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
|
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
|
||||||
b[nB++] = lowByte(cab);
|
b[nB++] = lowByte(cab);
|
||||||
|
@ -154,35 +154,35 @@ bool DCC::getThrottleDirection(int cab) {
|
||||||
// Set function to value on or off
|
// Set function to value on or off
|
||||||
void DCC::setFn( int cab, int16_t functionNumber, bool on) {
|
void DCC::setFn( int cab, int16_t functionNumber, bool on) {
|
||||||
if (cab<=0 ) return;
|
if (cab<=0 ) return;
|
||||||
|
|
||||||
if (functionNumber>28) {
|
if (functionNumber>28) {
|
||||||
//non reminding advanced binary bit set
|
//non reminding advanced binary bit set
|
||||||
byte b[5];
|
byte b[5];
|
||||||
byte nB = 0;
|
byte nB = 0;
|
||||||
if (cab > HIGHEST_SHORT_ADDR)
|
if (cab > HIGHEST_SHORT_ADDR)
|
||||||
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
|
b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
|
||||||
b[nB++] = lowByte(cab);
|
b[nB++] = lowByte(cab);
|
||||||
if (functionNumber <= 127) {
|
if (functionNumber <= 127) {
|
||||||
b[nB++] = 0b11011101; // Binary State Control Instruction short form
|
b[nB++] = 0b11011101; // Binary State Control Instruction short form
|
||||||
b[nB++] = functionNumber | (on ? 0x80 : 0);
|
b[nB++] = functionNumber | (on ? 0x80 : 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
b[nB++] = 0b11000000; // Binary State Control Instruction long form
|
b[nB++] = 0b11000000; // Binary State Control Instruction long form
|
||||||
b[nB++] = (functionNumber & 0x7F) | (on ? 0x80 : 0); // low order bits and state flag
|
b[nB++] = (functionNumber & 0x7F) | (on ? 0x80 : 0); // low order bits and state flag
|
||||||
b[nB++] = functionNumber >>7 ; // high order bits
|
b[nB++] = functionNumber >>7 ; // high order bits
|
||||||
}
|
}
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 4);
|
DCCWaveform::mainTrack.schedulePacket(b, nB, 4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int reg = lookupSpeedTable(cab);
|
int reg = lookupSpeedTable(cab);
|
||||||
if (reg<0) return;
|
if (reg<0) return;
|
||||||
|
|
||||||
// Take care of functions:
|
// Take care of functions:
|
||||||
// Set state of function
|
// Set state of function
|
||||||
unsigned long previous=speedTable[reg].functions;
|
unsigned long previous=speedTable[reg].functions;
|
||||||
unsigned long funcmask = (1UL<<functionNumber);
|
unsigned long funcmask = (1UL<<functionNumber);
|
||||||
if (on) {
|
if (on) {
|
||||||
speedTable[reg].functions |= funcmask;
|
speedTable[reg].functions |= funcmask;
|
||||||
} else {
|
} else {
|
||||||
speedTable[reg].functions &= ~funcmask;
|
speedTable[reg].functions &= ~funcmask;
|
||||||
|
@ -197,24 +197,24 @@ void DCC::setFn( int cab, int16_t functionNumber, bool on) {
|
||||||
void DCC::changeFn( int cab, int16_t functionNumber) {
|
void DCC::changeFn( int cab, int16_t functionNumber) {
|
||||||
if (cab<=0 || functionNumber>28) return;
|
if (cab<=0 || functionNumber>28) return;
|
||||||
int reg = lookupSpeedTable(cab);
|
int reg = lookupSpeedTable(cab);
|
||||||
if (reg<0) return;
|
if (reg<0) return;
|
||||||
unsigned long funcmask = (1UL<<functionNumber);
|
unsigned long funcmask = (1UL<<functionNumber);
|
||||||
speedTable[reg].functions ^= funcmask;
|
speedTable[reg].functions ^= funcmask;
|
||||||
updateGroupflags(speedTable[reg].groupFlags, functionNumber);
|
updateGroupflags(speedTable[reg].groupFlags, functionNumber);
|
||||||
CommandDistributor::broadcastLoco(reg);
|
CommandDistributor::broadcastLoco(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DCC::getFn( int cab, int16_t functionNumber) {
|
int DCC::getFn( int cab, int16_t functionNumber) {
|
||||||
if (cab<=0 || functionNumber>28) return -1; // unknown
|
if (cab<=0 || functionNumber>28) return -1; // unknown
|
||||||
int reg = lookupSpeedTable(cab);
|
int reg = lookupSpeedTable(cab);
|
||||||
if (reg<0) return -1;
|
if (reg<0) return -1;
|
||||||
|
|
||||||
unsigned long funcmask = (1UL<<functionNumber);
|
unsigned long funcmask = (1UL<<functionNumber);
|
||||||
return (speedTable[reg].functions & funcmask)? 1 : 0;
|
return (speedTable[reg].functions & funcmask)? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the group flag to say we have touched the particular group.
|
// Set the group flag to say we have touched the particular group.
|
||||||
// A group will be reminded only if it has been touched.
|
// A group will be reminded only if it has been touched.
|
||||||
void DCC::updateGroupflags(byte & flags, int16_t functionNumber) {
|
void DCC::updateGroupflags(byte & flags, int16_t functionNumber) {
|
||||||
byte groupMask;
|
byte groupMask;
|
||||||
if (functionNumber<=4) groupMask=FN_GROUP_1;
|
if (functionNumber<=4) groupMask=FN_GROUP_1;
|
||||||
|
@ -222,13 +222,13 @@ void DCC::updateGroupflags(byte & flags, int16_t functionNumber) {
|
||||||
else if (functionNumber<=12) groupMask=FN_GROUP_3;
|
else if (functionNumber<=12) groupMask=FN_GROUP_3;
|
||||||
else if (functionNumber<=20) groupMask=FN_GROUP_4;
|
else if (functionNumber<=20) groupMask=FN_GROUP_4;
|
||||||
else groupMask=FN_GROUP_5;
|
else groupMask=FN_GROUP_5;
|
||||||
flags |= groupMask;
|
flags |= groupMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t DCC::getFunctionMap(int cab) {
|
uint32_t DCC::getFunctionMap(int cab) {
|
||||||
if (cab<=0) return 0; // unknown pretend all functions off
|
if (cab<=0) return 0; // unknown pretend all functions off
|
||||||
int reg = lookupSpeedTable(cab);
|
int reg = lookupSpeedTable(cab);
|
||||||
return (reg<0)?0:speedTable[reg].functions;
|
return (reg<0)?0:speedTable[reg].functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::setAccessory(int address, byte number, bool activate) {
|
void DCC::setAccessory(int address, byte number, bool activate) {
|
||||||
|
@ -301,64 +301,64 @@ void DCC::setProgTrackBoost(bool on) {
|
||||||
FSH* DCC::getMotorShieldName() {
|
FSH* DCC::getMotorShieldName() {
|
||||||
return shieldName;
|
return shieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ackOp FLASH WRITE_BIT0_PROG[] = {
|
const ackOp FLASH WRITE_BIT0_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
W0,WACK,
|
W0,WACK,
|
||||||
V0, WACK, // validate bit is 0
|
V0, WACK, // validate bit is 0
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
const ackOp FLASH WRITE_BIT1_PROG[] = {
|
const ackOp FLASH WRITE_BIT1_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
W1,WACK,
|
W1,WACK,
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp FLASH VERIFY_BIT0_PROG[] = {
|
const ackOp FLASH VERIFY_BIT0_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
V0, WACK, // validate bit is 0
|
V0, WACK, // validate bit is 0
|
||||||
ITC0, // if acked, callback(0)
|
ITC0, // if acked, callback(0)
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
ITC1,
|
ITC1,
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
const ackOp FLASH VERIFY_BIT1_PROG[] = {
|
const ackOp FLASH VERIFY_BIT1_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
V0, WACK,
|
V0, WACK,
|
||||||
ITC0,
|
ITC0,
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp FLASH READ_BIT_PROG[] = {
|
const ackOp FLASH READ_BIT_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
V0, WACK, // validate bit is zero
|
V0, WACK, // validate bit is zero
|
||||||
ITC0, // if acked callback 0
|
ITC0, // if acked callback 0
|
||||||
FAIL // bit not readable
|
FAIL // bit not readable
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp FLASH WRITE_BYTE_PROG[] = {
|
const ackOp FLASH WRITE_BYTE_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
WB,WACK,ITC1, // Write and callback(1) if ACK
|
WB,WACK,ITC1, // Write and callback(1) if ACK
|
||||||
// handle decoders that dont ack a write
|
// handle decoders that dont ack a write
|
||||||
VB,WACK,ITC1, // validate byte and callback(1) if correct
|
VB,WACK,ITC1, // validate byte and callback(1) if correct
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp FLASH VERIFY_BYTE_PROG[] = {
|
const ackOp FLASH VERIFY_BYTE_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
BIV, // ackManagerByte initial value
|
BIV, // ackManagerByte initial value
|
||||||
VB,WACK, // validate byte
|
VB,WACK, // validate byte
|
||||||
ITCB, // if ok callback value
|
ITCB, // if ok callback value
|
||||||
STARTMERGE, //clear bit and byte values ready for merge pass
|
STARTMERGE, //clear bit and byte values ready for merge pass
|
||||||
// each bit is validated against 0 and the result inverted in MERGE
|
// each bit is validated against 0 and the result inverted in MERGE
|
||||||
// this is because there tend to be more zeros in cv values than ones.
|
// this is because there tend to be more zeros in cv values than ones.
|
||||||
// There is no need for one validation as entire byte is validated at the end
|
// There is no need for one validation as entire byte is validated at the end
|
||||||
V0, WACK, MERGE, // read and merge first tested bit (7)
|
V0, WACK, MERGE, // read and merge first tested bit (7)
|
||||||
ITSKIP, // do small excursion if there was no ack
|
ITSKIP, // do small excursion if there was no ack
|
||||||
|
@ -375,13 +375,13 @@ const ackOp FLASH VERIFY_BYTE_PROG[] = {
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
VB, WACK, ITCBV, // verify merged byte and return it if acked ok - with retry report
|
VB, WACK, ITCBV, // verify merged byte and return it if acked ok - with retry report
|
||||||
FAIL };
|
FAIL };
|
||||||
|
|
||||||
|
|
||||||
const ackOp FLASH READ_CV_PROG[] = {
|
const ackOp FLASH READ_CV_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
STARTMERGE, //clear bit and byte values ready for merge pass
|
STARTMERGE, //clear bit and byte values ready for merge pass
|
||||||
// each bit is validated against 0 and the result inverted in MERGE
|
// each bit is validated against 0 and the result inverted in MERGE
|
||||||
// this is because there tend to be more zeros in cv values than ones.
|
// this is because there tend to be more zeros in cv values than ones.
|
||||||
// There is no need for one validation as entire byte is validated at the end
|
// There is no need for one validation as entire byte is validated at the end
|
||||||
V0, WACK, MERGE, // read and merge first tested bit (7)
|
V0, WACK, MERGE, // read and merge first tested bit (7)
|
||||||
ITSKIP, // do small excursion if there was no ack
|
ITSKIP, // do small excursion if there was no ack
|
||||||
|
@ -396,20 +396,20 @@ const ackOp FLASH READ_CV_PROG[] = {
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
VB, WACK, ITCB, // verify merged byte and return it if acked ok
|
VB, WACK, ITCB, // verify merged byte and return it if acked ok
|
||||||
FAIL }; // verification failed
|
FAIL }; // verification failed
|
||||||
|
|
||||||
|
|
||||||
const ackOp FLASH LOCO_ID_PROG[] = {
|
const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
SETCV, (ackOp)19, // CV 19 is consist setting
|
SETCV, (ackOp)19, // CV 19 is consist setting
|
||||||
SETBYTE, (ackOp)0,
|
SETBYTE, (ackOp)0,
|
||||||
VB, WACK, ITSKIP, // ignore consist if cv19 is zero (no consist)
|
VB, WACK, ITSKIP, // ignore consist if cv19 is zero (no consist)
|
||||||
SETBYTE, (ackOp)128,
|
SETBYTE, (ackOp)128,
|
||||||
VB, WACK, ITSKIP, // ignore consist if cv19 is 128 (no consist, direction bit set)
|
VB, WACK, ITSKIP, // ignore consist if cv19 is 128 (no consist, direction bit set)
|
||||||
STARTMERGE, // Setup to read cv 19
|
STARTMERGE, // Setup to read cv 19
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
|
@ -417,13 +417,13 @@ const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
VB, WACK, ITCB7, // return 7 bits only, No_ACK means CV19 not supported so ignore it
|
VB, WACK, ITCB7, // return 7 bits only, No_ACK means CV19 not supported so ignore it
|
||||||
|
|
||||||
SKIPTARGET, // continue here if CV 19 is zero or fails all validation
|
SKIPTARGET, // continue here if CV 19 is zero or fails all validation
|
||||||
SETCV,(ackOp)29,
|
SETCV,(ackOp)29,
|
||||||
SETBIT,(ackOp)5,
|
SETBIT,(ackOp)5,
|
||||||
V0, WACK, ITSKIP, // Skip to SKIPTARGET if bit 5 of CV29 is zero
|
V0, WACK, ITSKIP, // Skip to SKIPTARGET if bit 5 of CV29 is zero
|
||||||
|
|
||||||
// Long locoid
|
// Long locoid
|
||||||
SETCV, (ackOp)17, // CV 17 is part of locoid
|
SETCV, (ackOp)17, // CV 17 is part of locoid
|
||||||
STARTMERGE,
|
STARTMERGE,
|
||||||
V0, WACK, MERGE, // read and merge bit 1 etc
|
V0, WACK, MERGE, // read and merge bit 1 etc
|
||||||
|
@ -435,8 +435,8 @@ const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
VB, WACK, NAKFAIL, // verify merged byte and return -1 it if not acked ok
|
VB, WACK, NAKFAIL, // verify merged byte and return -1 it if not acked ok
|
||||||
STASHLOCOID, // keep stashed cv 17 for later
|
STASHLOCOID, // keep stashed cv 17 for later
|
||||||
// Read 2nd part from CV 18
|
// Read 2nd part from CV 18
|
||||||
SETCV, (ackOp)18,
|
SETCV, (ackOp)18,
|
||||||
STARTMERGE,
|
STARTMERGE,
|
||||||
V0, WACK, MERGE, // read and merge bit 1 etc
|
V0, WACK, MERGE, // read and merge bit 1 etc
|
||||||
|
@ -449,8 +449,8 @@ const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
VB, WACK, NAKFAIL, // verify merged byte and return -1 it if not acked ok
|
VB, WACK, NAKFAIL, // verify merged byte and return -1 it if not acked ok
|
||||||
COMBINELOCOID, // Combile byte with stash to make long locoid and callback
|
COMBINELOCOID, // Combile byte with stash to make long locoid and callback
|
||||||
|
|
||||||
// ITSKIP Skips to here if CV 29 bit 5 was zero. so read CV 1 and return that
|
// ITSKIP Skips to here if CV 29 bit 5 was zero. so read CV 1 and return that
|
||||||
SKIPTARGET,
|
SKIPTARGET,
|
||||||
SETCV, (ackOp)1,
|
SETCV, (ackOp)1,
|
||||||
STARTMERGE,
|
STARTMERGE,
|
||||||
|
@ -464,7 +464,7 @@ const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
V0, WACK, MERGE,
|
V0, WACK, MERGE,
|
||||||
VB, WACK, ITCB, // verify merged byte and callback
|
VB, WACK, ITCB, // verify merged byte and callback
|
||||||
FAIL
|
FAIL
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp FLASH SHORT_LOCO_ID_PROG[] = {
|
const ackOp FLASH SHORT_LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
|
@ -476,12 +476,12 @@ const ackOp FLASH SHORT_LOCO_ID_PROG[] = {
|
||||||
SETBIT,(ackOp)5,
|
SETBIT,(ackOp)5,
|
||||||
W0,WACK,
|
W0,WACK,
|
||||||
V0,WACK,NAKFAIL,
|
V0,WACK,NAKFAIL,
|
||||||
SETCV, (ackOp)1,
|
SETCV, (ackOp)1,
|
||||||
SETBYTEL, // low byte of word
|
SETBYTEL, // low byte of word
|
||||||
WB,WACK, // some decoders don't ACK writes
|
WB,WACK, // some decoders don't ACK writes
|
||||||
VB,WACK,ITCB,
|
VB,WACK,ITCB,
|
||||||
FAIL
|
FAIL
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp FLASH LONG_LOCO_ID_PROG[] = {
|
const ackOp FLASH LONG_LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
|
@ -496,16 +496,16 @@ const ackOp FLASH LONG_LOCO_ID_PROG[] = {
|
||||||
V1,WACK,NAKFAIL,
|
V1,WACK,NAKFAIL,
|
||||||
// Store high byte of address in cv 17
|
// Store high byte of address in cv 17
|
||||||
SETCV, (ackOp)17,
|
SETCV, (ackOp)17,
|
||||||
SETBYTEH, // high byte of word
|
SETBYTEH, // high byte of word
|
||||||
WB,WACK,
|
WB,WACK,
|
||||||
VB,WACK,NAKFAIL,
|
VB,WACK,NAKFAIL,
|
||||||
// store
|
// store
|
||||||
SETCV, (ackOp)18,
|
SETCV, (ackOp)18,
|
||||||
SETBYTEL, // low byte of word
|
SETBYTEL, // low byte of word
|
||||||
WB,WACK,
|
WB,WACK,
|
||||||
VB,WACK,ITC1, // callback(1) means Ok
|
VB,WACK,ITC1, // callback(1) means Ok
|
||||||
FAIL
|
FAIL
|
||||||
};
|
};
|
||||||
|
|
||||||
void DCC::writeCVByte(int16_t cv, byte byteValue, ACK_CALLBACK callback) {
|
void DCC::writeCVByte(int16_t cv, byte byteValue, ACK_CALLBACK callback) {
|
||||||
ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback);
|
ackManagerSetup(cv, byteValue, WRITE_BYTE_PROG, callback);
|
||||||
|
@ -551,17 +551,17 @@ void DCC::setLocoId(int id,ACK_CALLBACK callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco
|
void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco
|
||||||
setThrottle2(cab,1); // ESTOP this loco if still on track
|
setThrottle2(cab,1); // ESTOP this loco if still on track
|
||||||
int reg=lookupSpeedTable(cab);
|
int reg=lookupSpeedTable(cab);
|
||||||
if (reg>=0) speedTable[reg].loco=0;
|
if (reg>=0) speedTable[reg].loco=0;
|
||||||
setThrottle2(cab,1); // ESTOP if this loco still on track
|
setThrottle2(cab,1); // ESTOP if this loco still on track
|
||||||
}
|
}
|
||||||
void DCC::forgetAllLocos() { // removes all speed reminders
|
void DCC::forgetAllLocos() { // removes all speed reminders
|
||||||
setThrottle2(0,1); // ESTOP all locos still on track
|
setThrottle2(0,1); // ESTOP all locos still on track
|
||||||
for (int i=0;i<MAX_LOCOS;i++) speedTable[i].loco=0;
|
for (int i=0;i<MAX_LOCOS;i++) speedTable[i].loco=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte DCC::loopStatus=0;
|
byte DCC::loopStatus=0;
|
||||||
|
|
||||||
void DCC::loop() {
|
void DCC::loop() {
|
||||||
DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks
|
DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks
|
||||||
|
@ -576,58 +576,58 @@ void DCC::issueReminders() {
|
||||||
// This loop searches for a loco in the speed table starting at nextLoco and cycling back around
|
// This loop searches for a loco in the speed table starting at nextLoco and cycling back around
|
||||||
for (int reg=0;reg<MAX_LOCOS;reg++) {
|
for (int reg=0;reg<MAX_LOCOS;reg++) {
|
||||||
int slot=reg+nextLoco;
|
int slot=reg+nextLoco;
|
||||||
if (slot>=MAX_LOCOS) slot-=MAX_LOCOS;
|
if (slot>=MAX_LOCOS) slot-=MAX_LOCOS;
|
||||||
if (speedTable[slot].loco > 0) {
|
if (speedTable[slot].loco > 0) {
|
||||||
// have found the next loco to remind
|
// have found the next loco to remind
|
||||||
// issueReminder will return true if this loco is completed (ie speed and functions)
|
// issueReminder will return true if this loco is completed (ie speed and functions)
|
||||||
if (issueReminder(slot)) nextLoco=slot+1;
|
if (issueReminder(slot)) nextLoco=slot+1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DCC::issueReminder(int reg) {
|
bool DCC::issueReminder(int reg) {
|
||||||
unsigned long functions=speedTable[reg].functions;
|
unsigned long functions=speedTable[reg].functions;
|
||||||
int loco=speedTable[reg].loco;
|
int loco=speedTable[reg].loco;
|
||||||
byte flags=speedTable[reg].groupFlags;
|
byte flags=speedTable[reg].groupFlags;
|
||||||
|
|
||||||
switch (loopStatus) {
|
switch (loopStatus) {
|
||||||
case 0:
|
case 0:
|
||||||
// DIAG(F("Reminder %d speed %d"),loco,speedTable[reg].speedCode);
|
// DIAG(F("Reminder %d speed %d"),loco,speedTable[reg].speedCode);
|
||||||
setThrottle2(loco, speedTable[reg].speedCode);
|
setThrottle2(loco, speedTable[reg].speedCode);
|
||||||
break;
|
break;
|
||||||
case 1: // remind function group 1 (F0-F4)
|
case 1: // remind function group 1 (F0-F4)
|
||||||
if (flags & FN_GROUP_1)
|
if (flags & FN_GROUP_1)
|
||||||
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4)); // 100D DDDD
|
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4)); // 100D DDDD
|
||||||
break;
|
break;
|
||||||
case 2: // remind function group 2 F5-F8
|
case 2: // remind function group 2 F5-F8
|
||||||
if (flags & FN_GROUP_2)
|
if (flags & FN_GROUP_2)
|
||||||
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F)); // 1011 DDDD
|
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F)); // 1011 DDDD
|
||||||
break;
|
break;
|
||||||
case 3: // remind function group 3 F9-F12
|
case 3: // remind function group 3 F9-F12
|
||||||
if (flags & FN_GROUP_3)
|
if (flags & FN_GROUP_3)
|
||||||
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F)); // 1010 DDDD
|
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F)); // 1010 DDDD
|
||||||
break;
|
break;
|
||||||
case 4: // remind function group 4 F13-F20
|
case 4: // remind function group 4 F13-F20
|
||||||
if (flags & FN_GROUP_4)
|
if (flags & FN_GROUP_4)
|
||||||
setFunctionInternal(loco,222, ((functions>>13)& 0xFF));
|
setFunctionInternal(loco,222, ((functions>>13)& 0xFF));
|
||||||
flags&= ~FN_GROUP_4; // dont send them again
|
flags&= ~FN_GROUP_4; // dont send them again
|
||||||
break;
|
break;
|
||||||
case 5: // remind function group 5 F21-F28
|
case 5: // remind function group 5 F21-F28
|
||||||
if (flags & FN_GROUP_5)
|
if (flags & FN_GROUP_5)
|
||||||
setFunctionInternal(loco,223, ((functions>>21)& 0xFF));
|
setFunctionInternal(loco,223, ((functions>>21)& 0xFF));
|
||||||
flags&= ~FN_GROUP_5; // dont send them again
|
flags&= ~FN_GROUP_5; // dont send them again
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
loopStatus++;
|
loopStatus++;
|
||||||
// if we reach status 6 then this loco is done so
|
// if we reach status 6 then this loco is done so
|
||||||
// reset status to 0 for next loco and return true so caller
|
// reset status to 0 for next loco and return true so caller
|
||||||
// moves on to next loco.
|
// moves on to next loco.
|
||||||
if (loopStatus>5) loopStatus=0;
|
if (loopStatus>5) loopStatus=0;
|
||||||
return loopStatus==0;
|
return loopStatus==0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///// Private helper functions below here /////////////////////
|
///// Private helper functions below here /////////////////////
|
||||||
|
@ -662,9 +662,9 @@ int DCC::lookupSpeedTable(int locoId) {
|
||||||
}
|
}
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::updateLocoReminder(int loco, byte speedCode) {
|
void DCC::updateLocoReminder(int loco, byte speedCode) {
|
||||||
|
|
||||||
if (loco==0) {
|
if (loco==0) {
|
||||||
// broadcast stop/estop but dont change direction
|
// broadcast stop/estop but dont change direction
|
||||||
for (int reg = 0; reg < MAX_LOCOS; reg++) {
|
for (int reg = 0; reg < MAX_LOCOS; reg++) {
|
||||||
|
@ -675,11 +675,11 @@ void DCC::updateLocoReminder(int loco, byte speedCode) {
|
||||||
CommandDistributor::broadcastLoco(reg);
|
CommandDistributor::broadcastLoco(reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine speed reg for this loco
|
// determine speed reg for this loco
|
||||||
int reg=lookupSpeedTable(loco);
|
int reg=lookupSpeedTable(loco);
|
||||||
if (reg>=0 && speedTable[reg].speedCode!=speedCode) {
|
if (reg>=0 && speedTable[reg].speedCode!=speedCode) {
|
||||||
speedTable[reg].speedCode = speedCode;
|
speedTable[reg].speedCode = speedCode;
|
||||||
CommandDistributor::broadcastLoco(reg);
|
CommandDistributor::broadcastLoco(reg);
|
||||||
|
@ -715,19 +715,19 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[]
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ackManagerRejoin=DCCWaveform::progTrackSyncMain;
|
ackManagerRejoin=DCCWaveform::progTrackSyncMain;
|
||||||
if (ackManagerRejoin ) {
|
if (ackManagerRejoin ) {
|
||||||
// Change from JOIN must zero resets packet.
|
// Change from JOIN must zero resets packet.
|
||||||
setProgTrackSyncMain(false);
|
setProgTrackSyncMain(false);
|
||||||
DCCWaveform::progTrack.sentResetsSincePacket = 0;
|
DCCWaveform::progTrack.sentResetsSincePacket = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DCCWaveform::progTrack.autoPowerOff=false;
|
DCCWaveform::progTrack.autoPowerOff=false;
|
||||||
if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) {
|
if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) {
|
||||||
DCCWaveform::progTrack.autoPowerOff=true; // power off afterwards
|
DCCWaveform::progTrack.autoPowerOff=true; // power off afterwards
|
||||||
if (Diag::ACK) DIAG(F("Auto Prog power on"));
|
if (Diag::ACK) DIAG(F("Auto Prog power on"));
|
||||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||||
DCCWaveform::progTrack.sentResetsSincePacket = 0;
|
DCCWaveform::progTrack.sentResetsSincePacket = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ackManagerCv = cv;
|
ackManagerCv = cv;
|
||||||
|
@ -755,7 +755,7 @@ bool DCC::checkResets(uint8_t numResets) {
|
||||||
void DCC::ackManagerLoop() {
|
void DCC::ackManagerLoop() {
|
||||||
while (ackManagerProg) {
|
while (ackManagerProg) {
|
||||||
byte opcode=GETFLASH(ackManagerProg);
|
byte opcode=GETFLASH(ackManagerProg);
|
||||||
|
|
||||||
// breaks from this switch will step to next prog entry
|
// breaks from this switch will step to next prog entry
|
||||||
// returns from this switch will stay on same entry
|
// returns from this switch will stay on same entry
|
||||||
// (typically waiting for a reset counter or ACK waiting, or when all finished.)
|
// (typically waiting for a reset counter or ACK waiting, or when all finished.)
|
||||||
|
@ -765,57 +765,57 @@ void DCC::ackManagerLoop() {
|
||||||
if (checkResets(DCCWaveform::progTrack.autoPowerOff || ackManagerRejoin ? 20 : 3)) return;
|
if (checkResets(DCCWaveform::progTrack.autoPowerOff || ackManagerRejoin ? 20 : 3)) return;
|
||||||
DCCWaveform::progTrack.setAckBaseline();
|
DCCWaveform::progTrack.setAckBaseline();
|
||||||
callbackState=READY;
|
callbackState=READY;
|
||||||
break;
|
break;
|
||||||
case W0: // write 0 bit
|
case W0: // write 0 bit
|
||||||
case W1: // write 1 bit
|
case W1: // write 1 bit
|
||||||
{
|
{
|
||||||
if (checkResets(RESET_MIN)) return;
|
if (checkResets(RESET_MIN)) return;
|
||||||
if (Diag::ACK) DIAG(F("W%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum);
|
if (Diag::ACK) DIAG(F("W%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum);
|
||||||
byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum;
|
byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum;
|
||||||
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
||||||
DCCWaveform::progTrack.setAckPending();
|
DCCWaveform::progTrack.setAckPending();
|
||||||
callbackState=AFTER_WRITE;
|
callbackState=AFTER_WRITE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WB: // write byte
|
case WB: // write byte
|
||||||
{
|
{
|
||||||
if (checkResets( RESET_MIN)) return;
|
if (checkResets( RESET_MIN)) return;
|
||||||
if (Diag::ACK) DIAG(F("WB cv=%d value=%d"),ackManagerCv,ackManagerByte);
|
if (Diag::ACK) DIAG(F("WB cv=%d value=%d"),ackManagerCv,ackManagerByte);
|
||||||
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
||||||
DCCWaveform::progTrack.setAckPending();
|
DCCWaveform::progTrack.setAckPending();
|
||||||
callbackState=AFTER_WRITE;
|
callbackState=AFTER_WRITE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VB: // Issue validate Byte packet
|
case VB: // Issue validate Byte packet
|
||||||
{
|
{
|
||||||
if (checkResets( RESET_MIN)) return;
|
if (checkResets( RESET_MIN)) return;
|
||||||
if (Diag::ACK) DIAG(F("VB cv=%d value=%d"),ackManagerCv,ackManagerByte);
|
if (Diag::ACK) DIAG(F("VB cv=%d value=%d"),ackManagerCv,ackManagerByte);
|
||||||
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
||||||
DCCWaveform::progTrack.setAckPending();
|
DCCWaveform::progTrack.setAckPending();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case V0:
|
case V0:
|
||||||
case V1: // Issue validate bit=0 or bit=1 packet
|
case V1: // Issue validate bit=0 or bit=1 packet
|
||||||
{
|
{
|
||||||
if (checkResets(RESET_MIN)) return;
|
if (checkResets(RESET_MIN)) return;
|
||||||
if (Diag::ACK) DIAG(F("V%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum);
|
if (Diag::ACK) DIAG(F("V%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum);
|
||||||
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
|
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
|
||||||
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
|
||||||
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS);
|
||||||
DCCWaveform::progTrack.setAckPending();
|
DCCWaveform::progTrack.setAckPending();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WACK: // wait for ack (or absence of ack)
|
case WACK: // wait for ack (or absence of ack)
|
||||||
{
|
{
|
||||||
byte ackState=2; // keep polling
|
byte ackState=2; // keep polling
|
||||||
|
|
||||||
ackState=DCCWaveform::progTrack.getAck();
|
ackState=DCCWaveform::progTrack.getAck();
|
||||||
if (ackState==2) return; // keep polling
|
if (ackState==2) return; // keep polling
|
||||||
ackReceived=ackState==1;
|
ackReceived=ackState==1;
|
||||||
|
@ -828,14 +828,14 @@ void DCC::ackManagerLoop() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ITCB: // If True callback(byte)
|
case ITCB: // If True callback(byte)
|
||||||
if (ackReceived) {
|
if (ackReceived) {
|
||||||
callback(ackManagerByte);
|
callback(ackManagerByte);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ITCBV: // If True callback(byte) - Verify
|
case ITCBV: // If True callback(byte) - Verify
|
||||||
if (ackReceived) {
|
if (ackReceived) {
|
||||||
if (ackManagerByte == ackManagerByteVerify) {
|
if (ackManagerByte == ackManagerByteVerify) {
|
||||||
|
@ -846,21 +846,21 @@ void DCC::ackManagerLoop() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ITCB7: // If True callback(byte & 0x7F)
|
case ITCB7: // If True callback(byte & 0x7F)
|
||||||
if (ackReceived) {
|
if (ackReceived) {
|
||||||
callback(ackManagerByte & 0x7F);
|
callback(ackManagerByte & 0x7F);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NAKFAIL: // If nack callback(-1)
|
case NAKFAIL: // If nack callback(-1)
|
||||||
if (!ackReceived) {
|
if (!ackReceived) {
|
||||||
callback(-1);
|
callback(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FAIL: // callback(-1)
|
case FAIL: // callback(-1)
|
||||||
callback(-1);
|
callback(-1);
|
||||||
return;
|
return;
|
||||||
|
@ -871,63 +871,63 @@ void DCC::ackManagerLoop() {
|
||||||
|
|
||||||
case STARTMERGE:
|
case STARTMERGE:
|
||||||
ackManagerBitNum=7;
|
ackManagerBitNum=7;
|
||||||
ackManagerByte=0;
|
ackManagerByte=0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MERGE: // Merge previous Validate zero wack response with byte value and update bit number (use for reading CV bytes)
|
case MERGE: // Merge previous Validate zero wack response with byte value and update bit number (use for reading CV bytes)
|
||||||
ackManagerByte <<= 1;
|
ackManagerByte <<= 1;
|
||||||
// ackReceived means bit is zero.
|
// ackReceived means bit is zero.
|
||||||
if (!ackReceived) ackManagerByte |= 1;
|
if (!ackReceived) ackManagerByte |= 1;
|
||||||
ackManagerBitNum--;
|
ackManagerBitNum--;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETBIT:
|
case SETBIT:
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
ackManagerBitNum=GETFLASH(ackManagerProg);
|
ackManagerBitNum=GETFLASH(ackManagerProg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETCV:
|
case SETCV:
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
ackManagerCv=GETFLASH(ackManagerProg);
|
ackManagerCv=GETFLASH(ackManagerProg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETBYTE:
|
case SETBYTE:
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
ackManagerByte=GETFLASH(ackManagerProg);
|
ackManagerByte=GETFLASH(ackManagerProg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETBYTEH:
|
case SETBYTEH:
|
||||||
ackManagerByte=highByte(ackManagerWord);
|
ackManagerByte=highByte(ackManagerWord);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETBYTEL:
|
case SETBYTEL:
|
||||||
ackManagerByte=lowByte(ackManagerWord);
|
ackManagerByte=lowByte(ackManagerWord);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STASHLOCOID:
|
case STASHLOCOID:
|
||||||
ackManagerStash=ackManagerByte; // stash value from CV17
|
ackManagerStash=ackManagerByte; // stash value from CV17
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMBINELOCOID:
|
case COMBINELOCOID:
|
||||||
// ackManagerStash is cv17, ackManagerByte is CV 18
|
// ackManagerStash is cv17, ackManagerByte is CV 18
|
||||||
callback( LONG_ADDR_MARKER | ( ackManagerByte + ((ackManagerStash - 192) << 8)));
|
callback( LONG_ADDR_MARKER | ( ackManagerByte + ((ackManagerStash - 192) << 8)));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case ITSKIP:
|
case ITSKIP:
|
||||||
if (!ackReceived) break;
|
if (!ackReceived) break;
|
||||||
// SKIP opcodes until SKIPTARGET found
|
// SKIP opcodes until SKIPTARGET found
|
||||||
while (opcode!=SKIPTARGET) {
|
while (opcode!=SKIPTARGET) {
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
opcode=GETFLASH(ackManagerProg);
|
opcode=GETFLASH(ackManagerProg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SKIPTARGET:
|
case SKIPTARGET:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DIAG(F("!! ackOp %d FAULT!!"),opcode);
|
DIAG(F("!! ackOp %d FAULT!!"),opcode);
|
||||||
callback( -1);
|
callback( -1);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} // end of switch
|
} // end of switch
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
}
|
}
|
||||||
|
@ -948,7 +948,7 @@ void DCC::callback(int value) {
|
||||||
// Rule 1: If we have written to a decoder we must maintain power for 100mS
|
// Rule 1: If we have written to a decoder we must maintain power for 100mS
|
||||||
// Rule 2: If we are re-joining the main track we must power off for 30mS
|
// Rule 2: If we are re-joining the main track we must power off for 30mS
|
||||||
|
|
||||||
switch (callbackState) {
|
switch (callbackState) {
|
||||||
case AFTER_WRITE: // first attempt to callback after a write operation
|
case AFTER_WRITE: // first attempt to callback after a write operation
|
||||||
if (!ackManagerRejoin && !DCCWaveform::progTrack.autoPowerOff) {
|
if (!ackManagerRejoin && !DCCWaveform::progTrack.autoPowerOff) {
|
||||||
callbackState=READY;
|
callbackState=READY;
|
||||||
|
@ -958,7 +958,7 @@ void DCC::callback(int value) {
|
||||||
callbackState=WAITING_100;
|
callbackState=WAITING_100;
|
||||||
if (Diag::ACK) DIAG(F("Stable 100mS"));
|
if (Diag::ACK) DIAG(F("Stable 100mS"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WAITING_100: // waiting for 100mS
|
case WAITING_100: // waiting for 100mS
|
||||||
if (millis()-callbackStart < 100) break;
|
if (millis()-callbackStart < 100) break;
|
||||||
// stable after power maintained for 100mS
|
// stable after power maintained for 100mS
|
||||||
|
@ -967,20 +967,20 @@ void DCC::callback(int value) {
|
||||||
// but if we will keep the power on, we must off it for 30mS
|
// but if we will keep the power on, we must off it for 30mS
|
||||||
if (DCCWaveform::progTrack.autoPowerOff) callbackState=READY;
|
if (DCCWaveform::progTrack.autoPowerOff) callbackState=READY;
|
||||||
else { // Need to cycle power off and on
|
else { // Need to cycle power off and on
|
||||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
|
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
|
||||||
callbackStart=millis();
|
callbackStart=millis();
|
||||||
callbackState=WAITING_30;
|
callbackState=WAITING_30;
|
||||||
if (Diag::ACK) DIAG(F("OFF 30mS"));
|
if (Diag::ACK) DIAG(F("OFF 30mS"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WAITING_30: // waiting for 30mS with power off
|
case WAITING_30: // waiting for 30mS with power off
|
||||||
if (millis()-callbackStart < 30) break;
|
if (millis()-callbackStart < 30) break;
|
||||||
//power has been off for 30mS
|
//power has been off for 30mS
|
||||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||||
callbackState=READY;
|
callbackState=READY;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case READY: // ready after read, or write after power delay and off period.
|
case READY: // ready after read, or write after power delay and off period.
|
||||||
// power off if we powered it on
|
// power off if we powered it on
|
||||||
if (DCCWaveform::progTrack.autoPowerOff) {
|
if (DCCWaveform::progTrack.autoPowerOff) {
|
||||||
|
@ -991,8 +991,8 @@ void DCC::callback(int value) {
|
||||||
if (ackManagerRejoin) {
|
if (ackManagerRejoin) {
|
||||||
setProgTrackSyncMain(true);
|
setProgTrackSyncMain(true);
|
||||||
if (Diag::ACK) DIAG(F("Auto JOIN"));
|
if (Diag::ACK) DIAG(F("Auto JOIN"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ackManagerProg=NULL; // no more steps to execute
|
ackManagerProg=NULL; // no more steps to execute
|
||||||
if (Diag::ACK) DIAG(F("Callback(%d)"),value);
|
if (Diag::ACK) DIAG(F("Callback(%d)"),value);
|
||||||
(ackManagerCallback)( value);
|
(ackManagerCallback)( value);
|
||||||
|
@ -1005,10 +1005,10 @@ void DCC::displayCabList(Print * stream) {
|
||||||
for (int reg = 0; reg < MAX_LOCOS; reg++) {
|
for (int reg = 0; reg < MAX_LOCOS; reg++) {
|
||||||
if (speedTable[reg].loco>0) {
|
if (speedTable[reg].loco>0) {
|
||||||
used ++;
|
used ++;
|
||||||
StringFormatter::send(stream,F("cab=%d, speed=%d, dir=%c \n"),
|
StringFormatter::send(stream,F("cab=%d, speed=%d, dir=%c \n"),
|
||||||
speedTable[reg].loco, speedTable[reg].speedCode & 0x7f,(speedTable[reg].speedCode & 0x80) ? 'F':'R');
|
speedTable[reg].loco, speedTable[reg].speedCode & 0x7f,(speedTable[reg].speedCode & 0x80) ? 'F':'R');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream,F("Used=%d, max=%d\n"),used,MAX_LOCOS);
|
StringFormatter::send(stream,F("Used=%d, max=%d\n"),used,MAX_LOCOS);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,66 +372,66 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '1': // POWERON <1 [MAIN|PROG|JOIN]>
|
case '1': // POWERON <1 [MAIN|PROG|JOIN]>
|
||||||
{
|
{
|
||||||
bool main=false;
|
bool main=false;
|
||||||
bool prog=false;
|
bool prog=false;
|
||||||
bool join=false;
|
bool join=false;
|
||||||
if (params > 1) break;
|
if (params > 1) break;
|
||||||
if (params==0) { // <1>
|
if (params==0) { // <1>
|
||||||
main=true;
|
main=true;
|
||||||
prog=true;
|
prog=true;
|
||||||
}
|
}
|
||||||
else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN>
|
else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN>
|
||||||
main=true;
|
main=true;
|
||||||
prog=true;
|
prog=true;
|
||||||
join=!MotorDriver::commonFaultPin;
|
join=!MotorDriver::commonFaultPin;
|
||||||
}
|
}
|
||||||
else if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN>
|
else if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN>
|
||||||
main=true;
|
main=true;
|
||||||
}
|
}
|
||||||
else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG>
|
else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG>
|
||||||
prog=true;
|
prog=true;
|
||||||
}
|
}
|
||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
|
|
||||||
if (main) DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
if (main) DCCWaveform::mainTrack.setPowerMode(POWERMODE::ON);
|
||||||
if (prog) DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
if (prog) DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
|
||||||
DCC::setProgTrackSyncMain(join);
|
DCC::setProgTrackSyncMain(join);
|
||||||
|
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '0': // POWEROFF <0 [MAIN | PROG] >
|
case '0': // POWEROFF <0 [MAIN | PROG] >
|
||||||
{
|
{
|
||||||
bool main=false;
|
bool main=false;
|
||||||
bool prog=false;
|
bool prog=false;
|
||||||
if (params > 1) break;
|
if (params > 1) break;
|
||||||
if (params==0) { // <0>
|
if (params==0) { // <0>
|
||||||
main=true;
|
main=true;
|
||||||
prog=true;
|
prog=true;
|
||||||
}
|
}
|
||||||
else if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN>
|
else if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN>
|
||||||
main=true;
|
main=true;
|
||||||
}
|
}
|
||||||
else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG>
|
else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG>
|
||||||
prog=true;
|
prog=true;
|
||||||
}
|
}
|
||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
|
|
||||||
if (main) DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
|
if (main) DCCWaveform::mainTrack.setPowerMode(POWERMODE::OFF);
|
||||||
if (prog) {
|
if (prog) {
|
||||||
DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off
|
DCC::setProgTrackBoost(false); // Prog track boost mode will not outlive prog track off
|
||||||
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
|
DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF);
|
||||||
}
|
}
|
||||||
DCC::setProgTrackSyncMain(false);
|
DCC::setProgTrackSyncMain(false);
|
||||||
|
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '!': // ESTOP ALL <!>
|
case '!': // ESTOP ALL <!>
|
||||||
DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1.
|
DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'c': // SEND METER RESPONSES <c>
|
case 'c': // SEND METER RESPONSES <c>
|
||||||
|
|
599
WiThrottle.cpp
599
WiThrottle.cpp
|
@ -117,11 +117,11 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||||
for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){
|
for(Turnout *tt=Turnout::first();tt!=NULL;tt=tt->next()){
|
||||||
int id=tt->getId();
|
int id=tt->getId();
|
||||||
StringFormatter::send(stream,F("]\\[%d}|{"), id);
|
StringFormatter::send(stream,F("]\\[%d}|{"), id);
|
||||||
#ifdef RMFT_ACTIVE
|
#ifdef RMFT_ACTIVE
|
||||||
RMFT2::emitTurnoutDescription(stream,id);
|
RMFT2::emitTurnoutDescription(stream,id);
|
||||||
#else
|
#else
|
||||||
StringFormatter::send(stream,F("%d"), id);
|
StringFormatter::send(stream,F("%d"), id);
|
||||||
#endif
|
#endif
|
||||||
StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4');
|
StringFormatter::send(stream,F("}|{%c"), Turnout::isClosed(id)?'2':'4');
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream,F("\n"));
|
StringFormatter::send(stream,F("\n"));
|
||||||
|
@ -134,105 +134,104 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
||||||
#ifdef RMFT_ACTIVE
|
#ifdef RMFT_ACTIVE
|
||||||
RMFT2::emitWithrottleRouteList(stream);
|
RMFT2::emitWithrottleRouteList(stream);
|
||||||
#endif
|
#endif
|
||||||
// allow heartbeat to slow down once all metadata sent
|
// allow heartbeat to slow down once all metadata sent
|
||||||
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS);
|
StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cmd[0]) {
|
while (cmd[0]) {
|
||||||
switch (cmd[0]) {
|
switch (cmd[0]) {
|
||||||
case '*': // heartbeat control
|
case '*': // heartbeat control
|
||||||
if (cmd[1]=='+') heartBeatEnable=true;
|
if (cmd[1]=='+') heartBeatEnable=true;
|
||||||
else if (cmd[1]=='-') heartBeatEnable=false;
|
else if (cmd[1]=='-') heartBeatEnable=false;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
if (cmd[1]=='P' && cmd[2]=='A' ) { //PPA power mode
|
if (cmd[1]=='P' && cmd[2]=='A' ) { //PPA power mode
|
||||||
DCCWaveform::mainTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
DCCWaveform::mainTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
||||||
if (MotorDriver::commonFaultPin) // commonFaultPin prevents individual track handling
|
if (MotorDriver::commonFaultPin) // commonFaultPin prevents individual track handling
|
||||||
DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
}
|
}
|
||||||
#if defined(RMFT_ACTIVE)
|
#if defined(RMFT_ACTIVE)
|
||||||
else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate
|
else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate
|
||||||
// exrail routes are RA2Rn , Animations are RA2An
|
// exrail routes are RA2Rn , Animations are RA2An
|
||||||
int route=getInt(cmd+5);
|
int route=getInt(cmd+5);
|
||||||
uint16_t cab=cmd[4]=='A' ? mostRecentCab : 0;
|
uint16_t cab=cmd[4]=='A' ? mostRecentCab : 0;
|
||||||
RMFT2::createNewTask(route, cab);
|
RMFT2::createNewTask(route, cab);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (cmd[1]=='T' && cmd[2]=='A') { // PTA accessory toggle
|
else if (cmd[1]=='T' && cmd[2]=='A') { // PTA accessory toggle
|
||||||
int id=getInt(cmd+4);
|
int id=getInt(cmd+4);
|
||||||
if (!Turnout::exists(id)) {
|
if (!Turnout::exists(id)) {
|
||||||
// If turnout does not exist, create it
|
// If turnout does not exist, create it
|
||||||
int addr = ((id - 1) / 4) + 1;
|
int addr = ((id - 1) / 4) + 1;
|
||||||
int subaddr = (id - 1) % 4;
|
int subaddr = (id - 1) % 4;
|
||||||
DCCTurnout::create(id,addr,subaddr);
|
DCCTurnout::create(id,addr,subaddr);
|
||||||
StringFormatter::send(stream, F("HmTurnout %d created\n"),id);
|
StringFormatter::send(stream, F("HmTurnout %d created\n"),id);
|
||||||
}
|
}
|
||||||
switch (cmd[3]) {
|
switch (cmd[3]) {
|
||||||
// T and C according to RCN-213 where 0 is Stop, Red, Thrown, Diverging.
|
// T and C according to RCN-213 where 0 is Stop, Red, Thrown, Diverging.
|
||||||
case 'T':
|
case 'T':
|
||||||
Turnout::setClosed(id,false);
|
Turnout::setClosed(id,false);
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
Turnout::setClosed(id,true);
|
Turnout::setClosed(id,true);
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
Turnout::setClosed(id,!Turnout::isClosed(id));
|
Turnout::setClosed(id,!Turnout::isClosed(id));
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
Turnout::setClosed(id,true);
|
Turnout::setClosed(id,true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream, F("PTA%c%d\n"),Turnout::isClosed(id)?'2':'4',id );
|
StringFormatter::send(stream, F("PTA%c%d\n"),Turnout::isClosed(id)?'2':'4',id );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'N': // Heartbeat (2), only send if connection completed by 'HU' message
|
case 'N': // Heartbeat (2), only send if connection completed by 'HU' message
|
||||||
if (initSent) {
|
if (initSent) {
|
||||||
StringFormatter::send(stream, F("*%d\n"),HEARTBEAT_SECONDS); // return timeout value
|
StringFormatter::send(stream, F("*%d\n"),HEARTBEAT_SECONDS); // return timeout value
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'M': // multithrottle
|
case 'M': // multithrottle
|
||||||
multithrottle(stream, cmd);
|
multithrottle(stream, cmd);
|
||||||
break;
|
break;
|
||||||
case 'H': // send initial connection info after receiving "HU" message
|
case 'H': // send initial connection info after receiving "HU" message
|
||||||
if (cmd[1] == 'U') {
|
if (cmd[1] == 'U') {
|
||||||
StringFormatter::send(stream,F("VN2.0\nHTDCC-EX\nRL0\n"));
|
StringFormatter::send(stream,F("VN2.0\nHTDCC-EX\nRL0\n"));
|
||||||
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("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("PTT]\\[Turnouts}|{Turnout]\\[THROW}|{2]\\[CLOSE}|{4\n"));
|
||||||
StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON);
|
StringFormatter::send(stream,F("PPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON);
|
||||||
#ifdef RMFT_ACTIVE
|
#ifdef RMFT_ACTIVE
|
||||||
RMFT2::emitWithrottleRoster(stream);
|
RMFT2::emitWithrottleRoster(stream);
|
||||||
#endif
|
#endif
|
||||||
// set heartbeat to 1 second because we need to sync the metadata
|
// set heartbeat to 1 second because we need to sync the metadata
|
||||||
StringFormatter::send(stream,F("*1\n"));
|
StringFormatter::send(stream,F("*1\n"));
|
||||||
initSent = true;
|
initSent = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'Q': //
|
case 'Q': //
|
||||||
LOOPLOCOS('*', -1) { // tell client to drop any locos still assigned to this WiThrottle
|
LOOPLOCOS('*', -1) { // tell client to drop any locos still assigned to this WiThrottle
|
||||||
if (myLocos[loco].throttle!='\0') {
|
if (myLocos[loco].throttle!='\0') {
|
||||||
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), myLocos[loco].throttle, LorS(myLocos[loco].cab), myLocos[loco].cab);
|
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), myLocos[loco].throttle, LorS(myLocos[loco].cab), myLocos[loco].cab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) Quit"),millis(),clientid);
|
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) Quit"),millis(),clientid);
|
||||||
delete this;
|
delete this;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
// skip over cmd until 0 or past \r or \n
|
|
||||||
while(*cmd !='\0' && *cmd != '\r' && *cmd !='\n') cmd++;
|
|
||||||
if (*cmd!='\0') cmd++; // skip \r or \n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int WiThrottle::getInt(byte * cmd) {
|
|
||||||
int i=0;
|
|
||||||
while (cmd[0]>='0' && cmd[0]<='9') {
|
|
||||||
i=i*10 + (cmd[0]-'0');
|
|
||||||
cmd++;
|
|
||||||
}
|
}
|
||||||
return i;
|
// skip over cmd until 0 or past \r or \n
|
||||||
|
while(*cmd !='\0' && *cmd != '\r' && *cmd !='\n') cmd++;
|
||||||
|
if (*cmd!='\0') cmd++; // skip \r or \n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WiThrottle::getInt(byte * cmd) {
|
||||||
|
int i=0;
|
||||||
|
while (cmd[0]>='0' && cmd[0]<='9') {
|
||||||
|
i=i*10 + (cmd[0]-'0');
|
||||||
|
cmd++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WiThrottle::getLocoId(byte * cmd) {
|
int WiThrottle::getLocoId(byte * cmd) {
|
||||||
|
@ -242,183 +241,183 @@ int WiThrottle::getLocoId(byte * cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiThrottle::multithrottle(RingStream * stream, byte * cmd){
|
void WiThrottle::multithrottle(RingStream * stream, byte * cmd){
|
||||||
char throttleChar=cmd[1];
|
char throttleChar=cmd[1];
|
||||||
int locoid=getLocoId(cmd+3); // -1 for *
|
int locoid=getLocoId(cmd+3); // -1 for *
|
||||||
byte * aval=cmd;
|
byte * aval=cmd;
|
||||||
while(*aval !=';' && *aval !='\0') aval++;
|
while(*aval !=';' && *aval !='\0') aval++;
|
||||||
if (*aval) aval+=2; // skip ;>
|
if (*aval) aval+=2; // skip ;>
|
||||||
|
|
||||||
// DIAG(F("Multithrottle aval=%c cab=%d"), aval[0],locoid);
|
// DIAG(F("Multithrottle aval=%c cab=%d"), aval[0],locoid);
|
||||||
switch(cmd[2]) {
|
switch(cmd[2]) {
|
||||||
case '+': // add loco request
|
case '+': // add loco request
|
||||||
if (cmd[3]=='*') {
|
if (cmd[3]=='*') {
|
||||||
// M+* means get loco from prog track, then join tracks ready to drive away
|
// M+* means get loco from prog track, then join tracks ready to drive away
|
||||||
// Stash the things the callback will need later
|
// Stash the things the callback will need later
|
||||||
stashStream= stream;
|
stashStream= stream;
|
||||||
stashClient=stream->peekTargetMark();
|
stashClient=stream->peekTargetMark();
|
||||||
stashThrottleChar=throttleChar;
|
stashThrottleChar=throttleChar;
|
||||||
stashInstance=this;
|
stashInstance=this;
|
||||||
// ask DCC to call us back when the loco id has been read
|
// ask DCC to call us back when the loco id has been read
|
||||||
DCC::getLocoId(getLocoCallback); // will remove any previous join
|
DCC::getLocoId(getLocoCallback); // will remove any previous join
|
||||||
return; // return nothing in stream as response is sent later in the callback
|
return; // return nothing in stream as response is sent later in the callback
|
||||||
}
|
}
|
||||||
//return error if address zero requested
|
//return error if address zero requested
|
||||||
if (locoid==0) {
|
if (locoid==0) {
|
||||||
StringFormatter::send(stream, F("HMAddress '0' not supported!\n"), cmd[3] ,locoid);
|
StringFormatter::send(stream, F("HMAddress '0' not supported!\n"), cmd[3] ,locoid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//return error if L or S from request doesn't match DCC++ assumptions
|
//return error if L or S from request doesn't match DCC++ assumptions
|
||||||
if (cmd[3] != LorS(locoid)) {
|
if (cmd[3] != LorS(locoid)) {
|
||||||
StringFormatter::send(stream, F("HMLength '%c' not valid for %d!\n"), cmd[3] ,locoid);
|
StringFormatter::send(stream, F("HMLength '%c' not valid for %d!\n"), cmd[3] ,locoid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//use first empty "slot" on this client's list, will be added to DCC registration list
|
//use first empty "slot" on this client's list, will be added to DCC registration list
|
||||||
for (int loco=0;loco<MAX_MY_LOCO;loco++) {
|
for (int loco=0;loco<MAX_MY_LOCO;loco++) {
|
||||||
if (myLocos[loco].throttle=='\0') {
|
if (myLocos[loco].throttle=='\0') {
|
||||||
myLocos[loco].throttle=throttleChar;
|
myLocos[loco].throttle=throttleChar;
|
||||||
myLocos[loco].cab=locoid;
|
myLocos[loco].cab=locoid;
|
||||||
myLocos[loco].functionMap=DCC::getFunctionMap(locoid);
|
myLocos[loco].functionMap=DCC::getFunctionMap(locoid);
|
||||||
myLocos[loco].broadcastPending=true; // means speed/dir will be sent later
|
myLocos[loco].broadcastPending=true; // means speed/dir will be sent later
|
||||||
mostRecentCab=locoid;
|
mostRecentCab=locoid;
|
||||||
StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid); //tell client to add loco
|
StringFormatter::send(stream, F("M%c+%c%d<;>\n"), throttleChar, cmd[3] ,locoid); //tell client to add loco
|
||||||
int fkeys=29;
|
int fkeys=29;
|
||||||
myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle
|
myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle
|
||||||
|
|
||||||
#ifdef RMFT_ACTIVE
|
#ifdef RMFT_ACTIVE
|
||||||
const char * functionNames=(char *) RMFT2::getRosterFunctions(locoid);
|
const char * functionNames=(char *) RMFT2::getRosterFunctions(locoid);
|
||||||
if (!functionNames) {
|
if (!functionNames) {
|
||||||
// no roster, use presets as above
|
// no roster, use presets as above
|
||||||
}
|
}
|
||||||
else if (GETFLASH(functionNames)=='\0') {
|
else if (GETFLASH(functionNames)=='\0') {
|
||||||
// "" = Roster but no functions given
|
// "" = Roster but no functions given
|
||||||
fkeys=0;
|
fkeys=0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// we have function names...
|
// we have function names...
|
||||||
// scan names list emitting names, counting functions and
|
// scan names list emitting names, counting functions and
|
||||||
// flagging non-toggling things like horn.
|
// flagging non-toggling things like horn.
|
||||||
myLocos[loco].functionToggles =0;
|
myLocos[loco].functionToggles =0;
|
||||||
StringFormatter::send(stream, F("M%cL%c%d<;>]\\["), throttleChar,cmd[3],locoid);
|
StringFormatter::send(stream, F("M%cL%c%d<;>]\\["), throttleChar,cmd[3],locoid);
|
||||||
fkeys=0;
|
fkeys=0;
|
||||||
bool firstchar=true;
|
bool firstchar=true;
|
||||||
for (int fx=0;;fx++) {
|
for (int fx=0;;fx++) {
|
||||||
char c=GETFLASH(functionNames+fx);
|
char c=GETFLASH(functionNames+fx);
|
||||||
if (c=='\0') {
|
if (c=='\0') {
|
||||||
fkeys++;
|
fkeys++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (c=='/') {
|
if (c=='/') {
|
||||||
fkeys++;
|
fkeys++;
|
||||||
StringFormatter::send(stream,F("]\\["));
|
StringFormatter::send(stream,F("]\\["));
|
||||||
firstchar=true;
|
firstchar=true;
|
||||||
}
|
}
|
||||||
else if (firstchar && c=='*') {
|
else if (firstchar && c=='*') {
|
||||||
myLocos[loco].functionToggles |= 1UL<<fkeys;
|
myLocos[loco].functionToggles |= 1UL<<fkeys;
|
||||||
firstchar=false;
|
firstchar=false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
firstchar=false;
|
firstchar=false;
|
||||||
stream->write(c);
|
stream->write(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream,F("\n"));
|
StringFormatter::send(stream,F("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for(int fKey=0; fKey<fkeys; fKey++) {
|
for(int fKey=0; fKey<fkeys; fKey++) {
|
||||||
int fstate=DCC::getFn(locoid,fKey);
|
int fstate=DCC::getFn(locoid,fKey);
|
||||||
if (fstate>=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),throttleChar,cmd[3],locoid,fstate,fKey);
|
if (fstate>=0) StringFormatter::send(stream,F("M%cA%c%d<;>F%d%d\n"),throttleChar,cmd[3],locoid,fstate,fKey);
|
||||||
}
|
}
|
||||||
//speed and direction will be published at next broadcast cycle
|
//speed and direction will be published at next broadcast cycle
|
||||||
StringFormatter::send(stream, F("M%cA%c%d<;>s1\n"), throttleChar, cmd[3], locoid); //default speed step 128
|
StringFormatter::send(stream, F("M%cA%c%d<;>s1\n"), throttleChar, cmd[3], locoid); //default speed step 128
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream, F("HMMax locos (%d) exceeded, %d not added!\n"), MAX_MY_LOCO ,locoid);
|
StringFormatter::send(stream, F("HMMax locos (%d) exceeded, %d not added!\n"), MAX_MY_LOCO ,locoid);
|
||||||
break;
|
break;
|
||||||
case '-': // remove loco(s) from this client (leave in DCC registration)
|
case '-': // remove loco(s) from this client (leave in DCC registration)
|
||||||
LOOPLOCOS(throttleChar, locoid) {
|
LOOPLOCOS(throttleChar, locoid) {
|
||||||
myLocos[loco].throttle='\0';
|
myLocos[loco].throttle='\0';
|
||||||
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), throttleChar, LorS(myLocos[loco].cab), myLocos[loco].cab);
|
StringFormatter::send(stream, F("M%c-%c%d<;>\n"), throttleChar, LorS(myLocos[loco].cab), myLocos[loco].cab);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'A':
|
case 'A':
|
||||||
locoAction(stream,aval, throttleChar, locoid);
|
locoAction(stream,aval, throttleChar, locoid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiThrottle::locoAction(RingStream * stream, byte* aval, char throttleChar, int cab){
|
void WiThrottle::locoAction(RingStream * stream, byte* aval, char throttleChar, int cab){
|
||||||
// Note cab=-1 for all cabs in the consist called throttleChar.
|
// Note cab=-1 for all cabs in the consist called throttleChar.
|
||||||
// DIAG(F("Loco Action aval=%c%c throttleChar=%c, cab=%d"), aval[0],aval[1],throttleChar, cab);
|
// DIAG(F("Loco Action aval=%c%c throttleChar=%c, cab=%d"), aval[0],aval[1],throttleChar, cab);
|
||||||
(void) stream;
|
(void) stream;
|
||||||
switch (aval[0]) {
|
switch (aval[0]) {
|
||||||
case 'V': // Vspeed
|
case 'V': // Vspeed
|
||||||
{
|
{
|
||||||
int witSpeed=getInt(aval+1);
|
int witSpeed=getInt(aval+1);
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
mostRecentCab=myLocos[loco].cab;
|
mostRecentCab=myLocos[loco].cab;
|
||||||
DCC::setThrottle(myLocos[loco].cab, WiTToDCCSpeed(witSpeed), DCC::getThrottleDirection(myLocos[loco].cab));
|
DCC::setThrottle(myLocos[loco].cab, WiTToDCCSpeed(witSpeed), DCC::getThrottleDirection(myLocos[loco].cab));
|
||||||
// SetThrottle will cause speed change broadcast
|
// SetThrottle will cause speed change broadcast
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'F': // Function key pressed/released
|
case 'F': // Function key pressed/released
|
||||||
{
|
{
|
||||||
bool pressed=aval[1]=='1';
|
bool pressed=aval[1]=='1';
|
||||||
int fKey = getInt(aval+2);
|
int fKey = getInt(aval+2);
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
bool unsetOnRelease = myLocos[loco].functionToggles & (1L<<fKey);
|
bool unsetOnRelease = myLocos[loco].functionToggles & (1L<<fKey);
|
||||||
if (unsetOnRelease) DCC::setFn(myLocos[loco].cab,fKey, pressed);
|
if (unsetOnRelease) DCC::setFn(myLocos[loco].cab,fKey, pressed);
|
||||||
else if (pressed) DCC::changeFn(myLocos[loco].cab, fKey);
|
else if (pressed) DCC::changeFn(myLocos[loco].cab, fKey);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'q':
|
case 'q':
|
||||||
if (aval[1]=='V' || aval[1]=='R' ) { //qV or qR
|
if (aval[1]=='V' || aval[1]=='R' ) { //qV or qR
|
||||||
// just flag the loco for broadcast and it will happen.
|
// just flag the loco for broadcast and it will happen.
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
myLocos[loco].broadcastPending=true;
|
myLocos[loco].broadcastPending=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
{
|
{
|
||||||
bool forward=aval[1]!='0';
|
bool forward=aval[1]!='0';
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
mostRecentCab=myLocos[loco].cab;
|
mostRecentCab=myLocos[loco].cab;
|
||||||
DCC::setThrottle(myLocos[loco].cab, DCC::getThrottleSpeed(myLocos[loco].cab), forward);
|
DCC::setThrottle(myLocos[loco].cab, DCC::getThrottleSpeed(myLocos[loco].cab), forward);
|
||||||
// setThrottle will cause a broadcast so notification will be sent
|
// setThrottle will cause a broadcast so notification will be sent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'X':
|
case 'X':
|
||||||
//Emergency Stop (speed code 1)
|
//Emergency Stop (speed code 1)
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
DCC::setThrottle(myLocos[loco].cab, 1, DCC::getThrottleDirection(myLocos[loco].cab));
|
DCC::setThrottle(myLocos[loco].cab, 1, DCC::getThrottleDirection(myLocos[loco].cab));
|
||||||
// setThrottle will cause a broadcast so notification will be sent
|
// setThrottle will cause a broadcast so notification will be sent
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'I': // Idle, set speed to 0
|
case 'I': // Idle, set speed to 0
|
||||||
case 'Q': // Quit, set speed to 0
|
case 'Q': // Quit, set speed to 0
|
||||||
LOOPLOCOS(throttleChar, cab) {
|
LOOPLOCOS(throttleChar, cab) {
|
||||||
mostRecentCab=myLocos[loco].cab;
|
mostRecentCab=myLocos[loco].cab;
|
||||||
DCC::setThrottle(myLocos[loco].cab, 0, DCC::getThrottleDirection(myLocos[loco].cab));
|
DCC::setThrottle(myLocos[loco].cab, 0, DCC::getThrottleDirection(myLocos[loco].cab));
|
||||||
// setThrottle will cause a broadcast so notification will be sent
|
// setThrottle will cause a broadcast so notification will be sent
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert between DCC++ speed values and WiThrottle speed values
|
// convert between DCC++ speed values and WiThrottle speed values
|
||||||
int WiThrottle::DCCToWiTSpeed(int DCCSpeed) {
|
int WiThrottle::DCCToWiTSpeed(int DCCSpeed) {
|
||||||
if (DCCSpeed == 0) return 0; //stop is stop
|
if (DCCSpeed == 0) return 0; //stop is stop
|
||||||
if (DCCSpeed == 1) return -1; //eStop value
|
if (DCCSpeed == 1) return -1; //eStop value
|
||||||
return DCCSpeed - 1; //offset others by 1
|
return DCCSpeed - 1; //offset others by 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert between WiThrottle speed values and DCC++ speed values
|
// convert between WiThrottle speed values and DCC++ speed values
|
||||||
int WiThrottle::WiTToDCCSpeed(int WiTSpeed) {
|
int WiThrottle::WiTToDCCSpeed(int WiTSpeed) {
|
||||||
if (WiTSpeed == 0) return 0; //stop is stop
|
if (WiTSpeed == 0) return 0; //stop is stop
|
||||||
if (WiTSpeed == -1) return 1; //eStop value
|
if (WiTSpeed == -1) return 1; //eStop value
|
||||||
|
@ -428,15 +427,15 @@ int WiThrottle::WiTToDCCSpeed(int WiTSpeed) {
|
||||||
void WiThrottle::loop(RingStream * stream) {
|
void WiThrottle::loop(RingStream * stream) {
|
||||||
// for each WiThrottle, check the heartbeat and broadcast needed
|
// for each WiThrottle, check the heartbeat and broadcast needed
|
||||||
for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle)
|
for (WiThrottle* wt=firstThrottle; wt!=NULL ; wt=wt->nextThrottle)
|
||||||
wt->checkHeartbeat(stream);
|
wt->checkHeartbeat(stream);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiThrottle::checkHeartbeat(RingStream * stream) {
|
void WiThrottle::checkHeartbeat(RingStream * stream) {
|
||||||
// if eStop time passed... eStop any locos still assigned to this client and then drop the connection
|
// if eStop time passed... eStop any locos still assigned to this client and then drop the connection
|
||||||
if(heartBeatEnable && (millis()-heartBeat > ESTOP_SECONDS*1000)) {
|
if(heartBeatEnable && (millis()-heartBeat > ESTOP_SECONDS*1000)) {
|
||||||
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) eStop(%ds) timeout, drop connection"), millis(), clientid, ESTOP_SECONDS);
|
if (Diag::WITHROTTLE) DIAG(F("%l WiThrottle(%d) eStop(%ds) timeout, drop connection"), millis(), clientid, ESTOP_SECONDS);
|
||||||
LOOPLOCOS('*', -1) {
|
LOOPLOCOS('*', -1) {
|
||||||
if (myLocos[loco].throttle!='\0') {
|
if (myLocos[loco].throttle!='\0') {
|
||||||
if (Diag::WITHROTTLE) DIAG(F("%l eStopping cab %d"),millis(),myLocos[loco].cab);
|
if (Diag::WITHROTTLE) DIAG(F("%l eStopping cab %d"),millis(),myLocos[loco].cab);
|
||||||
|
@ -445,45 +444,45 @@ void WiThrottle::checkHeartbeat(RingStream * stream) {
|
||||||
}
|
}
|
||||||
delete this;
|
delete this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send any outstanding speed/direction/function changes for this clients locos
|
// send any outstanding speed/direction/function changes for this clients locos
|
||||||
// Changes may have been caused by this client, or another non-Withrottle or Exrail
|
// Changes may have been caused by this client, or another non-Withrottle or Exrail
|
||||||
bool streamHasBeenMarked=false;
|
bool streamHasBeenMarked=false;
|
||||||
LOOPLOCOS('*', -1) {
|
LOOPLOCOS('*', -1) {
|
||||||
if (myLocos[loco].throttle!='\0' && myLocos[loco].broadcastPending) {
|
if (myLocos[loco].throttle!='\0' && myLocos[loco].broadcastPending) {
|
||||||
if (!streamHasBeenMarked) {
|
if (!streamHasBeenMarked) {
|
||||||
stream->mark(clientid);
|
stream->mark(clientid);
|
||||||
streamHasBeenMarked=true;
|
streamHasBeenMarked=true;
|
||||||
}
|
|
||||||
myLocos[loco].broadcastPending=false;
|
|
||||||
int cab=myLocos[loco].cab;
|
|
||||||
char lors=LorS(cab);
|
|
||||||
char throttle=myLocos[loco].throttle;
|
|
||||||
StringFormatter::send(stream,F("M%cA%c%d<;>V%d\n"),
|
|
||||||
throttle, lors , cab, DCCToWiTSpeed(DCC::getThrottleSpeed(cab)));
|
|
||||||
StringFormatter::send(stream,F("M%cA%c%d<;>R%d\n"),
|
|
||||||
throttle, lors , cab, DCC::getThrottleDirection(cab));
|
|
||||||
|
|
||||||
// compare the DCC functionmap with the local copy and send changes
|
|
||||||
uint32_t dccFunctionMap=DCC::getFunctionMap(cab);
|
|
||||||
uint32_t myFunctionMap=myLocos[loco].functionMap;
|
|
||||||
myLocos[loco].functionMap=dccFunctionMap;
|
|
||||||
|
|
||||||
// loop the maps sending any bit changed
|
|
||||||
// Loop is terminated as soon as no changes are left
|
|
||||||
for (byte fn=0;dccFunctionMap!=myFunctionMap;fn++) {
|
|
||||||
if ((dccFunctionMap&1) != (myFunctionMap&1)) {
|
|
||||||
StringFormatter::send(stream,F("M%cA%c%d<;>F%c%d\n"),
|
|
||||||
throttle, lors , cab, (dccFunctionMap&1)?'1':'0',fn);
|
|
||||||
}
|
|
||||||
// shift just checked bit off end of both maps
|
|
||||||
dccFunctionMap>>=1;
|
|
||||||
myFunctionMap>>=1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
myLocos[loco].broadcastPending=false;
|
||||||
|
int cab=myLocos[loco].cab;
|
||||||
|
char lors=LorS(cab);
|
||||||
|
char throttle=myLocos[loco].throttle;
|
||||||
|
StringFormatter::send(stream,F("M%cA%c%d<;>V%d\n"),
|
||||||
|
throttle, lors , cab, DCCToWiTSpeed(DCC::getThrottleSpeed(cab)));
|
||||||
|
StringFormatter::send(stream,F("M%cA%c%d<;>R%d\n"),
|
||||||
|
throttle, lors , cab, DCC::getThrottleDirection(cab));
|
||||||
|
|
||||||
|
// compare the DCC functionmap with the local copy and send changes
|
||||||
|
uint32_t dccFunctionMap=DCC::getFunctionMap(cab);
|
||||||
|
uint32_t myFunctionMap=myLocos[loco].functionMap;
|
||||||
|
myLocos[loco].functionMap=dccFunctionMap;
|
||||||
|
|
||||||
|
// loop the maps sending any bit changed
|
||||||
|
// Loop is terminated as soon as no changes are left
|
||||||
|
for (byte fn=0;dccFunctionMap!=myFunctionMap;fn++) {
|
||||||
|
if ((dccFunctionMap&1) != (myFunctionMap&1)) {
|
||||||
|
StringFormatter::send(stream,F("M%cA%c%d<;>F%c%d\n"),
|
||||||
|
throttle, lors , cab, (dccFunctionMap&1)?'1':'0',fn);
|
||||||
|
}
|
||||||
|
// shift just checked bit off end of both maps
|
||||||
|
dccFunctionMap>>=1;
|
||||||
|
myFunctionMap>>=1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (streamHasBeenMarked) stream->commit();
|
}
|
||||||
|
if (streamHasBeenMarked) stream->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiThrottle::markForBroadcast(int cab) {
|
void WiThrottle::markForBroadcast(int cab) {
|
||||||
|
@ -492,17 +491,17 @@ void WiThrottle::markForBroadcast(int cab) {
|
||||||
}
|
}
|
||||||
void WiThrottle::markForBroadcast2(int cab) {
|
void WiThrottle::markForBroadcast2(int cab) {
|
||||||
LOOPLOCOS('*', cab) {
|
LOOPLOCOS('*', cab) {
|
||||||
myLocos[loco].broadcastPending=true;
|
myLocos[loco].broadcastPending=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char WiThrottle::LorS(int cab) {
|
char WiThrottle::LorS(int cab) {
|
||||||
return (cab<=HIGHEST_SHORT_ADDR)?'S':'L';
|
return (cab<=HIGHEST_SHORT_ADDR)?'S':'L';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drive Away feature. Callback handling
|
// Drive Away feature. Callback handling
|
||||||
|
|
||||||
RingStream * WiThrottle::stashStream;
|
RingStream * WiThrottle::stashStream;
|
||||||
WiThrottle * WiThrottle::stashInstance;
|
WiThrottle * WiThrottle::stashInstance;
|
||||||
byte WiThrottle::stashClient;
|
byte WiThrottle::stashClient;
|
||||||
|
@ -510,7 +509,7 @@ char WiThrottle::stashThrottleChar;
|
||||||
|
|
||||||
void WiThrottle::getLocoCallback(int16_t locoid) {
|
void WiThrottle::getLocoCallback(int16_t locoid) {
|
||||||
stashStream->mark(stashClient);
|
stashStream->mark(stashClient);
|
||||||
|
|
||||||
if (locoid<=0)
|
if (locoid<=0)
|
||||||
StringFormatter::send(stashStream,F("HMNo loco found on prog track\n"));
|
StringFormatter::send(stashStream,F("HMNo loco found on prog track\n"));
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -335,10 +335,10 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
||||||
void WifiInterface::ATCommand(HardwareSerial * stream,const byte * command) {
|
void WifiInterface::ATCommand(HardwareSerial * stream,const byte * command) {
|
||||||
command++;
|
command++;
|
||||||
if (*command=='\0') { // User gave <+> command
|
if (*command=='\0') { // User gave <+> command
|
||||||
stream->print(F("\nES AT command passthrough mode, use ! to exit\n"));
|
stream->print(F("\nES AT command passthrough mode, use ! to exit\n"));
|
||||||
while(stream->available()) stream->read(); // Drain serial input first
|
while(stream->available()) stream->read(); // Drain serial input first
|
||||||
bool startOfLine=true;
|
bool startOfLine=true;
|
||||||
while(true) {
|
while(true) {
|
||||||
while (wifiStream->available()) stream->write(wifiStream->read());
|
while (wifiStream->available()) stream->write(wifiStream->read());
|
||||||
if (stream->available()) {
|
if (stream->available()) {
|
||||||
int cx=stream->read();
|
int cx=stream->read();
|
||||||
|
@ -348,19 +348,19 @@ void WifiInterface::ATCommand(HardwareSerial * stream,const byte * command) {
|
||||||
else startOfLine=false;
|
else startOfLine=false;
|
||||||
stream->write(cx);
|
stream->write(cx);
|
||||||
wifiStream->write(cx);
|
wifiStream->write(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream->print(F("Passthrough Ended"));
|
stream->print(F("Passthrough Ended"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*command=='X') {
|
if (*command=='X') {
|
||||||
connected = true;
|
connected = true;
|
||||||
DIAG(F("++++++ Wifi Connction forced on ++++++++"));
|
DIAG(F("++++++ Wifi Connction forced on ++++++++"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
StringFormatter:: send(wifiStream, F("AT+%s\r\n"), command);
|
StringFormatter:: send(wifiStream, F("AT+%s\r\n"), command);
|
||||||
checkForOK(10000, true);
|
checkForOK(10000, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user