Timing display input

The code below is taken directly from my code in the TX part of the project:

void serialEvent() { // function declaration
while (Serial.available()) { // is there data to be processed?
char inChar = (char)Serial.read(); // get the new byte
if (inChar != ‘[‘ && inChar != ‘]’) { // filter out the [ and ] brackets
inputString += inChar; // add it to the inputString:
}
// if the incoming character is ‘]’ (ie end of message from RC-Timing),
// set a flag so the main loop can do something about it:
if (inChar == ‘]’) {
stringComplete = true;
}
}
}

I wanted to break this out and explain it seperately as it differs slightly from the Serial Event tutorial. Firstly of oucrse, we have to check that there is data from the serial port to be collected (serial.available). Having confirmed this we read in a single character. Since we know the information from RC-Timing is of a specific format ie:

[roundno:raceno:minutes:seconds:car1,carN,:laps1,lapsN,:improver1,improverN,:]

Then we can discard the ‘[‘ and ‘]’ characters at the beginning and end of the string since they serve no purpose to us. Hence we have an IF statement that takes no action unless the character being read is NOT [ or ]. The reason I use && (logical AND) is because if I used || (logical OR) then the IF condition would be true if the character was ‘[‘ (since it is not ‘]’) and also true for the other character ‘]’ (sincice it is not ‘[‘). Bit confusing but it’s correct and it works.

So we filter out the invalid [ and ] characters and read the rest of the string into the Arduino ‘String’ variable inputString

When eventually we come across the ‘]’ character (indicating we have the entire message from RC-Timing), we set the flag variable stringComplete to boolean value TRUE. When the Arduino ‘Loop’ function (which ‘loops’ continuously hence the name) encounters the TRUE stringComplete condition, it then acts on the inputString variable to process the information contained therein.

Transmitter – Prototype Code Explanation

This is an explanation of the code I developed for the transmitter part of the project. Although it’s already heavily commented, I hope I can explain as clearly and fully as possible what I am trying to achieve and the reasons for the choices and decisions I have made in the code.

/*
RC Information Board Project

This project reads in RC car race information output from RC-Timing by Dale Burr
(race information such as heat number, round number, time elapsed etc) and also
gantry/start lights information.

Race information is read in via the normal Arduino serial port (USB on an Uno
or pins 0 and 1 on an Uno or Atmega 328P-Pu IC) whilst gantry/start lights
information is read in via 2 Arduino pins after having been converted from RS232
by a MAX232 or similar.

The Arduino then processes the information and transforms it into 7 seperate
integer variables which are then joined using the sprintf function and
transmitted using the VirtualWire library from a 433Mhz MX-FS-03V transmitter
connected to the Arduino

For more information on this project, visit:

https://rcinfoboard.wordpress.com/

Created by Joe Keaveney
February 2016
*/

#include // Library required for LCD 16×2 display for testing
#include // Library required for data transmission over 433Mhz

LiquidCrystal lcd(12, 9, 5, 4, 3, 2); // tell Arduino the LCD pin configuration

char tx_array[20]; // The array of variables that is transmitted
char inputChar[50]; // The string (char array) that we search through for data
char *i;
char *rnum; // round number character
char *hnum; // heat number character
char *mnum; // etc
char *snum; // etc
int lstate = 0; // start/gantry lights state integer
int r_int; // round number integer
int h_int; // etc
int m_int; // etc
int s_int; // etc
int timeone; //integer that represents the first number of a 4-digit display
int timetwo; // second number
int timethree; // third number
int timefour; // fourth number

const int dtrPin = 6; // DTR input pin from MAX232/USB
const int rtsPin = 7; // RTS input pin from MAX232/USB
int dtrState = 0; // stores the state of DTR pin when read
int rtsState = 0; // stores the state of RTS pin when read

String inputString = “”; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete

void setup() { // setup Arduino in it’s initial state & start services
pinMode(dtrPin, INPUT); // set input pins for reading RS232 pin states
pinMode(rtsPin, INPUT);
lcd.begin(16, 2); // initialize LCD display and tell Arduino how big it is
Serial.begin(9600); // initialize serial so we can read in race information
inputString.reserve(200); // reserve 200 bytes for the inputString:
vw_set_ptt_inverted(true); // setup 433Mhz hardware – need this for MX-FS-03V
vw_set_tx_pin(8); // set Tx pin number
vw_setup(2000);// speed of data transfer Kbps – 2000 is fine & stable
}

void loop() {

dtrState = digitalRead(dtrPin); // read the state of the DTR and RTS inputs
rtsState = digitalRead(rtsPin);
//dtrState = !dtrState ; // Inverts the inputs, necessary due to TTL conversion
//rtsState = !rtsState ;

if (dtrState == LOW && rtsState == LOW) {
// DTR HIGH and RTS HIGH mean Green Light On
lstate = 2;
} else if (dtrState == LOW && rtsState == HIGH) {
// DTR HIGH and RTS LOW means Red Light On
lstate = 1;
}
else {
// Any other combination means both lights Off
lstate = 0;
}

if (stringComplete) {
inputString.toCharArray(inputChar, 50); // convert the input string to char
rnum = strtok_r(inputChar,”:”,&i); // start breaking down the input info
hnum = strtok_r(NULL,”:”,&i); // from RC-Timing into the data we need
mnum = strtok_r(NULL,”:”,&i);
snum = strtok_r(NULL,”:”,&i);
r_int = atoi(rnum); // convert the info into integers
h_int = atoi(hnum);
m_int = atoi(mnum);
s_int = atoi(snum);
if (m_int <= 9) { // break the time info into two integers for use on a
timeone = 0; // 4-digit display
timetwo = m_int;
// lcd.print(timeone);
// lcd.print(timetwo);
}
else {
timeone = m_int / 10;
timetwo = m_int % 10;
// lcd.print(timeone);
// lcd.print(timetwo);
}
// lcd.print(“:”);
if (s_int <= 9) {
timethree = 0;
timefour = s_int;
// lcd.print(timethree);
// lcd.print(timefour);
}
else {
timethree = s_int / 10;
timefour = s_int % 10;
// lcd.print(timethree);
// lcd.print(timefour);
}

lcd.clear(); //this section output the data to an LCD display
lcd.print(“R:”); //in the finished device, this will be removed
lcd.print(r_int);
lcd.print(” H:”);
lcd.print(h_int);
lcd.setCursor(4,1);
lcd.print(“TIME:”);
lcd.print(timeone);
lcd.print(timetwo);
lcd.print(“:”);
lcd.print(timethree);
lcd.print(timefour);

inputString = “”; // clear the string and flag ready to listen again
stringComplete = false;
}
// the sprintf function consolidates all the integer variables together
// and then vw_send and vw_wait send the info out on the 433Mhz Tx
sprintf(tx_array, “%d,%d,%d,%d,%d,%d,%d.”,lstate,r_int,h_int,timeone,timetwo,timethree,timefour);
vw_send((uint8_t*)tx_array, strlen(tx_array));
vw_wait_tx();
}

/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX (USB or Arduino pins 0 and 1).
This routine is run between each
time loop() runs.
*/
void serialEvent() { // function declaration
while (Serial.available()) { // is there data to be processed?
char inChar = (char)Serial.read(); // get the new byte
if (inChar != ‘[‘ && inChar != ‘]’) { // filter out the [ and ] brackets
inputString += inChar; // add it to the inputString:
}
// if the incoming character is ‘]’ (ie end of message from RC-Timing),
// set a flag so the main loop can do something about it:
if (inChar == ‘]’) {
stringComplete = true;
}
}
}

Coding almost complete

So now, in February 2016, I am finally at the point where I have the code 99% complete. I have a prototype system up and working, in which there is a TX device (which has inputs of race information and start light information) and two RX devices (one which displays race info and the other which displays red/green start lights). The working prototype is in the video below (there is a fault on one of the TPIC6B595s which means that one of the red LEDs is constantly illuminated):

I have attached the code for the devices in the ‘Download’ tab in the Navigation Menu above. Just download this, unzip the folder then open it in the Arduino IDE and upload to your Arduino. You can find explanations of the prototype code, prototype wiring diagrams and schematics in the section relevant to each device.

Making the project wireless

Soon after getting the Start/Gantry lights prototype working, I wondered where I’d be able to place the start lights for best effect. Would I place it so the drivers could see it or where the pitmen could see it in the pitlane? Also would I be able to get a USB cable long enough to put it in the right place? At this point I realised I needed to make the project wireless.

For reasons of cost and simplicity of coding, I chose the MX-FS-03V 433Mhz transmitter & receiver combination. The transmitter allows an input voltage of between 5V and 12V meaning it is possible to extend the range of the system by increasing the input voltage. The other reason I chose the 433Mhz Tx/Rx combo is due to the fact that the VirtualWire library supports it. I realised fairly quickly that there is a lot of experience in the Arduino community about VirtualWire/433Mhz so I figured that I would go for this system so I had advice to draw on if necessary.

 

Start/Gantry Lights

I guess it was at the end of the 2015 season that I first thought about adding start/gantry lights to the project. You know the red/green lights that you see at the start of every F1 or Touring Car race? Yeah, something like that basically.

RC-Timing by Dale Burr is one of many RC timing programs, I know there are others but it’s the one our club uses, and I like it due to how easy it is to use. Anyway, RC-Timing has three display choices – a second monitor, a Race Information Display Board, and Start Lights. The latter two of these use serial (COM) ports to output the information.

In the case of the gantry lights, this is achieved using a combination of the LOW/HIGH state of the RS232 DTR and RTS signals. The actual format is below:

RTS DTR Result
On On Green Lights On
Off On Red Lights On
Off Off Lights Off

Initially I used a USB to RS232 cable (for serial to RS232 level conversion) and MAX232 IC (for RS232 to TTL level conversion) to get the information into the Arduino. Lately, I’ve realised that I just need TTL conversion, so one of these CP2102-based breakout boards is more than good enough.

Anyway once conversion is achieved, all that is required is a DigitalRead of the input pin, and some branching logic (IF statements) to decide the output based on the input. Using this code and knowledge already gained from the use of the TPIC6B595, I arrived at this working prototype:

 

A recap

As mentioned in the first post of this blog, I have a full time job (and a young family) as well as helping out at my local model car club, so for all these reasons, sometimes life takes over and these projects take a back seat. However, recently I’ve had the urge again to get on with this project and see where I can get with it.

So let me take you back first to 2014 & 2015, a quick recap as it were. I’d started this blog in which I was trying to explain what I wanted to do (the requirements) and the high-level explanation of how I was going to do it (the algorithm in coding terms). At the same time as this was happening, I was getting some actual code in to my programming platform (the Arduino) to see what I could get from the PC running RC-Timing.

RC-Timing (and other RC timing programs) outputs race information and start lights information on serial (COM) ports, so to find out what info I could get from RC-Timing, I needed code that would read the incoming data on the serial port and let me see it.

This first piece of code just used the Arduino Serial Event tutorial mixed with the use of the Liquid Crystal library to report what the serial port was getting from RC-Timing. I could see that I was getting data in the format:

[RdNo:HtNo:MM:SS:etc etc]

After an email from Dale Burr (who is the developer of RC-Timing) I learned the format is thus:

[roundno:raceno:minutes:seconds:car1,carN,:laps1,lapsN,:improver1,improverN,:]

At this time, I hacked together a program that used a mess of loops and nested IF statements that let me extract the various bits of data from the string above. It really was a mess and, thank goodness, I’ve learned a lot since then and tidied it up a lot. Anyway, soon enough I had this working:

Soon enough, I was already thinking about the LED 7-segment displays I wanted to use for the board. I ended up making my own using some foamboard and lots of 5mm LEDs. Changing the output code from LCD library calls to ShiftOut calls although I have to say I used a TPIC6B595 shift register rather than the 74HC595 in the tutorial due to the amount of current needed to drive the LEDs. This tutorial by Mika Tuupola helped me translate the pinouts between the two different shift registers. Anyway, this next video is a quick demo of the ‘seconds’ display in action:

And with that done, the project kind of stalled there. Life took over and then early in 2015 I was busy helping out at the club. I didn’t really pick the project up again until after the outdoor season had finished in late 2015.

The Arduino Program – Initial Pseudocode / Algorithm

From an earlier post, let’s recap what we need the Arduino to do – it needs to read in data given to it by the RC timing program, process it, and pass it out on the Arduino’s output pins (to the 74HC595 shift registers but more about them another time).

So we know what it has to do. Now we need to figure out how to code it. In order to help us to do this, I’m going to use a thing called pseudocode. Pseudocode is NOT code – this won’t work as a program. It’s almost code – it describes in detail what the program will do, in terms that we can all understand.

So start off with, I guess I should explain that any Arduino program MUST have at least two functions – the SETUP function and the LOOP function. The SETUP function runs once and we can perform actions there to set the board up in it’s initial state. The LOOP function runs over and over (it LOOPs round on itself and starts over). We will use other functions also, and I’ll explain what these do as we go on.

SETUP function

1) Set the board up in it’s initial state (LEDs all OFF? Or everything set to 0?)

2) Open the USB port to listen for output from the RC timing program

 

LOOP function

If (we find out that something from the timing output has changed) Then

     Update the Board

Otherwise Do Nothing

 

SERIAL INPUT FUNCTION (this runs whenever new data comes in via USB)

Get the data

If (the Data is for Time Elapsed) Then

     Store this data with a label saying TimeElapsed

If (the Data is for Round Number) Then

     Store this with a label saying RoundNumber

If (the Data is for Heat Number) Then

     Store this with a label saying HeatNumber

Tell the Loop function that something from the timing output has changed

 

UPDATE THE BOARD function

Find out what data has changed

If it’s for Time Elapsed Then

     Find out what the data is for TimeElapsed

     Change it into the correct format for the TimeElapsed shift registers

     Push it out on the Arduino Output Pins

If it’s for Round Number Then

     Find out what the data is for RoundNumber

    Change it into the correct format for the RoundNumber shift registers

     Push it out on the Arduino Output Pins

If it’s for Time Elapsed Then

     Find out what the data is for HeatNumber

     Change it into the correct format for the Heat Number shift registers

     Push it out on the Arduino Output Pins

 

That’s it so far, I’m sure this pseudocode is a) incomplete and b) wrong by some programming purist’s standards, but I don’t really care. What it does do is it informs the structure of my code. This will be my starting point. Actually – it won’t be quite be my starting point as I’ll be coding other programs in the meantime to accomplish tasks such as:

a) Working out what information the RC timing program is giving us

b) Testing the circuit designs for our 7-segment displays

 

In my next blog, I’m probably going to start discussing the 7-segment displays and how exactly I envision them working.

The Design (A brief outline)

In the last blog, I briefly explained the objectives of the project. Numbers 1 to 3 can be seen as being the ‘Arduino-side’ of the project; data input, processing, and output. No 4 is the ‘display-hardware’ side of the project. So we have two main areas to design.

1) The Arduino side

This part of the project takes the output of the RC timing program as an input via the Arduino’s serial communications port (USB). It then processes the information (deciding whether the information relates to time elapsed, or round number, or heat number etc) then passing the information to a process which will then convert the information into a form that the display hardware can use. Lastly, it pushes the processed information out via the Arduino’s output pins.

Looking at this closer, there are several things for me to find out.

a) How do I get information into the Arduino via USB?

b) Once I’ve done that, how do I get information into the Arduino from a RC timing program?

c) What does the display board output of an RC timing program look like?

d) How do I process the information received by the Arduino?

e) How often do I process it and how do I code the frequency at which I process it?

f) What form should my processed information take? (i.e. what are the requirements of my display hardware?)

g) How do I push the information to the display hardware via the output pins? And how often do I do this?

 

2) The Display Hardware side

Assuming I can code the Arduino correctly, I should be looking at dealing with the processed information coming out towards my display hardware on the Arduino’s output pins. As mentioned previously the initial objective is to design and construct a board that displays:

Round Number (1 digit)

Heat Number (2 digits – although the second (tens) digit only needs to display the number 1 when required)

Time Elapsed in race / Time remaining to next heat (4 digits)
In the interests of (mainly) keeping the project as cheap as possible, I’m looking at use an array of standard high brightness LEDs to mimic the operation of a 7-segment display. Each 7-segment display will be around 8 inches high, and each of the 7 segments will use 4 LEDs. So we’re looking at 28 LEDs for each digit. Looking at our initial requirement, we need 7 digits, plus 2 segments to show the number 1 for heat numbers.

Doing the math for a second – 28 LEDs per digit, 7 digits required = 28 x 7 = 196 LEDs + 8 LEDs (2 segments @ 4 LEDs each for the second digit of Heat Numbers) = 204 LEDs

This brings up the next point to consider – these LEDs consume 20mA of current each, so 204 LEDs are going to consume just over 4A between them. This is way beyond the output capabilities of an Arduino output pin, so a power supply external to the Arduino will be needed to drive the LEDs. This brings me to a choice in my circuit design, do I drive the LEDs in each segment in series or parallel? If I drive them in parallel, I need lots of resistors to give current protection, whereas as a series circuit would enable me to use one resistor in series with the 4 LEDs. I’ve decided to drive the LEDs in series, this saves time, expense and simplifies the circuit. This means I need a power supply higher than 8.4V

Having looked online for what power supplies are available, it seems 12v power supplies are pretty commonplace and also cheap, so I’ll be using a 12V supply in this project and will therefore base my calculations on 12V.

7 digits = 7 x 7 segments = 49 individual segments. The Arduino cannot support switching this many individual components with the pin headers it has, so we need to look at how to achieve switching these segments without using as many wires! I’ve decided to use the 74HC595 shift register to achieve this. It allows me to drive an entire digit on the display, at the cost of 3 output pins on the Arduino. But it gets better still – it’s possible to daisy-chain the shift registers together to drive more digits for the same pin cost at the Arduino. So our time elapsed display (4-digits) will use 4 shift registers daisy-chained together all whilst using only 3 pins on the Arduino. More on the 74HC595 shift register in a later post.

Coming back to the power all those LEDs are consuming. We know the Arduino can’t output the power we need to drive the LEDs, and neither can the shift registers. That’s why we’re using an external supply. Well, we still need a way to have the Arduino switch the LED segments on and off as required. To do this, I’ve decided to use a NPN transistor (PN2222) to switch the LED segments on. So the voltage that the Arduino (and hence the shift register) supplies on the output pins will switch the transistor to allow 12v through the segments and the collector to the emitter and ground, lighting the segment.

Anyway, I realise this post is quite long and quite haphazard in it’s structure – this has been more about me putting my thoughts down about how the implementation looks in my head. In later posts I will explore all these individual areas seperately and in better detail (with circuit diagrams!)

The Objectives

Obviously, the end aim is to produce a working RC Race Information Display board. There’s a long way to go to that, though, and a lot of basic aims need to be met first. So we’ll be taking small steps (“baby steps”) all the way. Let’s look at what the board does.

At it’s most basic level, it takes information from the RC timing program, does some processing on it, and then updates the board with the information received. So there are our three of the four main areas of work. I’ll go into each of these in detail in later posts. But a brief description of the objectives for each work area is probably helpful.

1) Take input from RC timing program

The first objective here is to have the Arduino read in data via USB. Then, once we have demonstrated that this is possible, we need to get the RC timing program and the Arduino talking to each other. After that, we need to get the RC timing program to pass the Arduino some data, and finally, we want the Arduino to store the information passed to it for further processing.

2) Process the information/data

What we need to do here is to identify the type of data that has been read in (e.g. time elapsed in race) , and then to convert it to a format which the display hardware will understand.

3) Update the board

This is where we take the processed information and load it into the hardware that will display the information on the board. We then ‘flick the switch’ and update the board with the information.

4) Design & Operate the display hardware

This is the basic electronics end of the project. LEDs, Shift Registers, transistor, resistors, capacitors etc. Don’t worry if you don’t know what that means right now.

Disclaimer

I aplogise in advance – I will definitely not be tackling each phase of the project in the order you see above. It is after all my I’ll be experimenting with things as I go along and with ideas as they come to me, so although there is a basic structure to the project, it’s growth may be somewhat organic! I’ll refer to these four area whenever I post

The Analysis

I suppose the first thing I should do is to explain what an ‘RC race information board’ does.

Well it’s self-explanatory I suppose, it displays information about RC races. It shows things like time elapsed in the race, which heat of which round is being run, and sometimes has other information like: number of laps that the leader has completed, and race positions (i.e. 1st – car number 3, 2nd – car number 2, etc).

So the analysis is very simple. What are my requirements? I need to identify what RC race information I want to display.

Having given it some thought, my initial requirements are:

Current Round Number

Current Heat Number

Time Elapsed in current race / time to next race

Car Positions (Car 1 to Car 10)

 

 

Looking at it a little further, I also need to work out how many digits each I need for each piece of information.

Current Round Number

Round number only requires 1 digit as I do not envisage ever running more than 9 rounds of qualifying!

Current Heat Number

Heat Number requires 2 digits as we sometimes run up to 12 heats. The 2nd (tens) digit only needs to display the number 1 as we will never run more than 19 heats in a single round!

Time Elapsed in current race / time to next race

Time elapsed needs to be 4 digits. Our club runs Nitro/Gas meetings where we regularly run 30 minute finals so we need to be able to display this. This should be in the format MM:SS

Car Positions (Car 1 to Car 10)

This can either be single digit (numbers 1 through 0 for cars 1 to 10) or 2-digit (with the second (tens) digit displaying the number 1 when required. The display of car positions is not essential, and also takes a lot of space to display, so this will be tackled at a later stage in the project. Rest assured though, it will be tackled.