Source Code for project NCE_Bus_LCD that will display messages from the NCE_Bus (RS485) on an LCD.
Summary: The project is basically designed to display messages received that occur on the NCE Bus. It will inherit the NCE_Bus_Probe class.
Library <NCE_Bus_LCD.h>
Implementation file requires <NCE_Bus_Probe.h>, <LiquidCrystal.h>
Constructor NCE_Bus_LCD lcd(5) Examples uses address 5 or ping address 85.
Public Methods: lcd.begin( );
go_to_myaddress( ); //will loop until address 0x85
To us the NCE_Bus_Probe Library to obtain the NCE messages this project will require a method void NCE_Message(9 uunit8_t parameters) and a statement setMessHandler(&NCE_Message); as part of the setup() method.
Required Hardware: Project was tested with Arduino UNO and LCD keypad and display. (Keypad not required for this project).
Photo shows the project hardware. (Photo from an earlier project). At the bottom is the Arduino Uno, in the middle a prototyping board with a RS285 module and on top the LCD Keypad.
Client/Application/Test code (*ino).
At this point the *.ino code includes the necessary libraries and creates two objects bus(5) and lcd(8,9,4,5,6,7). The object bus(5) is a user choice with several values of the argument (The project address on the NCE bus) possible whereas the lcd parameters are the wiring determined by the manufacturer.
Implementation Code (*.cpp)
At this point all the implementation file needs to do is to include the header file.
Header File: NCE_Bus_LCD.h
The critical aspects of the header file at this point are to include the necessary libraries. In this case it is NCE_Bus_Probe which was the subject of the previous project and the standard SoftwareSerial library for the RS485 (NCE Bus) interface.
A class/library NCE_Bus_LCD that inherits the class/library NCE_Bus_Probe will be created. As illustrated this will have the format
class NCE_Bus_LCD : NCE_Bus_Probe {};.
The constructor will be declared as NCE_Bus_LCD(uint8_t my address); where my_address will be the desired address (+0x80) of the object/instance of NCE_Bus_LCD.
In the implementation file my address is passed to the inherited class. That is
NCE_Bus_LCD::NCE_Bus_LCD(uint_8 myaddress): NCE_Bus_Probe(my_address) { };
In the application file one instance of the class NCE_Bus_LCD is created. ie NCE_Bus_LCD lcd(5); with the instance assigned the address 5.
Add to header file: (*.h)
#include <NCE_Bus_Probe.h>
class NCE_Bus_LCD : NCE_Bus_Probe {
public: NCE_Bus_LCD(uint8_t my address);
void begin( );
};
Implementation File (*.cpp)
NCE_Bus_LCD::NCE_Bus_LCD(uint_8 myaddress):
NCE_Bus_Probe(my_address) { };
void NCE_Bus_LCD::begin( ){
NCE_Bus_Probe.begin( );
}
Client or application file (*.ino)
#include "NCE_Bus_LCD.h"
NCE_Bus_LCD lcd(5);
void setup( ){
lcd.begin( );
}
The above code creates a class NCE_Bus_LCD which inherits the NCE_Bus_Probe class. The NCE_Bus_Probe class expects the address to which the object is attached. Address 5 in this example to which NCE_Bus_Probe has added 0x80 to obtain the ping address of interest (0x85).
Also included is the begin( ) method. At this point the NCE_Bus_LCD will call the begin method for the NCE_Bus_Probe class. Since there are multiple definitions of begin the class resolution identifier :: is absolutely necessary
With the TRACE 1 option in the inherited class NCE_Bus_Probe active the following code may be observed as a result of lcd.begin( ) calling NCE_Bus_Probe::begin( ) . It is the job of this project to extract the messages captured by the NCE_Bus_Probe project and display these on screen.
In detail the NCE_Bus_LCD::begin( ) calls NCE_Bus_Probe::begin( ) that will initialize the RS485 interface. It also polls through the pin address from the NCE Twin waiting for the address 0x85. At that address NCE_Bus_Probe sends a message 7E/7F advising of its presence. The NCE Twin sends out the message C0,E0,CE,C3,C5,E0,E0,E0,E0 which after some adjusting becomes "NCE ". It is this message that this project/class must display on the LCD. This trace is the starting point for this project. The C0 will indicate the display is to start at the top left.
The above output is the complete reset routine for NCE_Bus_Probe class. The trace will stop after the setup( ) or begin() methods are complete
For test purposes the object NCE_Bus_LCD must continue to output messages. For this NCE_Bus_Probe class has two public methods that may be used.
wait_for_myaddress( ); //polls until address 0x85
I_am_here( ); //tells NCE Twin I'm stll here
These two methods are called by a new function go_to_my_address(). A summary of the new code is given below:
In the *.ino code add the following method to the loop() function
void loop( ) {
lcd.go_to_myaddress( ); //will loop until address 0x85
}
-------------------------------------
In the implementation *.cpp file add the method add the implementation for go_to_myaddress.
void NCE_Bus_LCD::go_to_myaddress( ){
wait_for_myaddress( ); //polls until address 0x85
I_am_here( ); //tells NCE Twin I'm stll here
}
The new method calls two functions from the NCE_Bus_Probe class.
Since they have very unique names there is no need for the NCE_Bus_Probe:: qualifier.
--------------------------------------------
go_to_myaddress( ) must be added to the class in the header *.h file
class NCE_Bus_LCD : NCE_Bus_Probe{
public : ..............
void go_to_myaddress( );
};
With this additional code the trace of the NCE bus activity is continuous. A new partial trace following reset is shown below..
Following reset embedded in the above trace as part of the NCE Twin response to finding a new device address will be three messages:
LOC: 000
FWD: 000 -------
The trace has also captured a broadcast message that will be printed in the top right half. With the 4th message the full LCD display becomes:
LOC: 000 10:20 AM
FWD: 000 -------
Until "action" requests are sent to the NCE Twin there will be no change in the loco number and speed. However, the clock data will be frequently updated. This data will be used as the test code for this class.
In this project all the LCD operations will be place in the *.cpp file. That is the end user will know nothing of the LCD operation - it will just happen. To the implementation file add the following:
With the LCD the critical operation is to have the information transferred to a function/method to write the message to the LCD. When a new LCD message is available the class NCE_Bus_Probe has made provision for calling the code that handles the LCD .
As seen in the previous traces the longest message is 9 bytes. It is therefore decided that a method with 9 arguments is required.
The NCE_Bus_Probe has defined a type that has 9 arguments. The new code, NCE_Bus_LCD must develop a method that has 9 arguments.
The new code NCE_Bus_LCD must inform the inherited class of the method of interest. To allow this the NCE_Bus_Probe has a public method setMessageHandler( ) that the new code calls as part of its initialisation.
The inherited class NCE_Bus_Probe includes code that when a new message is received the message function in NCE_Bus_LCD is called.
The whole operation is illustrated below. The right-hand boxes is existing code - in this project the task is to write the code for the left-hand boxes.
The new code to allow messages to be passed is shown. At this point via the #if TRACE the dats is displayed on the serial monitor. A later section will display these on the LCD.
The previous section the method NCE_Message has collected the 9 bytes of data. The next task is to display the messages on the LCD messages.
For the NCE clock the controller has broadcast the message:
C1,E0,F1,F0,FA,F2,F0,C1,CD
The leading byte specifies where the message is to go and the remaining 8 bytes the actual message. The class NCE_Bus_Probe has converted the 8 data bytes to ASCII making the message:
C1,20,30,3A,32,30,41,4D
It is this data message that are the 9 parameters transferred to the method NCE_Message(9).
The first task will be to use the first byte to place the LCD cursor in the desired position. The LCD layout will be:
The byte C1 will place the cursor at column 8 of row 0. The code will be if (buffer[0] = 0xC1) setCursor(8,0); Each character will then be written after the cursor. The full code, including debugging becomes:
The above code worked as expected. At startup the first request from myAddress is to update the display. Th e NCE Bus response is the messages
LOCO:0000
FWD: 0000 -------
This display will be updated with a broadcast message of the NCE time which is displayed on the top right half of the LCD. This messages will be continually updated.
Since at this point there are no NCE requests such as set accessory that require additional messages/commands to the LCD these capabilities cannot be tested. However sample code that will be investigated/verified in later projects is given below.
The project has really been tested as it went along using an NCE Twin. It was also setup on a different system at the author's club. No problems were found except that there was also a ontroller at address 5 so the start response from the NCE was a little different. Since the other controlled had specified loco 2025 when requested by this controller the display was LOCO:2025 rather than LOCO:0000 . Further the device at address 5 was a more complex controller there was 4 lines of startup messages rather than two.
LOCO:2025
FWD: 000 -------
PRESS <EXPN> FOR
A NORMAL DISPLAY
Note the display returned to two lines with loco 0000 when the address was set to one not used at the club.
There are a number of additional projects that will both test but also extend the LCD capabilities
NCE_Bus_Macro that will fire existing Macros
NCE_Bus_Points that will control individual points (accessories)
NCE_Bus_Loco that will control a loco.
For this trace there is already a terminal at address 5 so when we join the bus and send the message 7E-7F to request a display update the NCE Controller thinks it knows all about us and does not request the type of terminal. It just sends out a number of messages for example 0xCE to disable cursor.
It then sends the highlighted messages which make sense for the original terminal but not our terminal.
The final message highlighted is a broadcast message going to all terminals giving a clock update.