For those, who want to compile Firmata for RPi-Pico boards, hereby I'm sharing my improved .INO file that is working better, until it is officially released as v3.4.0+
It has many improvements compared to the standard ConfigurableFirmata example file.
(See comments inside the code)
I strongly recommend to compile it with board library, called: Earle Phil's Arduino-Pico instead of the 4 year old "embedOS".
Remember to
overwrite the boards.h file in the library folder!
/*
* ConfigurableFirmata standard example file, for Serial + Wifi communication.
*
* Read more about board support and how to install necessary libraries here:
* https://github.com/firmata/ConfigurableFirmata/blob/master/BoardSupport.md
*/
/* Usage: Easily enable or disable each module.
* To disable, put: // before "#define ... " lines.
* Disable everything you do not need.
* Enable modules one by one, and re-re-try to compile to see if it works?
*/
/* 1.1) Change this string to identify this board easier. Like: "My Pico-robot2025"
* Firmata will send this info to client library (which is running on a PC or RPi-Pi) after first connect.
* Also: version protocol + capabilities will be sent too. */
const char* boardName = "myFirmataBoard";
/* 1.2.) Safety Password for future use. Not implemented yet.
* ! WARNING ! Never send the plain boardPassw back to the host!
* Mix it with the "salt" ( = random code / timestamp), you recieved from host, and create a (hexa)HASH from those two before sending it back. */
const char* boardPassw = "";
/* 2.1.) ENABLE_WIFI
* Uncomment this, to enable WIFI instead of serial communication.
* (Tested on ESP32, but should also work with Wifi-enabled Arduinos + PicoW + Pico2W)
*/
//#define ENABLE_WIFI
const char* ssid = "your-ssid";
const char* password = "your-password";
const int NETWORK_PORT = 27016;
/* 2.2.) ... or set serial baud rate:
* !WARNING! This value should match with the client library connection speed!
* (Because usually UART chips can not handle assimetric connections.)
* Default was 57600 for long time. The "firmata.js" client library is still using that value.
* If connected via USB-UART, this value has no effect, because the rate is always maximum. (1Mbit)
* read more here: https://github.com/firmata/ConfigurableFirmata/issues/180
*/
const int BAUD_RATE = 1200; // 2400 3600 7200 9600 14400 28800 57600 115200 230400 460800 921600 or higher?.
/* 3.1.) DISABLE_BLINK_VERSION
* Uncomment to save a couple of seconds by disabling the startup blink sequence,
* !WARNING! Some LEDs are connected to real GPIO ports! Like at: Arduino Nano board.
* So this prevents turning relays ON/OFF/ON/OFF... quickly during boot, what can be bad. :-(
*/
//#define DISABLE_BLINK_VERSION
/* 3.2.) ENABLE_START_BLINK
Uncomment this to pause booting process by blinking the LED 2x, 7 seconds long.
See at: setup() at near end of this file.
It is useful, if you need time to start a debugger process or serial monitor before boot happens.
*/
//#define ENABLE_START_BLINK
/* 3.3.) Uncomment these 2 lines, to Set On / Off state to then opposite:
* (Some cheap relay boards need GND to close the circuit, and 5V to open.)
* ! WARNING ! This "Software Solution" is not 100% safe to use!
* ! DANGER ! On critical applications, (water boil, solar, industry ...) rather solve it on the the hardware side.
* it is always wiser to use proper opto NPN decouplers between this board and the relays.
*/
// #define LOW 1
// #define HIGH 0
/* 4.1) Note that the SERVO module is not supported yet on ESP32. So either disable this or patch the library
* More info / current state of fix: https://github.com/firmata/ConfigurableFirmata/issues/178 */
#ifndef ESP32
// #define ENABLE_SERVO
#endif
// 4.2.) Other modules: (simply disable everything you do not need)
//#define ENABLE_ONE_WIRE
//#define ENABLE_ACCELSTEPPER
//#define ENABLE_BASIC_SCHEDULER // This is rarely used and probably will not compile/work
//#define ENABLE_SERIAL // This is for using an other serial through GPIO pins. Not for default Host-USB-serial.
//#define ENABLE_I2C
//#define ENABLE_SPI
#define ENABLE_ANALOG
#define ENABLE_DIGITAL
//#define ENABLE_DHT
#define ENABLE_FREQUENCY
// 4.3.) SLEEP is supported only for AVR and ESP32
#if defined (ESP32) || defined (ARDUINO_ARCH_AVR)
#define ENABLE_SLEEP
#endif
// 5.) LED signaling the state of board.
//#define BLINK_SERIAL_STATE // enable this if you want your LED blinking: slow/fast, if serial is: OK/not
#ifdef BLINK_SERIAL_STATE
#define NORMAL_BLINK_INTERVAL 1000
#define FAST_BLINK_INTERVAL 100
#define SERIAL_TIMEOUT 2000
unsigned long lastSerialTime = 0;
unsigned long lastBlinkTime = 0;
unsigned long blinkInterval = NORMAL_BLINK_INTERVAL;
bool ledState = false;
#endif
/***********************************************
*** DO NOT CHANGE things down from here ***
(unless you know what you do)
The setup() and loop() are at the end.
************************************************/
#include <ConfigurableFirmata.h>
#ifdef ENABLE_DIGITAL
#include <DigitalInputFirmata.h>
DigitalInputFirmata digitalInput;
#include <DigitalOutputFirmata.h>
DigitalOutputFirmata digitalOutput;
#endif
#ifdef ENABLE_SLEEP
#include "ArduinoSleep.h"
ArduinoSleep sleeper(39, 0);
#endif
#ifdef ENABLE_ANALOG
#include <AnalogInputFirmata.h>
AnalogInputFirmata analogInput;
#include <AnalogOutputFirmata.h>
AnalogOutputFirmata analogOutput;
#endif
#ifdef ENABLE_WIFI
#include <WiFi.h>
#include "utility/WiFiClientStream.h"
#include "utility/WiFiServerStream.h"
WiFiServerStream serverStream(NETWORK_PORT);
#endif
#ifdef ENABLE_I2C
#include <Wire.h>
#include <I2CFirmata.h>
I2CFirmata i2c;
#endif
#ifdef ENABLE_SPI
#include <Wire.h>
#include <SpiFirmata.h>
SpiFirmata spi;
#endif
#ifdef ENABLE_ONE_WIRE
#include <OneWireFirmata.h>
OneWireFirmata oneWire;
#endif
#ifdef ENABLE_SERIAL
#include <SerialFirmata.h>
SerialFirmata serial;
#endif
#ifdef ENABLE_DHT
#include <DhtFirmata.h>
DhtFirmata dhtFirmata;
#endif
#include <FirmataExt.h>
FirmataExt firmataExt;
#ifdef ENABLE_SERVO
#include <Servo.h>
#include <ServoFirmata.h>
ServoFirmata servo;
#endif
#include <FirmataReporting.h>
FirmataReporting reporting;
#ifdef ENABLE_ACCELSTEPPER
#include <AccelStepperFirmata.h>
AccelStepperFirmata accelStepper;
#endif
#ifdef ENABLE_FREQUENCY
#include <Frequency.h>
Frequency frequency;
#endif
#ifdef ENABLE_BASIC_SCHEDULER
// The scheduler allows to store scripts on the board, however this requires a kind of compiler on the client side.
// When running dotnet/iot on the client side, prefer using the FirmataIlExecutor module instead
#include <FirmataScheduler.h>
FirmataScheduler scheduler;
#endif
void systemResetCallback()
{
// Does more harm than good on ESP32 (because may touch pins reserved
// for memory IO and other reserved functions)
#ifndef ESP32
for (byte i = 0; i < TOTAL_PINS; i++)
{
if (FIRMATA_IS_PIN_ANALOG(i))
{
Firmata.setPinMode(i, PIN_MODE_ANALOG);
}
else if (IS_PIN_DIGITAL(i))
{
Firmata.setPinMode (i, PIN_MODE_OUTPUT);
}
}
#endif
firmataExt.reset();
}
void initTransport()
{
#ifdef DISABLE_BLINK_VERSION
Firmata.disableBlinkVersion();
#endif
#ifdef ESP8266
// need to ignore pins 1 and 3 when using an ESP8266 board. These are used for the serial communication.
Firmata.setPinMode(1, PIN_MODE_IGNORE);
Firmata.setPinMode(3, PIN_MODE_IGNORE);
#endif
#ifdef ENABLE_WIFI
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
pinMode(VERSION_BLINK_PIN, OUTPUT);
bool pinIsOn = false;
while (WiFi.status() != WL_CONNECTED)
{
delay(100);
pinIsOn = !pinIsOn;
digitalWrite(VERSION_BLINK_PIN, pinIsOn);
}
Firmata.begin(serverStream);
Firmata.blinkVersion(); // Because the above doesn't do it.
#else
Firmata.begin(BAUD_RATE);
#endif
}
void initFirmata()
{
#ifdef ENABLE_DIGITAL
firmataExt.addFeature(digitalInput);
firmataExt.addFeature(digitalOutput);
#endif
#ifdef ENABLE_ANALOG
firmataExt.addFeature(analogInput);
firmataExt.addFeature(analogOutput);
#endif
#ifdef ENABLE_SERVO
firmataExt.addFeature(servo);
#endif
#ifdef ENABLE_I2C
firmataExt.addFeature(i2c);
#endif
#ifdef ENABLE_ONE_WIRE
firmataExt.addFeature(oneWire);
#endif
#ifdef ENABLE_SERIAL
firmataExt.addFeature(serial);
#endif
#ifdef ENABLE_BASIC_SCHEDULER
firmataExt.addFeature(scheduler);
#endif
firmataExt.addFeature(reporting);
#ifdef ENABLE_SPI
firmataExt.addFeature(spi);
#endif
#ifdef ENABLE_ACCELSTEPPER
firmataExt.addFeature(accelStepper);
#endif
#ifdef ENABLE_DHT
firmataExt.addFeature(dhtFirmata);
#endif
#ifdef ENABLE_FREQUENCY
firmataExt.addFeature(frequency);
#endif
#ifdef ENABLE_SLEEP
firmataExt.addFeature(sleeper);
#endif
Firmata.attach(SYSTEM_RESET, systemResetCallback);
}
/************* END of Definitions part *************/
void setup()
{
// Boot pause BLINK test:
#ifdef ENABLE_START_BLINK
pinMode(VERSION_BLINK_PIN, OUTPUT);
digitalWrite(VERSION_BLINK_PIN, HIGH);
delay(4000);
digitalWrite(VERSION_BLINK_PIN, LOW);
delay(1000);
digitalWrite(VERSION_BLINK_PIN, HIGH);
delay(100);
digitalWrite(VERSION_BLINK_PIN, LOW);
#endif
#ifdef BLINK_SERIAL_STATE
pinMode(VERSION_BLINK_PIN, OUTPUT);
digitalWrite(VERSION_BLINK_PIN, LOW);
lastSerialTime = millis();
#endif
// Set firmware name and version.
// (Need to do this before initTransport(), because some client libraries expect that a reset sends this automatically.)
Firmata.setFirmwareNameAndVersion(boardName, FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
initTransport();
Firmata.sendString(F("Booting device. Stand by...\n"));
initFirmata();
Firmata.sendString(F("init complete \n"));
Firmata.parse(SYSTEM_RESET);
Firmata.sendString(F("parse complete \n"));
// Raspberry Pi Pico/Pico2/W temperature initialization, using Earle F.'s library.
analogReadResolution(12);
// send TEMP °C + RawTempValue, formatted as JSON.
#include <stdio.h>
float c = analogReadTemp();
char buf[127], buf2[32];
snprintf(buf , 25, "{\"celsius\": %f ", c);
#include <hardware/structs/adc.h>
hw_set_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS);
delay(1); // Allow things to settle. Without this, readings can be erratic
int _rt = analogRead(TOTAL_PINS - 1);
snprintf(buf2, 25, ",\"rawTemp\": %d", _rt);
strcat (buf , buf2);
// float celsius = 27.0f - ((v * vref / 4096.0f) - 0.706f) / 0.001721f; // const float vref = 3.3f;
// Send serial + boardName
uint len;
pico_get_unique_board_id_string(buf2, len);
strcat (buf , ",\"serial\":\"");
strcat (buf , buf2);
strcat (buf , "\",\"boardName\":\"");
strcat (buf , boardName);
strcat (buf , "\"}");
Firmata.sendString(F( buf ));
/*
// speed test: send 1000*100 char and measure speed (100 char = 1424 bit including SYSEX start/stop)
unsigned long t1, t2 = 0;
t1 = millis();
for (int k = 0; k<1000; k++) {
Firmata.sendString(F( "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" ));
}
t2 = millis();
snprintf(buf , 32, "start (ms): %d ", t1); // 1882ms
Firmata.sendString(F( buf ));
snprintf(buf , 32, "end (ms): %d ", t2); // 3466ms
Firmata.sendString(F( buf ));
snprintf(buf , 32, "Ellapsed: %d ", t2-t1); //1424 bit = 1584ms -> 904126 baud
Firmata.sendString(F( buf ));
*/
}
void loop()
{
#ifdef BLINK_SERIAL_STATE
// Check for incoming serial data
if (Serial.available()) {
lastSerialTime = millis();
}
// Adjust blink interval based on serial timeout
if (millis() - lastSerialTime > SERIAL_TIMEOUT) {
blinkInterval = FAST_BLINK_INTERVAL;
} else {
blinkInterval = NORMAL_BLINK_INTERVAL;
}
// Blink LED
if (millis() - lastBlinkTime >= blinkInterval) {
ledState = !ledState;
digitalWrite(VERSION_BLINK_PIN, ledState);
lastBlinkTime = millis();
}
#endif
while(Firmata.available())
{
Firmata.processInput();
if (!Firmata.isParsingMessage())
{
break;
}
}
// delay(1);
firmataExt.report(reporting.elapsed());
#ifdef ENABLE_WIFI
serverStream.maintain();
#endif
}