Category Archives: Transmitter-Module

The first PCB

So with the majority of the code complete, and with the requirements for the transmitter part of the project finalised, I can now design the PCB for the transmitter module. The requirements are that it needs to house the Atmega328P-PU (the Arduino chip), as well as crystal, it also needs to bring in the data from 2 CP2102 USB to UART convertors and take data out to the nRF24L01 transceiver:

 

RCINFOBOARD_TX

So this is the design, it’s been ordered from the fabricators, and I’ll be waiting impatiently for a few weeks while it’s made and delivered! You can look at the schematic here on 123D Circuits.

 

External display board output using SoftwareSerial

Our club uses a RIDE board for showing round number, heat number, time elapsed etc to the racers. The RIDE board is a very nice board, well made with large red 7-segment displays made of 3mm LEDs. It was when we had issues with the board (caused by a flaky USB connection long since fixed) that I had the idea for this project. My initial intention was to make my own version of the RIDE board but I have since realised that’s not really what I want (although I may revisit that in the future), however what the project does need (because we have one of these boards and because it was expensive to purchase) is the ability to output the timing information to it.

This has been the elephant in the room for this project from my point of view. Without it, the project is good but is only attractive to those clubs who don’t own a board like this. With the ability to output to the RIDE board, it means my club and others who have the RIDE board can use the project to add more wireless boards and start lights around their circuit.

I’ve been developing this system on several Arduino Unos. The first one I bought from Maplins (an electronics retailer in the UK). The next 4 or 5, I’ve bought from eBay shops where they cost just a few pounds.

Anyway, the major drawback of an Arduino Uno for a project that needs serial input and serial output from the same device (like ours does – RC timing input and RIDE board output) is that the Arduino only has one serial port! So I was faced with a dilemna – do I buy an expensive Arduino Mega which has 4 hardware serial ports, or do I try to emulate a serial port in software on the Arduino? I thought I’d give the Arduino SoftwareSerial library a go.

In fact, it was very easy. From a software point of view, I’d already worked out how to generate the correct message for the output to the RIDE board. One thing I’d found when testing the system was that the VirtualWire library and the SoftwareSerial library had a conflict when using their default pins. Specifically, VirtualWire uses Arduino pins 10 and 11 for PTT (Press-to-talk?) which are the default RX and TX pins for SoftwareSerial. After I changed the pins for SoftwareSerial to pins 5 and 6, I was good to go again. Remember also that the two devices should also be connected to each other’s ground. (So the final device’s serial output ground connection will connect to the RIDE board’s ground connection).

Anyway, here is the breadboard proof-of-concept in action:

Getting rid of Strings

The timing output from the race timing PC running RC-Timing (or other RC timing program) becomes an input to our system on the Arduino serial port (USB port or pins 0 & 1). Up until now I’d been relying on code from the Serial Event tutorial in the official Arduino documentation.

It was when I was trying to add a second serial port to the project that I seemed to run up against some issues with the way data was being stored, for some reason I could inject a small amount of data into the serial buffer myself (and the code would handle the data correctly) but when I connected the Arduino to RC-Timing I was getting or creating garbage. Anyway, I asked a question on the Arduino forum and received an interesting reply from a user called Robin2 (it’s reply #8 in the thread).

Anyway, I figured these people probably aren’t wrong with their views on String objects; I haven’t delved deep into the issue but I was willing to try the alternative (character arrays).

So let’s look at the code:

void serialEvent(){
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = ‘[‘;
char endMarker = ‘]’;
char rc;

// if (Serial.available() > 0) {
while (Serial.available() > 0 && stringComplete == false) {
rc = Serial.read();

if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
rideChars[(ndx + 1)] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars – 1;
}
}
else {
receivedChars[ndx] = ‘\0’; // terminate the string
rideChars[(ndx + 1)] = rc;
rideChars[(ndx + 2)] = ‘\0’;
recvInProgress = false;
ndx = 0;
stringComplete = true;
}
}

else if (rc == startMarker) {
recvInProgress = true;
rideChars[ndx] = rc;
}
}
}

You’ll notice there’s a bunch of local variables declared at the start of the function. These serve various functions – recvInProgress lets the program know whether there is still data to be read in from the serial buffer. ndx is an index variable (used to write to a specific character in the character array). Then we have the two markers, starMarker and endMarker – these indicate to the program when we should start reading into the receivedChars array (which is a global variable declared at the start of the main program), and when we should stop reading into receivedChars. This then resets all the variables other than receivedChars to empty and terminates the array with a ‘\0’ character.

There are three circumstances that the serialEvent() function deals with – is the received character (the variable called rc which is of char type) our start marker, something inbetween the start and end marker, or is it the end marker?

You’ll notice in the code that I also store the received character from the serial buffer into a seperate character array (again, a global variable) called rideChars. This stores the message received with the ‘[‘ and ‘]’ characters added onto the beginning and end of the array. This is for the feature described in the post ‘External display board output using SoftwareSerial’.

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;
}
}
}