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

Compare commits

..

16 Commits

Author SHA1 Message Date
Harald Barth
4a3d8729c6 Merge branch 'devel' of https://github.com/DCC-EX/CommandStation-EX into devel 2022-11-09 00:52:31 +01:00
Harald Barth
d874ad8cc3 Make GETFLASHW code more universal 2022-11-09 00:52:07 +01:00
Harald Barth
bd8439c2f9 Fix: Roster indexing when sending to withrottle 2022-11-08 23:35:07 +01:00
pmantoine
65714ed1f2 Rename Nucleo build target 2022-11-08 07:48:35 +11:00
Harald Barth
280e61e1fc Make EthernetInterface code more robust 2022-11-07 11:53:56 +01:00
Harald Barth
b061c0b347 version 2022-11-07 11:22:15 +01:00
Harald Barth
7f3d547541 Initialize outboundRing properly to NULL 2022-11-07 11:20:00 +01:00
Harald Barth
eb0861959c version 2022-11-06 21:33:40 +01:00
Harald Barth
f1d445e056 Do not abort ethernet startup on W5100 2022-11-06 21:32:54 +01:00
Harald Barth
4f19a60621 number of ADC inputs was reversed 2022-11-06 21:30:32 +01:00
Harald Barth
2b3ba514b0 Use X as the questionmark sign in <T 17 X> 2022-11-05 23:11:54 +01:00
Harald Barth
a199de6d3e Make <T nn ?> return long config print 2022-11-04 23:43:26 +01:00
Harald Barth
c0cb643cb5 When sending all turnouts, keep it short 2022-11-04 23:15:29 +01:00
Harald Barth
be2f3b0db7 Ethernet restructure 2022-11-04 16:08:43 +01:00
pmantoine
f939ea0768 Add MEGAAVR ADCeee skeleton. 2022-11-02 13:55:10 +08:00
pmantoine
863c839563 Add Teensy ADCee class skeleton. 2022-11-02 13:46:16 +08:00
16 changed files with 192 additions and 95 deletions

View File

@@ -73,6 +73,7 @@ const int16_t HASH_KEYWORD_A='A';
const int16_t HASH_KEYWORD_C='C';
const int16_t HASH_KEYWORD_R='R';
const int16_t HASH_KEYWORD_T='T';
const int16_t HASH_KEYWORD_X='X';
const int16_t HASH_KEYWORD_LCN = 15137;
const int16_t HASH_KEYWORD_HAL = 10853;
const int16_t HASH_KEYWORD_SHOW = -21309;
@@ -730,15 +731,7 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[])
switch (params)
{
case 0: // <T> list turnout definitions
{
bool gotOne = false;
for (Turnout *tt = Turnout::first(); tt != NULL; tt = tt->next())
{
gotOne = true;
tt->print(stream);
}
return gotOne; // will <X> if none found
}
return Turnout::printAll(stream); // will <X> if none found
case 1: // <T id> delete turnout
if (!Turnout::remove(p[0]))
@@ -759,12 +752,19 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[])
case HASH_KEYWORD_T:
state= false;
break;
default:
return false; // Invalid parameter
case HASH_KEYWORD_X:
{
Turnout *tt = Turnout::get(p[0]);
if (tt) {
tt->print(stream);
return true;
}
return false;
}
default: // Invalid parameter
return false;
}
if (!Turnout::setClosed(p[0], state)) return false;
return true;
}

View File

@@ -120,9 +120,9 @@ void DCCTimer::reset() {
}
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
#define NUM_ADC_INPUTS 7
#else
#define NUM_ADC_INPUTS 15
#else
#define NUM_ADC_INPUTS 7
#endif
uint16_t ADCee::usedpins = 0;
int * ADCee::analogvals = NULL;

View File

@@ -1,4 +1,5 @@
/*
* © 2022 Paul M. Antoine
* © 2021 Mike S
* © 2021 Harald Barth
* © 2021 Fred Decker
@@ -124,5 +125,31 @@ void DCCTimer::reset() {
while(true){}
}
int16_t ADCee::ADCmax() {
return 4095;
}
int ADCee::init(uint8_t pin) {
return analogRead(pin);
}
/*
* Read function ADCee::read(pin) to get value instead of analogRead(pin)
*/
int ADCee::read(uint8_t pin, bool fromISR) {
int current;
if (!fromISR) noInterrupts();
current = analogRead(pin);
if (!fromISR) interrupts();
return current;
}
/*
* Scan function that is called from interrupt
*/
void ADCee::scan() {
}
void ADCee::begin() {
noInterrupts();
interrupts();
}
#endif

View File

@@ -141,4 +141,31 @@ void DCCTimer::reset() {
SCB_AIRCR = 0x05FA0004;
}
int16_t ADCee::ADCmax() {
return 4095;
}
int ADCee::init(uint8_t pin) {
return analogRead(pin);
}
/*
* Read function ADCee::read(pin) to get value instead of analogRead(pin)
*/
int ADCee::read(uint8_t pin, bool fromISR) {
int current;
if (!fromISR) noInterrupts();
current = analogRead(pin);
if (!fromISR) interrupts();
return current;
}
/*
* Scan function that is called from interrupt
*/
void ADCee::scan() {
}
void ADCee::begin() {
noInterrupts();
interrupts();
}
#endif

View File

@@ -90,10 +90,13 @@ LookList * RMFT2::onDeactivateLookup=NULL;
LookList * RMFT2::onRedLookup=NULL;
LookList * RMFT2::onAmberLookup=NULL;
LookList * RMFT2::onGreenLookup=NULL;
LookList * RMFT2::onChangeLookup=NULL;
#define GET_OPCODE GETFLASH(RMFT2::RouteCode+progCounter)
#ifdef ARDUINO_ARCH_AVR
#define GET_OPERAND(n) GETFLASHW(RMFT2::RouteCode+progCounter+1+(n*3))
#else
#define GET_OPERAND(n) GETOPW(RMFT2::RouteCode+progCounter+1+(n*3))
#define GETOPW(A) (((uint32_t)A)%2 ? GETFLASH((const byte *)A) | (GETFLASH(1+(const byte *)A)<<8) : GETFLASHW(A))
#endif
#define SKIPOP progCounter+=3
@@ -154,7 +157,6 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
onRedLookup=LookListLoader(OPCODE_ONRED);
onAmberLookup=LookListLoader(OPCODE_ONAMBER);
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
// Second pass startup, define any turnouts or servos, set signals red
// add sequences onRoutines to the lookups
@@ -723,10 +725,6 @@ void RMFT2::loop2() {
case OPCODE_IFNOT: // do next operand if sensor not set
skipIf=readSensor(operand);
break;
case OPCODE_IFRE: // do next operand if rotary encoder != position
skipIf=IODevice::readAnalogue(operand)!=(int)(GET_OPERAND(1));
break;
case OPCODE_IFRANDOM: // do block on random percentage
skipIf=(int16_t)(micros()%100) >= operand;
@@ -950,7 +948,6 @@ void RMFT2::loop2() {
case OPCODE_ONRED:
case OPCODE_ONAMBER:
case OPCODE_ONGREEN:
case OPCODE_ONCHANGE:
break;
@@ -1070,17 +1067,13 @@ void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) {
else handleEvent(F("THROW"),onThrowLookup,turnoutId);
}
void RMFT2::activateEvent(int16_t addr, bool activate) {
// Hunt for an ONACTIVATE/ONDEACTIVATE for this accessory
if (activate) handleEvent(F("ACTIVATE"),onActivateLookup,addr);
else handleEvent(F("DEACTIVATE"),onDeactivateLookup,addr);
}
void RMFT2::changeEvent(int16_t vpin, bool change) {
// Hunt for an ONCHANGE for this sensor
if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin);
}
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
int pc= handlers->find(id);
if (pc<0) return;

View File

@@ -54,7 +54,6 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_ENDTASK,OPCODE_ENDEXRAIL,
OPCODE_SET_TRACK,
OPCODE_ONRED,OPCODE_ONAMBER,OPCODE_ONGREEN,
OPCODE_ONCHANGE,
// OPcodes below this point are skip-nesting IF operations
// placed here so that they may be skipped as a group
@@ -65,8 +64,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
OPCODE_IFTIMEOUT,
OPCODE_IF,OPCODE_IFNOT,
OPCODE_IFRANDOM,OPCODE_IFRESERVE,
OPCODE_IFCLOSED,OPCODE_IFTHROWN,
OPCODE_IFRE,
OPCODE_IFCLOSED,OPCODE_IFTHROWN
};
@@ -109,7 +107,6 @@ class LookList {
static void createNewTask(int route, uint16_t cab);
static void turnoutEvent(int16_t id, bool closed);
static void activateEvent(int16_t addr, bool active);
static void changeEvent(int16_t id, bool change);
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
static const int16_t DCC_SIGNAL_FLAG=0x1000;
@@ -164,7 +161,6 @@ private:
static LookList * onRedLookup;
static LookList * onAmberLookup;
static LookList * onGreenLookup;
static LookList * onChangeLookup;
// Local variables - exist for each instance/task

View File

@@ -72,7 +72,6 @@
#undef IFRESERVE
#undef IFTHROWN
#undef IFTIMEOUT
#undef IFRE
#undef INVERT_DIRECTION
#undef JOIN
#undef KILLALL
@@ -89,7 +88,6 @@
#undef ONGREEN
#undef ONRED
#undef ONTHROW
#undef ONCHANGE
#undef PARSE
#undef PAUSE
#undef PIN_TURNOUT
@@ -184,7 +182,6 @@
#define IFTHROWN(turnout_id)
#define IFRESERVE(block)
#define IFTIMEOUT
#define IFRE(sensor_id,value)
#define INVERT_DIRECTION
#define JOIN
#define KILLALL
@@ -201,7 +198,6 @@
#define ONGREEN(signal_id)
#define ONRED(signal_id)
#define ONTHROW(turnout_id)
#define ONCHANGE(sensor_id)
#define PAUSE
#define PIN_TURNOUT(id,pin,description...)
#define PRINT(msg)

View File

@@ -261,7 +261,6 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = {
#define IFRESERVE(block) OPCODE_IFRESERVE,V(block),
#define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id),
#define IFTIMEOUT OPCODE_IFTIMEOUT,0,0,
#define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value),
#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0,
#define JOIN OPCODE_JOIN,0,0,
#define KILLALL OPCODE_KILLALL,0,0,
@@ -278,7 +277,6 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = {
#define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id),
#define ONRED(signal_id) OPCODE_ONRED,V(signal_id),
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
#define PAUSE OPCODE_PAUSE,0,0,
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),

View File

@@ -1,6 +1,7 @@
/*
* © 2022 Bruno Sanches
* © 2021 Fred Decker
* © 2020-2021 Harald Barth
* © 2020-2022 Harald Barth
* © 2020-2021 Chris Harlow
* © 2020 Gregor Baues
* All rights reserved.
@@ -36,8 +37,13 @@ EthernetInterface * EthernetInterface::singleton=NULL;
*/
void EthernetInterface::setup()
{
singleton=new EthernetInterface();
if (!singleton->connected) singleton=NULL;
if (singleton!=NULL) {
DIAG(F("Prog Error!"));
return;
}
if ((singleton=new EthernetInterface()))
return;
DIAG(F("Ethernet not initialized"));
};
@@ -54,45 +60,41 @@ EthernetInterface::EthernetInterface()
connected=false;
#ifdef IP_ADDRESS
Ethernet.begin(mac, IP_ADDRESS);
if (Ethernet.begin(mac, IP_ADDRESS) == 0)
#else
if (Ethernet.begin(mac) == 0)
#endif
{
DIAG(F("Ethernet.begin FAILED"));
return;
}
#endif
DIAG(F("begin OK."));
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
DIAG(F("Ethernet shield not found"));
return;
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
DIAG(F("Ethernet shield not found or W5100"));
}
unsigned long startmilli = millis();
while ((millis() - startmilli) < 5500) // Loop to give time to check for cable connection
{
while ((millis() - startmilli) < 5500) { // Loop to give time to check for cable connection
if (Ethernet.linkStatus() == LinkON)
break;
DIAG(F("Ethernet waiting for link (1sec) "));
delay(1000);
}
// now we either do have link of we have a W5100
// where we do not know if we have link. That's
// the reason to now run checkLink.
// CheckLinks sets up outboundRing if it does
// not exist yet as well.
checkLink();
}
if (Ethernet.linkStatus() == LinkOFF) {
DIAG(F("Ethernet cable not connected"));
return;
}
connected=true;
IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address
server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT
server->begin();
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d"), IP_PORT);
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
/**
* @brief Cleanup any resources
*
* @return none
*/
EthernetInterface::~EthernetInterface() {
delete server;
delete outboundRing;
}
/**
@@ -101,33 +103,70 @@ EthernetInterface::EthernetInterface()
*/
void EthernetInterface::loop()
{
if (!singleton) return;
if (!singleton || (!singleton->checkLink()))
return;
switch (Ethernet.maintain())
{
switch (Ethernet.maintain()) {
case 1:
//renewed fail
DIAG(F("Ethernet Error: renewed fail"));
singleton=NULL;
return;
case 3:
//rebind fail
DIAG(F("Ethernet Error: rebind fail"));
singleton=NULL;
return;
default:
//nothing happened
break;
}
singleton->loop2();
}
void EthernetInterface::loop2()
{
/**
* @brief Checks ethernet link cable status and detects when it connects / disconnects
*
* @return true when cable is connected, false otherwise
*/
bool EthernetInterface::checkLink() {
if (Ethernet.linkStatus() != LinkOFF) { // check for not linkOFF instead of linkON as the W5100 does return LinkUnknown
//if we are not connected yet, setup a new server
if(!connected) {
DIAG(F("Ethernet cable connected"));
connected=true;
IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address
server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT
server->begin();
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d"), IP_PORT);
// only create a outboundRing it none exists, this may happen if the cable
// gets disconnected and connected again
if(!outboundRing)
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
}
return true;
} else { // connected
DIAG(F("Ethernet cable disconnected"));
connected=false;
//clean up any client
for (byte socket = 0; socket < MAX_SOCK_NUM; socket++) {
if(clients[socket].connected())
clients[socket].stop();
}
// tear down server
delete server;
server = nullptr;
LCD(4,F("IP: None"));
}
return false;
}
void EthernetInterface::loop2() {
if (!outboundRing) { // no idea to call loop2() if we can't handle outgoing data in it
if (Diag::ETHERNET) DIAG(F("No outboundRing"));
return;
}
// get client from the server
EthernetClient client = server->accept();
@@ -182,7 +221,9 @@ void EthernetInterface::loop()
// handle at most 1 outbound transmission
int socketOut=outboundRing->read();
if (socketOut>=0) {
if (socketOut >= MAX_SOCK_NUM) {
DIAG(F("Ethernet outboundRing socket=%d error"), socketOut);
} else if (socketOut >= 0) {
int count=outboundRing->count();
if (Diag::ETHERNET) DIAG(F("Ethernet reply socket=%d, count=:%d"), socketOut,count);
for(;count>0;count--) clients[socketOut].write(outboundRing->read());

View File

@@ -56,15 +56,16 @@ class EthernetInterface {
static void loop();
private:
static EthernetInterface * singleton;
bool connected;
EthernetInterface();
void loop2();
EthernetServer * server;
static EthernetInterface * singleton;
bool connected;
EthernetInterface();
~EthernetInterface();
void loop2();
bool checkLink();
EthernetServer * server = NULL;
EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield
uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv
RingStream * outboundRing;
RingStream * outboundRing = NULL;
};
#endif

18
FSH.h
View File

@@ -37,7 +37,9 @@
*
*/
#include <Arduino.h>
#if defined(ARDUINO_ARCH_MEGAAVR)
#ifdef F
#undef F
#endif
@@ -48,18 +50,26 @@ typedef char FSH;
#define FLASH
#define strlen_P strlen
#define strcpy_P strcpy
#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32)
typedef __FlashStringHelper FSH;
#define GETFLASH(addr) pgm_read_byte(addr)
#define GETFLASHW(addr) (*(const unsigned int8_t *)(addr)) | ((*(const unsigned int8_t *)(addr+1)) << 8)
// pgm_read_word is buggy if addr is odd but here
// we do only read well aligned addrs, the others are
// taken care about in the GET_OPERAND(n) macro in EXRAIL2.cpp.
#define GETFLASHW(addr) pgm_read_word(addr)
#ifdef FLASH
#undef FLASH
#endif
#define FLASH PROGMEM
#else
#else // AVR and AVR compat here
typedef __FlashStringHelper FSH;
#define GETFLASH(addr) pgm_read_byte_near(addr)
#define GETFLASHW(addr) pgm_read_word_near(addr)
#define FLASH PROGMEM
#endif
#endif
#endif // flash stuff
#endif // FSH

View File

@@ -1 +1 @@
#define GITHUB_SHA "devel-202210311845Z"
#define GITHUB_SHA "devel-202211082310Z"

View File

@@ -171,9 +171,14 @@ public:
// Save all turnout definitions
static void store();
#endif
static void printAll(Print *stream) {
static bool printAll(Print *stream) {
bool gotOne=false;
for (Turnout *tt = _firstTurnout; tt != 0; tt = tt->_nextTurnout)
if (!tt->isHidden()) StringFormatter::send(stream, F("<H %d %d>\n"),tt->getId(), tt->isThrown());
if (!tt->isHidden()) {
gotOne=true;
StringFormatter::send(stream, F("<H %d %d>\n"),tt->getId(), tt->isThrown());
}
return gotOne;
}

View File

@@ -298,7 +298,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
#ifdef EXRAIL_ACTIVE
StringFormatter::send(stream,F("RL%d"), RMFT2::rosterNameCount);
for (int16_t r=0;r<RMFT2::rosterNameCount;r++) {
int16_t cabid=GETFLASHW(RMFT2::rosterIdList+r*2);
int16_t cabid=GETFLASHW(RMFT2::rosterIdList+r);
StringFormatter::send(stream,F("]\\[%S}|{%d}|{%c"),
RMFT2::getRosterName(cabid),cabid,cabid<128?'S':'L');
}

View File

@@ -18,7 +18,7 @@ default_envs =
samd21-dev-usb
samd21-zero-usb
ESP32
Nucleo-STM32F411RE
Nucleo-F411RE
Teensy3.2
Teensy3.5
Teensy3.6
@@ -172,7 +172,7 @@ framework = arduino
lib_deps = ${env.lib_deps}
build_flags = -std=c++17
[env:Nucleo-STM32F411RE]
[env:Nucleo-F411RE]
platform = ststm32
board = nucleo_f411re
framework = arduino

View File

@@ -4,7 +4,10 @@
#include "StringFormatter.h"
#define VERSION "4.2.4"
#define VERSION "4.2.5"
// 4.2.5 Make GETFLASHW code more universal
// FIX: Withrottle roster index
// Ethernet start improvement and link detection
// 4.2.4 ESP32 experimental BT support
// More DC configurations possible and lower frequency
// Handle decoders that do not ack at write better