2
This introduces modifications on the normal Firmata made for Arduino so that the LED
3
blinks until receiving the first command over serial.
5
Copyright (C) 2010 David Cuartielles. All rights reserved.
7
based at 99.9% on Firmata by HC Steiner according to the following license terms:
9
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
11
This library is free software; you can redistribute it and/or
12
modify it under the terms of the GNU Lesser General Public
13
License as published by the Free Software Foundation; either
14
version 2.1 of the License, or (at your option) any later version.
16
See file LICENSE.txt for further informations on licensing terms.
18
formatted using the GNU C formatting and indenting
22
* TODO: use Program Control to load stored profiles from EEPROM
28
/*==============================================================================
30
*============================================================================*/
32
/* has the command arrived? */
33
boolean firstCommand = false;
35
boolean statusLed = false;
38
int analogInputsToReport = 0; // bitwise array to store pin reporting
40
/* digital input ports */
41
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
42
byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent
44
/* pins configuration */
45
byte pinConfig[TOTAL_PINS]; // configuration of every pin
46
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
47
int pinState[TOTAL_PINS]; // any value that has been written
50
unsigned long currentMillis; // store the current value from millis()
51
unsigned long previousMillis; // for comparison with currentMillis
52
int samplingInterval = 19; // how often to run the main loop (in ms)
53
unsigned long toggleMillis;
55
Servo servos[MAX_SERVOS];
57
/*==============================================================================
59
*============================================================================*/
63
if (millis() - toggleMillis > 500) {
64
statusLed = !statusLed;
65
digitalWrite(13, statusLed);
66
toggleMillis = millis();
70
void outputPort(byte portNumber, byte portValue, byte forceSend)
72
// pins not configured as INPUT are cleared to zeros
73
portValue = portValue & portConfigInputs[portNumber];
74
// only send if the value is different than previously sent
75
if(forceSend || previousPINs[portNumber] != portValue) {
76
Firmata.sendDigitalPort(portNumber, portValue);
77
previousPINs[portNumber] = portValue;
81
/* -----------------------------------------------------------------------------
82
* check all the active digital inputs for change of state, then add any events
83
* to the Serial output queue using Serial.print() */
84
void checkDigitalInputs(void)
86
/* Using non-looping code allows constants to be given to readPort().
87
* The compiler will apply substantial optimizations if the inputs
88
* to readPort() are compile-time constants. */
89
if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);
90
if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);
91
if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);
92
if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);
93
if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);
94
if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);
95
if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);
96
if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);
97
if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);
98
if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);
99
if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);
100
if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);
101
if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);
102
if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);
103
if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);
104
if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
107
// -----------------------------------------------------------------------------
108
/* sets the pin mode to the correct state and sets the relevant bits in the
109
* two bit-arrays that track Digital I/O and PWM status
111
void setPinModeCallback(byte pin, int mode)
113
if (IS_PIN_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) {
114
servos[PIN_TO_SERVO(pin)].detach();
116
if (IS_PIN_ANALOG(pin)) {
117
reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting
119
if (IS_PIN_DIGITAL(pin)) {
121
portConfigInputs[pin/8] |= (1 << (pin & 7));
123
portConfigInputs[pin/8] &= ~(1 << (pin & 7));
129
if (IS_PIN_ANALOG(pin)) {
130
if (IS_PIN_DIGITAL(pin)) {
131
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
132
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
134
pinConfig[pin] = ANALOG;
138
if (IS_PIN_DIGITAL(pin)) {
139
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
140
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
141
pinConfig[pin] = INPUT;
145
if (IS_PIN_DIGITAL(pin)) {
146
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM
147
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
148
pinConfig[pin] = OUTPUT;
152
if (IS_PIN_PWM(pin)) {
153
pinMode(PIN_TO_PWM(pin), OUTPUT);
154
analogWrite(PIN_TO_PWM(pin), 0);
155
pinConfig[pin] = PWM;
159
if (IS_PIN_SERVO(pin)) {
160
pinConfig[pin] = SERVO;
161
if (!servos[PIN_TO_SERVO(pin)].attached()) {
162
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
164
Firmata.sendString("Servo only on pins from 2 to 13");
169
pinConfig[pin] = mode;
170
Firmata.sendString("I2C mode not yet supported");
173
Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM
175
// TODO: save status to EEPROM here, if changed
178
void analogWriteCallback(byte pin, int value)
180
if (pin < TOTAL_PINS) {
181
switch(pinConfig[pin]) {
183
if (IS_PIN_SERVO(pin))
184
servos[PIN_TO_SERVO(pin)].write(value);
185
pinState[pin] = value;
189
analogWrite(PIN_TO_PWM(pin), value);
190
pinState[pin] = value;
196
void digitalWriteCallback(byte port, int value)
198
byte pin, lastPin, mask=1, pinWriteMask=0;
200
if (port < TOTAL_PORTS) {
201
// create a mask of the pins on this port that are writable.
203
if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS;
204
for (pin=port*8; pin < lastPin; pin++) {
205
// do not disturb non-digital pins (eg, Rx & Tx)
206
if (IS_PIN_DIGITAL(pin)) {
207
// only write to OUTPUT and INPUT (enables pullup)
208
// do not touch pins in PWM, ANALOG, SERVO or other modes
209
if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) {
210
pinWriteMask |= mask;
211
pinState[pin] = ((byte)value & mask) ? 1 : 0;
216
writePort(port, (byte)value, pinWriteMask);
221
// -----------------------------------------------------------------------------
222
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
224
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
226
void reportAnalogCallback(byte analogPin, int value)
228
if (analogPin < TOTAL_ANALOG_PINS) {
230
analogInputsToReport = analogInputsToReport &~ (1 << analogPin);
232
analogInputsToReport = analogInputsToReport | (1 << analogPin);
235
// TODO: save status to EEPROM here, if changed
238
void reportDigitalCallback(byte port, int value)
240
if (port < TOTAL_PORTS) {
241
reportPINs[port] = (byte)value;
243
// do not disable analog reporting on these 8 pins, to allow some
244
// pins used for digital, others analog. Instead, allow both types
245
// of reporting to be enabled, but check if the pin is configured
246
// as analog when sampling the analog inputs. Likewise, while
247
// scanning digital pins, portConfigInputs will mask off values from any
248
// pins configured as analog
251
/*==============================================================================
252
* SYSEX-BASED commands
253
*============================================================================*/
255
void sysexCallback(byte command, byte argc, byte *argv)
260
// these vars are here for clarity, they'll optimized away by the compiler
262
int minPulse = argv[1] + (argv[2] << 7);
263
int maxPulse = argv[3] + (argv[4] << 7);
265
if (IS_PIN_SERVO(pin)) {
266
// servos are pins from 2 to 13, so offset for array
267
if (servos[PIN_TO_SERVO(pin)].attached())
268
servos[PIN_TO_SERVO(pin)].detach();
269
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
270
setPinModeCallback(pin, SERVO);
274
case SAMPLING_INTERVAL:
276
samplingInterval = argv[0] + (argv[1] << 7);
278
Firmata.sendString("Not enough data");
280
case EXTENDED_ANALOG:
283
if (argc > 2) val |= (argv[2] << 7);
284
if (argc > 3) val |= (argv[3] << 14);
285
analogWriteCallback(argv[0], val);
288
case CAPABILITY_QUERY:
289
Serial.write(START_SYSEX);
290
Serial.write(CAPABILITY_RESPONSE);
291
for (byte pin=0; pin < TOTAL_PINS; pin++) {
292
if (IS_PIN_DIGITAL(pin)) {
293
Serial.write((byte)INPUT);
295
Serial.write((byte)OUTPUT);
298
if (IS_PIN_ANALOG(pin)) {
299
Serial.write(ANALOG);
302
if (IS_PIN_PWM(pin)) {
306
if (IS_PIN_SERVO(pin)) {
312
Serial.write(END_SYSEX);
314
case PIN_STATE_QUERY:
317
Serial.write(START_SYSEX);
318
Serial.write(PIN_STATE_RESPONSE);
320
if (pin < TOTAL_PINS) {
321
Serial.write((byte)pinConfig[pin]);
322
Serial.write((byte)pinState[pin] & 0x7F);
323
if (pinState[pin] & 0xFF80) Serial.write((byte)(pinState[pin] >> 7) & 0x7F);
324
if (pinState[pin] & 0xC000) Serial.write((byte)(pinState[pin] >> 14) & 0x7F);
326
Serial.write(END_SYSEX);
329
case ANALOG_MAPPING_QUERY:
330
Serial.write(START_SYSEX);
331
Serial.write(ANALOG_MAPPING_RESPONSE);
332
for (byte pin=0; pin < TOTAL_PINS; pin++) {
333
Serial.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127);
335
Serial.write(END_SYSEX);
340
/*==============================================================================
342
*============================================================================*/
347
Firmata.setFirmwareVersion(2, 2);
349
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
350
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
351
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
352
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
353
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
354
Firmata.attach(START_SYSEX, sysexCallback);
356
// TODO: load state from EEPROM here
358
/* these are initialized to zero by the compiler startup code
359
for (i=0; i < TOTAL_PORTS; i++) {
360
reportPINs[i] = false;
361
portConfigInputs[i] = 0;
365
for (i=0; i < TOTAL_PINS; i++) {
366
if (IS_PIN_ANALOG(i)) {
367
// turns off pullup, configures everything
368
setPinModeCallback(i, ANALOG);
370
// sets the output to 0, configures portConfigInputs
371
setPinModeCallback(i, OUTPUT);
374
// by defult, do not report any analog inputs
375
analogInputsToReport = 0;
377
Firmata.begin(57600);
379
/* send digital inputs to set the initial state on the host computer,
380
* since once in the loop(), this firmware will only send on change */
381
for (i=0; i < TOTAL_PORTS; i++) {
382
outputPort(i, readPort(i, portConfigInputs[i]), true);
385
/* init the toggleLed counter */
386
toggleMillis = millis();
390
/*==============================================================================
392
*============================================================================*/
397
/* DIGITALREAD - as fast as possible, check for changes and output them to the
398
* FTDI buffer using Serial.print() */
399
checkDigitalInputs();
401
//XXX: hack Firmata to blink until serial command arrives
402
dataOnSerial = Firmata.available();
403
if (dataOnSerial > 0 && !firstCommand) {
406
//XXX: do the blink if the first command hasn't arrived yet
407
// configures pin 13 as output and then back as input
412
/* SERIALREAD - processing incoming messagse as soon as possible, while still
413
* checking digital inputs. */
414
while(dataOnSerial) {
415
Firmata.processInput();
416
dataOnSerial = Firmata.available();
419
/* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over
420
* 60 bytes. use a timer to sending an event character every 4 ms to
421
* trigger the buffer to dump. */
423
currentMillis = millis();
424
if (currentMillis - previousMillis > samplingInterval) {
425
previousMillis += samplingInterval;
426
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
427
for(pin=0; pin<TOTAL_PINS; pin++) {
428
if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) {
429
analogPin = PIN_TO_ANALOG(pin);
430
if (analogInputsToReport & (1 << analogPin)) {
431
Firmata.sendAnalog(analogPin, analogRead(analogPin));