This is part 2 of the Build Indicator series. Part 1 the construction of the hardware is here.
The firmware is written using the Arduinos own programming language which is much like C/C++, using the IDE provided its easy to develop the code and push it down to the Arduino.
I decided that as the Arduino has 13 IO pins it would be a shame not to make these available for 6 build indicators so 6 individual projects could be monitored. Ive not used OO to implement the code so its a little messy with individual arrays for project status, red and green led pins which are indexed based on the project number. I think you can create classes but this has to be done in external C++ files so for a simple application like this I didnt worry to add that extra complexity.
Serial (RS232) communications is used to send a status message down to the Arduino build indicator to update the project status.
The full Arduino build indicator code can be download here.
License : Please use, copy and modify this code as you wish, all I ask is that you dont take credit for the bits I wrote. You should ensure its fit for purpose before using it.
Variable declaration :
1 /*
2 Build Indicator (c) Analysis UK Ltd 2008
3 *
4 Digital Pin Assignments:
5 0 - RX
6 1 - TX
7 2 - Project 6 - Green LED
8 3 - Project 6 - Red LED
9 4 - Project 5 - Green LED
10 5 - Project 5 - Red LED
11 6 - Project 4 - Green LED
12 7 - Project 4 - Red LED
13 8 - Project 3 - Green LED
14 9 - Project 3 - Red LED
15 10 - Project 2 - Green LED
16 11 - Project 2 - Red LED
17 12 - Project 1 - Green LED
18 13 - Project 1 - Red LED
19 */
20
21 #define VERSION "1.0"
22
23 // Project not enabled of not connected.
24 int PROJECT_OFF = 0;
25
26 // Project build failed
27 int PROJECT_FAIL = 1; // 001
28
29 // Project build good
30 int PROJECT_GOOD = 2; // 010
31
32 // project building from a failed project.
33 int PROJECT_BUILDING_FAIL = 5; // 101
34
35 // project building from a good build.
36 int PROJECT_BUILDING_GOOD = 6; // 110
37
38 // Maximum number of projects.
39 int maxProjects = 6;
40
41 // Project status codes. Indexed by project.
42 int projectStatus[] = {
43 0, 0, 0, 0, 0, 0};
44
45 // Pins for the Red LEDs. Indexed by project.
46 int redLEDPin[] = {
47 13, 11, 9, 7, 5, 3};
48
49 // Pins for the Green LEDs. Indexed by project.
50 int greenLEDPin[] = {
51 12, 10, 8, 6, 4, 2}; // pins 0 and 1 reserverd for RS232.
The arrays redLEDPin and greenLEDPin represent the pins to use for the red/green leds and are indexed on the project number (i.e. project 0's red led is on pin 13). The array projectStatus holds the status of the project and again is indexed by the project number.
Setup:
54 // run once, when the sketch starts
55 void setup()
56 {
57 // Initialise the ports for output to drive the LEDs
58 for (int i=0; i<maxProjects; i++) {
59 pinMode(greenLEDPin[i], OUTPUT); // sets the digital pin as output for the green/blue part of the tri color LED
60 pinMode(redLEDPin[i], OUTPUT); // sets the digital pin as output for the red part of the tri color LED
61 }
62
63 // Setup Serial communications
64 Serial.begin(57600);
65 }
The setup method iterates through all the projects setting up the pins designated as leds for output and then sets up serial communications at a baud rate or 57,600. Fortunately the Arduino takes care of the difficult serial comms bits for us.
Main Loop:
67 // Main loop, runs over and over again
68 void loop()
69 {
70 // Check and Read settings from PC
71 ReadCommands();
72
73 // Update the LED's to indicate project status'
74 UpdateLEDs();
75
76 // Idle.
77 Idle();
78 }
79
The loop method is the main application loop that the Arduino will enter once setup is complete and will keep repeating.
Within loop we call 3 basic methods, ReadCommands() which will check the serial port for commands from the host, UpdateLEDs() which will update the leds based on the project status and Idle() which just inserts a small delay but can be used for other background tasks.
The comms protocol is a fairly simple one. All messages should start with a byte value of 2 and terminate with a byte value of 3 so the Arduino can easily know when a instruction has been received.
To update a project status send the ascii string @P[x]=y with x being the project number 1-6 and y being the status (0, 1, 2, 5, 6).
To query the version number of the firmware send ?V.
Ive stolen the comms protocol from another project Im working on with the Arduino that has more commands and queries so Ive based all commands where the Arduino has to do work on the @ character and all queries on the ? character to help separate out the commands and queries.
RS232 Message handling:
86 // Read commands sent from the PC
87 void ReadCommands()
88 {
89 // Check if serial data available, if so then read this in
90
91 // Read all from the serial port until no more bytes available looking for the
92 // start byte (1) of a message.
93 while (Serial.available()) {
94 //read the incoming byte:
95 int incomingByte = Serial.read();
96
97 // Start identifier. STX
98 if (incomingByte == 2) {
99 // Found start byte now read in until we get the end of message byte.
100 ReadSeialCommand();
101 return;
102 }
103 }
104 }
105
106 // Read a command from the serial port. Read until the read byte
107 // is an end of message (new line) indicator.
108 void ReadSeialCommand() {
109 // Expect maximum of 25 bytes (normally 6)
110 byte buffer[25];
111 int index=0;
112
113 while (true) {
114 if (Serial.available()) {
115 // read the incoming byte:
116 int incomingByte = Serial.read();
117
118 // Terminating byte
119 // Wait for ETX (End of text - transmision)
120 if (incomingByte==3) {
121 ProcessRequest(buffer);
122 return;
123 }
124 else {
125 buffer[index] = incomingByte;
126 index++;
127 }
128
129 // Check for buffer overflow and give up if it has.
130 if (index>25) {
131 Serial.print("Error:Buffer Overflow.\n\r");
132 return;
133 }
134 }
135 }
136 }
137
138 void ProcessRequest(byte request[]) {
139
140 boolean processed = false;
141
142 // Check for Query commands (? at the start)
143 if (request[0] == 63) {
144 // Query
145 processed = ProcessQuery(request);
146 }
147 else if (request[0] == 64) {
148 // Set (@P[x]=0) - Set Project x = status.
149 processed = ProcessSetValue(request);
150 }
151
152 if (!processed) {
153 Serial.print ("Error:Unknown Request.\n\r");
154 }
155 }
156
157 boolean ProcessQuery(byte request[]) {
158
159 boolean processed = false;
160
161 switch (request[1]) {
162 case 86: // ?V - version
163 processed = SendVersion();
164 default:
165 Serial.print ("Error:Unknown Query.\n\r");
166 }
167 return processed;
168 }
169
170 boolean ProcessSetValue(byte request[]) {
171
172 boolean processed = false;
173 byte command = request[1];
174 // Allow ascii version of the fan number.
175 //Position 2 should be [
176 byte project = request[3] - 48; // 48 = 0
177 //position 4 should be ]
178 //position 5 should be =
179 //position 6 should be the raw value.
180 byte value = request[6];
181
182 switch (command) {
183 case 80: //@P[x]=y Set project x status y. y is ascii version of the status (0-9). so subtract 48.
184 processed = SetProjectStatus((int)project, (int)value - 48);
185 break;
186 default:
187 Serial.print ("Error:Unknown set command.");
188 }
189
190 return processed;
191 }
192
193 boolean SendVersion() {
194 Serial.print ("Version=");
195 Serial.print (VERSION);
196 Serial.print ("\n\r");
197 return true;
198 }
199
200 // Set the status for the project.
201 boolean SetProjectStatus(int project, int status) {
202
203 if (projectStatus[project-1] != status) {
204 projectStatus[project-1] = status;
205
206 // Build failed. Flash the red LED briefly to get attention.
207 if (status == 1) {
208 digitalWrite(greenLEDPin[project-1], LOW);
209
210 for (int i=0; i<6; i++) {
211 digitalWrite(redLEDPin[project-1], HIGH);
212 delay(100);
213 digitalWrite(redLEDPin[project-1], LOW);
214 delay(100);
215 }
216 }
217 }
218
219 return true;
220 }
221
The method ReadCommands() will read the input buffer until it receives the start byte then call ReadSerialCommand() which reads the rest of the command into a buffer until it received the end byte. Ive limited the buffer to 25 bytes which should be plenty and if this overflows then we just abandon it. Their is no timeout between receiving the start and end bytes so this could cause a problem if the end byte is not received.
Notice in the method SetProjectStatus() that the project index used is -1 from the project value sent. When sending commands the first project is project 1 however the Arduino uses 0 based arrays.
In SetProjectStatus() if the project state changes to be failure (value 1) then the red led is flashed 6 times to draw attention to the indicator.
Project State Indication:
222 boolean UpdateLEDs() {
223
224 for (int i=0; i<maxProjects; i++) {
225 int greenLEDStatus = LOW;
226 int redLEDStatus = LOW;
227
228 switch (projectStatus[i]) {
229 case 0: // NC
230 // No acton
231 break;
232 case 1: // Fail
233 redLEDStatus = HIGH;
234 break;
235 case 2: // Good
236 greenLEDStatus = HIGH;
237 break;
238 case 5: // Building from a Fail build
239 case 6: // Building from a good build
240 greenLEDStatus = HIGH;
241 redLEDStatus = HIGH;
242 break;
243 default:
244 redLEDStatus = HIGH;
245 break;
246 }
247
248 // Determine project LED pins and set them appropriatly.
249 digitalWrite(redLEDPin[i], redLEDStatus);
250 digitalWrite(greenLEDPin[i], greenLEDStatus);
251 }
252 }
In the method UpdateLEDs() we update the led status based on the project status.
If you are wondering what happened to status codes 3 & 4 Ive used a bit based project status. 0001 (1) is fail, 0010 (2) is good, 01xx is building so 0101 (5) is building from a previously failed state and 0110 (6) is building from a good previous good state.
If you want to use indicators other than leds for your project state then you can update the UpdateLEDs() method with another way to indicate the project status (maybe a LCD panel?).
Thats basically the firmware, not much to it, the Arduino does most of the work for us which is the best bit!
To program the Arduino connect it up, install the drivers (my Vista x64 and Vista x86 installs got the drivers from Windows Update without a problem and also installed the VCP virtual com port drivers). Open up the IDE, ensure the board and serial port are correct and load the build indicator firmware, then hit the upload to I/O board button.
In the next entry Ill talk about the PC application to drive the Arduino.