Skip to content

Commit

Permalink
Merge pull request #20 from DCC-EX:fix-ssize_t
Browse files Browse the repository at this point in the history
Fix-ssize_t
  • Loading branch information
peteGSX authored Apr 26, 2024
2 parents 6547f57 + c13f7a2 commit d835817
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 4 deletions.
181 changes: 181 additions & 0 deletions examples/DCCEXProtocol_Serial/DCCEXProtocol_Serial.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// DCCEXProtocol_Serial
//
// Shows how to use DCCEXProtocol over serial using a Mega2560
// This uses a Mega2560 (or other AVR device) that has a second hardware serial port (Serial1)
// Tested with Arduino Mega2560
//
// Peter Cole (PeteGSX) 2024

#include <DCCEXProtocol.h>

// If we haven't got a custom config.h, use the example
#if __has_include("config.h")
#include "config.h"
#else
#warning config.h not found. Using defaults from config.example.h
#include "config.example.h"
#endif

// Setup serial macros if not already defined elsewhere
#ifndef CONSOLE
#define CONSOLE Serial // All output should go to this
#endif
#ifndef CLIENT
#define CLIENT Serial1 // All DCCEXProtocol commands/responses/broadcasts use this
#endif

// Declare functions to call from our delegate
void printRoster();
void printTurnouts();
void printRoutes();
void printTurntables();

// Setup the delegate class to process broadcasts/responses
class MyDelegate : public DCCEXProtocolDelegate {
public:
void receivedServerVersion(int major, int minor, int patch) {
CONSOLE.print("\n\nReceived version: ");
CONSOLE.print(major);
CONSOLE.print(".");
CONSOLE.print(minor);
CONSOLE.print(".");
CONSOLE.println(patch);
}

void receivedTrackPower(TrackPower state) {
CONSOLE.print("\n\nReceived Track Power: ");
CONSOLE.println(state);
CONSOLE.println("\n\n");
}

void receivedRosterList() {
CONSOLE.println("\n\nReceived Roster");
printRoster();
}
void receivedTurnoutList() {
CONSOLE.print("\n\nReceived Turnouts/Points list");
printTurnouts();
CONSOLE.println("\n\n");
}
void receivedRouteList() {
CONSOLE.print("\n\nReceived Routes List");
printRoutes();
CONSOLE.println("\n\n");
}
void receivedTurntableList() {
CONSOLE.print("\n\nReceived Turntables list");
printTurntables();
CONSOLE.println("\n\n");
}

void receivedScreenUpdate(int screen, int row, char *message) {
CONSOLE.println("\n\nReceived screen|row|message");
CONSOLE.print(screen);
CONSOLE.print("|");
CONSOLE.print(row);
CONSOLE.print("|");
CONSOLE.println(message);
}

};

// Global objects
DCCEXProtocol dccexProtocol;
MyDelegate myDelegate;

void printRoster() {
for (Loco *loco = dccexProtocol.roster->getFirst(); loco; loco = loco->getNext()) {
int id = loco->getAddress();
char *name = loco->getName();
CONSOLE.print(id);
CONSOLE.print(" ~");
CONSOLE.print(name);
CONSOLE.println("~");
for (int i = 0; i < 32; i++) {
char *fName = loco->getFunctionName(i);
if (fName != nullptr) {
CONSOLE.print("loadFunctionLabels() ");
CONSOLE.print(fName);
if (loco->isFunctionMomentary(i)) {
CONSOLE.print(" - Momentary");
}
CONSOLE.println();
}
}
}
CONSOLE.println("\n");
}

void printTurnouts() {
for (Turnout *turnout = dccexProtocol.turnouts->getFirst(); turnout; turnout = turnout->getNext()) {
int id = turnout->getId();
char *name = turnout->getName();
CONSOLE.print(id);
CONSOLE.print(" ~");
CONSOLE.print(name);
CONSOLE.println("~");
}
CONSOLE.println("\n");
}

void printRoutes() {
for (Route *route = dccexProtocol.routes->getFirst(); route; route = route->getNext()) {
int id = route->getId();
char *name = route->getName();
CONSOLE.print(id);
CONSOLE.print(" ~");
CONSOLE.print(name);
CONSOLE.println("~");
}
CONSOLE.println("\n");
}

void printTurntables() {
for (Turntable *turntable = dccexProtocol.turntables->getFirst(); turntable; turntable = turntable->getNext()) {
int id = turntable->getId();
char *name = turntable->getName();
CONSOLE.print(id);
CONSOLE.print(" ~");
CONSOLE.print(name);
CONSOLE.println("~");

int j = 0;
for (TurntableIndex *turntableIndex = turntable->getFirstIndex(); turntableIndex;
turntableIndex = turntableIndex->getNextIndex()) {
char *indexName = turntableIndex->getName();
CONSOLE.print(" index");
CONSOLE.print(j);
CONSOLE.print(" ~");
CONSOLE.print(indexName);
CONSOLE.println("~");
j++;
}
}
CONSOLE.println("\n");
}

void setup() {
CONSOLE.begin(115200);
CLIENT.begin(115200);
CONSOLE.println(F("DCCEXProtocol Serial Connection Demo"));
CONSOLE.println(F(""));

// Direct logs to CONSOLE
dccexProtocol.setLogStream(&CONSOLE);

// Set the delegate for broadcasts/responses
dccexProtocol.setDelegate(&myDelegate);

// Connect to the CS via CLIENT
dccexProtocol.connect(&CLIENT);
CONSOLE.println(F("DCC-EX connected"));

dccexProtocol.requestServerVersion();
}

void loop() {
// Parse incoming messages
dccexProtocol.check();

dccexProtocol.getLists(true, true, true, true);
}
5 changes: 5 additions & 0 deletions examples/DCCEXProtocol_Serial/config.example.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Example config file for the DCCEXProtocol_Serial example.
// Copy to config.h if you need to customise these for your setup.

#define CONSOLE Serial
#define CLIENT Serial1
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=DCCEXProtocol
version=0.0.12
version=0.0.13
author=Peter Cole, Peter Akers <[email protected]>
maintainer=Peter Cole, Peter Akers <[email protected]>
sentence=DCC-EX Native Protocol implementation
Expand Down
18 changes: 16 additions & 2 deletions src/DCCEXProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ void DCCEXProtocol::check() {
_cmdBuffer[_bufflen] = r;
_bufflen++;
_cmdBuffer[_bufflen] = 0;
} else {
// Clear buffer if full
_cmdBuffer[0] = 0;
_bufflen = 0;
}

if (r == '>') {
Expand Down Expand Up @@ -523,6 +527,12 @@ void DCCEXProtocol::_sendCommand() {
void DCCEXProtocol::_processCommand() {
if (_delegate) {
switch (DCCEXInbound::getOpcode()) {
case '@': // Screen update
if (DCCEXInbound::isTextParameter(2) && DCCEXInbound::getParameterCount() == 3) {
_processScreenUpdate();
}
break;

case 'i': // iDCC-EX server info
if (DCCEXInbound::isTextParameter(0)) {
_processServerDescription();
Expand Down Expand Up @@ -627,7 +637,7 @@ void DCCEXProtocol::_processCommand() {

void DCCEXProtocol::_processServerDescription() { //<iDCCEX version / microprocessorType / MotorControllerType /
// buildNumber>
// console->println(F("processServerDescription()"));
// _console->println(F("processServerDescription()"));
if (_delegate) {
char *description{DCCEXInbound::getTextParameter(0) + 7};
int *version = _version;
Expand Down Expand Up @@ -665,13 +675,17 @@ void DCCEXProtocol::_processServerDescription() { //<iDCCEX version / microproce
_receivedVersion = true;
_delegate->receivedServerVersion(_version[0], _version[1], _version[2]);
}
// console->println(F("processServerDescription(): end"));
// _console->println(F("processServerDescription(): end"));
}

void DCCEXProtocol::_processMessage() { //<m "message">
_delegate->receivedMessage(DCCEXInbound::getTextParameter(0));
}

void DCCEXProtocol::_processScreenUpdate() { //<@ screen row "message">
_delegate->receivedScreenUpdate(DCCEXInbound::getNumber(0), DCCEXInbound::getNumber(1), DCCEXInbound::getTextParameter(2));
}

// Consist/loco methods

void DCCEXProtocol::_processLocoBroadcast() { //<l cab reg speedByte functMap>
Expand Down
13 changes: 12 additions & 1 deletion src/DCCEXProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
/*
Version information:
0.0.13 - Fix bug to allow compilation on AVR platforms, change ssize_t to int
- Add serial connectivity example
- Add support for SCREEN updates to delegate
- Enhance buffer management to clear command buffer if full
0.0.12 - Improved memory management
0.0.11 - support for individual track power receivedIndividualTrackPower(TrackPower state, int track)
- improved logic for overall track power
Expand Down Expand Up @@ -176,6 +180,12 @@ class DCCEXProtocolDelegate {
/// @brief Notify when a loco address is read from the programming track
/// @param address DCC address read from the programming track, or -1 for a failure to read
virtual void receivedReadLoco(int address) {}

/// @brief Notify when a screen update is received
/// @param screen Screen number
/// @param row Row number
/// @param message Message to display on the screen/row
virtual void receivedScreenUpdate(int screen, int row, char *message) {}
};

/// @brief Main class for the DCCEXProtocol library
Expand Down Expand Up @@ -445,6 +455,7 @@ class DCCEXProtocol {
void _processCommand();
void _processServerDescription();
void _processMessage();
void _processScreenUpdate();

// Consist/loco methods
void _processLocoBroadcast();
Expand Down Expand Up @@ -506,7 +517,7 @@ class DCCEXProtocol {
DCCEXProtocolDelegate *_delegate = nullptr; // Pointer to the delegate for notifications
unsigned long _lastServerResponseTime; // Records the timestamp of the last server response
char _inputBuffer[512]; // Char array for input buffer
ssize_t _nextChar; // where the next character to be read goes in the buffer
int _nextChar; // where the next character to be read goes in the buffer
bool _receivedVersion = false; // Flag that server version has been received
bool _receivedLists = false; // Flag if all requested lists have been received
bool _rosterRequested = false; // Flag that roster has been requested
Expand Down
12 changes: 12 additions & 0 deletions tests/DCCEXProtocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "DCCEXProtocolTest.hpp"

TEST_F(DCCEXProtocolTest, clearBufferWhenFull) {
// Fill buffer with garbage
for (auto i{0uz}; i < 500uz; ++i)
_stream.write(static_cast<uint8_t>('A' + (random() % 26)));

// Proceed with normal message
_stream << R"(<m "Hello World">)";
EXPECT_CALL(_delegate, receivedMessage(StrEq("Hello World"))).Times(Exactly(1));
_dccexProtocol.check();
}

0 comments on commit d835817

Please sign in to comment.