Skip to content

Commit 45fc6fc

Browse files
sync with StandardFirmata in master
1 parent 859f7dd commit 45fc6fc

File tree

1 file changed

+100
-32
lines changed

1 file changed

+100
-32
lines changed

examples/StandardFirmata/StandardFirmata.ino

Lines changed: 100 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
1414
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
1515
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
16-
Copyright (C) 2009-2013 Jeff Hoefs. All rights reserved.
16+
Copyright (C) 2009-2015 Jeff Hoefs. All rights reserved.
1717
1818
This library is free software; you can redistribute it and/or
1919
modify it under the terms of the GNU Lesser General Public
@@ -77,16 +77,62 @@ struct i2c_device_info {
7777
/* for i2c read continuous more */
7878
i2c_device_info query[MAX_QUERIES];
7979

80+
boolean isResetting = false;
81+
8082
byte i2cRxData[32];
8183
boolean isI2CEnabled = false;
8284
signed char queryIndex = -1;
8385
unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom()
8486

8587
Servo servos[MAX_SERVOS];
88+
byte servoPinMap[TOTAL_PINS];
89+
byte detachedServos[MAX_SERVOS];
90+
byte detachedServoCount = 0;
91+
byte servoCount = 0;
92+
93+
8694
/*==============================================================================
8795
* FUNCTIONS
8896
*============================================================================*/
8997

98+
void attachServo(byte pin, int minPulse, int maxPulse)
99+
{
100+
if (servoCount < MAX_SERVOS) {
101+
// reuse indexes of detached servos until all have been reallocated
102+
if (detachedServoCount > 0) {
103+
servoPinMap[pin] = detachedServos[detachedServoCount - 1];
104+
if (detachedServoCount > 0) detachedServoCount--;
105+
} else {
106+
servoPinMap[pin] = servoCount;
107+
servoCount++;
108+
}
109+
if (minPulse > 0 && maxPulse > 0) {
110+
servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
111+
} else {
112+
servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin));
113+
}
114+
} else {
115+
Firmata.sendString("Max servos attached");
116+
}
117+
}
118+
119+
void detachServo(byte pin)
120+
{
121+
servos[servoPinMap[pin]].detach();
122+
// if we're detaching the last servo, decrement the count
123+
// otherwise store the index of the detached servo
124+
if (servoPinMap[pin] == servoCount && servoCount > 0) {
125+
servoCount--;
126+
} else if (servoCount > 0) {
127+
// keep track of detached servos because we want to reuse their indexes
128+
// before incrementing the count of attached servos
129+
detachedServoCount++;
130+
detachedServos[detachedServoCount - 1] = servoPinMap[pin];
131+
}
132+
133+
servoPinMap[pin] = 255;
134+
}
135+
90136
void readAndReportData(byte address, int theRegister, byte numBytes) {
91137
// allow I2C requests that don't require a register read
92138
// for example, some devices using an interrupt pin to signify new data available
@@ -111,23 +157,21 @@ void readAndReportData(byte address, int theRegister, byte numBytes) {
111157
Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom
112158

113159
// check to be sure correct number of bytes were returned by slave
114-
if (numBytes == Wire.available()) {
115-
i2cRxData[0] = address;
116-
i2cRxData[1] = theRegister;
117-
for (int i = 0; i < numBytes; i++) {
160+
if (numBytes < Wire.available()) {
161+
Firmata.sendString("I2C Read Error: Too many bytes received");
162+
} else if (numBytes > Wire.available()) {
163+
Firmata.sendString("I2C Read Error: Too few bytes received");
164+
}
165+
166+
i2cRxData[0] = address;
167+
i2cRxData[1] = theRegister;
168+
169+
for (int i = 0; i < numBytes && Wire.available(); i++) {
118170
#if ARDUINO >= 100
119-
i2cRxData[2 + i] = Wire.read();
171+
i2cRxData[2 + i] = Wire.read();
120172
#else
121-
i2cRxData[2 + i] = Wire.receive();
173+
i2cRxData[2 + i] = Wire.receive();
122174
#endif
123-
}
124-
}
125-
else {
126-
if (numBytes > Wire.available()) {
127-
Firmata.sendString("I2C: Too many bytes received");
128-
} else {
129-
Firmata.sendString("I2C: Too few bytes received");
130-
}
131175
}
132176

133177
// send slave address, register and received bytes
@@ -182,8 +226,10 @@ void setPinModeCallback(byte pin, int mode)
182226
// the following if statements should reconfigure the pins properly
183227
disableI2CPins();
184228
}
185-
if (IS_PIN_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) {
186-
servos[PIN_TO_SERVO(pin)].detach();
229+
if (IS_PIN_DIGITAL(pin) && mode != SERVO) {
230+
if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) {
231+
detachServo(pin);
232+
}
187233
}
188234
if (IS_PIN_ANALOG(pin)) {
189235
reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting
@@ -228,10 +274,12 @@ void setPinModeCallback(byte pin, int mode)
228274
}
229275
break;
230276
case SERVO:
231-
if (IS_PIN_SERVO(pin)) {
277+
if (IS_PIN_DIGITAL(pin)) {
232278
pinConfig[pin] = SERVO;
233-
if (!servos[PIN_TO_SERVO(pin)].attached()) {
234-
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
279+
if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) {
280+
// pass -1 for min and max pulse values to use default values set
281+
// by Servo library
282+
attachServo(pin, -1, -1);
235283
}
236284
}
237285
break;
@@ -253,8 +301,8 @@ void analogWriteCallback(byte pin, int value)
253301
if (pin < TOTAL_PINS) {
254302
switch (pinConfig[pin]) {
255303
case SERVO:
256-
if (IS_PIN_SERVO(pin))
257-
servos[PIN_TO_SERVO(pin)].write(value);
304+
if (IS_PIN_DIGITAL(pin))
305+
servos[servoPinMap[pin]].write(value);
258306
pinState[pin] = value;
259307
break;
260308
case PWM:
@@ -303,6 +351,14 @@ void reportAnalogCallback(byte analogPin, int value)
303351
analogInputsToReport = analogInputsToReport & ~ (1 << analogPin);
304352
} else {
305353
analogInputsToReport = analogInputsToReport | (1 << analogPin);
354+
// prevent during system reset or all analog pin values will be reported
355+
// which may report noise for unconnected analog pins
356+
if (!isResetting) {
357+
// Send pin value immediately. This is helpful when connected via
358+
// ethernet, wi-fi or bluetooth so pin states can be known upon
359+
// reconnecting.
360+
Firmata.sendAnalog(analogPin, analogRead(analogPin));
361+
}
306362
}
307363
}
308364
// TODO: save status to EEPROM here, if changed
@@ -312,6 +368,10 @@ void reportDigitalCallback(byte port, int value)
312368
{
313369
if (port < TOTAL_PORTS) {
314370
reportPINs[port] = (byte)value;
371+
// Send port value immediately. This is helpful when connected via
372+
// ethernet, wi-fi or bluetooth so pin states can be known upon
373+
// reconnecting.
374+
if (value) outputPort(port, readPort(port, portConfigInputs[port]), true);
315375
}
316376
// do not disable analog reporting on these 8 pins, to allow some
317377
// pins used for digital, others analog. Instead, allow both types
@@ -412,7 +472,7 @@ void sysexCallback(byte command, byte argc, byte *argv)
412472
for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) {
413473
if (i < MAX_QUERIES) {
414474
query[i].addr = query[i + 1].addr;
415-
query[i].reg = query[i + 1].addr;
475+
query[i].reg = query[i + 1].reg;
416476
query[i].bytes = query[i + 1].bytes;
417477
}
418478
}
@@ -442,10 +502,11 @@ void sysexCallback(byte command, byte argc, byte *argv)
442502
int minPulse = argv[1] + (argv[2] << 7);
443503
int maxPulse = argv[3] + (argv[4] << 7);
444504

445-
if (IS_PIN_SERVO(pin)) {
446-
if (servos[PIN_TO_SERVO(pin)].attached())
447-
servos[PIN_TO_SERVO(pin)].detach();
448-
servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
505+
if (IS_PIN_DIGITAL(pin)) {
506+
if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) {
507+
detachServo(pin);
508+
}
509+
attachServo(pin, minPulse, maxPulse);
449510
setPinModeCallback(pin, SERVO);
450511
}
451512
}
@@ -486,7 +547,7 @@ void sysexCallback(byte command, byte argc, byte *argv)
486547
Firmata.write(PWM);
487548
Firmata.write(8);
488549
}
489-
if (IS_PIN_SERVO(pin)) {
550+
if (IS_PIN_DIGITAL(pin)) {
490551
Firmata.write(SERVO);
491552
Firmata.write(14);
492553
}
@@ -547,8 +608,6 @@ void disableI2CPins() {
547608
isI2CEnabled = false;
548609
// disable read continuous mode for all devices
549610
queryIndex = -1;
550-
// uncomment the following if or when the end() method is added to Wire library
551-
// Wire.end();
552611
}
553612

554613
/*==============================================================================
@@ -557,30 +616,38 @@ void disableI2CPins() {
557616

558617
void systemResetCallback()
559618
{
619+
isResetting = true;
560620
// initialize a defalt state
561621
// TODO: option to load config from EEPROM instead of default
562622
if (isI2CEnabled) {
563623
disableI2CPins();
564624
}
625+
565626
for (byte i = 0; i < TOTAL_PORTS; i++) {
566627
reportPINs[i] = false; // by default, reporting off
567628
portConfigInputs[i] = 0; // until activated
568629
previousPINs[i] = 0;
569630
}
570-
// pins with analog capability default to analog input
571-
// otherwise, pins default to digital output
631+
572632
for (byte i = 0; i < TOTAL_PINS; i++) {
633+
// pins with analog capability default to analog input
634+
// otherwise, pins default to digital output
573635
if (IS_PIN_ANALOG(i)) {
574636
// turns off pullup, configures everything
575637
setPinModeCallback(i, ANALOG);
576638
} else {
577639
// sets the output to 0, configures portConfigInputs
578640
setPinModeCallback(i, OUTPUT);
579641
}
642+
643+
servoPinMap[i] = 255;
580644
}
581645
// by default, do not report any analog inputs
582646
analogInputsToReport = 0;
583647

648+
detachedServoCount = 0;
649+
servoCount = 0;
650+
584651
/* send digital inputs to set the initial state on the host computer,
585652
* since once in the loop(), this firmware will only send on change */
586653
/*
@@ -590,6 +657,7 @@ void systemResetCallback()
590657
outputPort(i, readPort(i, portConfigInputs[i]), true);
591658
}
592659
*/
660+
isResetting = false;
593661
}
594662

595663
void setup()

0 commit comments

Comments
 (0)