Compile errors with PlatformIO in VS Code
What happened?
I have a working Arduino Project in the Arduino IDE using Bluepad32. When I switch to PlatformIO in VS Code it does not compile. I added Bluepad32 as library to my project with respective PlatformIO function.
I expected Bluepad32 to compile without problems.
Bluepad32 Version
1.3.1
Bluepad32 Platform and its version
PlatformIO in VS Code, Core 6.1.7·Home 3.4.4
Which controller
VR Park Bluetooth Controller
Which ESP32
ESP32 NodeMCU
OS
Windows 10
Logs
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 (6.3.2) > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES:
- framework-arduinoespressif32 @ 3.20009.0 (2.0.9)
- tool-esptoolpy @ 1.40501.0 (4.5.1)
- toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 34 compatible libraries
Scanning dependencies...
Dependency Graph
|-- Bluepad32 @ 1.3.1
Building in release mode
Compiling .pio\build\esp32dev\src\main.cpp.o
Building .pio\build\esp32dev\bootloader.bin
Generating partitions .pio\build\esp32dev\partitions.bin
esptool.py v4.5.1
Creating esp32 image...
Merged 1 ELF section
Successfully created esp32 image.
Compiling .pio\build\esp32dev\lib842\SPI\SPI.cpp.o
Compiling .pio\build\esp32dev\lib9f9\Bluepad32\Bluepad32.cpp.o
Compiling .pio\build\esp32dev\lib9f9\Bluepad32\Controller.cpp.o
Archiving .pio\build\esp32dev\lib842\libSPI.a
Compiling .pio\build\esp32dev\lib9f9\Bluepad32\Gamepad.cpp.o
Compiling .pio\build\esp32dev\lib9f9\Bluepad32\utility\spi_drv.cpp.o
Compiling .pio\build\esp32dev\FrameworkArduino\Esp.cpp.o
Compiling .pio\build\esp32dev\FrameworkArduino\FirmwareMSC.cpp.o
Compiling .pio\build\esp32dev\FrameworkArduino\FunctionalInterrupt.cpp.o
Compiling .pio\build\esp32dev\FrameworkArduino\HWCDC.cpp.o
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp: In static member function 'static void SpiDrv::begin()':
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:91:20: error: 'PINS_COUNT' was not declared in this scope
if (SLAVERESET > PINS_COUNT) {
^~~~~~~~~~
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:91:20: note: suggested alternative: 'ICOUNT'
if (SLAVERESET > PINS_COUNT) {
^~~~~~~~~~
ICOUNT
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:100:11: error: 'NINA_GPIO0' was not declared in this scope
pinMode(NINA_GPIO0, OUTPUT);
^~~~~~~~~~
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:100:11: note: suggested alternative: 'NINA_GPIOIRQ'
pinMode(NINA_GPIO0, OUTPUT);
^~~~~~~~~~
NINA_GPIOIRQ
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp: In static member function 'static int SpiDrv::available()':
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:66:22: error: 'NINA_GPIO0' was not declared in this scope
#define NINA_GPIOIRQ NINA_GPIO0
^~~~~~~~~~
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:539:47: note: in expansion of macro 'NINA_GPIOIRQ'
int SpiDrv::available() { return (digitalRead(NINA_GPIOIRQ) != LOW); }
^~~~~~~~~~~~
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:66:22: note: suggested alternative: 'NINA_GPIOIRQ'
#define NINA_GPIOIRQ NINA_GPIO0
^~~~~~~~~~
.pio/libdeps/esp32dev/Bluepad32/src/utility/spi_drv.cpp:539:47: note: in expansion of macro 'NINA_GPIOIRQ'
int SpiDrv::available() { return (digitalRead(NINA_GPIOIRQ) != LOW); }
^~~~~~~~~~~~
*** [.pio\build\esp32dev\lib9f9\Bluepad32\utility\spi_drv.cpp.o] Error 1
======================================================== [FAILED] Took 12.74 seconds ========================================================
Sketch
#include <Arduino.h>
#include <Bluepad32.h>
GamepadPtr myGamepads[BP32_MAX_GAMEPADS];
int ff = 1;
bool revbut[2] = { 0 };
bool reverse = false;
#define PIN_R_F 12
#define PIN_R_B 14
#define PIN_L_F 26
#define PIN_L_B 27
#define PWM_R_F 0
#define PWM_R_B 1
#define PWM_L_F 2
#define PWM_L_B 3
#define PWM_FREQ 2000
#define PWM_BITS 8
// This callback gets called any time a new gamepad is connected.
// Up to 4 gamepads can be connected at the same time.
void onConnectedGamepad(GamepadPtr gp) {
bool foundEmptySlot = false;
for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
if (myGamepads[i] == nullptr) {
Serial.printf("CALLBACK: Gamepad is connected, index=%d\n", i);
// Additionally, you can get certain gamepad properties like:
// Model, VID, PID, BTAddr, flags, etc.
GamepadProperties properties = gp->getProperties();
Serial.printf("Gamepad model: %s, VID=0x%04x, PID=0x%04x\n",
gp->getModelName().c_str(), properties.vendor_id,
properties.product_id);
myGamepads[i] = gp;
foundEmptySlot = true;
break;
}
}
if (!foundEmptySlot) {
Serial.println(
"CALLBACK: Gamepad connected, but could not found empty slot");
}
}
void onDisconnectedGamepad(GamepadPtr gp) {
bool foundGamepad = false;
for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
if (myGamepads[i] == gp) {
Serial.printf("CALLBACK: Gamepad is disconnected from index=%d\n", i);
myGamepads[i] = nullptr;
foundGamepad = true;
break;
}
}
if (!foundGamepad) {
Serial.println(
"CALLBACK: Gamepad disconnected, but not found in myGamepads");
}
}
// Arduino setup function. Runs in CPU 1
void setup() {
Serial.begin(115200);
Serial.printf("Firmware: %s\n", BP32.firmwareVersion());
const uint8_t *addr = BP32.localBdAddress();
Serial.printf("BD Addr: %2X:%2X:%2X:%2X:%2X:%2X\n", addr[0], addr[1], addr[2],
addr[3], addr[4], addr[5]);
// Setup the Bluepad32 callbacks
BP32.setup(&onConnectedGamepad, &onDisconnectedGamepad);
// "forgetBluetoothKeys()" should be called when the user performs
// a "device factory reset", or similar.
// Calling "forgetBluetoothKeys" in setup() just as an example.
// Forgetting Bluetooth keys prevents "paired" gamepads to reconnect.
// But might also fix some connection / re-connection issues.
//BP32.forgetBluetoothKeys();
pinMode(PIN_R_F, OUTPUT);
pinMode(PIN_R_B, OUTPUT);
pinMode(PIN_L_F, OUTPUT);
pinMode(PIN_L_B, OUTPUT);
digitalWrite(PIN_R_F, LOW);
digitalWrite(PIN_R_B, LOW);
digitalWrite(PIN_L_F, LOW);
digitalWrite(PIN_L_B, LOW);
ledcSetup(PWM_R_F, PWM_FREQ, PWM_BITS);
ledcAttachPin(PIN_R_F, PWM_R_F);
ledcSetup(PWM_R_B, PWM_FREQ, PWM_BITS);
ledcAttachPin(PIN_R_B, PWM_R_B);
ledcSetup(PWM_L_F, PWM_FREQ, PWM_BITS);
ledcAttachPin(PIN_L_F, PWM_L_F);
ledcSetup(PWM_L_B, PWM_FREQ, PWM_BITS);
ledcAttachPin(PIN_L_B, PWM_L_B);
}
// Arduino loop function. Runs in CPU 1
void loop() {
// This call fetches all the gamepad info from the NINA (ESP32) module.
// Just call this function in your main loop.
// The gamepads pointer (the ones received in the callbacks) gets updated
// automatically.
BP32.update();
// It is safe to always do this before using the gamepad API.
// This guarantees that the gamepad is valid and connected.
for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
GamepadPtr myGamepad = myGamepads[i];
if (myGamepad && myGamepad->isConnected()) {
// There are different ways to query whether a button is pressed.
// By query each button individually:
// a(), b(), x(), y(), l1(), etc...
if (myGamepad->a()) {
static int colorIdx = 0;
// Some gamepads like DS4 and DualSense support changing the color LED.
// It is possible to change it by calling:
switch (colorIdx % 3) {
case 0:
// Red
myGamepad->setColorLED(255, 0, 0);
break;
case 1:
// Green
myGamepad->setColorLED(0, 255, 0);
break;
case 2:
// Blue
myGamepad->setColorLED(0, 0, 255);
break;
}
colorIdx++;
}
if (myGamepad->b()) {
// Turn on the 4 LED. Each bit represents one LED.
static int led = 0;
led++;
// Some gamepads like the DS3, DualSense, Nintendo Wii, Nintendo Switch
// support changing the "Player LEDs": those 4 LEDs that usually
// indicate the "gamepad seat". It is possible to change them by
// calling:
myGamepad->setPlayerLEDs(led & 0x0f);
}
if (myGamepad->x()) {
// Duration: 255 is ~2 seconds
// force: intensity
// Some gamepads like DS3, DS4, DualSense, Switch, Xbox One S support
// rumble.
// It is possible to set it by calling:
myGamepad->setRumble(0xc0 /* force */, 0xc0 /* duration */);
}
ff = 1-ff;
revbut[ff] = myGamepad->buttons() && 0x0004;
if(!revbut[1-ff] && revbut[ff]) {
reverse = !reverse;
}
// Another way to query the buttons, is by calling buttons(), or
// miscButtons() which return a bitmask.
// Some gamepads also have DPAD, axis and more.
Serial.printf(
"idx=%d, dpad: 0x%02x, buttons: 0x%04x, reverse: %s, ff: %4d, revbut[1-ff]: %s, revbut[ff]: %s, axis L: %4d, %4d, axis R: "
"%4d, %4d, brake: %4d, throttle: %4d, misc: 0x%02x, gyro x:%6d y:%6d "
"z:%6d, accel x:%6d y:%6d z:%6d\n",
i, // Gamepad Index
myGamepad->dpad(), // DPAD
myGamepad->buttons(), // bitmask of pressed buttons
reverse ? "true" : "false",
ff,
revbut[1-ff] ? "true" : "false",
revbut[ff] ? "true" : "false",
myGamepad->axisX(), // (-511 - 512) left X Axis
myGamepad->axisY(), // (-511 - 512) left Y axis
myGamepad->axisRX(), // (-511 - 512) right X axis
myGamepad->axisRY(), // (-511 - 512) right Y axis
myGamepad->brake(), // (0 - 1023): brake button
myGamepad->throttle(), // (0 - 1023): throttle (AKA gas) button
myGamepad->miscButtons(), // bitmak of pressed "misc" buttons
myGamepad->gyroX(), // Gyro X
myGamepad->gyroY(), // Gyro Y
myGamepad->gyroZ(), // Gyro Z
myGamepad->accelX(), // Accelerometer X
myGamepad->accelY(), // Accelerometer Y
myGamepad->accelZ() // Accelerometer Z
);
int x = (reverse ? -1 : 1)*(-myGamepad->axisY()/2); // rootate coordinate system clockwise by 90°
int y = (reverse ? -1 : 1)*(-(myGamepad->axisX()+4)/2); // rootate coordinate system clockwise by 90° and correct offset
int py = sqrt(x*x + y*y); // py = distance from origin
if (py>255) {
py = 255;
}
int sp = y<0 ? -py : py; // add a negative sign for y < 0 ==> speed
float f = cos(acos((float)y/(float)py)*2); // factor for inner chain-wheel (inner means "inner side of a curve")
//if (y==0) { // If the joystick is in the middle right or left,
// f = -1.0f; // the rover has to turn on the spot. So one chainwheel rotates
//} // forward and the other turns in the opposite direction => f=-1
int so = sp; // so = speed for the outer chain-wheel (outer side of a curve)
int si = sp * f; // si = speed for the inner chain-wheel
int sl, sr; // speed left and right
if (x==0) { // x==0 means: straight forward or straight backward,
sl = so; // with both chain-wheels at the same speed.
sr = so;
} else if (x>0) { // x>0 means: the joystick is pulled to the right
sl = so; // so the left side is on the outer of the curve
sr = si; // and the right side is on the inner of the curve
} else { // x<0 means: the joystick is pulled to the left
sl = si; // so the left side is on the inner of the curve
sr = so; // and the right side is on the outer of the curve
}
if (sr > 0) {
ledcWrite(PWM_L_B, 0);
ledcWrite(PWM_L_F, abs(sr));
} else {
ledcWrite(PWM_L_F, 0);
ledcWrite(PWM_L_B, abs(sr));
}
if (sl > 0) {
ledcWrite(PWM_R_B, 0);
ledcWrite(PWM_R_F, abs(sl));
} else {
ledcWrite(PWM_R_F, 0);
ledcWrite(PWM_R_B, abs(sl));
}
/*Serial.printf(
"idx=%d, axis X,Y: %4d, %4d, Distance: %4d, Factor: %f, Speed innner, outer: %4d, %4d, Speed Left, Right %4d, %4d, LEDC0, LEDC1, %4d, %4d\n",
i, // Gamepad Index
x, // (-511 - 512) left X Axis
y, // (-511 - 512) left Y axis
py, // Distance
f, // Factor
si, // Speed inner wheel-chain
so, // Speed outer wheel-chain
sl, // Speed Left
sr, // Speed Right
ledcRead(0),
ledcRead(1)
);*/
// You can query the axis and other properties as well. See Gamepad.h
// For all the available functions.
}
}
// The main loop must have some kind of "yield to lower priority task" event.
// Otherwise the watchdog will get triggered.
// If your main loop doesn't have one, just add a simple `vTaskDelay(1)`.
// Detailed info here:
// https://stackoverflow.com/questions/66278271/task-watchdog-got-triggered-the-tasks-did-not-reset-the-watchdog-in-time
// vTaskDelay(1);
delay(150);
}