mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-04-20 20:21:18 +02:00
Update to 5.1.16 from DCC-EX/devel
Updates to 5.1.16
This commit is contained in:
commit
a0f6af6a1b
@ -58,14 +58,9 @@ template<typename... Targs> void CommandDistributor::broadcastReply(clientType t
|
|||||||
#ifdef CD_HANDLE_RING
|
#ifdef CD_HANDLE_RING
|
||||||
// wifi or ethernet ring streams with multiple client types
|
// wifi or ethernet ring streams with multiple client types
|
||||||
RingStream * CommandDistributor::ring=0;
|
RingStream * CommandDistributor::ring=0;
|
||||||
CommandDistributor::clientType CommandDistributor::clients[20]={
|
CommandDistributor::clientType CommandDistributor::clients[MAX_NUM_TCP_CLIENTS]={ NONE_TYPE }; // 0 is and must be 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,NONE_TYPE,
|
|
||||||
NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE};
|
|
||||||
|
|
||||||
// Parse is called by Wifi or Ethernet interface to determine which
|
// Parse is called by Withrottle or Ethernet interface to determine which
|
||||||
// protocol the client is using and call the appropriate part of dcc++Ex
|
// protocol the client is using and call the appropriate part of dcc++Ex
|
||||||
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) {
|
void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) {
|
||||||
if (clientId>=sizeof (clients)) {
|
if (clientId>=sizeof (clients)) {
|
||||||
|
@ -37,13 +37,13 @@
|
|||||||
|
|
||||||
class CommandDistributor {
|
class CommandDistributor {
|
||||||
public:
|
public:
|
||||||
enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE,WEBSOCK_CONNECTING_TYPE,WEBSOCKET_TYPE};
|
enum clientType: byte {NONE_TYPE = 0,COMMAND_TYPE,WITHROTTLE_TYPE,WEBSOCK_CONNECTING_TYPE,WEBSOCKET_TYPE}; // independent of other types, NONE_TYPE must be 0
|
||||||
private:
|
private:
|
||||||
static void broadcastToClients(clientType type);
|
static void broadcastToClients(clientType type);
|
||||||
static StringBuffer * broadcastBufferWriter;
|
static StringBuffer * broadcastBufferWriter;
|
||||||
#ifdef CD_HANDLE_RING
|
#ifdef CD_HANDLE_RING
|
||||||
static RingStream * ring;
|
static RingStream * ring;
|
||||||
static clientType clients[20];
|
static clientType clients[MAX_NUM_TCP_CLIENTS];
|
||||||
#endif
|
#endif
|
||||||
public :
|
public :
|
||||||
static void parse(byte clientId,byte* buffer, RingStream * ring);
|
static void parse(byte clientId,byte* buffer, RingStream * ring);
|
||||||
|
47
DCC.cpp
47
DCC.cpp
@ -38,6 +38,7 @@
|
|||||||
#include "TrackManager.h"
|
#include "TrackManager.h"
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
#include "Railcom.h"
|
#include "Railcom.h"
|
||||||
|
#include "DCCQueue.h"
|
||||||
|
|
||||||
// This module is responsible for converting API calls into
|
// This module is responsible for converting API calls into
|
||||||
// messages to be sent to the waveform generator.
|
// messages to be sent to the waveform generator.
|
||||||
@ -157,8 +158,8 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) {
|
|||||||
b[nB++] = speedCode; // for encoding see setThrottle
|
b[nB++] = speedCode; // for encoding see setThrottle
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if ((speedCode & 0x7F) == 1) DCCQueue::scheduleEstopPacket(b, nB, 4, cab); // highest priority
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 0);
|
else DCCQueue::scheduleDCCSpeedPacket( b, nB, 4, cab);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) {
|
void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) {
|
||||||
@ -172,7 +173,7 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) {
|
|||||||
if (byte1!=0) b[nB++] = byte1;
|
if (byte1!=0) b[nB++] = byte1;
|
||||||
b[nB++] = byte2;
|
b[nB++] = byte2;
|
||||||
|
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, count);
|
DCCQueue::scheduleDCCPacket(b, nB, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns speed steps 0 to 127 (1 == emergency stop)
|
// returns speed steps 0 to 127 (1 == emergency stop)
|
||||||
@ -238,7 +239,7 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) {
|
|||||||
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);
|
DCCQueue::scheduleDCCPacket(b, nB, 4);
|
||||||
}
|
}
|
||||||
// We use the reminder table up to 28 for normal functions.
|
// We use the reminder table up to 28 for normal functions.
|
||||||
// We use 29 to 31 for DC frequency as well so up to 28
|
// We use 29 to 31 for DC frequency as well so up to 28
|
||||||
@ -339,16 +340,17 @@ void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) {
|
|||||||
// second byte is of the form 1AAACPPG, where C is 1 for on, PP the ports 0 to 3 and G the gate (coil).
|
// second byte is of the form 1AAACPPG, where C is 1 for on, PP the ports 0 to 3 and G the gate (coil).
|
||||||
b[0] = address % 64 + 128;
|
b[0] = address % 64 + 128;
|
||||||
b[1] = ((((address / 64) % 8) << 4) + (port % 4 << 1) + gate % 2) ^ 0xF8;
|
b[1] = ((((address / 64) % 8) << 4) + (port % 4 << 1) + gate % 2) ^ 0xF8;
|
||||||
if (onoff != 0) {
|
if (onoff==0) { // off packet only
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, 2, 3); // Repeat on packet three times
|
|
||||||
#if defined(EXRAIL_ACTIVE)
|
|
||||||
RMFT2::activateEvent(address<<2|port,gate);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (onoff != 1) {
|
|
||||||
b[1] &= ~0x08; // set C to 0
|
b[1] &= ~0x08; // set C to 0
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, 2, 3); // Repeat off packet three times
|
DCCQueue::scheduleDCCPacket(b, 2, 3);
|
||||||
|
} else if (onoff==1) { // on packet only
|
||||||
|
DCCQueue::scheduleDCCPacket(b, 2, 3);
|
||||||
|
} else { // auto timed on then off
|
||||||
|
DCCQueue::scheduleAccOnOffPacket(b, 2, 3, 100); // On then off after 100mS
|
||||||
}
|
}
|
||||||
|
#if defined(EXRAIL_ACTIVE)
|
||||||
|
if (onoff !=0) RMFT2::activateEvent(address<<2|port,gate);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) {
|
bool DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) {
|
||||||
@ -398,7 +400,7 @@ whole range of the 11 bits sent to track.
|
|||||||
| (((~(address>>8)) & 0x07)<<4) // shift out 8, invert, mask 3 bits, shift up 4
|
| (((~(address>>8)) & 0x07)<<4) // shift out 8, invert, mask 3 bits, shift up 4
|
||||||
| ((address & 0x03)<<1); // mask 2 bits, shift up 1
|
| ((address & 0x03)<<1); // mask 2 bits, shift up 1
|
||||||
b[2]=value;
|
b[2]=value;
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats);
|
DCCQueue::scheduleDCCPacket(b, sizeof(b), repeats);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +419,7 @@ void DCC::writeCVByteMain(int cab, int cv, byte bValue) {
|
|||||||
b[nB++] = cv2(cv);
|
b[nB++] = cv2(cv);
|
||||||
b[nB++] = bValue;
|
b[nB++] = bValue;
|
||||||
|
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 4);
|
DCCQueue::scheduleDCCPacket(b, nB, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -435,7 +437,7 @@ void DCC::readCVByteMain(int cab, int cv, ACK_CALLBACK callback) {
|
|||||||
b[nB++] = cv2(cv);
|
b[nB++] = cv2(cv);
|
||||||
b[nB++] = 0;
|
b[nB++] = 0;
|
||||||
|
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 4);
|
DCCQueue::scheduleDCCPacket(b, nB, 4);
|
||||||
Railcom::anticipate(cab,cv,callback);
|
Railcom::anticipate(cab,cv,callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +459,7 @@ void DCC::writeCVBitMain(int cab, int cv, byte bNum, bool bValue) {
|
|||||||
b[nB++] = cv2(cv);
|
b[nB++] = cv2(cv);
|
||||||
b[nB++] = WRITE_BIT | (bValue ? BIT_ON : BIT_OFF) | bNum;
|
b[nB++] = WRITE_BIT | (bValue ? BIT_ON : BIT_OFF) | bNum;
|
||||||
|
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 4);
|
DCCQueue::scheduleDCCPacket(b, nB, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DCC::setTime(uint16_t minutes,uint8_t speed, bool suddenChange) {
|
bool DCC::setTime(uint16_t minutes,uint8_t speed, bool suddenChange) {
|
||||||
@ -494,7 +496,7 @@ b[1]=0b11000001; // 1100-0001 (model time)
|
|||||||
b[2]=minutes % 60 ; // MM
|
b[2]=minutes % 60 ; // MM
|
||||||
b[3]= 0b11100000 | (minutes/60); // 111H-HHHH weekday not supported
|
b[3]= 0b11100000 | (minutes/60); // 111H-HHHH weekday not supported
|
||||||
b[4]= (suddenChange ? 0b10000000 : 0) | speed;
|
b[4]= (suddenChange ? 0b10000000 : 0) | speed;
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), 2);
|
DCCQueue::scheduleDCCPacket(b, sizeof(b), 2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -844,12 +846,17 @@ byte DCC::loopStatus=0;
|
|||||||
|
|
||||||
void DCC::loop() {
|
void DCC::loop() {
|
||||||
TrackManager::loop(); // power overload checks
|
TrackManager::loop(); // power overload checks
|
||||||
issueReminders();
|
if (DCCWaveform::mainTrack.isReminderWindowOpen()) {
|
||||||
|
// Now is a good time to choose a packet to be sent
|
||||||
|
// Either highest priority from the queues or a reminder
|
||||||
|
if (!DCCQueue::scheduleNext()) {
|
||||||
|
issueReminders();
|
||||||
|
DCCQueue::scheduleNext(); // push through any just created reminder
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::issueReminders() {
|
void DCC::issueReminders() {
|
||||||
// if the main track transmitter still has a pending packet, skip this time around.
|
|
||||||
if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return;
|
|
||||||
// Move to next loco slot. If occupied, send a reminder.
|
// Move to next loco slot. If occupied, send a reminder.
|
||||||
auto slot = nextLocoReminder;
|
auto slot = nextLocoReminder;
|
||||||
if (slot >= &speedTable[MAX_LOCOS]) slot=&speedTable[0]; // Go to start of table
|
if (slot >= &speedTable[MAX_LOCOS]) slot=&speedTable[0]; // Go to start of table
|
||||||
|
185
DCCQueue.cpp
Normal file
185
DCCQueue.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* © 2025 Chris Harlow
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "defines.h"
|
||||||
|
#include "DCCQueue.h"
|
||||||
|
#include "DCCWaveform.h"
|
||||||
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
// create statics
|
||||||
|
DCCQueue* DCCQueue::lowPriorityQueue=new DCCQueue();
|
||||||
|
DCCQueue* DCCQueue::highPriorityQueue=new DCCQueue();
|
||||||
|
PendingSlot* DCCQueue::recycleList=nullptr;
|
||||||
|
|
||||||
|
DCCQueue::DCCQueue() {
|
||||||
|
head=nullptr;
|
||||||
|
tail=nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCQueue::addQueue(PendingSlot* p) {
|
||||||
|
if (tail) tail->next=p;
|
||||||
|
else head=p;
|
||||||
|
tail=p;
|
||||||
|
p->next=nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCQueue::jumpQueue(PendingSlot* p) {
|
||||||
|
p->next=head;
|
||||||
|
head=p;
|
||||||
|
if (!tail) tail=p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DCCQueue::recycle(PendingSlot* p) {
|
||||||
|
p->next=recycleList;
|
||||||
|
recycleList=p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packet joins end of low priority queue.
|
||||||
|
void DCCQueue::scheduleDCCPacket(byte* packet, byte length, byte repeats) {
|
||||||
|
lowPriorityQueue->addQueue(getSlot(NORMAL_PACKET,packet,length,repeats,0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packet replaces existing loco speed packet or joins end of high priority queue.
|
||||||
|
|
||||||
|
void DCCQueue::scheduleDCCSpeedPacket(byte* packet, byte length, byte repeats, uint16_t loco) {
|
||||||
|
for (auto p=highPriorityQueue->head;p;p=p->next) {
|
||||||
|
if (p->locoId==loco) {
|
||||||
|
// replace existing packet
|
||||||
|
memcpy(p->packet,packet,length);
|
||||||
|
p->packetLength=length;
|
||||||
|
p->packetRepeat=repeats;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highPriorityQueue->addQueue(getSlot(NORMAL_PACKET,packet,length,repeats,loco));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ESTOP -
|
||||||
|
// any outstanding throttle packet for this loco (all if loco=0) discarded
|
||||||
|
// Packet joins start of queue,
|
||||||
|
|
||||||
|
|
||||||
|
void DCCQueue::scheduleEstopPacket(byte* packet, byte length, byte repeats,uint16_t loco) {
|
||||||
|
|
||||||
|
// DIAG(F("DCC ESTOP loco=%d"),loco);
|
||||||
|
|
||||||
|
// kill any existing throttle packets for this loco
|
||||||
|
PendingSlot * previous=nullptr;
|
||||||
|
auto p=highPriorityQueue->head;
|
||||||
|
while(p) {
|
||||||
|
if (loco==0 || p->locoId==loco) {
|
||||||
|
// drop this packet from the highPriority queue
|
||||||
|
if (previous) previous->next=p->next;
|
||||||
|
else highPriorityQueue->head=p->next;
|
||||||
|
|
||||||
|
recycle(p); // recycle this slot
|
||||||
|
|
||||||
|
// address next packet
|
||||||
|
p=previous?previous->next : highPriorityQueue->head;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
previous=p;
|
||||||
|
p=p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the estop packet to the start of the queue
|
||||||
|
highPriorityQueue->jumpQueue(getSlot(NORMAL_PACKET,packet,length,repeats,0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessory gate-On Packet joins end of queue as normal.
|
||||||
|
// When dequeued, packet is retained at start of queue
|
||||||
|
// but modified to gate-off and given the delayed start.
|
||||||
|
// getNext will ignore this packet until the requested start time.
|
||||||
|
void DCCQueue::scheduleAccOnOffPacket(byte* packet, byte length, byte repeats,int16_t delayms) {
|
||||||
|
auto p=getSlot(ACC_ON_PACKET,packet,length,repeats,0);
|
||||||
|
p->delayOff=delayms;
|
||||||
|
lowPriorityQueue->addQueue(p);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Obtain packet (fills packet, length and repeats)
|
||||||
|
// returns 0 length if nothing in queue.
|
||||||
|
|
||||||
|
bool DCCQueue::scheduleNext() {
|
||||||
|
// check high priority queue first
|
||||||
|
if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return false;
|
||||||
|
PendingSlot* previous=nullptr;
|
||||||
|
for (auto p=highPriorityQueue->head;p;p=p->next) {
|
||||||
|
// skip over pending ACC_OFF packets which are still delayed
|
||||||
|
if (p->type == ACC_OFF_PACKET && millis()<p->startTime) continue;
|
||||||
|
// use this slot
|
||||||
|
DCCWaveform::mainTrack.schedulePacket(p->packet,p->packetLength,p->packetRepeat);
|
||||||
|
// remove this slot from the queue
|
||||||
|
if (previous) previous->next=p->next;
|
||||||
|
else highPriorityQueue->head=p->next;
|
||||||
|
if (!highPriorityQueue->head) highPriorityQueue->tail=nullptr;
|
||||||
|
|
||||||
|
// and recycle it.
|
||||||
|
recycle(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No high priopity packets found, check low priority queue
|
||||||
|
auto p=lowPriorityQueue->head;
|
||||||
|
if (!p) return false; // nothing in queues
|
||||||
|
|
||||||
|
// schedule first packet in queue
|
||||||
|
DCCWaveform::mainTrack.schedulePacket(p->packet,p->packetLength,p->packetRepeat);
|
||||||
|
|
||||||
|
// remove from queue
|
||||||
|
lowPriorityQueue->head=p->next;
|
||||||
|
if (!lowPriorityQueue->head) lowPriorityQueue->tail=nullptr;
|
||||||
|
|
||||||
|
if (p->type == ACC_ON_PACKET) {
|
||||||
|
// convert to a delayed off packet and jump the high priority queue
|
||||||
|
p->type= ACC_OFF_PACKET;
|
||||||
|
p->packet[1] &= ~0x08; // set C to 0 (gate off)
|
||||||
|
p->startTime=millis()+p->delayOff;
|
||||||
|
highPriorityQueue->jumpQueue(p);
|
||||||
|
}
|
||||||
|
else recycle(p); // recycle this slot
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain and initialise slot for a PendingSlot.
|
||||||
|
PendingSlot* DCCQueue::getSlot(PendingType type, byte* packet, byte length, byte repeats,uint16_t loco) {
|
||||||
|
PendingSlot * p;
|
||||||
|
if (recycleList) {
|
||||||
|
p=recycleList;
|
||||||
|
recycleList=p->next;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DIAG(F("New DCC queue slot"));
|
||||||
|
p=new PendingSlot; // need a queue entry
|
||||||
|
}
|
||||||
|
p->next=nullptr;
|
||||||
|
p->type=type;
|
||||||
|
p->packetLength=length;
|
||||||
|
p->packetRepeat=repeats;
|
||||||
|
memcpy((void*)p->packet,packet,length);
|
||||||
|
p->locoId=loco;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
84
DCCQueue.h
Normal file
84
DCCQueue.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* © 2025 Chris Harlow
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DCCQueue_h
|
||||||
|
#define DCCQueue_h
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "DCCWaveform.h"
|
||||||
|
|
||||||
|
enum PendingType:byte {NORMAL_PACKET,ACC_ON_PACKET,ACC_OFF_PACKET,DEAD_PACKET};
|
||||||
|
struct PendingSlot {
|
||||||
|
PendingSlot* next;
|
||||||
|
PendingType type;
|
||||||
|
byte packetLength;
|
||||||
|
byte packetRepeat;
|
||||||
|
byte packet[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
union { // use depends on packet type
|
||||||
|
uint16_t locoId; // NORMAL_PACKET .. only set >0 for speed change packets
|
||||||
|
// so they can be easily discarded if an estop jumps the queue.
|
||||||
|
uint16_t delayOff; // ACC_ON_PACKET delay to apply between on/off
|
||||||
|
uint32_t startTime; // ACC_OFF_PACKET time (mS) to transmit
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class DCCQueue {
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
// Non-speed packets are queued in the main queue
|
||||||
|
static void scheduleDCCPacket(byte* packet, byte length, byte repeats);
|
||||||
|
|
||||||
|
// Speed packets are queued in the high priority queue
|
||||||
|
static void scheduleDCCSpeedPacket(byte* packet, byte length, byte repeats, uint16_t loco);
|
||||||
|
|
||||||
|
// ESTOP packets jump the high priority queue and discard any outstanding throttle packets for this loco
|
||||||
|
static void scheduleEstopPacket(byte* packet, byte length, byte repeats,uint16_t loco);
|
||||||
|
|
||||||
|
// Accessory gate-On Packet joins end of main queue as normal.
|
||||||
|
// When dequeued, packet is modified to gate-off and given the delayed start in the high priority queue.
|
||||||
|
// getNext will ignore this packet until the requested start time.
|
||||||
|
static void scheduleAccOnOffPacket(byte* packet, byte length, byte repeats,int16_t delayms);
|
||||||
|
|
||||||
|
|
||||||
|
// Schedules a main track packet from the queues if none pending.
|
||||||
|
// returns true if a packet was scheduled.
|
||||||
|
static bool scheduleNext();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// statics to manage high and low priority queues and recycleing of PENDINGs
|
||||||
|
static PendingSlot* recycleList;
|
||||||
|
static DCCQueue* highPriorityQueue;
|
||||||
|
static DCCQueue* lowPriorityQueue;
|
||||||
|
|
||||||
|
DCCQueue();
|
||||||
|
|
||||||
|
PendingSlot* head;
|
||||||
|
PendingSlot * tail;
|
||||||
|
|
||||||
|
// obtain and initialise slot for a PendingSlot.
|
||||||
|
static PendingSlot* getSlot(PendingType type, byte* packet, byte length, byte repeats, uint16_t loco);
|
||||||
|
static void recycle(PendingSlot* p);
|
||||||
|
void addQueue(PendingSlot * p);
|
||||||
|
void jumpQueue(PendingSlot * p);
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif
|
1127
EXRAIL2MacroReset.h
1127
EXRAIL2MacroReset.h
File diff suppressed because it is too large
Load Diff
200
EXmDNS.cpp
Normal file
200
EXmDNS.cpp
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* © 2024 Harald Barth
|
||||||
|
* © 2024 Paul M. Antoine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "EthernetInterface.h"
|
||||||
|
#ifdef DO_MDNS
|
||||||
|
#include "EXmDNS.h"
|
||||||
|
|
||||||
|
// fixed values for mDNS
|
||||||
|
static IPAddress mdnsMulticastIPAddr = IPAddress(224, 0, 0, 251);
|
||||||
|
#define MDNS_SERVER_PORT 5353
|
||||||
|
|
||||||
|
// dotToLen()
|
||||||
|
// converts stings of form ".foo.barbar.x" to a string with the
|
||||||
|
// dots replaced with lenght. So string above would result in
|
||||||
|
// "\x03foo\x06barbar\x01x" in C notation. If not NULL, *substr
|
||||||
|
// will point to the beginning of the last component, in this
|
||||||
|
// example that would be "\x01x".
|
||||||
|
//
|
||||||
|
static void dotToLen(char *str, char **substr) {
|
||||||
|
char *dotplace = NULL;
|
||||||
|
char *s;
|
||||||
|
byte charcount = 0;
|
||||||
|
for (s = str;/*see break*/ ; s++) {
|
||||||
|
if (*s == '.' || *s == '\0') {
|
||||||
|
// take care of accumulated
|
||||||
|
if (dotplace != NULL && charcount != 0) {
|
||||||
|
*dotplace = charcount;
|
||||||
|
}
|
||||||
|
if (*s == '\0')
|
||||||
|
break;
|
||||||
|
if (substr && *s == '.')
|
||||||
|
*substr = s;
|
||||||
|
// set new values
|
||||||
|
dotplace = s;
|
||||||
|
charcount = 0;
|
||||||
|
} else {
|
||||||
|
charcount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MDNS::MDNS(EthernetUDP& udp) {
|
||||||
|
_udp = &udp;
|
||||||
|
}
|
||||||
|
MDNS::~MDNS() {
|
||||||
|
_udp->stop();
|
||||||
|
if (_name) free(_name);
|
||||||
|
if (_serviceName) free(_serviceName);
|
||||||
|
if (_serviceProto) free(_serviceProto);
|
||||||
|
}
|
||||||
|
int MDNS::begin(const IPAddress& ip, char* name) {
|
||||||
|
// if we were called very soon after the board was booted, we need to give the
|
||||||
|
// EthernetShield (WIZnet) some time to come up. Hence, we delay until millis() is at
|
||||||
|
// least 3000. This is necessary, so that if we need to add a service record directly
|
||||||
|
// after begin, the announce packet does not get lost in the bowels of the WIZnet chip.
|
||||||
|
//while (millis() < 3000)
|
||||||
|
// delay(100);
|
||||||
|
|
||||||
|
_ipAddress = ip;
|
||||||
|
_name = (char *)malloc(strlen(name)+2);
|
||||||
|
byte n;
|
||||||
|
for(n = 0; n<strlen(name); n++)
|
||||||
|
_name[n+1] = name[n];
|
||||||
|
_name[n+1] = '\0';
|
||||||
|
_name[0] = '.';
|
||||||
|
dotToLen(_name, NULL);
|
||||||
|
return _udp->beginMulticast(mdnsMulticastIPAddr, MDNS_SERVER_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MDNS::addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto) {
|
||||||
|
// we ignore proto, assume TCP
|
||||||
|
(void)proto;
|
||||||
|
_serviceName = (char *)malloc(strlen(name) + 2);
|
||||||
|
byte n;
|
||||||
|
for(n = 0; n<strlen(name); n++)
|
||||||
|
_serviceName[n+1] = name[n];
|
||||||
|
_serviceName[n+1] = '\0';
|
||||||
|
_serviceName[0] = '.';
|
||||||
|
_serviceProto = NULL; //to be filled in
|
||||||
|
dotToLen(_serviceName, &_serviceProto);
|
||||||
|
_servicePort = port;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char dns_rr_services[] = "\x09_services\x07_dns-sd\x04_udp\x05local";
|
||||||
|
static char dns_rr_tcplocal[] = "\x04_tcp\x05local";
|
||||||
|
static char *dns_rr_local = dns_rr_tcplocal + dns_rr_tcplocal[0] + 1;
|
||||||
|
|
||||||
|
typedef struct _DNSHeader_t
|
||||||
|
{
|
||||||
|
uint16_t xid;
|
||||||
|
uint16_t flags; // flags condensed
|
||||||
|
uint16_t queryCount;
|
||||||
|
uint16_t answerCount;
|
||||||
|
uint16_t authorityCount;
|
||||||
|
uint16_t additionalCount;
|
||||||
|
} __attribute__((__packed__)) DNSHeader_t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// MDNS::run()
|
||||||
|
// This broadcasts whatever we got evey BROADCASTTIME seconds.
|
||||||
|
// Why? Too much brokenness i all mDNS implementations available
|
||||||
|
//
|
||||||
|
void MDNS::run() {
|
||||||
|
static long int lastrun = BROADCASTTIME * 1000UL;
|
||||||
|
unsigned long int now = millis();
|
||||||
|
if (!(now - lastrun > BROADCASTTIME * 1000UL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastrun = now;
|
||||||
|
DNSHeader_t dnsHeader = {0, 0, 0, 0, 0, 0};
|
||||||
|
// DNSHeader_t dnsHeader = { 0 };
|
||||||
|
|
||||||
|
_udp->beginPacket(mdnsMulticastIPAddr, MDNS_SERVER_PORT);
|
||||||
|
|
||||||
|
// dns header
|
||||||
|
dnsHeader.flags = HTONS((uint16_t)0x8400); // Response, authorative
|
||||||
|
dnsHeader.answerCount = HTONS(4 /*5 if TXT but we do not do that */);
|
||||||
|
_udp->write((uint8_t*)&dnsHeader, sizeof(DNSHeader_t));
|
||||||
|
|
||||||
|
// rr #1, the PTR record from generic _services.x.local to service.x.local
|
||||||
|
_udp->write((uint8_t*)dns_rr_services, sizeof(dns_rr_services));
|
||||||
|
|
||||||
|
byte buf[10];
|
||||||
|
buf[0] = 0x00;
|
||||||
|
buf[1] = 0x0c; //PTR
|
||||||
|
buf[2] = 0x00;
|
||||||
|
buf[3] = 0x01; //IN
|
||||||
|
*((uint32_t*)(buf+4)) = HTONL(120); //TTL in sec
|
||||||
|
*((uint16_t*)(buf+8)) = HTONS( _serviceProto[0] + 1 + strlen(dns_rr_tcplocal) + 1);
|
||||||
|
_udp->write(buf, 10);
|
||||||
|
|
||||||
|
_udp->write(_serviceProto,_serviceProto[0]+1);
|
||||||
|
_udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1);
|
||||||
|
|
||||||
|
// rr #2, the PTR record from proto.x to name.proto.x
|
||||||
|
_udp->write(_serviceProto,_serviceProto[0]+1);
|
||||||
|
_udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1);
|
||||||
|
*((uint16_t*)(buf+8)) = HTONS(strlen(_serviceName) + strlen(dns_rr_tcplocal) + 1); // recycle most of buf
|
||||||
|
_udp->write(buf, 10);
|
||||||
|
|
||||||
|
_udp->write(_serviceName, strlen(_serviceName));
|
||||||
|
_udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1);
|
||||||
|
// rr #3, the SRV record for the service that points to local name
|
||||||
|
_udp->write(_serviceName, strlen(_serviceName));
|
||||||
|
_udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1);
|
||||||
|
|
||||||
|
buf[1] = 0x21; // recycle most of buf but here SRV
|
||||||
|
buf[2] = 0x80; // cache flush
|
||||||
|
*((uint16_t*)(buf+8)) = HTONS(strlen(_name) + strlen(dns_rr_local) + 1 + 6);
|
||||||
|
_udp->write(buf, 10);
|
||||||
|
|
||||||
|
byte srv[6];
|
||||||
|
// priority and weight
|
||||||
|
srv[0] = srv[1] = srv[2] = srv[3] = 0;
|
||||||
|
// port
|
||||||
|
*((uint16_t*)(srv+4)) = HTONS(_servicePort);
|
||||||
|
_udp->write(srv, 6);
|
||||||
|
// target
|
||||||
|
_udp->write(_name, _name[0]+1);
|
||||||
|
_udp->write(dns_rr_local, strlen(dns_rr_local)+1);
|
||||||
|
|
||||||
|
// rr #4, the A record for the name.local
|
||||||
|
_udp->write(_name, _name[0]+1);
|
||||||
|
_udp->write(dns_rr_local, strlen(dns_rr_local)+1);
|
||||||
|
|
||||||
|
buf[1] = 0x01; // recycle most of buf but here A
|
||||||
|
*((uint16_t*)(buf+8)) = HTONS(4);
|
||||||
|
_udp->write(buf, 10);
|
||||||
|
byte ip[4];
|
||||||
|
ip[0] = _ipAddress[0];
|
||||||
|
ip[1] = _ipAddress[1];
|
||||||
|
ip[2] = _ipAddress[2];
|
||||||
|
ip[3] = _ipAddress[3];
|
||||||
|
_udp->write(ip, 4);
|
||||||
|
|
||||||
|
_udp->endPacket();
|
||||||
|
_udp->flush();
|
||||||
|
//
|
||||||
|
}
|
||||||
|
#endif //DO_MDNS
|
50
EXmDNS.h
Normal file
50
EXmDNS.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* © 2024 Harald Barth
|
||||||
|
* © 2024 Paul M. Antoine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifdef DO_MDNS
|
||||||
|
#define BROADCASTTIME 15 //seconds
|
||||||
|
|
||||||
|
// We do this ourselves because every library is different and/or broken...
|
||||||
|
#define HTONS(x) ((uint16_t)(((x) << 8) | (((x) >> 8) & 0xFF)))
|
||||||
|
#define HTONL(x) ( ((uint32_t)(x) << 24) | (((uint32_t)(x) << 8) & 0xFF0000) | \
|
||||||
|
(((uint32_t)(x) >> 8) & 0xFF00) | ((uint32_t)(x) >> 24) )
|
||||||
|
|
||||||
|
typedef enum _MDNSServiceProtocol_t
|
||||||
|
{
|
||||||
|
MDNSServiceTCP,
|
||||||
|
MDNSServiceUDP
|
||||||
|
} MDNSServiceProtocol_t;
|
||||||
|
|
||||||
|
class MDNS {
|
||||||
|
public:
|
||||||
|
MDNS(EthernetUDP& udp);
|
||||||
|
~MDNS();
|
||||||
|
int begin(const IPAddress& ip, char* name);
|
||||||
|
int addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto);
|
||||||
|
void run();
|
||||||
|
private:
|
||||||
|
EthernetUDP *_udp;
|
||||||
|
IPAddress _ipAddress;
|
||||||
|
char* _name;
|
||||||
|
char* _serviceName;
|
||||||
|
char* _serviceProto;
|
||||||
|
int _servicePort;
|
||||||
|
};
|
||||||
|
#endif //DO_MDNS
|
@ -31,13 +31,12 @@
|
|||||||
#include "CommandDistributor.h"
|
#include "CommandDistributor.h"
|
||||||
#include "WiThrottle.h"
|
#include "WiThrottle.h"
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
#if __has_include ( "MDNS_Generic.h")
|
|
||||||
#include "MDNS_Generic.h"
|
|
||||||
#define DO_MDNS
|
|
||||||
EthernetUDP udp;
|
|
||||||
MDNS mdns(udp);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#ifdef DO_MDNS
|
||||||
|
#include "EXmDNS.h"
|
||||||
|
EthernetUDP udp;
|
||||||
|
MDNS mdns(udp);
|
||||||
|
#endif
|
||||||
|
|
||||||
//extern void looptimer(unsigned long timeout, const FSH* message);
|
//extern void looptimer(unsigned long timeout, const FSH* message);
|
||||||
#define looptimer(a,b)
|
#define looptimer(a,b)
|
||||||
@ -116,10 +115,10 @@ void EthernetInterface::setup()
|
|||||||
|
|
||||||
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
|
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
|
||||||
#ifdef DO_MDNS
|
#ifdef DO_MDNS
|
||||||
mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME); // hostname
|
if (!mdns.begin(Ethernet.localIP(), (char *)WIFI_HOSTNAME))
|
||||||
|
DIAG(F("mdns.begin fail")); // hostname
|
||||||
mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP);
|
mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP);
|
||||||
// Not sure if we need to run it once, but just in case!
|
mdns.run(); // run it right away to get out info ASAP
|
||||||
mdns.run();
|
|
||||||
#endif
|
#endif
|
||||||
connected=true;
|
connected=true;
|
||||||
}
|
}
|
||||||
@ -144,7 +143,9 @@ void EthernetInterface::acceptClient() { // STM32 version
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DIAG(F("Ethernet OVERFLOW"));
|
// reached here only if more than MAX_SOCK_NUM clients want to connect
|
||||||
|
DIAG(F("Ethernet more than %d clients, not accepting new connection"), MAX_SOCK_NUM);
|
||||||
|
client.stop();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void EthernetInterface::acceptClient() { // non-STM32 version
|
void EthernetInterface::acceptClient() { // non-STM32 version
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* © 2021 Neil McKechnie
|
* © 2021 Neil McKechnie
|
||||||
* © 2021 Mike S
|
* © 2021 Mike S
|
||||||
* © 2021 Fred Decker
|
* © 2021 Fred Decker
|
||||||
* © 2020-2022 Harald Barth
|
* © 2020-2024 Harald Barth
|
||||||
* © 2020-2024 Chris Harlow
|
* © 2020-2024 Chris Harlow
|
||||||
* © 2020 Gregor Baues
|
* © 2020 Gregor Baues
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
@ -31,24 +31,32 @@
|
|||||||
#define EthernetInterface_h
|
#define EthernetInterface_h
|
||||||
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
#if ETHERNET_ON == true
|
||||||
#include "DCCEXParser.h"
|
#include "DCCEXParser.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
//#include <avr/pgmspace.h>
|
//#include <avr/pgmspace.h>
|
||||||
#if defined (ARDUINO_TEENSY41)
|
#if defined (ARDUINO_TEENSY41)
|
||||||
#include <NativeEthernet.h> //TEENSY Ethernet Treiber
|
#include <NativeEthernet.h> //TEENSY Ethernet Treiber
|
||||||
#include <NativeEthernetUdp.h>
|
#include <NativeEthernetUdp.h>
|
||||||
|
#ifndef MAX_SOCK_NUM
|
||||||
#define MAX_SOCK_NUM 4
|
#define MAX_SOCK_NUM 4
|
||||||
|
#endif
|
||||||
|
// can't use our MDNS because of a namespace clash with Teensy's NativeEthernet library!
|
||||||
|
// #define DO_MDNS
|
||||||
#elif defined (ARDUINO_NUCLEO_F429ZI) || defined (ARDUINO_NUCLEO_F439ZI) || defined (ARDUINO_NUCLEO_F4X9ZI)
|
#elif defined (ARDUINO_NUCLEO_F429ZI) || defined (ARDUINO_NUCLEO_F439ZI) || defined (ARDUINO_NUCLEO_F4X9ZI)
|
||||||
#include <LwIP.h>
|
#include <LwIP.h>
|
||||||
// #include "STM32lwipopts.h"
|
|
||||||
#include <STM32Ethernet.h>
|
#include <STM32Ethernet.h>
|
||||||
#include <lwip/netif.h>
|
#include <lwip/netif.h>
|
||||||
extern "C" struct netif gnetif;
|
extern "C" struct netif gnetif;
|
||||||
#define STM32_ETHERNET
|
#define STM32_ETHERNET
|
||||||
#define MAX_SOCK_NUM 8
|
#define MAX_SOCK_NUM MAX_NUM_TCP_CLIENTS
|
||||||
|
#define DO_MDNS
|
||||||
#else
|
#else
|
||||||
#include "Ethernet.h"
|
#include "Ethernet.h"
|
||||||
|
#define DO_MDNS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "RingStream.h"
|
#include "RingStream.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,5 +85,5 @@ class EthernetInterface {
|
|||||||
static void dropClient(byte socketnum);
|
static void dropClient(byte socketnum);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
#endif // ETHERNET_ON
|
||||||
#endif
|
#endif
|
||||||
|
@ -1 +1 @@
|
|||||||
#define GITHUB_SHA "devel-202501171827Z"
|
#define GITHUB_SHA "devel-202503022043Z"
|
||||||
|
101
STM32lwipopts.h.copyme
Normal file
101
STM32lwipopts.h.copyme
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* © 2024 Harald Barth
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of CommandStation-EX
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// Rewrite of the STM32lwipopts.h file from STM
|
||||||
|
// To be copied into where lwipopts_default.h resides
|
||||||
|
// typically into STM32Ethernet/src/STM32lwipopts.h
|
||||||
|
// or STM32Ethernet\src\STM32lwipopts.h
|
||||||
|
// search for `lwipopts_default.h` and copy this file into the
|
||||||
|
// same directory but name it STM32lwipopts.h
|
||||||
|
//
|
||||||
|
#ifndef __STM32LWIPOPTS_H__
|
||||||
|
#define __STM32LWIPOPTS_H__
|
||||||
|
|
||||||
|
// include this here and then override things we do differnet
|
||||||
|
#include "lwipopts_default.h"
|
||||||
|
|
||||||
|
// we can not include our "defines.h" here
|
||||||
|
// so we need to duplicate that define
|
||||||
|
#define MAX_NUM_TCP_CLIENTS_HERE 9
|
||||||
|
|
||||||
|
#ifdef MAX_NUM_TCP_CLIENTS
|
||||||
|
#if MAX_NUM_TCP_CLIENTS != MAX_NUM_TCP_CLIENTS_HERE
|
||||||
|
#error MAX_NUM_TCP_CLIENTS and MAX_NUM_TCP_CLIENTS_HERE must be same
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define MAX_NUM_TCP_CLIENTS MAX_NUM_TCP_CLIENTS_HERE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// increase ARP cache
|
||||||
|
#undef MEMP_NUM_APR_QUEUE
|
||||||
|
#define MEMP_NUM_ARP_QUEUE MAX_NUM_TCP_CLIENTS+3 // one for each client (all on different HW) and a few extra
|
||||||
|
|
||||||
|
// Example for debug
|
||||||
|
//#define LWIP_DEBUG 1
|
||||||
|
//#define TCP_DEBUG LWIP_DBG_ON
|
||||||
|
|
||||||
|
// NOT STRICT NECESSARY ANY MORE BUT CAN BE USED TO SAVE RAM
|
||||||
|
#undef MEM_LIBC_MALLOC
|
||||||
|
#define MEM_LIBC_MALLOC 1 // use the same malloc as for everything else
|
||||||
|
#undef MEMP_MEM_MALLOC
|
||||||
|
#define MEMP_MEM_MALLOC 1 // uses malloc which means no pools which means slower but not mean 32KB up front
|
||||||
|
|
||||||
|
#undef MEMP_NUM_TCP_PCB
|
||||||
|
#define MEMP_NUM_TCP_PCB MAX_NUM_TCP_CLIENTS+1 // one extra so we can reject number N+1 from our code
|
||||||
|
#define MEMP_NUM_TCP_PCB_LISTEN 6
|
||||||
|
|
||||||
|
#undef MEMP_NUM_TCP_SEG
|
||||||
|
#define MEMP_NUM_TCP_SEG MAX_NUM_TCP_CLIENTS
|
||||||
|
|
||||||
|
#undef MEMP_NUM_SYS_TIMEOUT
|
||||||
|
#define MEMP_NUM_SYS_TIMEOUT MAX_NUM_TCP_CLIENTS+2
|
||||||
|
|
||||||
|
#undef PBUF_POOL_SIZE
|
||||||
|
#define PBUF_POOL_SIZE MAX_NUM_TCP_CLIENTS
|
||||||
|
|
||||||
|
#undef LWIO_ICMP
|
||||||
|
#define LWIP_ICMP 1
|
||||||
|
#undef LWIP_RAW
|
||||||
|
#define LWIP_RAW 1 /* PING changed to 1 */
|
||||||
|
#undef DEFAULT_RAW_RECVMBOX_SIZE
|
||||||
|
#define DEFAULT_RAW_RECVMBOX_SIZE 3 /* for ICMP PING */
|
||||||
|
|
||||||
|
#undef LWIP_DHCP
|
||||||
|
#define LWIP_DHCP 1
|
||||||
|
#undef LWIP_UDP
|
||||||
|
#define LWIP_UDP 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
|
||||||
|
- To use this feature let the following define uncommented.
|
||||||
|
- To disable it and process by CPU comment the the checksum.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if CHECKSUM_GEN_TCP == 1
|
||||||
|
#error On STM32 TCP checksum should be in HW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef LWIP_IGMP
|
||||||
|
#define LWIP_IGMP 1
|
||||||
|
|
||||||
|
//#define SO_REUSE 1
|
||||||
|
//#define SO_REUSE_RXTOALL 1
|
||||||
|
|
||||||
|
#endif /* __STM32LWIPOPTS_H__ */
|
@ -137,6 +137,16 @@ The configuration file for DCC-EX Command Station
|
|||||||
//
|
//
|
||||||
//#define ENABLE_ETHERNET true
|
//#define ENABLE_ETHERNET true
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// MAX_NUM_TCP_CLIENTS: If you on STM32 Ethernet (and only there) want more than
|
||||||
|
// 9 (*) TCP clients, change this number to for example 20 here **AND** in
|
||||||
|
// STM32lwiopts.h and follow the instructions in STM32lwiopts.h
|
||||||
|
//
|
||||||
|
// (*) It would be 10 if there would not be a bug in LwIP by STM32duino.
|
||||||
|
//
|
||||||
|
//#define MAX_NUM_TCP_CLIENTS 20
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
21
defines.h
21
defines.h
@ -239,4 +239,25 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_STM32)
|
||||||
|
// The LwIP library for the STM32 wired ethernet has by default 10 TCP
|
||||||
|
// clients defined but because of a bug in the library #11 is not
|
||||||
|
// rejected but kicks out any old connection. By restricting our limit
|
||||||
|
// to 9 the #10 will be rejected by our code so that the number can
|
||||||
|
// never get to 11 which would kick an existing connection.
|
||||||
|
// If you want to change this value, do that in
|
||||||
|
// config.h AND in STM32lwipopts.h.
|
||||||
|
#ifndef MAX_NUM_TCP_CLIENTS
|
||||||
|
#define MAX_NUM_TCP_CLIENTS 9
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
// Espressif LWIP stack
|
||||||
|
#define MAX_NUM_TCP_CLIENTS 10
|
||||||
|
#else
|
||||||
|
// Wifi shields etc
|
||||||
|
#define MAX_NUM_TCP_CLIENTS 8
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif //DEFINES_H
|
||||||
|
@ -96,7 +96,6 @@ lib_deps =
|
|||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
MDNS_Generic
|
|
||||||
|
|
||||||
lib_ignore = WiFi101
|
lib_ignore = WiFi101
|
||||||
WiFi101_Generic
|
WiFi101_Generic
|
||||||
@ -115,7 +114,6 @@ framework = arduino
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
MDNS_Generic
|
|
||||||
SPI
|
SPI
|
||||||
lib_ignore = WiFi101
|
lib_ignore = WiFi101
|
||||||
WiFi101_Generic
|
WiFi101_Generic
|
||||||
@ -261,7 +259,7 @@ monitor_echo = yes
|
|||||||
; monitor_echo = yes
|
; monitor_echo = yes
|
||||||
; upload_protocol = stlink
|
; upload_protocol = stlink
|
||||||
|
|
||||||
; Experimental - Ethernet work still in progress
|
; Experimental - Ethernet beta test
|
||||||
;
|
;
|
||||||
[env:Nucleo-F429ZI]
|
[env:Nucleo-F429ZI]
|
||||||
platform = ststm32 @ 19.0.0
|
platform = ststm32 @ 19.0.0
|
||||||
@ -270,7 +268,6 @@ framework = arduino
|
|||||||
lib_deps = ${env.lib_deps}
|
lib_deps = ${env.lib_deps}
|
||||||
stm32duino/STM32Ethernet @ ^1.4.0
|
stm32duino/STM32Ethernet @ ^1.4.0
|
||||||
stm32duino/STM32duino LwIP @ ^2.1.3
|
stm32duino/STM32duino LwIP @ ^2.1.3
|
||||||
MDNS_Generic
|
|
||||||
lib_ignore = WiFi101
|
lib_ignore = WiFi101
|
||||||
WiFi101_Generic
|
WiFi101_Generic
|
||||||
WiFiEspAT
|
WiFiEspAT
|
||||||
@ -281,20 +278,15 @@ monitor_speed = 115200
|
|||||||
monitor_echo = yes
|
monitor_echo = yes
|
||||||
upload_protocol = stlink
|
upload_protocol = stlink
|
||||||
|
|
||||||
; Experimental - Ethernet work still in progress
|
; Experimental - Ethernet beta test
|
||||||
;
|
;
|
||||||
[env:Nucleo-F439ZI]
|
[env:Nucleo-F439ZI]
|
||||||
platform = ststm32 @ 19.0.0
|
platform = ststm32 @ 19.0.0
|
||||||
; board = nucleo_f439zi
|
board = nucleo_f439zi
|
||||||
; Temporarily treat it as an F429ZI (they are code compatible) until
|
|
||||||
; the PR to PlatformIO to update the F439ZI JSON file is available
|
|
||||||
; PMA - 28-Sep-2024
|
|
||||||
board = nucleo_f429zi
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps = ${env.lib_deps}
|
lib_deps = ${env.lib_deps}
|
||||||
stm32duino/STM32Ethernet @ ^1.4.0
|
stm32duino/STM32Ethernet @ ^1.4.0
|
||||||
stm32duino/STM32duino LwIP @ ^2.1.3
|
stm32duino/STM32duino LwIP @ ^2.1.3
|
||||||
MDNS_Generic
|
|
||||||
lib_ignore = WiFi101
|
lib_ignore = WiFi101
|
||||||
WiFi101_Generic
|
WiFi101_Generic
|
||||||
WiFiEspAT
|
WiFiEspAT
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
#define VERSION "5.5.13 F439"
|
#define VERSION "5.5.16"
|
||||||
// - Nucleo-F4 DC mode timer sync
|
// 5.5.16 - DOXYGEN comments in EXRAIL2MacroReset.h
|
||||||
// - <JL display startRow> Track power status
|
// 5.5.15 - Support for F429ZI/F329ZI
|
||||||
|
// - Own mDNS support for (wired) Ethernet
|
||||||
|
// 5.5.14 - DCC Non-blocking packet queue with priority
|
||||||
// 5.5.13 - Update STM32duino core to v19.0.0. for updated PeripheralPins.c in preparation for F429/439ZI Ethernet support
|
// 5.5.13 - Update STM32duino core to v19.0.0. for updated PeripheralPins.c in preparation for F429/439ZI Ethernet support
|
||||||
// 5.5.12 - Websocket support (wifi only)
|
// 5.5.12 - Websocket support (wifi only)
|
||||||
// 5.5.11 - (5.4.2) accessory command reverse
|
// 5.5.11 - (5.4.2) accessory command reverse
|
||||||
|
Loading…
x
Reference in New Issue
Block a user