1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
|
/* Copyright (C) 2006 Hans-Christoph Steiner
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
* -----------------------------
* Firmata, the Arduino firmware
* -----------------------------
*
* Firmata turns the Arduino into a Plug-n-Play sensorbox, servo
* controller, and/or PWM motor/lamp controller.
*
* It was originally designed to work with the Pd object [arduino]
* which is included in Pd-extended. This firmware is intended to
* work with any host computer software package. It can easily be
* used with other programs like Max/MSP, Processing, or whatever can
* do serial communications.
*
* @authors: Hans-Christoph Steiner <hans@at.or.at>
* help with protocol redesign: Jamie Allen <jamie@heavyside.net>
* key bugfixes: Georg Holzmann <grh@mur.at>
* Gerda Strobl <gerda.strobl@student.tugraz.at>
* @date: 2006-05-19
* @locations: STEIM, Amsterdam, Netherlands
* IDMI/Polytechnic University, Brookyn, NY, USA
* Electrolobby Ars Electronica, Linz, Austria
*/
/*
* TODO: add pulseOut functionality for servos
* TODO: add software PWM for servos, etc (servo.h or pulse.h)
* TODO: redesign protocol to accomodate boards with more I/Os
* TODO:
* TODO: add "pinMode all 0/1" command
* TODO: add cycle markers to mark start of analog, digital, pulseIn, and PWM
*/
/* cvs version: $Id: Pd_firmware.pde,v 1.21 2006-10-31 00:39:07 eighthave Exp $ */
/* Version numbers for the protocol. The protocol is still changing, so these
* version numbers are important. This number can be queried so that host
* software can test whether it will be compatible with the currently
* installed firmware. */
#define MAJOR_VERSION 0
#define MINOR_VERSION 2
/* firmata protocol
* ===============
* data: 0-127
* control: 128-255
*/
/* computer<->Arduino commands
* -------------------- */
/* 128-129 // UNASSIGNED */
#define SET_PIN_ZERO_TO_IN 130 // set digital pin 0 to INPUT
#define SET_PIN_ONE_TO_IN 131 // set digital pin 1 to INPUT
#define SET_PIN_TWO_TO_IN 132 // set digital pin 2 to INPUT
#define SET_PIN_THREE_TO_IN 133 // set digital pin 3 to INPUT
#define SET_PIN_FOUR_TO_IN 134 // set digital pin 4 to INPUT
#define SET_PIN_FIVE_TO_IN 135 // set digital pin 5 to INPUT
#define SET_PIN_SIX_TO_IN 136 // set digital pin 6 to INPUT
#define SET_PIN_SEVEN_TO_IN 137 // set digital pin 7 to INPUT
#define SET_PIN_EIGHT_TO_IN 138 // set digital pin 8 to INPUT
#define SET_PIN_NINE_TO_IN 139 // set digital pin 9 to INPUT
#define SET_PIN_TEN_TO_IN 140 // set digital pin 10 to INPUT
#define SET_PIN_ELEVEN_TO_IN 141 // set digital pin 11 to INPUT
#define SET_PIN_TWELVE_TO_IN 142 // set digital pin 12 to INPUT
#define SET_PIN_THIRTEEN_TO_IN 143 // set digital pin 13 to INPUT
/* 144-149 // UNASSIGNED */
#define DISABLE_DIGITAL_INPUTS 150 // disable reporting of digital inputs
#define ENABLE_DIGITAL_INPUTS 151 // enable reporting of digital inputs
/* 152-159 // UNASSIGNED */
#define ZERO_ANALOG_INS 160 // disable reporting on all analog ins
#define ONE_ANALOG_IN 161 // enable reporting for 1 analog in (0)
#define TWO_ANALOG_INS 162 // enable reporting for 2 analog ins (0,1)
#define THREE_ANALOG_INS 163 // enable reporting for 3 analog ins (0-2)
#define FOUR_ANALOG_INS 164 // enable reporting for 4 analog ins (0-3)
#define FIVE_ANALOG_INS 165 // enable reporting for 5 analog ins (0-4)
#define SIX_ANALOG_INS 166 // enable reporting for 6 analog ins (0-5)
#define SEVEN_ANALOG_INS 167 // enable reporting for 6 analog ins (0-6)
#define EIGHT_ANALOG_INS 168 // enable reporting for 6 analog ins (0-7)
#define NINE_ANALOG_INS 169 // enable reporting for 6 analog ins (0-8)
/* 170-199 // UNASSIGNED */
#define SET_PIN_ZERO_TO_OUT 200 // set digital pin 0 to OUTPUT
#define SET_PIN_ONE_TO_OUT 201 // set digital pin 1 to OUTPUT
#define SET_PIN_TWO_TO_OUT 202 // set digital pin 2 to OUTPUT
#define SET_PIN_THREE_TO_OUT 203 // set digital pin 3 to OUTPUT
#define SET_PIN_FOUR_TO_OUT 204 // set digital pin 4 to OUTPUT
#define SET_PIN_FIVE_TO_OUT 205 // set digital pin 5 to OUTPUT
#define SET_PIN_SIX_TO_OUT 206 // set digital pin 6 to OUTPUT
#define SET_PIN_SEVEN_TO_OUT 207 // set digital pin 7 to OUTPUT
#define SET_PIN_EIGHT_TO_OUT 208 // set digital pin 8 to OUTPUT
#define SET_PIN_NINE_TO_OUT 209 // set digital pin 9 to OUTPUT
#define SET_PIN_TEN_TO_OUT 210 // set digital pin 10 to OUTPUT
#define SET_PIN_ELEVEN_TO_OUT 211 // set digital pin 11 to OUTPUT
#define SET_PIN_TWELVE_TO_OUT 212 // set digital pin 12 to OUTPUT
#define SET_PIN_THIRTEEN_TO_OUT 213 // set digital pin 13 to OUTPUT
/* 214-228 // UNASSIGNED */
#define OUTPUT_TO_DIGITAL_PINS 229 // next two bytes set digital output data
/* 230-239 // UNASSIGNED */
#define REPORT_VERSION 240 // return the firmware version
/* 240-249 // UNASSIGNED */
#define DISABLE_PWM 250 // next byte sets pin # to disable
#define ENABLE_PWM 251 // next two bytes set pin # and duty cycle
#define DISABLE_SOFTWARE_PWM 252 // next byte sets pin # to disable
#define ENABLE_SOFTWARE_PWM 253 // next two bytes set pin # and duty cycle
#define SET_SOFTWARE_PWM_FREQ 254 // set master frequency for software PWMs
/* 255 // UNASSIGNED */
/* two byte digital output data format
* ----------------------
* 0 get ready for digital input bytes (229)
* 1 digitalOut 7-13 bitmask
* 2 digitalOut 0-6 bitmask
*/
/* two byte PWM data format
* ----------------------
* 0 get ready for digital input bytes (ENABLE_SOFTWARE_PWM/ENABLE_PWM)
* 1 pin #
* 2 duty cycle expressed as 1 byte (255 = 100%)
*/
/* digital input message format
* ----------------------
* 0 digital input marker (255/11111111)
* 1 digital read from Arduino // 7-13 bitmask
* 2 digital read from Arduino // 0-6 bitmask
*/
/* analog input message format
* ----------------------
* 0 analog input marker (160 + pin number reported)
* 1 high byte from analog input
* 2 low byte from analog input
*/
#define TOTAL_DIGITAL_PINS 14
// for comparing along with INPUT and OUTPUT
#define PWM 2
// maximum number of post-command data bytes
#define MAX_DATA_BYTES 2
// this flag says the next serial input will be data
byte waitForData = 0;
byte executeMultiByteCommand = 0; // command to execute after getting multi-byte data
byte storedInputData[MAX_DATA_BYTES] = {0,0}; // multi-byte data
// this flag says the first data byte for the digital outs is next
boolean firstInputByte = false;
/* store the previously sent digital inputs to compare against the current
* digital inputs. If there is no change, do not transmit. */
byte previousDigitalInputHighByte = 0;
byte previousDigitalInputLowByte = 0;
byte digitalInputHighByte = 0;
byte digitalInputLowByte = 0;
/* this int serves as a bit-wise array to store pin status
* 0 = INPUT, 1 = OUTPUT */
int digitalPinStatus = 0;
/* this byte stores the status off whether PWM is on or not
* bit 9 = PWM0, bit 10 = PWM1, bit 11 = PWM2
* the rest of the bits are unused and should remain 0 */
int pwmStatus = 0;
boolean digitalInputsEnabled = true;
byte analogInputsEnabled = 6;
byte analogPin;
int analogData;
// -------------------------------------------------------------------------
byte transmitDigitalInput(byte startPin) {
byte i;
byte digitalPin;
// byte digitalPinBit;
byte returnByte = 0;
byte digitalData;
for(i=0;i<7;++i) {
digitalPin = i+startPin;
/* digitalPinBit = OUTPUT << digitalPin;
// only read the pin if its set to input
if(digitalPinStatus & digitalPinBit) {
digitalData = 0; // pin set to OUTPUT, don't read
}
else if( (digitalPin >= 9) && (pwmStatus & (1 << digitalPin)) ) {
digitalData = 0; // pin set to PWM, don't read
}*/
if( !(digitalPinStatus & (1 << digitalPin)) ) {
digitalData = (byte) digitalRead(digitalPin);
returnByte = returnByte + ((1 << i) * digitalData);
}
}
return(returnByte);
}
// -------------------------------------------------------------------------
/* this function sets the pin mode to the correct state and sets the relevant
* bits in the two bit-arrays that track Digital I/O and PWM status
*/
void setPinMode(int pin, int mode) {
if(mode == INPUT) {
digitalPinStatus = digitalPinStatus &~ (1 << pin);
pwmStatus = pwmStatus &~ (1 << pin);
pinMode(pin,INPUT);
}
else if(mode == OUTPUT) {
digitalPinStatus = digitalPinStatus | (1 << pin);
pwmStatus = pwmStatus &~ (1 << pin);
pinMode(pin,OUTPUT);
}
else if( (mode == PWM) && (pin >= 9) && (pin <= 11) ) {
digitalPinStatus = digitalPinStatus | (1 << pin);
pwmStatus = pwmStatus | (1 << pin);
pinMode(pin,OUTPUT);
}
// TODO: save status to EEPROM here, if changed
}
/* -------------------------------------------------------------------------
* this function checks to see if there is data waiting on the serial port
* then processes all of the stored data
*/
void checkForInput() {
if(Serial.available()) {
while(Serial.available()) {
processInput( (byte)Serial.read() );
}
}
}
/* -------------------------------------------------------------------------
* processInput() is called whenever a byte is available on the
* Arduino's serial port. This is where the comm1ands are handled.
*/
void processInput(byte inputData) {
int i;
int mask;
// a few commands have byte(s) of data following the command
if( waitForData > 0) {
waitForData--;
storedInputData[waitForData] = inputData;
if(executeMultiByteCommand && (waitForData==0)) {
//we got everything
switch(executeMultiByteCommand) {
case ENABLE_PWM:
setPinMode(storedInputData[1],PWM);
analogWrite(storedInputData[1], storedInputData[0]);
break;
case DISABLE_PWM:
setPinMode(storedInputData[0],INPUT);
break;
case ENABLE_SOFTWARE_PWM:
break;
case DISABLE_SOFTWARE_PWM:
break;
case SET_SOFTWARE_PWM_FREQ:
break;
}
executeMultiByteCommand = 0;
}
}
else if(inputData < 128) {
if(firstInputByte) {
// output data for pins 7-13
for(i=7; i<TOTAL_DIGITAL_PINS; ++i) {
mask = 1 << i;
if( (digitalPinStatus & mask) && !(pwmStatus & mask) ) {
// inputData is a byte and mask is an int, so align the high part of mask
digitalWrite(i, inputData & (mask >> 7));
}
}
firstInputByte = false;
}
else { //
for(i=0; i<7; ++i) {
mask = 1 << i;
if( (digitalPinStatus & mask) && !(pwmStatus & mask) ) {
digitalWrite(i, inputData & mask);
}
}
}
}
else {
switch (inputData) {
case SET_PIN_ZERO_TO_IN: // set digital pins to INPUT
case SET_PIN_ONE_TO_IN:
case SET_PIN_TWO_TO_IN:
case SET_PIN_THREE_TO_IN:
case SET_PIN_FOUR_TO_IN:
case SET_PIN_FIVE_TO_IN:
case SET_PIN_SIX_TO_IN:
case SET_PIN_SEVEN_TO_IN:
case SET_PIN_EIGHT_TO_IN:
case SET_PIN_NINE_TO_IN:
case SET_PIN_TEN_TO_IN:
case SET_PIN_ELEVEN_TO_IN:
case SET_PIN_TWELVE_TO_IN:
case SET_PIN_THIRTEEN_TO_IN:
setPinMode(inputData - SET_PIN_ZERO_TO_IN, INPUT);
break;
case SET_PIN_ZERO_TO_OUT: // set digital pins to OUTPUT
case SET_PIN_ONE_TO_OUT:
case SET_PIN_TWO_TO_OUT:
case SET_PIN_THREE_TO_OUT:
case SET_PIN_FOUR_TO_OUT:
case SET_PIN_FIVE_TO_OUT:
case SET_PIN_SIX_TO_OUT:
case SET_PIN_SEVEN_TO_OUT:
case SET_PIN_EIGHT_TO_OUT:
case SET_PIN_NINE_TO_OUT:
case SET_PIN_TEN_TO_OUT:
case SET_PIN_ELEVEN_TO_OUT:
case SET_PIN_TWELVE_TO_OUT:
case SET_PIN_THIRTEEN_TO_OUT:
setPinMode(inputData - SET_PIN_ZERO_TO_OUT, OUTPUT);
break;
case DISABLE_DIGITAL_INPUTS: // all digital inputs off
digitalInputsEnabled = false;
break;
case ENABLE_DIGITAL_INPUTS: // all digital inputs on
digitalInputsEnabled = true;
break;
case ZERO_ANALOG_INS: // analog input off
case ONE_ANALOG_IN: // analog 0 on
case TWO_ANALOG_INS: // analog 0,1 on
case THREE_ANALOG_INS: // analog 0-2 on
case FOUR_ANALOG_INS: // analog 0-3 on
case FIVE_ANALOG_INS: // analog 0-4 on
case SIX_ANALOG_INS: // analog 0-5 on
case SEVEN_ANALOG_INS: // analog 0-6 on
case EIGHT_ANALOG_INS: // analog 0-7 on
case NINE_ANALOG_INS: // analog 0-8 on
analogInputsEnabled = inputData - ZERO_ANALOG_INS;
break;
case ENABLE_PWM:
waitForData = 2; // 2 bytes needed (pin#, dutyCycle)
executeMultiByteCommand = inputData;
break;
case DISABLE_PWM:
waitForData = 1; // 1 byte needed (pin#)
executeMultiByteCommand = inputData;
break;
case SET_SOFTWARE_PWM_FREQ:
waitForData = 1; // 1 byte needed (pin#)
executeMultiByteCommand = inputData;
break;
case ENABLE_SOFTWARE_PWM:
waitForData = 2; // 2 bytes needed (pin#, dutyCycle)
executeMultiByteCommand = inputData;
break;
case DISABLE_SOFTWARE_PWM:
waitForData = 1; // 1 byte needed (pin#)
executeMultiByteCommand = inputData;
break;
case OUTPUT_TO_DIGITAL_PINS: // bytes to send to digital outputs
firstInputByte = true;
break;
case REPORT_VERSION:
Serial.print(REPORT_VERSION, BYTE);
Serial.print(MAJOR_VERSION, BYTE);
Serial.print(MINOR_VERSION, BYTE);
break;
}
}
}
// =========================================================================
// -------------------------------------------------------------------------
void setup() {
byte i;
// TODO: load state from EEPROM here
/* TODO: send digital inputs here, if enabled, to set the initial state on the
* host computer, since once in the loop(), the Arduino will only send data on
* change. */
// flash the pin 13 with the protocol minor version (add major once > 0)
pinMode(13,OUTPUT);
for(i-0; i<MINOR_VERSION; i++) {
digitalWrite(13,1);
delay(100);
digitalWrite(13,0);
delay(200);
}
for(i=0; i<TOTAL_DIGITAL_PINS; ++i) {
setPinMode(i,INPUT);
}
Serial.begin(115200);
}
// -------------------------------------------------------------------------
void loop() {
checkForInput();
// read all digital pins, in enabled
if(digitalInputsEnabled) {
digitalInputHighByte = transmitDigitalInput(7);
checkForInput();
digitalInputLowByte = transmitDigitalInput(0);
checkForInput();
// only send data if it has changed
if( (digitalInputHighByte != previousDigitalInputHighByte) &&
(digitalInputLowByte != previousDigitalInputLowByte) ) {
Serial.print(ENABLE_DIGITAL_INPUTS, BYTE);
Serial.print(digitalInputHighByte, BYTE);
Serial.print(digitalInputLowByte, BYTE);
previousDigitalInputHighByte = digitalInputHighByte;
previousDigitalInputLowByte = digitalInputLowByte;
}
checkForInput();
}
/* get analog in, for the number enabled */
for(analogPin=0; analogPin<analogInputsEnabled; ++analogPin) {
analogData = analogRead(analogPin);
/* These two bytes get converted back into the whole number on host.
Highest bits should be zeroed so the 8th bit doesn't get set */
Serial.print(ONE_ANALOG_IN + analogPin, BYTE);
Serial.print(analogData >> 7, BYTE); // shift high bits into output byte
Serial.print(analogData % 128, BYTE); // mod by 32 for the small byte
checkForInput();
}
}
|