mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-07-29 02:13:45 +02:00
Compare commits
13 Commits
v5.2.16-De
...
v5.2.21-De
Author | SHA1 | Date | |
---|---|---|---|
|
c336ab0bb4 | ||
|
5ac26ce505 | ||
|
b51a8fe126 | ||
|
718e78fca6 | ||
|
70a1b9538c | ||
|
39d0cbb791 | ||
|
4a3d3228a9 | ||
|
74f7af1675 | ||
|
bd44184f57 | ||
|
e7d3d92c23 | ||
|
e3bab887a2 | ||
|
041a6534da | ||
|
198d762a21 |
@@ -76,6 +76,12 @@ void setup()
|
||||
|
||||
DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com"));
|
||||
|
||||
// If user has defined a startup delay, delay here before starting IO
|
||||
#if defined(STARTUP_DELAY)
|
||||
DIAG(F("Delaying startup for %dms"), STARTUP_DELAY);
|
||||
delay(STARTUP_DELAY);
|
||||
#endif
|
||||
|
||||
// Initialise HAL layer before reading EEprom or setting up MotorDrivers
|
||||
IODevice::begin();
|
||||
|
||||
|
50
DCCRMT.cpp
50
DCCRMT.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* © 2021-2022, Harald Barth.
|
||||
* © 2021-2024, Harald Barth.
|
||||
*
|
||||
* This file is part of DCC-EX
|
||||
*
|
||||
@@ -25,6 +25,18 @@
|
||||
#include "DCCWaveform.h" // for MAX_PACKET_SIZE
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
// check for right type of ESP32
|
||||
#include "soc/soc_caps.h"
|
||||
#ifndef SOC_RMT_MEM_WORDS_PER_CHANNEL
|
||||
#error This symobol should be defined
|
||||
#endif
|
||||
#if SOC_RMT_MEM_WORDS_PER_CHANNEL < 64
|
||||
#warning This is not an ESP32-WROOM but some other unsupported variant
|
||||
#warning You are outside of the DCC-EX supported hardware
|
||||
#endif
|
||||
|
||||
static const byte RMT_CHAN_PER_DCC_CHAN = 2;
|
||||
|
||||
// Number of bits resulting out of X bytes of DCC payload data
|
||||
// Each byte has one bit extra and at the end we have one EOF marker
|
||||
#define DATA_LEN(X) ((X)*9+1)
|
||||
@@ -75,12 +87,30 @@ void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) {
|
||||
RMTChannel::RMTChannel(pinpair pins, bool isMain) {
|
||||
byte ch;
|
||||
byte plen;
|
||||
|
||||
// Below we check if the DCC packet actually fits into the RMT hardware
|
||||
// Currently MAX_PACKET_SIZE = 5 so with checksum there are
|
||||
// MAX_PACKET_SIZE+1 data packets. Each need DATA_LEN (9) bits.
|
||||
// To that we add the preamble length, the fencepost DCC end bit
|
||||
// and the RMT EOF marker.
|
||||
// SOC_RMT_MEM_WORDS_PER_CHANNEL is either 64 (original WROOM) or
|
||||
// 48 (all other ESP32 like the -C3 or -S2
|
||||
// The formula to get the possible MAX_PACKET_SIZE is
|
||||
//
|
||||
// ALLOCATED = RMT_CHAN_PER_DCC_CHAN * SOC_RMT_MEM_WORDS_PER_CHANNEL
|
||||
// MAX_PACKET_SIZE = floor((ALLOCATED - PREAMBLE_LEN - 2)/9 - 1)
|
||||
//
|
||||
|
||||
if (isMain) {
|
||||
ch = 0;
|
||||
plen = PREAMBLE_BITS_MAIN;
|
||||
static_assert (DATA_LEN(MAX_PACKET_SIZE+1) + PREAMBLE_BITS_MAIN + 2 <= RMT_CHAN_PER_DCC_CHAN * SOC_RMT_MEM_WORDS_PER_CHANNEL,
|
||||
"Number of DCC packet bits greater than ESP32 RMT memory available");
|
||||
} else {
|
||||
ch = 2;
|
||||
ch = RMT_CHAN_PER_DCC_CHAN; // number == offset
|
||||
plen = PREAMBLE_BITS_PROG;
|
||||
static_assert (DATA_LEN(MAX_PACKET_SIZE+1) + PREAMBLE_BITS_PROG + 2 <= RMT_CHAN_PER_DCC_CHAN * SOC_RMT_MEM_WORDS_PER_CHANNEL,
|
||||
"Number of DCC packet bits greater than ESP32 RMT memory available");
|
||||
}
|
||||
|
||||
// preamble
|
||||
@@ -115,7 +145,7 @@ RMTChannel::RMTChannel(pinpair pins, bool isMain) {
|
||||
// data: max packet size today is 5 + checksum
|
||||
maxDataLen = DATA_LEN(MAX_PACKET_SIZE+1); // plus checksum
|
||||
data = (rmt_item32_t*)malloc(maxDataLen*sizeof(rmt_item32_t));
|
||||
|
||||
|
||||
rmt_config_t config;
|
||||
// Configure the RMT channel for TX
|
||||
bzero(&config, sizeof(rmt_config_t));
|
||||
@@ -123,20 +153,10 @@ RMTChannel::RMTChannel(pinpair pins, bool isMain) {
|
||||
config.channel = channel = (rmt_channel_t)ch;
|
||||
config.clk_div = RMT_CLOCK_DIVIDER;
|
||||
config.gpio_num = (gpio_num_t)pins.pin;
|
||||
config.mem_block_num = 2; // With longest DCC packet 11 inc checksum (future expansion)
|
||||
// number of bits needed is 22preamble + start +
|
||||
// 11*9 + extrazero + EOT = 124
|
||||
// 2 mem block of 64 RMT items should be enough
|
||||
|
||||
config.mem_block_num = RMT_CHAN_PER_DCC_CHAN;
|
||||
// use config
|
||||
ESP_ERROR_CHECK(rmt_config(&config));
|
||||
addPin(pins.invpin, true);
|
||||
/*
|
||||
// test: config another gpio pin
|
||||
gpio_num_t gpioNum = (gpio_num_t)(pin-1);
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpioNum], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT);
|
||||
gpio_matrix_out(gpioNum, RMT_SIG_OUT0_IDX, 0, 0);
|
||||
*/
|
||||
|
||||
// NOTE: ESP_INTR_FLAG_IRAM is *NOT* included in this bitmask
|
||||
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, ESP_INTR_FLAG_LOWMED|ESP_INTR_FLAG_SHARED));
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* © 2021 M Steve Todd
|
||||
* © 2021 Mike S
|
||||
* © 2021 Fred Decker
|
||||
* © 2020-2021 Harald Barth
|
||||
* © 2020-2024 Harald Barth
|
||||
* © 2020-2021 Chris Harlow
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -33,9 +33,9 @@
|
||||
|
||||
|
||||
// Number of preamble bits.
|
||||
const int PREAMBLE_BITS_MAIN = 16;
|
||||
const int PREAMBLE_BITS_PROG = 22;
|
||||
const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum.
|
||||
const byte PREAMBLE_BITS_MAIN = 16;
|
||||
const byte PREAMBLE_BITS_PROG = 22;
|
||||
const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum.
|
||||
|
||||
|
||||
// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic
|
||||
|
@@ -59,15 +59,15 @@ EthernetInterface::EthernetInterface()
|
||||
DCCTimer::getSimulatedMacAddress(mac);
|
||||
connected=false;
|
||||
|
||||
#ifdef IP_ADDRESS
|
||||
Ethernet.begin(mac, IP_ADDRESS);
|
||||
#else
|
||||
#ifdef IP_ADDRESS
|
||||
if (Ethernet.begin(mac, IP_ADDRESS) == 0)
|
||||
#else
|
||||
if (Ethernet.begin(mac) == 0)
|
||||
#endif
|
||||
{
|
||||
DIAG(F("Ethernet.begin FAILED"));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
DIAG(F("Ethernet shield not found or W5100"));
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
#define GITHUB_SHA "devel-202312131041Z"
|
||||
#define GITHUB_SHA "devel-202401081219Z"
|
||||
|
@@ -74,25 +74,39 @@ class NetworkClient {
|
||||
public:
|
||||
NetworkClient(WiFiClient c) {
|
||||
wifi = c;
|
||||
};
|
||||
bool ok() {
|
||||
return (inUse && wifi.connected());
|
||||
};
|
||||
bool recycle(WiFiClient c) {
|
||||
|
||||
if (inUse == true) return false;
|
||||
|
||||
// return false here until we have
|
||||
// implemented a LRU timer
|
||||
// if (LRU too recent) return false;
|
||||
return false;
|
||||
|
||||
wifi = c;
|
||||
inUse = true;
|
||||
};
|
||||
bool active(byte clientId) {
|
||||
if (!inUse)
|
||||
return false;
|
||||
if(!wifi.connected()) {
|
||||
DIAG(F("Remove client %d"), clientId);
|
||||
CommandDistributor::forget(clientId);
|
||||
wifi.stop();
|
||||
inUse = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool recycle(WiFiClient c) {
|
||||
if (wifi == c) {
|
||||
if (inUse == true)
|
||||
DIAG(F("WARNING: Duplicate"));
|
||||
else
|
||||
DIAG(F("Returning"));
|
||||
inUse = true;
|
||||
return true;
|
||||
}
|
||||
if (inUse == false) {
|
||||
wifi = c;
|
||||
inUse = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
WiFiClient wifi;
|
||||
bool inUse = true;
|
||||
private:
|
||||
bool inUse;
|
||||
};
|
||||
|
||||
static std::vector<NetworkClient> clients; // a list to hold all clients
|
||||
@@ -165,7 +179,7 @@ bool WifiESP::setup(const char *SSid,
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
// DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str());
|
||||
DIAG(F("Wifi in STA mode"));
|
||||
LCD(7, F("IP: %s"), WiFi.softAPIP().toString().c_str());
|
||||
LCD(7, F("IP: %s"), WiFi.localIP().toString().c_str());
|
||||
wifiUp = true;
|
||||
} else {
|
||||
DIAG(F("Could not connect to Wifi SSID %s"),SSid);
|
||||
@@ -282,37 +296,26 @@ void WifiESP::loop() {
|
||||
// really no good way to check for LISTEN especially in AP mode?
|
||||
wl_status_t wlStatus;
|
||||
if (APmode || (wlStatus = WiFi.status()) == WL_CONNECTED) {
|
||||
// loop over all clients and remove inactive
|
||||
for (clientId=0; clientId<clients.size(); clientId++){
|
||||
// check if client is there and alive
|
||||
if(clients[clientId].inUse && !clients[clientId].wifi.connected()) {
|
||||
DIAG(F("Remove client %d"), clientId);
|
||||
CommandDistributor::forget(clientId);
|
||||
clients[clientId].wifi.stop();
|
||||
clients[clientId].inUse = false;
|
||||
//Do NOT clients.erase(clients.begin()+clientId) as
|
||||
//that would mix up clientIds for later.
|
||||
}
|
||||
}
|
||||
if (server->hasClient()) {
|
||||
WiFiClient client;
|
||||
while (client = server->available()) {
|
||||
for (clientId=0; clientId<clients.size(); clientId++){
|
||||
if (clients[clientId].recycle(client)) {
|
||||
DIAG(F("Recycle client %d %s"), clientId, client.remoteIP().toString().c_str());
|
||||
DIAG(F("Recycle client %d %s:%d"), clientId, client.remoteIP().toString().c_str(),client.remotePort());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clientId>=clients.size()) {
|
||||
NetworkClient nc(client);
|
||||
clients.push_back(nc);
|
||||
DIAG(F("New client %d, %s"), clientId, client.remoteIP().toString().c_str());
|
||||
DIAG(F("New client %d, %s:%d"), clientId, client.remoteIP().toString().c_str(),client.remotePort());
|
||||
}
|
||||
}
|
||||
}
|
||||
// loop over all connected clients
|
||||
// this removes as a side effect inactive clients when checking ::active()
|
||||
for (clientId=0; clientId<clients.size(); clientId++){
|
||||
if(clients[clientId].ok()) {
|
||||
if(clients[clientId].active(clientId)) {
|
||||
int len;
|
||||
if ((len = clients[clientId].wifi.available()) > 0) {
|
||||
// read data from client
|
||||
@@ -350,7 +353,7 @@ void WifiESP::loop() {
|
||||
}
|
||||
// buffer filled, end with '\0' so we can use it as C string
|
||||
buffer[count]='\0';
|
||||
if((unsigned int)clientId <= clients.size() && clients[clientId].ok()) {
|
||||
if((unsigned int)clientId <= clients.size() && clients[clientId].active(clientId)) {
|
||||
if (Diag::CMD || Diag::WITHROTTLE)
|
||||
DIAG(F("SEND %d:%s"), clientId, buffer);
|
||||
clients[clientId].wifi.write(buffer,count);
|
||||
@@ -383,8 +386,9 @@ void WifiESP::loop() {
|
||||
// prio task. On core1 this is not a problem
|
||||
// as there the wdt is disabled by the
|
||||
// arduio IDE startup routines.
|
||||
if (xPortGetCoreID() == 0)
|
||||
if (xPortGetCoreID() == 0) {
|
||||
feedTheDog0();
|
||||
yield();
|
||||
yield();
|
||||
}
|
||||
}
|
||||
#endif //ESP32
|
||||
|
@@ -68,7 +68,9 @@ Stream * WifiInterface::wifiStream;
|
||||
#define NUM_SERIAL 3
|
||||
#define SERIAL1 Serial3
|
||||
#define SERIAL3 Serial5
|
||||
#elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) || defined(ARDUINO_NUCLEO_F412ZG)
|
||||
#elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) \
|
||||
|| defined(ARDUINO_NUCLEO_F446ZE) || defined(ARDUINO_NUCLEO_F412ZG) \
|
||||
|| defined(ARDUINO_NUCLEO_F439ZI)
|
||||
#define NUM_SERIAL 2
|
||||
#define SERIAL1 Serial6
|
||||
#else
|
||||
|
@@ -222,6 +222,14 @@ The configuration file for DCC-EX Command Station
|
||||
// We do not support to use the same address, for example 100(long) and 100(short)
|
||||
// at the same time, there must be a border.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// Some newer 32bit microcontrollers boot very quickly, so powering on I2C and other
|
||||
// peripheral devices at the same time may result in the CommandStation booting too
|
||||
// quickly to detect them.
|
||||
// To work around this, uncomment the STARTUP_DELAY line below and set a value in
|
||||
// milliseconds that works for your environment, default is 3000 (3 seconds).
|
||||
// #define STARTUP_DELAY 3000
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE TURNOUTS/ACCESSORIES FOLLOW NORM RCN-213
|
||||
|
@@ -3,7 +3,12 @@
|
||||
|
||||
#include "StringFormatter.h"
|
||||
|
||||
#define VERSION "5.2.16"
|
||||
#define VERSION "5.2.21"
|
||||
// 5.2.21 - Add STARTUP_DELAY config option to delay CS bootup
|
||||
// 5.2.20 - Check return of Ethernet.begin()
|
||||
// 5.2.19 - ESP32: Determine if the RMT hardware can handle DCC
|
||||
// 5.2.18 - Display network IP fix
|
||||
// 5.2.17 - ESP32 simplify network logic
|
||||
// 5.2.16 - Bugfix to allow for devices using the EX-IOExpander protocol to have no analogue or no digital pins
|
||||
// 5.2.15 - move call to CommandDistributor::broadcastPower() into the TrackManager::setTrackPower(*) functions
|
||||
// - add repeats to function packets that are not reminded in accordance with accessory packets
|
||||
|
Reference in New Issue
Block a user