From 596f2c34835afc0e766beacd46bae47a72cf5c1d Mon Sep 17 00:00:00 2001 From: Jeff Hoefs Date: Sun, 28 May 2017 23:16:33 -0700 Subject: [PATCH] Initial pass at ESP32 support Wi-Fi transport only for now. Tested on Sparkfun ESP32 Thing board. --- .../ConfigurableFirmataWiFi.ino | 49 ++++++++++++++++--- src/AnalogInputFirmata.cpp | 10 +++- src/AnalogOutputFirmata.cpp | 5 ++ src/ConfigurableFirmata.h | 8 +-- src/DigitalInputFirmata.cpp | 6 +-- src/DigitalOutputFirmata.cpp | 12 ++--- src/utility/Boards.h | 33 ++++++++++++- src/utility/WiFiStream.h | 8 +-- 8 files changed, 106 insertions(+), 25 deletions(-) diff --git a/examples/ConfigurableFirmataWiFi/ConfigurableFirmataWiFi.ino b/examples/ConfigurableFirmataWiFi/ConfigurableFirmataWiFi.ino index 73368c1..c3d40b1 100644 --- a/examples/ConfigurableFirmataWiFi/ConfigurableFirmataWiFi.ino +++ b/examples/ConfigurableFirmataWiFi/ConfigurableFirmataWiFi.ino @@ -14,7 +14,7 @@ Copyright (C) 2013 Norbert Truchsess. All rights reserved. Copyright (C) 2014 Nicolas Panel. All rights reserved. Copyright (C) 2015-2016 Jesse Frush. All rights reserved. - Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved. + Copyright (C) 2009-2017 Jeff Hoefs. All rights reserved. Copyright (C) 2016 Jens B. All rights reserved. This library is free software; you can redistribute it and/or @@ -24,7 +24,7 @@ See file LICENSE.txt for further informations on licensing terms. - Last updated by Jeff Hoefs: August 14th, 2016 + Last updated by Jeff Hoefs: May 28th, 2017 */ /* @@ -43,6 +43,7 @@ - ESP8266 WiFi board compatible with ESP8266 Arduino core - Arduino WiFi Shield 101 - Arduino WiFi Shield (or clone) + - ESP32 board compatible with ESP32 Arduino core Follow the instructions in the wifiConfig.h file (wifiConfig.h tab in Arduino IDE) to configure your particular hardware. @@ -53,6 +54,7 @@ https://github.com/arduino-libraries/WiFi101) - ESP8266 requires the Arduino ESP8266 core v2.1.0 or higher which can be obtained here: https://github.com/esp8266/Arduino + - ESP32 requires the Arduino ESP32 core: https://github.com/espressif/arduino-esp32 In order to use the WiFi Shield 101 with Firmata you will need a board with at least 35k of Flash memory. This means you cannot use the WiFi Shield 101 with an Arduino Uno or any other @@ -85,7 +87,7 @@ * connection that may help in the event of connection issues. If defined, some boards may not begin * executing this sketch until the Serial console is opened. */ -//#define SERIAL_DEBUG +#define SERIAL_DEBUG #include "utility/firmataDebug.h" #define MAX_CONN_ATTEMPTS 20 // [500 ms] -> 10 s @@ -100,8 +102,8 @@ *============================================================================*/ // STEP 1 [REQUIRED] -// Uncomment / comment the appropriate set of includes for your hardware (OPTION A, B or C) -// Arduino MKR1000 or ESP8266 are enabled by default if compiling for either of those boards. +// Uncomment / comment the appropriate set of includes for your hardware (OPTION A, B, C, or D). +// Arduino MKR1000, ESP8266 and ESP32 boards are enabled by default if compiling for any of those boards. /* * OPTION A: Configure for Arduino MKR1000 or Arduino WiFi Shield 101 @@ -186,6 +188,30 @@ #endif #endif +/* + * OPTION D: Configure for ESP32 + * + * This will configure ConfigurableFirmataWiFi to use the ESP32 WiFi library for boards + * with an ESP32 chip. It is compatible with 802.11 B/G/N networks. + * + * The appropriate libraries are included automatically when compiling for the ESP32 so + * continue on to STEP 2. + * + * IMPORTANT: You must have the esp32 board support installed. To easily install this board see + * the instructions here: https://github.com/espressif/arduino-esp32#installation-instructions. + */ +#ifdef ESP32 +#define ESP32_WIFI +#include +#include "utility/WiFiClientStream.h" +#include "utility/WiFiServerStream.h" + #ifdef WIFI_LIB_INCLUDED + #define MULTIPLE_WIFI_LIB_INCLUDES + #else + #define WIFI_LIB_INCLUDED + #endif +#endif + // STEP 2 [OPTIONAL for all boards and shields] // By default the board/shield is configured as a TCP server. @@ -282,7 +308,7 @@ char wep_key[] = "your_wep_key"; #error "you must define a wifi security type in wifiConfig.h." #endif //WIFI_* security define check -#if (defined(ESP8266_WIFI) && !(defined(WIFI_NO_SECURITY) || (defined(WIFI_WPA_SECURITY)))) +#if ((defined(ESP8266_WIFI) || defined(ESP32_WIFI)) && !(defined(WIFI_NO_SECURITY) || (defined(WIFI_WPA_SECURITY)))) #error "you must choose between WIFI_NO_SECURITY and WIFI_WPA_SECURITY" #endif @@ -317,6 +343,9 @@ char wep_key[] = "your_wep_key"; #elif defined(ESP8266_WIFI) && defined(SERIAL_DEBUG) #define IS_IGNORE_PIN(p) ((p) == 1) +#elif defined(ESP32_WIFI) && defined(SERIAL_DEBUG) +#define IS_IGNORE_PIN(p) ((p) == 1 || (p) == 3) + #endif /*============================================================================== @@ -339,12 +368,18 @@ DigitalOutputFirmata digitalOutput; #include AnalogInputFirmata analogInput; +// analogWrite not supported for ESP32 boards +#ifndef ESP32 #include AnalogOutputFirmata analogOutput; +#endif +// servo not supported for ESP32 boards +#ifndef ESP32 #include #include ServoFirmata servo; +#endif // ServoFirmata depends on AnalogOutputFirmata #if defined ServoFirmata_h && ! defined AnalogOutputFirmata_h #error AnalogOutputFirmata must be included to use ServoFirmata @@ -514,6 +549,8 @@ void initTransport() DEBUG_PRINTLN( "using the legacy WiFi library." ); #elif defined(ESP8266_WIFI) DEBUG_PRINTLN( "using the ESP8266 WiFi library." ); +#elif defined(ESP32_WIFI) + DEBUG_PRINTLN( "using the ESP32 WiFi library." ); //else should never happen here as error-checking in wifiConfig.h will catch this #endif //defined(WIFI_101) diff --git a/src/AnalogInputFirmata.cpp b/src/AnalogInputFirmata.cpp index 4ac2377..efdad78 100644 --- a/src/AnalogInputFirmata.cpp +++ b/src/AnalogInputFirmata.cpp @@ -52,7 +52,8 @@ void AnalogInputFirmata::reportAnalog(byte analogPin, int value) // Send pin value immediately. This is helpful when connected via // ethernet, wi-fi or bluetooth so pin states can be known upon // reconnecting. - Firmata.sendAnalog(analogPin, analogRead(analogPin)); + // Firmata.sendAnalog(analogPin, analogRead(analogPin)); + Firmata.sendAnalog(analogPin, analogRead(analogInputToDigitalPin(analogPin))); } } } @@ -79,7 +80,11 @@ void AnalogInputFirmata::handleCapability(byte pin) { if (IS_PIN_ANALOG(pin)) { Firmata.write(PIN_MODE_ANALOG); +#ifdef DEFAULT_ADC_RESOLUTION + Firmata.write(DEFAULT_ADC_RESOLUTION); +#elif Firmata.write(10); // 10 = 10-bit resolution +#endif } } @@ -102,7 +107,8 @@ void AnalogInputFirmata::report() if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) { analogPin = PIN_TO_ANALOG(pin); if (analogInputsToReport & (1 << analogPin)) { - Firmata.sendAnalog(analogPin, analogRead(analogPin)); + // Firmata.sendAnalog(analogPin, analogRead(analogPin)); + Firmata.sendAnalog(analogPin, analogRead(analogInputToDigitalPin(analogPin))); } } } diff --git a/src/AnalogOutputFirmata.cpp b/src/AnalogOutputFirmata.cpp index 27522cf..4345dd5 100644 --- a/src/AnalogOutputFirmata.cpp +++ b/src/AnalogOutputFirmata.cpp @@ -20,6 +20,11 @@ #include "AnalogFirmata.h" #include "AnalogOutputFirmata.h" +// Hack to compile for ESP32 boards until arduino-esp32 supports analogWrite +#ifdef ESP32 +void analogWrite(byte pin, int value); +#endif + AnalogOutputFirmata::AnalogOutputFirmata() { Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); diff --git a/src/ConfigurableFirmata.h b/src/ConfigurableFirmata.h index cdb5ee6..77f88cd 100644 --- a/src/ConfigurableFirmata.h +++ b/src/ConfigurableFirmata.h @@ -100,8 +100,8 @@ #define SYSEX_SAMPLING_INTERVAL 0x7A // same as SAMPLING_INTERVAL // pin modes -//#define INPUT 0x00 // defined in Arduino.h -//#define OUTPUT 0x01 // defined in Arduino.h +#define PIN_MODE_INPUT 0x00 // pin connfigured for digital input +#define PIN_MODE_OUTPUT 0x01 // pin configured for digital output #define PIN_MODE_ANALOG 0x02 // analog pin in analogInput mode #define PIN_MODE_PWM 0x03 // digital pin in PWM output mode #define PIN_MODE_SERVO 0x04 // digital pin in Servo output mode @@ -115,7 +115,9 @@ #define PIN_MODE_IGNORE 0x7F // pin configured to be ignored by digitalWrite and capabilityResponse #define TOTAL_PIN_MODES 13 // DEPRECATED as of Firmata v2.5 -#define ANALOG 0x02 // same as PIN_MODE_ANALOG +// #undef ANALOG +// #define ANALOG 0x02 // same as PIN_MODE_ANALOG +#undef PWM #define PWM 0x03 // same as PIN_MODE_PWM #define SERVO 0x04 // same as PIN_MODE_SERVO #define SHIFT 0x05 // same as PIN_MODE_SHIFT diff --git a/src/DigitalInputFirmata.cpp b/src/DigitalInputFirmata.cpp index 565066d..5efd967 100644 --- a/src/DigitalInputFirmata.cpp +++ b/src/DigitalInputFirmata.cpp @@ -91,9 +91,9 @@ void DigitalInputFirmata::reportDigital(byte port, int value) boolean DigitalInputFirmata::handlePinMode(byte pin, int mode) { 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)); - if (mode == INPUT) { + if (mode == PIN_MODE_INPUT) { pinMode(PIN_TO_DIGITAL(pin), INPUT); } else { pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP); @@ -110,7 +110,7 @@ boolean DigitalInputFirmata::handlePinMode(byte pin, int mode) void DigitalInputFirmata::handleCapability(byte 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); diff --git a/src/DigitalOutputFirmata.cpp b/src/DigitalOutputFirmata.cpp index a4448f0..73d2462 100644 --- a/src/DigitalOutputFirmata.cpp +++ b/src/DigitalOutputFirmata.cpp @@ -35,7 +35,7 @@ void digitalOutputWriteCallback(byte port, int value) void handleSetPinValueCallback(byte pin, int value) { if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) { - if (Firmata.getPinMode(pin) == OUTPUT) { + if (Firmata.getPinMode(pin) == PIN_MODE_OUTPUT) { digitalWrite(pin, value); Firmata.setPinState(pin, value); } @@ -71,11 +71,11 @@ void DigitalOutputFirmata::digitalWritePort(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) { pinMode(pin, INPUT_PULLUP); } Firmata.setPinState(pin, pinValue); @@ -89,7 +89,7 @@ void DigitalOutputFirmata::digitalWritePort(byte port, int value) boolean DigitalOutputFirmata::handlePinMode(byte pin, int mode) { - if (IS_PIN_DIGITAL(pin) && mode == OUTPUT && Firmata.getPinMode(pin) != PIN_MODE_IGNORE) { + if (IS_PIN_DIGITAL(pin) && mode == PIN_MODE_OUTPUT && Firmata.getPinMode(pin) != PIN_MODE_IGNORE) { digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM pinMode(PIN_TO_DIGITAL(pin), OUTPUT); return true; @@ -100,7 +100,7 @@ boolean DigitalOutputFirmata::handlePinMode(byte pin, int mode) void DigitalOutputFirmata::handleCapability(byte pin) { if (IS_PIN_DIGITAL(pin)) { - Firmata.write((byte)OUTPUT); + Firmata.write((byte)PIN_MODE_OUTPUT); Firmata.write(1); } } diff --git a/src/utility/Boards.h b/src/utility/Boards.h index 762a47d..b0e766e 100644 --- a/src/utility/Boards.h +++ b/src/utility/Boards.h @@ -596,6 +596,33 @@ writePort(port, value, bitmask): Write an 8 bit port. #define PIN_TO_SERVO(p) (p) #define DEFAULT_PWM_RESOLUTION 10 +// ESP32 +// Tested on Sparkfun ESP32 Thing, may work with other ESP32 boards, but not verified. +// Pins D34 - D39 Digital Input only +// TODO: validate if Serial2 maps to rx = 16, tx = 17 +// ESP32 boards currently only work with Firmata over Wi-Fi. +// Supply > 500mA to the board (laptop USB current is typically not enough, use a powered USB hub). +#elif defined(ESP32) +#define TOTAL_ANALOG_PINS NUM_ANALOG_INPUTS +#define TOTAL_PINS NUM_DIGITAL_PINS +#define VERSION_BLINK_PIN LED_BUILTIN +// Serial0 not supported +// #define PIN_SERIAL_RX 3 +// #define PIN_SERIAL_TX 1 +#define IS_PIN_DIGITAL(p) ((p) < 6 || ((p) >= 12 && (p) < 24) || ((p) >= 25 && (p) < 28) || ((p) >= 32 && (p) <= 39)) +#define IS_PIN_ANALOG(p) ((p) == 0 || (p) == 2 || (p) == 4 || ((p) >= 12 && (p) < 16) || ((p >= 25 && (p) < 28) || ((p) >= 32 && (p) < 37) || (p) == 39)) +#define IS_PIN_PWM(p) (0) // analogWrite is not yet supported +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) < MAX_SERVOS) +#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) +#define PIN_TO_ANALOG(p) digitalPinToAnalogChannel(p) // defined in esp32-hal-gpio.h +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) (p) +#define DEFAULT_ADC_RESOLUTION 12 + // anything else #else @@ -604,7 +631,7 @@ writePort(port, value, bitmask): Write an 8 bit port. // as long this is not defined for all boards: #ifndef IS_PIN_SPI -#define IS_PIN_SPI(p) (0) +#define IS_PIN_SPI(p) 0 #endif #ifndef IS_PIN_SERIAL @@ -615,6 +642,10 @@ writePort(port, value, bitmask): Write an 8 bit port. #define DEFAULT_PWM_RESOLUTION 8 #endif +#ifndef DEFAULT_ADC_RESOLUTION +#define DEFAULT_ADC_RESOLUTION 10 +#endif + /*============================================================================== * readPort() - Read an 8 bit port *============================================================================*/ diff --git a/src/utility/WiFiStream.h b/src/utility/WiFiStream.h index 1ad44bb..d0bbd90 100644 --- a/src/utility/WiFiStream.h +++ b/src/utility/WiFiStream.h @@ -69,7 +69,7 @@ class WiFiStream : public Stream * network configuration ******************************************************************************/ -#ifndef ESP8266 +#if !defined(ESP8266) && !defined(ESP32) /** * configure a static local IP address without defining the local network * DHCP will be used as long as local IP address is not defined @@ -90,7 +90,7 @@ class WiFiStream : public Stream _local_ip = local_ip; _subnet = subnet; _gateway = gateway; -#ifndef ESP8266 +#if !defined(ESP8266) WiFi.config( local_ip, IPAddress(0, 0, 0, 0), gateway, subnet ); #else WiFi.config( local_ip, gateway, subnet ); @@ -115,7 +115,7 @@ class WiFiStream : public Stream */ virtual bool maintain() = 0; -#ifdef ESP8266 +#if defined(ESP8266) /** * get status of TCP connection * @return status of TCP connection @@ -160,7 +160,7 @@ class WiFiStream : public Stream return WiFi.status(); } -#ifndef ESP8266 +#if !defined(ESP8266) && !defined(ESP32) /** * initialize WiFi with WEP security and initiate client connection * if WiFi connection is already established