Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WiFi] added support for ESP32 architecture and XIAO ESP32C3 board #512

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions Boards.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Boards.h - Hardware Abstraction Layer for Firmata library
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2009-2017 Jeff Hoefs. All rights reserved.
Copyright (C) 2023 Jens B. All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
Expand All @@ -10,7 +11,7 @@

See file LICENSE.txt for further informations on licensing terms.

Last updated April 15th, 2018
Last updated December 17th, 2023
*/

#ifndef Firmata_Boards_h
Expand All @@ -29,7 +30,7 @@
// compile, but without support for any Servos. Hopefully that's what the
// user intended by not including Servo.h
#ifndef MAX_SERVOS
#define MAX_SERVOS 0
#define MAX_SERVOS 0
#endif

/*
Expand Down Expand Up @@ -1021,6 +1022,85 @@ writePort(port, value, bitmask): Write an 8 bit port.
#define PIN_TO_SERVO(p) (p)
#define DEFAULT_PWM_RESOLUTION 10

// XIAO ESP32C3
// note: Firmata pin numbering schema is by ESP32 GPIO -> IS_XXX checks GPIO number (Ax = Dx = GPIOx)
#elif defined(ARDUINO_XIAO_ESP32C3)
#define TOTAL_ANALOG_PINS (A2 + 1) // (max GPIOx + 1), there are 4 physical analog pins but only 3 are supported by ESP32 SDK 2.0.14 via ADC1
#define TOTAL_PINS NUM_DIGITAL_PINS // (max GPIOx + 1), there are 11 physical pins
#define PIN_SERIAL_RX RX
#define PIN_SERIAL_TX TX
#define IS_PIN_DIGITAL(p) (((p) >= D0 && (p) <= D10) || (p) == D6 || (p) == D7)
#define IS_PIN_ANALOG(p) ((p) >= A0 && (p) <= A2)
#define IS_PIN_PWM(p) 0
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && MAX_SERVOS > 0)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p) > NOT_AN_INTERRUPT)
#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL_RX || (p) == PIN_SERIAL_TX)
#define PIN_TO_DIGITAL(p) (p) // FIRMATAx to GPIOy
#define PIN_TO_ANALOG(p) (p) // FIRMATAx to GPIOy
#define PIN_TO_PWM(p) 127 // @TODO ESP32 SDK does not support analogWrite()
#define PIN_TO_SERVO(p) ((p) - D0)

#define DEFAULT_PWM_RESOLUTION 8 // see esp32-hal-led.c, analog_resolution
#define DEFAULT_ADC_RESOLUTION 12 // see esp32-hal-adc.h, analogSetWidth()

// WT32-ETH01 (ESP32-S1)
// note: Firmata pin numbering schema is by ESP32 GPIO -> IS_XXX checks GPIO number (Ax = Dx = IOx = GPIOx)
#elif defined(ARDUINO_WT32_ETH01)
#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS // (max GPIOx + 1), there are 9 physical analog pins
#define TOTAL_PINS NUM_DIGITAL_PINS // (max GPIOx + 1), there are 15 physical pins, 3 of them are input only
#define PIN_SERIAL1_RX RX
#define PIN_SERIAL1_TX TX
#define PIN_SERIAL2_RX TXD
#define PIN_SERIAL2_TX RXD
#define IS_PIN_DIGITAL(p) (((p) <= IO5) || (p) == IO12 || (p) == IO14 || (p) == IO15 || (p) == IO17 || (p) == IO32 || (p) == IO33 || (p) == IO35 || (p) == IO36 || (p) == IO39)
#define IS_PIN_ANALOG(p) (((p) <= IO5) || (p) == IO12 || (p) == IO14 || (p) == IO15)
#define IS_PIN_PWM(p) 0
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && MAX_SERVOS > 0)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p) > NOT_AN_INTERRUPT)
#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL1_RX || (p) == PIN_SERIAL1_TX || (p) == PIN_SERIAL2_RX || (p) == PIN_SERIAL2_TX)
#define PIN_TO_DIGITAL(p) (p) // FIRMATAx to GPIOy
#define PIN_TO_ANALOG(p) (p) // FIRMATAx to GPIOy
#define PIN_TO_PWM(p) 127 // @TODO ESP32 SDK does not support analogWrite()
#define PIN_TO_SERVO(p) (p)

#define DEFAULT_PWM_RESOLUTION 8 // see esp32-hal-led.c, analog_resolution
#define DEFAULT_ADC_RESOLUTION 12 // see esp32-hal-adc.h, analogSetWidth()

// generic ESP32
// note: Firmata pin numbering schema is by ESP32 GPIO -> IS_XXX checks GPIO number (Ax = Dx = IOx = GPIOx)
#elif defined(ARDUINO_ARCH_ESP32)
#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS // (max GPIOx + 1)
#define TOTAL_PINS NUM_DIGITAL_PINS // (max GPIOx + 1)
#define PIN_SERIAL_RX RX
#define PIN_SERIAL_TX TX
#if defined(CONFIG_IDF_TARGET_ESP32)
#define IS_PIN_DIGITAL(p) (((p) >= 0 && (p) < NUM_DIGITAL_PINS) && !((p) >= 6 && (p) <= 11)) // exclude SPI flash pins
#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define IS_PIN_DIGITAL(p) (((p) >= 0 && (p) < NUM_DIGITAL_PINS) && !((p) >= 26 && (p) <= 32)) // exclude SPI flash pins
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
#define IS_PIN_DIGITAL(p) (((p) >= 0 && (p) < NUM_DIGITAL_PINS) && !((p) >= 11 && (p) <= 17)) // exclude SPI flash pins
#else
#error "Please edit Boards.h with a hardware abstraction for this board"
#endif
#define IS_PIN_ANALOG(p) ((p) >= 0 && (p) < NUM_ANALOG_INPUTS)
#define IS_PIN_PWM(p) 0
#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && MAX_SERVOS > 0)
#define IS_PIN_I2C(p) ((p) == SDA || (p) == SCL)
#define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK)
#define IS_PIN_INTERRUPT(p) (digitalPinToInterrupt(p) > NOT_AN_INTERRUPT)
#define IS_PIN_SERIAL(p) ((p) == PIN_SERIAL_RX || (p) == PIN_SERIAL_TX)
#define PIN_TO_DIGITAL(p) (p) // FIRMATAx to GPIOy
#define PIN_TO_ANALOG(p) (p) // FIRMATAx to GPIOy
#define PIN_TO_PWM(p) 127 // @TODO ESP32 SDK does not support analogWrite()
#define PIN_TO_SERVO(p) (p)

#define DEFAULT_PWM_RESOLUTION 8 // see esp32-hal-led.c, analog_resolution
#define DEFAULT_ADC_RESOLUTION 12 // see esp32-hal-adc.h, analogSetWidth()

// STM32 based boards
#elif defined(ARDUINO_ARCH_STM32)
#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS
Expand Down
6 changes: 4 additions & 2 deletions Firmata.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Firmata.h - Firmata library v2.5.8 - 2018-04-15
Firmata.h - Firmata library v2.5.10 - 2023-12-16
Copyright (c) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2009-2017 Jeff Hoefs. All rights reserved.

Expand Down Expand Up @@ -38,7 +38,9 @@
//#define INPUT 0x00 // defined in Arduino.h
//#define OUTPUT 0x01 // defined in Arduino.h
// DEPRECATED as of Firmata v2.5
#define ANALOG 0x02 // same as PIN_MODE_ANALOG
#ifndef ARDUINO_ARCH_ESP32
#define ANALOG 0x02 // same as PIN_MODE_ANALOG
#endif
Comment on lines +41 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why this is necessary again?

Maybe we can figure out a creative solution to work around your need. My fear is that you are going to break backward compatibility with this change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to do a similar change in ConfigurableFirmata, as the problem is that there's a conflicting definition of ANALOG (as well as INPUT and OUTPUT) in the ESP32 headers. IIRC, the values don't even match, so setting the pin mode could strangely fail, depending on which of the definitions is in scope at the calling place. But I think the right solution is to call this PIN_MODE_ANALOG, to avoid further ambiguities.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does PIN_MODE_ANALOG come from Arduino.h?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see it. It's in FirmataDefines.h. It seems like the right move is to move away from ANALOG and deprecate it altogether, as we did for INPUT and OUTPUT.

#define PWM 0x03 // same as PIN_MODE_PWM
#define SERVO 0x04 // same as PIN_MODE_SERVO
#define SHIFT 0x05 // same as PIN_MODE_SHIFT
Expand Down
2 changes: 1 addition & 1 deletion FirmataConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace firmata {
*/
static const int FIRMWARE_MAJOR_VERSION = 2;
static const int FIRMWARE_MINOR_VERSION = 5;
static const int FIRMWARE_BUGFIX_VERSION = 7;
static const int FIRMWARE_BUGFIX_VERSION = 10;

/* Version numbers for the protocol. The protocol is still changing, so these
* version numbers are important.
Expand Down
54 changes: 33 additions & 21 deletions examples/StandardFirmata/StandardFirmata.ino
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
Copyright (C) 2023 Jens B. All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
Expand All @@ -20,10 +21,14 @@

See file LICENSE.txt for further informations on licensing terms.

Last updated August 17th, 2017
Last updated December 17th, 2023
*/

#include <Servo.h>
#ifdef ARDUINO_ARCH_ESP32
#include <ESP32Servo.h>
#else
#include <Servo.h>
#endif
#include <Wire.h>
#include <Firmata.h>

Expand Down Expand Up @@ -52,7 +57,7 @@ SerialFirmata serialFeature;
#endif

/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int analogInputsToReport = 0; // bitwise array to store pin reporting

/* digital input ports */
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
Expand All @@ -64,7 +69,7 @@ byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
unsigned int samplingInterval = 19; // how often to run the main loop (in ms)
unsigned int samplingInterval = 19; // how often to sample analog inputs and i2c (in ms)

/* i2c data */
struct i2c_device_info {
Expand All @@ -74,7 +79,7 @@ struct i2c_device_info {
byte stopTX;
};

/* for i2c read continuous more */
/* for i2c read continuous mode */
i2c_device_info query[I2C_MAX_QUERIES];

byte i2cRxData[64];
Expand Down Expand Up @@ -151,8 +156,10 @@ void detachServo(byte pin)
} else if (servoCount > 0) {
// keep track of detached servos because we want to reuse their indexes
// before incrementing the count of attached servos
detachedServoCount++;
detachedServos[detachedServoCount - 1] = servoPinMap[pin];
if (detachedServoCount < MAX_SERVOS) {
detachedServos[detachedServoCount] = servoPinMap[pin];
detachedServoCount++;
}
}

servoPinMap[pin] = 255;
Expand Down Expand Up @@ -277,10 +284,11 @@ void setPinModeCallback(byte pin, int mode)
}
}
if (IS_PIN_ANALOG(pin)) {
reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting
// turn on/off reporting
reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0);
}
if (IS_PIN_DIGITAL(pin)) {
if (mode == INPUT || mode == PIN_MODE_PULLUP) {
if (mode == PIN_MODE_INPUT || mode == PIN_MODE_PULLUP) {
portConfigInputs[pin / 8] |= (1 << (pin & 7));
} else {
portConfigInputs[pin / 8] &= ~(1 << (pin & 7));
Expand All @@ -300,14 +308,14 @@ void setPinModeCallback(byte pin, int mode)
Firmata.setPinMode(pin, PIN_MODE_ANALOG);
}
break;
case INPUT:
case PIN_MODE_INPUT:
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
#if ARDUINO <= 100
// deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
#endif
Firmata.setPinMode(pin, INPUT);
Firmata.setPinMode(pin, PIN_MODE_INPUT);
}
break;
case PIN_MODE_PULLUP:
Expand All @@ -317,14 +325,14 @@ void setPinModeCallback(byte pin, int mode)
Firmata.setPinState(pin, 1);
}
break;
case OUTPUT:
case PIN_MODE_OUTPUT:
if (IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == PIN_MODE_PWM) {
// Disable PWM if pin mode was previously set to PWM.
digitalWrite(PIN_TO_DIGITAL(pin), LOW);
}
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
Firmata.setPinMode(pin, OUTPUT);
Firmata.setPinMode(pin, PIN_MODE_OUTPUT);
}
break;
case PIN_MODE_PWM:
Expand Down Expand Up @@ -371,7 +379,7 @@ void setPinModeCallback(byte pin, int mode)
void setPinValueCallback(byte pin, int value)
{
if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == OUTPUT) {
if (Firmata.getPinMode(pin) == PIN_MODE_OUTPUT) {
Firmata.setPinState(pin, value);
digitalWrite(PIN_TO_DIGITAL(pin), value);
}
Expand Down Expand Up @@ -408,11 +416,11 @@ void digitalWriteCallback(byte port, int value)
// do not disturb non-digital pins (eg, Rx & Tx)
if (IS_PIN_DIGITAL(pin)) {
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) {
if (Firmata.getPinMode(pin) == PIN_MODE_OUTPUT || Firmata.getPinMode(pin) == PIN_MODE_INPUT) {
pinValue = ((byte)value & mask) ? 1 : 0;
if (Firmata.getPinMode(pin) == OUTPUT) {
if (Firmata.getPinMode(pin) == PIN_MODE_OUTPUT) {
pinWriteMask |= mask;
} else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) {
} else if (Firmata.getPinMode(pin) == PIN_MODE_INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) {
// only handle INPUT here for backwards compatibility
#if ARDUINO > 100
pinMode(pin, INPUT_PULLUP);
Expand Down Expand Up @@ -635,22 +643,26 @@ void sysexCallback(byte command, byte argc, byte *argv)
Firmata.write(CAPABILITY_RESPONSE);
for (byte pin = 0; pin < TOTAL_PINS; pin++) {
if (IS_PIN_DIGITAL(pin)) {
Firmata.write((byte)INPUT);
Firmata.write((byte)PIN_MODE_INPUT);
Firmata.write(1);
Firmata.write((byte)PIN_MODE_PULLUP);
Firmata.write(1);
Firmata.write((byte)OUTPUT);
Firmata.write((byte)PIN_MODE_OUTPUT);
Firmata.write(1);
}
if (IS_PIN_ANALOG(pin)) {
Firmata.write(PIN_MODE_ANALOG);
#ifdef DEFAULT_ADC_RESOLUTION
Firmata.write(DEFAULT_ADC_RESOLUTION);
#else
Firmata.write(10); // 10 = 10-bit resolution
#endif
}
if (IS_PIN_PWM(pin)) {
Firmata.write(PIN_MODE_PWM);
Firmata.write(DEFAULT_PWM_RESOLUTION);
}
if (IS_PIN_DIGITAL(pin)) {
if (IS_PIN_SERVO(pin)) {
Firmata.write(PIN_MODE_SERVO);
Firmata.write(14);
}
Expand Down Expand Up @@ -730,7 +742,7 @@ void systemResetCallback()
setPinModeCallback(i, PIN_MODE_ANALOG);
} else if (IS_PIN_DIGITAL(i)) {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, OUTPUT);
setPinModeCallback(i, PIN_MODE_OUTPUT);
}

servoPinMap[i] = 255;
Expand Down
Loading