Skip to content

Commit

Permalink
add include file
Browse files Browse the repository at this point in the history
  • Loading branch information
flash62au committed Dec 7, 2023
1 parent 80f8faa commit bef0990
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 22 deletions.
1 change: 1 addition & 0 deletions docs/documentation.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.. include:: /include/include.rst
:orphan:

Documentation
Expand Down
25 changes: 21 additions & 4 deletions docs/examples.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. include:: /include/include.rst

Examples
========

Expand All @@ -23,17 +25,17 @@ To configure WiFi for your settings in all examples, you will need to copy the p
DCCEXProtocol_Basic
-------------------

This example demonstrates the basics of creating a WiFi connection to your EX-CommandStation using the library, and monitoring for broadcasts and command responses.
This example demonstrates the basics of creating a WiFi connection to your |EX-CS| using the library, and monitoring for broadcasts and command responses.

DCCEXProtocol_Delegate
----------------------

This example builds on the basic example and, in addition, demonstrates how to implement a custom DCCEXProtocolDelegate class to respond to broadcasts and command responses received from EX-CommandStation.
This example builds on the basic example and, in addition, demonstrates how to implement a custom DCCEXProtocolDelegate class to respond to broadcasts and command responses received from |EX-CS|.

DCCEXProtocol_Roster_etc
------------------------

This example demonstrates how to retrieve the object types from EX-CommandStation, and further demonstrates how to use the delegate to display these object lists when received.
This example demonstrates how to retrieve the object types from |EX-CS|, and further demonstrates how to use the delegate to display these object lists when received.

DCCEXProtocol_Loco_Control
--------------------------
Expand All @@ -57,8 +59,23 @@ This example demonstrates how client throttle software may be written to control

What can't be demonstrated in this example is the control of speed and direction, which would typically be accomplished with the use of rotary encoders or similar.

Note when setting speed and direction, these should be sent to the EX-CommandStation via the DCCEXProtocol library, and any local references to these should be set based on the response received, not directly by the input method in use.
Note when setting speed and direction, these should be sent to the |EX-CS| via the |EX-PL|, and any local references to these should be set based on the response received, not directly by the input method in use.

For example, when setting the speed based on the position of a rotary encoder, send that value via the protocol's `setThrottle()` method, but do not display that speed directly. Instead, utlise the delegate's `receivedLocoUpdate()` method to update the displayed speed.

This ensures that the user of the throttle sees the accurate results of what the throttle is doing, and provides validation that the EX-CommandStation is responding to the user input.

----

Additional Examples
-------------------

The following examples are not strictly related to the |EX-PL|, but hopefully will be useful for anyone developing a throttle to use with any |EX-CS|.

DCCEXProtocol_mDNS
~~~~~~~~~~~~~~~~~~

This example demonstrates how client throttle software may be written to find all the |EX-CS| and WiThrottle servers that are advertising via mDNS.

Note that |DCC-EX| |EX-CS| only advertise as ``wiThrottle`` servers, but will use and respond with either the **WiThrottle protocol** or the |EX-PL| depending the type of command it first receives from the client (throttle).

44 changes: 44 additions & 0 deletions docs/include/include.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.. meta::
:keywords: DCC-EX DCC DCC++ EX DCC++EX
..
.. |DCC-EX| raw:: html

<span class="dccex-suffix">DCC-</span><span class="dccex-prefix">EX</span>
..
.. |EX-CS| raw:: html

<span class="ex-prefix">EX</span><span class="ex-suffix">&#8209;CommandStation</span>
..
.. |EX-PL| raw:: html

<span class="ex-prefix">DCCEX</span><span class="ex-suffix">Protocol library</span>
..
.. |EX-NP| raw:: html

<span class="dccex-suffix">DCC-</span><span class="dccex-prefix">EX</span>
<span class="ex-suffix"> native protocol</span>
..
.. |br| raw:: html

<br />
..
.. role:: dcc-ex-red
.. role:: dcc-ex-red-bold
.. role:: dcc-ex-red-bold-italic
.. role:: dcc-ex-code
..
.. role:: dcc-ex-text-size-80pct
.. role:: dcc-ex-text-size-60pct
.. role:: dcc-ex-text-size-200pct
..
.. |_| unicode:: 0xA0
:trim:
..
.. |force-break| raw:: html

<div style="display:block; box-sizing: border-box; clear: both;"> </div>
..
.. |image-note| raw:: html

<span style="font-weight: bold; font-style: italic; color: #767676;" >Note that you can click on any of the images to make them larger.</span>
..
4 changes: 3 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. include:: /include/include.rst

Documentation for the DCC-EX Native command library - DCCEXProtocol
===================================================================

Expand All @@ -15,7 +17,7 @@ Documentation for the DCC-EX Native command library - DCCEXProtocol
DCC-EX Native command protocol library
--------------------------------------

This library implements the DCC-EX Native command protocol (as used in the **DCC-EX** **EX-CommandStation** ONLY), allowing a device to connect to the server and act as a client (such as a hardware based throttle).
This library implements the **DCC-EX** Native command protocol (as used in the |DCC-EX| |EX-CS| ONLY), allowing a device to connect to the server and act as a client (such as a hardware based throttle).

The implementation of this library is tested on ESP32 based devices running the Arduino framework. There's nothing in here that's specific to the ESP32, and little of Arduino that couldn't be replaced as needed.

Expand Down
6 changes: 0 additions & 6 deletions docs/library.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
Library
=======

.. sidebar::

.. contents:: On this page
:depth: 2
:local:

.. doxygenindex::
:project: DCCEXProtocol
10 changes: 6 additions & 4 deletions docs/overview.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. include:: /include/include.rst

Library Design Principles
=========================

Expand All @@ -11,12 +13,12 @@ First of all, this library implements the DCC-EX Native protocol in a non-blocki

Then, you call the ```check()``` method as often as you can (ideally, once per invocation of the ```loop()``` method) and the library will manage the I/O stream, reading in/parsing commands and calling methods on the delegate as information is available.

These patterns (Dependency Injection and Delegation) allow you to keep the different parts of your sketch from becoming too intertwined with each other. Nothing in the code that manages the pushbuttons or speed knobs needs to have any detailed knowledge of the DCC-EX native protocol.
These patterns (Dependency Injection and Delegation) allow you to keep the different parts of your sketch from becoming too intertwined with each other. Nothing in the code that manages the pushbuttons or speed knobs needs to have any detailed knowledge of the |EX-NP|.

DCCEXProtocol Class
-------------------

The DCCEXProtocol class manages all relevant objects advertised by a DCC-EX EX-CommandStation and exposes simple methods to control these objects from the client software.
The `DCCEXProtocol` class manages all relevant objects advertised by a |DCC-EX| |EX-CS| and exposes simple methods to control these objects from the client software.

These objects include:

Expand All @@ -30,7 +32,7 @@ This means the client software does not need to explicitly manage the state of t
DCCEXProtocolDelegate Class
---------------------------

The DCCEXProtocolDelegate class enables the client software to respond to various events generated by a DCC-EX EX-CommandStation as either broadcasts or responses to commands.
The `DCCEXProtocolDelegate` class enables the client software to respond to various events generated by a |DCC-EX| |EX-CS| as either broadcasts or responses to commands.

The events able to be managed via this class are over and above those managed by the DCCEXProtocol class and are entirely customisable by the client software to provide dynamic user experience updates such as displaying status changes to objects as they are broadcast from the DCC-EX EX-CommandStation.
The events able to be managed via this class are over and above those managed by the `DCCEXProtocol` class and are entirely customisable by the client software to provide dynamic user experience updates such as displaying status changes to objects as they are broadcast from the |DCC-EX| |EX-CS|.

2 changes: 2 additions & 0 deletions docs/site-index.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. include:: /include/include.rst

Indices and tables
==================

Expand Down
16 changes: 9 additions & 7 deletions docs/usage.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. include:: /include/include.rst

Usage
=====

Expand All @@ -8,16 +10,16 @@ Usage
:local:


Whilst this library extrapolates the need for understanding the specific DCC-EX native commands from a throttle developer, it is highly recommended to familiarise yourself with the concepts outlined in the `<https://dcc-ex.com/throttles/tech-reference.html>`_.
Whilst this library extrapolates the need for understanding the specific |DCC-EX| native commands from a throttle developer, it is highly recommended to familiarise yourself with the concepts outlined in the `<https://dcc-ex.com/throttles/tech-reference.html>`_.

Setup
-----

Once the DCCEXProtocol object is instantiated, a connection must be made to the EX-CommandStation using the `connect(&stream)` method and providing a suitable Arduino Stream, such as a WiFi client or serial connection.
Once the `DCCEXProtocol` object is instantiated, a connection must be made to the |EX-CS| using the `connect(&stream)` method and providing a suitable Arduino Stream, such as a WiFi client or serial connection.

It is also recommended to enable logging to an Arduino Stream using the `setLogStream(&stream)` method.

As covered in the design principles above, you must include the `check()` method as often as possible to receive command responses and broadcasts and have these processed by the library and any event handlers defined in your custom DCCEXProtocolDelegate class.
As covered in the design principles above, you must include the `check()` method as often as possible to receive command responses and broadcasts and have these processed by the library and any event handlers defined in your custom `DCCEXProtocolDelegate` class.

Refer to the :doc:`examples` to see how this may be implemented.

Expand All @@ -28,9 +30,9 @@ It is up to the client software utilising this library to manage control and inp

For example, multiple rotary encoders may be used to simultaneously control multiple locomotives or consists.

There is, however, no need to instantiate more than one DCCEXProtocol or DCCEXProtocolDelegate object providing the client software is written appropriately, and we recommend creating a custom class that can take the DCCEXProtocol object as a parameter to enable this.
There is, however, no need to instantiate more than one `DCCEXProtocol` or `DCCEXProtocolDelegate` object providing the client software is written appropriately, and we recommend creating a custom class that can take the `DCCEXProtocol` object as a parameter to enable this.

See the DCCEXProtocol_Multi_Throttle_Control example for an idea of how this may be implemented.
See the `DCCEXProtocol_Multi_Throttle_Control` example for an idea of how this may be implemented.

A further note is that controls and inputs should be passed to the protocol only, and should not update local references to object attributes (such as speed and direction), but rather that the responses to these inputs as received by the protocol and delegate events should be used to update local references.

Expand All @@ -39,7 +41,7 @@ In this manner, the user of the throttle/client software will see the true resul
Retrieving and referring to object lists
----------------------------------------

To retrieve the various objects lists from EX-CommandStation, use the `getLists(bool rosterRequired, bool turnoutListRequired, bool routeListRequired, bool turntableListRequired)` method within your `loop()` function to ensure these are retrieved successfully.
To retrieve the various objects lists from |EX-CS|, use the `getLists(bool rosterRequired, bool turnoutListRequired, bool routeListRequired, bool turntableListRequired)` method within your `loop()` function to ensure these are retrieved successfully.

All objects are contained within linked lists and can be access via for loops:

Expand All @@ -64,4 +66,4 @@ All objects are contained within linked lists and can be access via for loops:
}
}
Refer to the DCCEXProtocol_Roster_etc example for an idea of how this may be implemented.
Refer to the `DCCEXProtocol_Roster_etc` example for an idea of how this may be implemented.
137 changes: 137 additions & 0 deletions examples/DCCEXProtocol_mDNS/DCCEXProtocol_mDNS.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// WiThrottleProtocol library: mDNS example
//
// Shows how to retrieve the list of discovered servers
// Tested with ESP32-WROOM board
//
// Peter Akers (Flash62au), Peter Cole (PeteGSX) and Chris Harlow (UKBloke), 2023


#include <ESPmDNS.h>
#include <WiFi.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

void printMdnsServers();


#define MAX_SERVERS 20

IPAddress foundWitServersIPs[MAX_SERVERS];
int foundWitServersPorts[MAX_SERVERS];
String foundWitServersNames[MAX_SERVERS];
int foundWitServersCount;

unsigned long lastTime = 0;

bool mdnsListenerStarted = false;

// Global objects
WiFiClient client;

bool setupMdnsListner() {
// setup the bonjour listener

if (!MDNS.begin("mDNSTest")) {
Serial.println("Error setting up MDNS responder!");
return false;
} else {
Serial.println("MDNS responder started");
return true;
}
}

void printMdnsServers() {
Serial.println("");

double startTime = millis();
double nowTime = startTime;

const char * service = "withrottle";
const char * proto= "tcp";

Serial.print("Browsing for service "); Serial.print(service); Serial.print("."); Serial.print(proto); Serial.print(".local. on "); Serial.print(ssid); Serial.println(" ... ");

int noOfWitServices = 0;
while ( (noOfWitServices == 0) && ((nowTime-startTime) <= 5000) ) { // try for 5 seconds
noOfWitServices = MDNS.queryService(service, proto);
if (noOfWitServices == 0 ) {
delay(500);
Serial.print(".");
}
nowTime = millis();
}
Serial.println("");

if (noOfWitServices > 0) {
for (int i = 0; ((i < noOfWitServices) && (i<MAX_SERVERS)); ++i) {
foundWitServersNames[i] = MDNS.hostname(i);
foundWitServersIPs[i] = MDNS.IP(i);
foundWitServersPorts[i] = MDNS.port(i);
if (MDNS.hasTxt(i,"jmri")) {
String node = MDNS.txt(i,"node");
node.toLowerCase();
if (foundWitServersNames[i].equals(node)) {
foundWitServersNames[i] = "JMRI (v" + MDNS.txt(i,"jmri") + ")";
}
}
}
}
foundWitServersCount = noOfWitServices;

// EX-CommnadStations in Access Point mode cannot advertise via mDNS,
// so we have to guess it based on the SSID name
String ssidString = String(ssid);
if (ssidString == "DCCEX_") {
foundWitServersIPs[foundWitServersCount].fromString("192.168.4.1");
foundWitServersPorts[foundWitServersCount] = 2560;
foundWitServersNames[foundWitServersCount] = "'Guessed' EX-CS WiT server";
foundWitServersCount++;
}

for (int i = 0; ((i < foundWitServersCount) && (i<MAX_SERVERS)); ++i) {
Serial.print("Name: "); Serial.print(foundWitServersNames[i]);
Serial.print(" IP: "); Serial.print(foundWitServersIPs[i]);
Serial.print(" : "); Serial.print(foundWitServersPorts[i]);
Serial.println();
}
}

void setup() {

Serial.begin(115200);
Serial.println("DCCEXProtocol mDNS example");
Serial.println();

// Connect to WiFi network
Serial.println("Connecting to WiFi..");
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) delay(1000);
Serial.print("Connected with IP: "); Serial.println(WiFi.localIP());
Serial.println();

// setup the mDNS listner
mdnsListenerStarted = setupMdnsListner();

// browse for services
if (mdnsListenerStarted) {
printMdnsServers();
}

}

void loop() {

delay(20000);
// Redo every 20 seconds - For demonstration purposes only!
// Normally this will only be required once, immediately after you connect to the ssid.
if (mdnsListenerStarted) {
printMdnsServers();
}

}
7 changes: 7 additions & 0 deletions examples/DCCEXProtocol_mDNS/config.example.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Example user configuration to use with the DCCEXProtocol examples
// Copy this file and rename to config.h
// Replace the variables below with the appropriate values for your environment
const char* ssid = "YOUR_SSID_HERE"; // WiFi SSID name here
const char* password = "YOUR_PASSWORD_HERE"; // WiFi password here
IPAddress serverAddress(192,168,4,1); // IP address of your EX-CommandStation
int serverPort = 2560; // Network port of your EX-CommandStation

0 comments on commit bef0990

Please sign in to comment.