slopping up a platformio library for people to make rover peripherals

This commit is contained in:
legop3
2026-09-09 18:43:39 -04:00
parent 8895ed6bd8
commit 9ca039229a
13 changed files with 1005 additions and 501 deletions
+50 -75
View File
@@ -1,10 +1,4 @@
#include <Arduino.h>
#include <ConfigurableFirmata.h>
#include <FirmataExt.h>
#include <RoverPeripheralFirmata.h>
FirmataExt firmataExtension;
RoverPeripheralFirmata peripheral("Rover GPIO");
#include <RoverPeripheral.h>
namespace {
// Every example pin is present on both the classic ESP32 DevKitC and the
@@ -12,18 +6,33 @@ namespace {
// S3 boards use them for USB D- and D+.
constexpr uint8_t kSpecialActionPin = 21;
void runSpecialAction() {
// This intentionally represents arbitrary device behavior rather than a raw
// pin mapping. Replace it with a motor sequence, LED animation, actuator
// routine, or any other application-specific function the accessory needs.
digitalWrite(kSpecialActionPin, HIGH);
delay(80);
digitalWrite(kSpecialActionPin, LOW);
int repeatCount = 1;
String displayMessage;
void runSpecialAction(bool pressed) {
// Receiving both button edges lets application hardware remain active only
// while the driver holds the momentary control.
digitalWrite(kSpecialActionPin, pressed ? HIGH : LOW);
}
void registerBuiltInRoverControls() {
// These three roles replace physical GPIO backends while preserving the
// existing camera, headlight, and laser commands and HUD controls.
void setRepeatCount(int value) {
// A real device can use this value when it starts its next animation or
// actuator sequence. Storing it keeps this reference callback non-blocking.
repeatCount = value;
}
void setDisplayMessage(const String& value) {
// Display hardware can render the stored value from updateRoverPeripheral().
// Avoiding Serial output is important because Serial belongs to Firmata.
displayMessage = value;
}
} // namespace
void configureRoverPeripheral(RoverPeripheral& io) {
io.name("Rover GPIO");
// Standard roles retain the rover's existing HUD controls while moving the
// electrical outputs to this ESP32 on either a Pi or laptop rover host.
RoverCameraServoConfig cameraServo;
cameraServo.pin = 14;
cameraServo.minimumAngleDegrees = -15;
@@ -34,89 +43,55 @@ void registerBuiltInRoverControls() {
cameraServo.maximumPulseMicroseconds = 2100;
cameraServo.allowRawPulse = false;
cameraServo.inverted = false;
peripheral.addRoverCameraServo(cameraServo);
io.addCameraServo(cameraServo);
RoverDigitalOutputConfig headlight;
headlight.pin = 18;
headlight.polarity = OutputPolarity::ActiveHigh;
headlight.initiallyOn = false;
peripheral.addRoverHeadlight(headlight);
io.addHeadlight(headlight);
RoverDigitalOutputConfig laser;
laser.pin = 16;
laser.polarity = OutputPolarity::ActiveHigh;
laser.initiallyOn = false;
peripheral.addRoverLaser(laser);
}
io.addLaser(laser);
void registerGenericControls() {
// Registration order is UI order. This servo slider is handled entirely by
// standard Firmata SET_PIN_MODE and EXTENDED_ANALOG messages from roverd.
pinMode(kSpecialActionPin, OUTPUT);
digitalWrite(kSpecialActionPin, LOW);
// Accessory controls render in precisely this registration order.
SliderControlConfig servoPosition;
servoPosition.id = "servoPosition";
servoPosition.name = "Servo position";
servoPosition.minimum = 0;
servoPosition.maximum = 180;
FirmataServoOutput servoOutput;
ServoOutput servoOutput;
servoOutput.pin = 13;
peripheral.addServoSlider(servoPosition, servoOutput);
io.addSlider(servoPosition, servoOutput);
// PWM brightness is another standard Firmata output. No sketch callback is
// involved when the driver moves this slider.
SliderControlConfig lightBrightness;
lightBrightness.id = "lightBrightness";
lightBrightness.name = "Light brightness";
lightBrightness.minimum = 0;
lightBrightness.maximum = 255;
FirmataPwmOutput lightOutput;
PwmOutput lightOutput;
lightOutput.pin = 17;
peripheral.addPwmSlider(lightBrightness, lightOutput);
io.addSlider(lightBrightness, lightOutput);
// A custom momentary control receives both press and release. This example
// runs a one-shot action only on press, but a motor could use both values to
// start while held and stop on release.
ButtonControlConfig specialAction;
specialAction.id = "specialAction";
specialAction.name = "Run special action";
specialAction.name = "Special action";
specialAction.mode = ButtonMode::Momentary;
peripheral.addButton(specialAction, [](bool pressed) {
if (pressed) {
runSpecialAction();
}
});
}
} // namespace
void setup() {
pinMode(kSpecialActionPin, OUTPUT);
digitalWrite(kSpecialActionPin, LOW);
registerBuiltInRoverControls();
registerGenericControls();
// Supplying Serial as a Stream keeps all protocol code identical between a
// CH340/CP210x UART bridge and native ESP32-S3 USB CDC. Only PlatformIO's S3
// build flags differ.
Serial.begin(115200);
Firmata.begin(Serial);
peripheral.begin(firmataExtension);
// A Firmata system reset establishes declared initial output states and also
// proves that all callbacks were installed before normal traffic begins.
Firmata.parse(SYSTEM_RESET);
}
void loop() {
// Processing one complete parser unit at a time prevents a long serial burst
// from starving application work while still draining ordinary USB traffic
// quickly on both supported transports.
while (Firmata.available()) {
Firmata.processInput();
if (!Firmata.isParsingMessage()) {
break;
}
}
peripheral.update();
io.addButton(specialAction, runSpecialAction);
NumberControlConfig repeats;
repeats.name = "Repeat count";
repeats.minimum = 1;
repeats.maximum = 20;
io.addNumber(repeats, setRepeatCount);
TextControlConfig message;
message.name = "Display message";
message.maximumLength = 64;
io.addText(message, setDisplayMessage);
}