feat: FTP server with Wi-Fi, NTP, LVGL UI — initial working build
- Rewrite main.cpp: SD_MMC mount, Wi-Fi from JSON, NTP Vienna, FTP server - Add ui.h/ui.cpp: LVGL status screen (IP, FTP status, SD bar, time) - Add build_framework_libs.py: dynamic ESP32 Core 3.x lib resolution - Remove unused sensor libs (IMU, magnetometer, fuel gauge, charger) - Add SimpleFTPServer + ArduinoJson dependencies - Build succeeds: RAM 19.2%, Flash 4.2%
This commit is contained in:
+358
-158
@@ -1,188 +1,388 @@
|
||||
/**
|
||||
* @file main.cpp
|
||||
* @brief Kode Dot FTP Explorer — ESP32-S3 FTP server serving microSD over Wi-Fi
|
||||
*
|
||||
* Reads Wi-Fi credentials from /sdcard/Wi-Fi.json, connects to Wi-Fi,
|
||||
* syncs time via NTP (Vienna timezone), and serves the SD card over FTP.
|
||||
* Status is shown on the AMOLED display via LVGL and on the NeoPixel LED.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <lvgl.h>
|
||||
#include <kodedot/display_manager.h>
|
||||
#include <TCA9555.h>
|
||||
#include <kodedot/pin_config.h>
|
||||
#include <TCA9555.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include "SD_MMC.h"
|
||||
#include "FS.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <SimpleFTPServer.h>
|
||||
#include <time.h>
|
||||
#include "ui.h"
|
||||
|
||||
// Display manager instance
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global objects
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Display manager (LVGL + panel + touch)
|
||||
DisplayManager display;
|
||||
|
||||
// UI labels
|
||||
static lv_obj_t *touch_label;
|
||||
static lv_obj_t *button_label;
|
||||
|
||||
// IO Expander
|
||||
// IO Expander (TCA9555)
|
||||
static TCA9555 ioexp(IOEXP_I2C_ADDR);
|
||||
|
||||
// NeoPixel
|
||||
// NeoPixel status LED
|
||||
static Adafruit_NeoPixel pixel(NEO_PIXEL_COUNT, NEO_PIXEL_PIN, NEO_GRB + NEO_KHZ800);
|
||||
|
||||
// Forward declarations
|
||||
void createUserInterface();
|
||||
void createFontExamples(lv_obj_t *parent);
|
||||
void updateTouchDisplay();
|
||||
void updateButtonDisplay();
|
||||
// FTP server
|
||||
FtpServer ftpSrv;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LED colour definitions
|
||||
// ---------------------------------------------------------------------------
|
||||
static const uint32_t COLOR_OFF = 0x000000;
|
||||
static const uint32_t COLOR_GREEN = Adafruit_NeoPixel::Color(0, 60, 0); // Wi-Fi OK, FTP idle
|
||||
static const uint32_t COLOR_BLUE = Adafruit_NeoPixel::Color(0, 0, 60); // FTP client connected
|
||||
static const uint32_t COLOR_PURPLE = Adafruit_NeoPixel::Color(40, 0, 60); // File transfer
|
||||
static const uint32_t COLOR_RED = Adafruit_NeoPixel::Color(60, 0, 0); // Error
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State tracking
|
||||
// ---------------------------------------------------------------------------
|
||||
static bool sd_ok = false;
|
||||
static bool wifi_connected = false;
|
||||
static bool ftp_client_on = false;
|
||||
static bool ftp_transfer = false;
|
||||
static unsigned long last_time_update = 0;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NeoPixel helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void setLed(uint32_t color) {
|
||||
pixel.setPixelColor(0, color);
|
||||
pixel.show();
|
||||
}
|
||||
|
||||
/** Refresh LED colour based on current state */
|
||||
static void updateLed() {
|
||||
if (ftp_transfer) {
|
||||
setLed(COLOR_PURPLE);
|
||||
} else if (ftp_client_on) {
|
||||
setLed(COLOR_BLUE);
|
||||
} else if (wifi_connected && sd_ok) {
|
||||
setLed(COLOR_GREEN);
|
||||
} else if (!sd_ok || !wifi_connected) {
|
||||
setLed(COLOR_RED);
|
||||
} else {
|
||||
setLed(COLOR_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SD card helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Get used / total space on SD card in MiB and push to UI */
|
||||
static void updateSdInfo() {
|
||||
if (!sd_ok) return;
|
||||
uint64_t total = SD_MMC.totalBytes() / (1024ULL * 1024ULL);
|
||||
uint64_t used = SD_MMC.usedBytes() / (1024ULL * 1024ULL);
|
||||
ui_set_sd(used, total);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FTP callbacks (SimpleFTPServer v3.x)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called on FTP connect / disconnect events.
|
||||
*/
|
||||
void ftpCallback(FtpOperation ftpOperation, uint32_t freeSpace, uint32_t totalSpace) {
|
||||
switch (ftpOperation) {
|
||||
case FTP_CONNECT:
|
||||
Serial.println("[FTP] Client connected");
|
||||
ftp_client_on = true;
|
||||
ftp_transfer = false;
|
||||
ui_set_ftp("Connected");
|
||||
break;
|
||||
case FTP_DISCONNECT:
|
||||
Serial.println("[FTP] Client disconnected");
|
||||
ftp_client_on = false;
|
||||
ftp_transfer = false;
|
||||
ui_set_ftp("Idle");
|
||||
break;
|
||||
case FTP_FREE_SPACE_CHANGE:
|
||||
Serial.printf("[FTP] Free space: %u / %u\n", freeSpace, totalSpace);
|
||||
updateSdInfo();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
updateLed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during file transfer operations.
|
||||
*/
|
||||
void ftpTransferCallback(FtpTransferOperation ftpOperation, const char *name, uint32_t transferredSize) {
|
||||
switch (ftpOperation) {
|
||||
case FTP_UPLOAD_START:
|
||||
Serial.printf("[FTP] Upload start: %s\n", name);
|
||||
ftp_transfer = true;
|
||||
ui_set_ftp("Transferring");
|
||||
break;
|
||||
case FTP_DOWNLOAD_START:
|
||||
Serial.printf("[FTP] Download start: %s\n", name);
|
||||
ftp_transfer = true;
|
||||
ui_set_ftp("Transferring");
|
||||
break;
|
||||
case FTP_UPLOAD:
|
||||
case FTP_DOWNLOAD:
|
||||
break; // Ongoing — silent to avoid serial flood
|
||||
case FTP_TRANSFER_STOP:
|
||||
Serial.printf("[FTP] Transfer done: %s (%u bytes)\n", name, transferredSize);
|
||||
ftp_transfer = false;
|
||||
ui_set_ftp(ftp_client_on ? "Connected" : "Idle");
|
||||
updateSdInfo();
|
||||
break;
|
||||
case FTP_TRANSFER_ERROR:
|
||||
Serial.printf("[FTP] Transfer error: %s\n", name);
|
||||
ftp_transfer = false;
|
||||
ui_set_ftp(ftp_client_on ? "Connected" : "Idle");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
updateLed();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Time display
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Update the UI time label once per second */
|
||||
static void updateTimeDisplay() {
|
||||
unsigned long now = millis();
|
||||
if (now - last_time_update < 1000) return;
|
||||
last_time_update = now;
|
||||
|
||||
struct tm timeinfo;
|
||||
if (getLocalTime(&timeinfo, 0)) {
|
||||
char buf[16];
|
||||
strftime(buf, sizeof(buf), "%H:%M:%S", &timeinfo);
|
||||
ui_set_time(buf);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Initialisation helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Mount SD card via SDMMC 1-bit mode. Returns true on success. */
|
||||
static bool initSdCard() {
|
||||
Serial.println("[SD] Mounting SD card (SDMMC 1-bit)...");
|
||||
SD_MMC.setPins(SD_PIN_CLK, SD_PIN_CMD, SD_PIN_D0);
|
||||
if (!SD_MMC.begin(SD_MOUNT_POINT, true)) { // true = 1-bit mode
|
||||
Serial.println("[SD] ERROR: Mount failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t cardType = SD_MMC.cardType();
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("[SD] ERROR: No card detected");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *typeStr = (cardType == CARD_MMC) ? "MMC" :
|
||||
(cardType == CARD_SD) ? "SD" :
|
||||
(cardType == CARD_SDHC) ? "SDHC" : "Unknown";
|
||||
uint64_t totalMB = SD_MMC.totalBytes() / (1024ULL * 1024ULL);
|
||||
uint64_t usedMB = SD_MMC.usedBytes() / (1024ULL * 1024ULL);
|
||||
Serial.printf("[SD] Card type: %s | %llu MB used / %llu MB total\n", typeStr, usedMB, totalMB);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Wi-Fi credentials from /sdcard/Wi-Fi.json and attempt connection.
|
||||
* JSON format: [{"ssid":"SSID","pass":"PASSWORD"}, ...]
|
||||
* Returns true when connected.
|
||||
*/
|
||||
static bool initWiFi() {
|
||||
Serial.println("[WiFi] Reading credentials from /sdcard/Wi-Fi.json...");
|
||||
|
||||
File file = SD_MMC.open("/Wi-Fi.json", FILE_READ);
|
||||
if (!file) {
|
||||
Serial.println("[WiFi] ERROR: Cannot open Wi-Fi.json");
|
||||
return false;
|
||||
}
|
||||
|
||||
String json = file.readString();
|
||||
file.close();
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
if (err) {
|
||||
Serial.printf("[WiFi] ERROR: JSON parse failed: %s\n", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonArray networks = doc.as<JsonArray>();
|
||||
if (networks.size() == 0) {
|
||||
Serial.println("[WiFi] ERROR: No networks in Wi-Fi.json");
|
||||
return false;
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
for (JsonObject net : networks) {
|
||||
const char *ssid = net["ssid"];
|
||||
const char *pass = net["pass"];
|
||||
if (!ssid) continue;
|
||||
|
||||
Serial.printf("[WiFi] Trying \"%s\" ...\n", ssid);
|
||||
WiFi.begin(ssid, pass);
|
||||
|
||||
unsigned long start = millis();
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - start < 20000) {
|
||||
delay(250);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.printf("[WiFi] Connected to \"%s\" IP: %s\n", ssid, WiFi.localIP().toString().c_str());
|
||||
ui_set_wifi(ssid, WiFi.localIP().toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
Serial.printf("[WiFi] Failed to connect to \"%s\"\n", ssid);
|
||||
WiFi.disconnect(true);
|
||||
delay(200);
|
||||
}
|
||||
|
||||
Serial.println("[WiFi] ERROR: Could not connect to any network");
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Configure NTP with Vienna timezone and wait briefly for sync */
|
||||
static void initNtp() {
|
||||
Serial.println("[NTP] Configuring time (Vienna CET/CEST)...");
|
||||
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org", "time.google.com");
|
||||
|
||||
struct tm timeinfo;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (getLocalTime(&timeinfo, 500)) {
|
||||
char buf[32];
|
||||
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
|
||||
Serial.printf("[NTP] Time synced: %s\n", buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Serial.println("[NTP] Warning: time not yet synced (will update in background)");
|
||||
}
|
||||
|
||||
/** Start FTP server with callbacks */
|
||||
static void initFtp() {
|
||||
Serial.println("[FTP] Starting FTP server (user: kode, port: 21)...");
|
||||
ftpSrv.setCallback(ftpCallback);
|
||||
ftpSrv.setTransferCallback(ftpTransferCallback);
|
||||
ftpSrv.begin("kode", "kode");
|
||||
Serial.println("[FTP] FTP server ready");
|
||||
ui_set_ftp("Idle");
|
||||
}
|
||||
|
||||
/** Check SD card presence via IO expander (best-effort) */
|
||||
static bool checkSdPresence() {
|
||||
if (!ioexp.begin(INPUT)) {
|
||||
Serial.println("[IO] Warning: IO Expander not responding — skipping SD detect");
|
||||
return true; // Assume present if expander unavailable
|
||||
}
|
||||
uint8_t val = ioexp.read1(EXPANDER_SD_CD);
|
||||
bool present = (val == LOW); // Card detect is active-low
|
||||
Serial.printf("[IO] SD card detect pin: %s\n", present ? "PRESENT" : "NOT PRESENT");
|
||||
return present;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Arduino setup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Base Project with LVGL...");
|
||||
delay(100);
|
||||
Serial.println();
|
||||
Serial.println("===================================");
|
||||
Serial.println(" Kode Dot FTP Explorer");
|
||||
Serial.println("===================================");
|
||||
|
||||
// Initialize display subsystem
|
||||
// NeoPixel off during init
|
||||
pixel.begin();
|
||||
pixel.setBrightness(30);
|
||||
setLed(COLOR_OFF);
|
||||
|
||||
// Initialise display + LVGL
|
||||
if (!display.init()) {
|
||||
Serial.println("Error: Failed to initialize display");
|
||||
while(1) {
|
||||
delay(1000);
|
||||
Serial.println("[DISP] FATAL: Display init failed");
|
||||
setLed(COLOR_RED);
|
||||
while (1) delay(1000);
|
||||
}
|
||||
Serial.println("[DISP] Display ready");
|
||||
|
||||
// Create the LVGL status UI
|
||||
ui_init();
|
||||
|
||||
// IO Expander — check SD card presence
|
||||
if (!checkSdPresence()) {
|
||||
Serial.println("[SD] WARNING: SD card not detected by IO expander");
|
||||
ui_set_error("No SD card detected");
|
||||
setLed(COLOR_RED);
|
||||
}
|
||||
|
||||
// Mount SD card
|
||||
sd_ok = initSdCard();
|
||||
if (!sd_ok) {
|
||||
ui_set_error("SD card mount failed");
|
||||
setLed(COLOR_RED);
|
||||
Serial.println("[MAIN] Halting — SD card required");
|
||||
while (1) {
|
||||
display.update();
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
updateSdInfo();
|
||||
|
||||
// Create the UI
|
||||
createUserInterface();
|
||||
|
||||
// Initialize IO Expander (inputs)
|
||||
if (!ioexp.begin(INPUT)) {
|
||||
Serial.println("Warning: IO Expander not connected");
|
||||
// Connect to Wi-Fi
|
||||
wifi_connected = initWiFi();
|
||||
if (!wifi_connected) {
|
||||
ui_set_error("Wi-Fi connection failed");
|
||||
setLed(COLOR_RED);
|
||||
Serial.println("[MAIN] Halting — Wi-Fi required");
|
||||
while (1) {
|
||||
display.update();
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
updateLed();
|
||||
|
||||
// Configure TOP button (GPIO with external pull-up)
|
||||
pinMode(BUTTON_TOP, INPUT);
|
||||
// Sync NTP time
|
||||
initNtp();
|
||||
|
||||
// Initialize NeoPixel
|
||||
pixel.begin();
|
||||
pixel.setBrightness(64);
|
||||
pixel.clear();
|
||||
pixel.show();
|
||||
// Start FTP server
|
||||
initFtp();
|
||||
|
||||
Serial.println("System ready!");
|
||||
Serial.println("[MAIN] Setup complete — entering main loop");
|
||||
Serial.printf("[MAIN] Connect FTP client to %s:21 (user: kode / pass: kode)\n",
|
||||
WiFi.localIP().toString().c_str());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Arduino loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void loop() {
|
||||
// Pump display subsystem
|
||||
ftpSrv.handleFTP();
|
||||
display.update();
|
||||
|
||||
// Update touch coordinates on the UI
|
||||
updateTouchDisplay();
|
||||
updateButtonDisplay();
|
||||
|
||||
updateTimeDisplay();
|
||||
delay(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create the main user interface
|
||||
*/
|
||||
void createUserInterface() {
|
||||
lv_obj_t * scr = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(scr, lv_color_hex(0x111111), 0);
|
||||
|
||||
// Title
|
||||
lv_obj_t * title = lv_label_create(scr);
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_30, 0);
|
||||
lv_label_set_text(title, "Base Project");
|
||||
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 30);
|
||||
|
||||
// Label for touch coordinates
|
||||
touch_label = lv_label_create(scr);
|
||||
lv_obj_set_style_text_font(touch_label, &lv_font_montserrat_22, 0);
|
||||
lv_label_set_text(touch_label, "Touch: (-, -)");
|
||||
lv_obj_set_style_text_color(touch_label, lv_color_hex(0x00FF00), 0);
|
||||
lv_obj_align(touch_label, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
// Label for buttons state (expander)
|
||||
button_label = lv_label_create(scr);
|
||||
lv_obj_set_style_text_font(button_label, &lv_font_montserrat_18, 0);
|
||||
lv_label_set_text(button_label, "Button: none");
|
||||
lv_obj_set_style_text_color(button_label, lv_color_hex(0x66CCFF), 0);
|
||||
lv_obj_align(button_label, LV_ALIGN_BOTTOM_MID, 0, -10);
|
||||
|
||||
// Ejemplos de diferentes fuentes
|
||||
createFontExamples(scr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create sample labels using different font sizes
|
||||
*/
|
||||
void createFontExamples(lv_obj_t *parent) {
|
||||
// Small font
|
||||
lv_obj_t * font_small = lv_label_create(parent);
|
||||
lv_obj_set_style_text_font(font_small, &lv_font_montserrat_14, 0);
|
||||
lv_label_set_text(font_small, "Montserrat 14px");
|
||||
lv_obj_set_style_text_color(font_small, lv_color_hex(0xCCCCCC), 0);
|
||||
lv_obj_align(font_small, LV_ALIGN_BOTTOM_LEFT, 20, -80);
|
||||
|
||||
// Medium font
|
||||
lv_obj_t * font_medium = lv_label_create(parent);
|
||||
lv_obj_set_style_text_font(font_medium, &lv_font_montserrat_18, 0);
|
||||
lv_label_set_text(font_medium, "Montserrat 18px");
|
||||
lv_obj_set_style_text_color(font_medium, lv_color_hex(0xCCCCCC), 0);
|
||||
lv_obj_align(font_medium, LV_ALIGN_BOTTOM_LEFT, 20, -50);
|
||||
|
||||
// Large font
|
||||
lv_obj_t * font_large = lv_label_create(parent);
|
||||
lv_obj_set_style_text_font(font_large, &lv_font_montserrat_42, 0);
|
||||
lv_label_set_text(font_large, "42");
|
||||
lv_obj_set_style_text_color(font_large, lv_color_hex(0x999999), 0);
|
||||
lv_obj_align(font_large, LV_ALIGN_BOTTOM_RIGHT, -30, -30);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the touch coordinates label
|
||||
*/
|
||||
void updateTouchDisplay() {
|
||||
if (!touch_label) return;
|
||||
|
||||
int16_t x, y;
|
||||
if (display.getTouchCoordinates(x, y)) {
|
||||
lv_label_set_text_fmt(touch_label, "Touch: (%d, %d)", x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool isPressed(uint8_t pinIndex) {
|
||||
// Entradas con pull-up externa: activo en LOW
|
||||
int v = ioexp.read1(pinIndex);
|
||||
return (v != TCA9555_INVALID_READ) && (v == LOW);
|
||||
}
|
||||
|
||||
static inline bool isGpioPressed(int gpio) {
|
||||
return digitalRead(gpio) == LOW; // activo en LOW por pull-up externa
|
||||
}
|
||||
|
||||
void updateButtonDisplay() {
|
||||
if (!button_label) return;
|
||||
|
||||
const char* status = "none";
|
||||
|
||||
if (isGpioPressed(BUTTON_TOP)) {
|
||||
status = "BUTTON_TOP";
|
||||
} else if (isPressed(EXPANDER_BUTTON_BOTTOM)) {
|
||||
status = "BUTTON_BOTTOM";
|
||||
} else if (isPressed(EXPANDER_PAD_TOP)) {
|
||||
status = "PAD_TOP";
|
||||
} else if (isPressed(EXPANDER_PAD_BOTTOM)) {
|
||||
status = "PAD_BOTTOM";
|
||||
} else if (isPressed(EXPANDER_PAD_LEFT)) {
|
||||
status = "PAD_LEFT";
|
||||
} else if (isPressed(EXPANDER_PAD_RIGHT)) {
|
||||
status = "PAD_RIGHT";
|
||||
}
|
||||
|
||||
lv_label_set_text_fmt(button_label, "Button: %s", status);
|
||||
|
||||
// Actualizar NeoPixel según botón
|
||||
uint32_t color = pixel.Color(0, 0, 0);
|
||||
if (status == (const char*)"BUTTON_TOP") {
|
||||
color = pixel.Color(255, 0, 0); // rojo
|
||||
} else if (status == (const char*)"BOTON_INFERIOR") {
|
||||
color = pixel.Color(0, 0, 255); // azul
|
||||
} else if (status == (const char*)"PAD_TOP") {
|
||||
color = pixel.Color(255, 255, 0); // amarillo
|
||||
} else if (status == (const char*)"PAD_BOTTOM") {
|
||||
color = pixel.Color(255, 0, 255); // magenta
|
||||
} else if (status == (const char*)"PAD_LEFT") {
|
||||
color = pixel.Color(0, 255, 0); // verde
|
||||
} else if (status == (const char*)"PAD_RIGHT") {
|
||||
color = pixel.Color(0, 255, 255); // cian
|
||||
}
|
||||
pixel.setPixelColor(0, color);
|
||||
pixel.show();
|
||||
}
|
||||
Reference in New Issue
Block a user