Snapshot: full project state

This commit is contained in:
2026-10-06 23:43:26 -07:00
commit c1f5e0ff42
15 changed files with 2816 additions and 0 deletions

104
include/board_config.h Normal file
View File

@@ -0,0 +1,104 @@
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H
// ============================================================================
// FREENOVE ESP32-S3 Display (2.8" Capacitive Touch, 240x320)
// ============================================================================
// Display (ILI9341V) - LCDWiki 2.8" ESP32-S3 Display
// Source: https://www.lcdwiki.com/2.8inch_ESP32-S3_Display
#define TFT_CS 10 // Chip Select (IO10)
#define TFT_DC 46 // Data/Command (IO46)
#define TFT_RST -1 // Reset shared with ESP32-S3 EN pin (no GPIO)
#define TFT_MOSI 11 // SPI MOSI (IO11)
#define TFT_CLK 12 // SPI Clock (IO12)
#define TFT_MISO 13 // SPI MISO (IO13)
#define TFT_BL 45 // Backlight (IO45, active high)
// Touch Screen (FT6336G Capacitive)
#define TOUCH_SDA 16 // I2C SDA (IO16)
#define TOUCH_SCL 15 // I2C SCL (IO15)
#define TOUCH_RST 18 // Reset (IO18)
#define TOUCH_INT 17 // Interrupt (IO17)
#define TOUCH_ADDR 0x38 // I2C address
// Display dimensions
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
// ============================================================================
// Radio Modules (VSPI Bus)
// ============================================================================
// SPI Bus (shared with SD card)
#define RADIO_SPI_MOSI 11 // GPIO 11 (VSPI)
#define RADIO_SPI_MISO 13 // GPIO 13 (VSPI)
#define RADIO_SPI_CLK 12 // GPIO 12 (VSPI)
// CC1101 SubGHz Radio (300-928 MHz)
#define CC1101_CS 7 // Chip Select
#define CC1101_GDO0 6 // TX (data out)
#define CC1101_GDO2 37 // RX (data in)
#define CC1101_TX_EN 40 // TX Enable (E07 PA module)
#define CC1101_RX_EN 41 // RX Enable (E07 PA module)
// NRF24L01+ 2.4GHz Radio
#define NRF24_CSN 14 // Chip Select Not
#define NRF24_CE 15 // Chip Enable
#define NRF24_IRQ 16 // Interrupt (optional)
// PN532 NFC/RFID Reader (SPI mode)
#define PN532_CS 17 // Chip Select
#define PN532_ADDR 0x24 // I2C address (if used)
// GPS Module (UART)
#define GPS_TX_PIN 1 // GPIO 1 (UART0 TX) - receive GPS data
#define GPS_RX_PIN 3 // GPIO 3 (UART0 RX) - unused
#define GPS_BAUD 9600
// ============================================================================
// SD Card (also on VSPI)
// ============================================================================
#define SD_CS 21 // Chip Select
// ============================================================================
// LED / Status Indicators
// ============================================================================
#define LED_R 42 // Red (optional RGB LED)
#define LED_G 2 // Green
#define LED_B 1 // Blue
// ============================================================================
// Button / Input
// ============================================================================
#define BUTTON_RESET 0 // Boot button (GPIO 0)
// ============================================================================
// Memory Configuration (ESP32-S3 has 520KB SRAM)
// ============================================================================
#define HEAP_SIZE_WIFI (32 * 1024) // WiFi buffers
#define HEAP_SIZE_BLE (64 * 1024) // BLE buffers
#define HEAP_SIZE_RADIO (48 * 1024) // Radio packet buffers
#define HEAP_SIZE_UI (80 * 1024) // Display/UI frame buffers
// ============================================================================
// Feature Flags
// ============================================================================
#define ENABLE_WIFI 1
#define ENABLE_BLE 1
#define ENABLE_CC1101 1
#define ENABLE_NRF24 1
#define ENABLE_PN532 1
#define ENABLE_GPS 1
#define ENABLE_SDCARD 1
// ============================================================================
// Board Info
// ============================================================================
#define BOARD_NAME "FREENOVE ESP32-S3"
#define BOARD_VARIANT "CYD-2.8-CAP"
#define CPU_FREQ_MHZ 240
#define RAM_SIZE_KB 520
#define FLASH_SIZE_MB 16
#endif // BOARD_CONFIG_H

224
include/radio_cc1101.h Normal file
View File

@@ -0,0 +1,224 @@
#ifndef RADIO_CC1101_H
#define RADIO_CC1101_H
#include <SPI.h>
#include "board_config.h"
// CC1101 SubGHz Radio Driver
// Supports 300-928 MHz, optional E07-433M20S PA module (20dBm amplified)
class RadioCC1101 {
public:
// Frequency bands
enum FreqBand {
BAND_433MHZ = 0,
BAND_868MHZ = 1,
BAND_915MHZ = 2,
};
// Initialize SPI and radio
static bool begin(FreqBand band = BAND_433MHZ, bool use_pa_module = false) {
// Setup GPIO
pinMode(CC1101_CS, OUTPUT);
pinMode(CC1101_GDO0, INPUT); // TX/data out
pinMode(CC1101_GDO2, INPUT); // RX/data in
if (use_pa_module) {
pinMode(CC1101_TX_EN, OUTPUT);
pinMode(CC1101_RX_EN, OUTPUT);
digitalWrite(CC1101_TX_EN, LOW);
digitalWrite(CC1101_RX_EN, LOW);
}
// Start SPI
SPI.begin(RADIO_SPI_CLK, RADIO_SPI_MISO, RADIO_SPI_MOSI);
SPI.setFrequency(10000000); // 10 MHz
SPI.setDataMode(SPI_MODE0);
// Reset radio
reset();
delay(100);
// Verify chip
uint8_t id = readReg(0x0F);
if (id != 0x04) {
return false; // Not a CC1101
}
// Configure for band
switch (band) {
case BAND_433MHZ:
setFreq(433.92f);
break;
case BAND_868MHZ:
setFreq(868.0f);
break;
case BAND_915MHZ:
setFreq(915.0f);
break;
}
// RX mode
setRxMode();
return true;
}
// Set frequency (MHz)
static void setFreq(float freq_mhz) {
// CC1101 frequency = FREQ_REG * (Fxosc / 2^16)
// Fxosc = 26 MHz
uint32_t freq = (uint32_t)(freq_mhz / (26.0f / 65536.0f));
writeReg(0x0D, (freq >> 16) & 0xFF);
writeReg(0x0E, (freq >> 8) & 0xFF);
writeReg(0x0F, freq & 0xFF);
}
// Set TX power (dBm)
// 0xFF = +10dBm, 0xFE = +7dBm, 0x84 = +5dBm, etc.
static void setTxPower(uint8_t pwr) {
writeReg(0x3E, pwr);
}
// Set max power (12dBm for stock, 20dBm with E07 PA)
static void setMaxPower(bool use_pa_module = false) {
if (use_pa_module) {
// E07 PA module: TX_EN + RX_EN control
setTxPower(0xFF); // +10 dBm + 10 dBm from PA = ~20 dBm
} else {
// Stock CC1101: +12 dBm
setTxPower(0x84);
}
}
// Switch to RX mode
static void setRxMode() {
strobe(0x34); // RX enable
}
// Switch to TX mode
static void setTxMode() {
strobe(0x35); // TX enable
}
// Transmit data
static void transmit(const uint8_t* data, uint8_t len) {
// Clear TX FIFO
strobe(0x3B);
delay(10);
// Fill TX FIFO
digitalWrite(CC1101_CS, LOW);
SPI.transfer(0x3F); // FIFO address
for (uint8_t i = 0; i < len; i++) {
SPI.transfer(data[i]);
}
digitalWrite(CC1101_CS, HIGH);
// TX
setTxMode();
}
// Receive data (blocking)
// Returns length of received packet or 0 if none
static uint8_t receive(uint8_t* buffer, uint8_t max_len, uint32_t timeout_ms) {
uint32_t start = millis();
setRxMode();
while (millis() - start < timeout_ms) {
// Check for RX packet (GDO0)
if (digitalRead(CC1101_GDO0) == HIGH) {
// Read FIFO
uint8_t rx_len = readReg(0x3F);
if (rx_len > 0 && rx_len <= max_len) {
digitalWrite(CC1101_CS, LOW);
SPI.transfer(0xBF); // RX FIFO read
for (uint8_t i = 0; i < rx_len; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(CC1101_CS, HIGH);
// Clear RX FIFO
strobe(0x3A);
return rx_len;
}
}
delayMicroseconds(100);
}
return 0; // Timeout
}
// Get RSSI (signal strength)
static int8_t getRSSI() {
uint8_t raw = readReg(0x34);
if (raw >= 128) {
return (raw - 256) / 2 - 74;
}
return raw / 2 - 74;
}
// Sleep mode (low power)
static void sleep() {
strobe(0x36); // IDLE
delay(10);
strobe(0x39); // Sleep
}
static void wakeup() {
strobe(0x3C); // Wakeup
delay(10);
}
private:
// Reset CC1101
static void reset() {
digitalWrite(CC1101_CS, LOW);
delayMicroseconds(10);
digitalWrite(CC1101_CS, HIGH);
delayMicroseconds(40);
digitalWrite(CC1101_CS, LOW);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
SPI.transfer(0x30); // SRES (reset strobe)
while (digitalRead(RADIO_SPI_MISO) == HIGH);
digitalWrite(CC1101_CS, HIGH);
delayMicroseconds(40);
}
// Send strobe command
static void strobe(uint8_t cmd) {
digitalWrite(CC1101_CS, LOW);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
SPI.transfer(cmd);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
digitalWrite(CC1101_CS, HIGH);
}
// Read register
static uint8_t readReg(uint8_t reg) {
digitalWrite(CC1101_CS, LOW);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
SPI.transfer(0x80 | reg); // Read bit
uint8_t val = SPI.transfer(0x00);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
digitalWrite(CC1101_CS, HIGH);
return val;
}
// Write register
static void writeReg(uint8_t reg, uint8_t val) {
digitalWrite(CC1101_CS, LOW);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
SPI.transfer(reg);
SPI.transfer(val);
while (digitalRead(RADIO_SPI_MISO) == HIGH);
digitalWrite(CC1101_CS, HIGH);
}
};
#endif // RADIO_CC1101_H

248
include/radio_nrf24.h Normal file
View File

@@ -0,0 +1,248 @@
#ifndef RADIO_NRF24_H
#define RADIO_NRF24_H
#include <SPI.h>
#include "board_config.h"
// NRF24L01+ 2.4GHz Radio Driver
// PA+LNA module for range, all TX at +20dBm
class RadioNRF24 {
public:
// Initialize NRF24
static bool begin() {
pinMode(NRF24_CSN, OUTPUT);
pinMode(NRF24_CE, OUTPUT);
pinMode(NRF24_IRQ, INPUT);
digitalWrite(NRF24_CSN, HIGH);
digitalWrite(NRF24_CE, LOW);
// Start SPI
SPI.begin(RADIO_SPI_CLK, RADIO_SPI_MISO, RADIO_SPI_MOSI);
SPI.setFrequency(10000000);
SPI.setDataMode(SPI_MODE0);
delay(100);
// Verify chip
uint8_t id = readReg(0x00); // CONFIG
if ((id & 0x0F) == 0x00) {
return false; // Not responding
}
// Reset config
writeReg(0x00, 0x08); // PWR_UP, CRC enabled
delay(150);
// Setup for max power 2.4GHz
writeReg(0x01, 0x03); // EN_AA = 0x03 (pipe 0,1)
writeReg(0x02, 0x03); // EN_RXADDR = 0x03 (pipe 0,1)
writeReg(0x03, 0x03); // SETUP_AW = 5 bytes
writeReg(0x04, 0x2F); // SETUP_RETR = ARD=750us, ARC=15
writeReg(0x05, 0x76); // RF_CH = 118 (2476 MHz center)
writeReg(0x06, 0x0F); // RF_SETUP = PA_MAX, 2Mbps, LNA on
// Set RX addresses
setRxAddress(0, 0x6E6B6C6D6ELL);
setRxAddress(1, 0x6B6C6D6E6FLL);
// Set TX address
setTxAddress(0x6E6B6C6D6ELL);
// Enable RX
writeReg(0x00, 0x0B); // PWR_UP + PRIM_RX
digitalWrite(NRF24_CE, HIGH);
return true;
}
// Set RX channel (0-125, maps to 2400-3525 MHz)
static void setChannel(uint8_t ch) {
if (ch > 125) ch = 125;
writeReg(0x05, ch);
}
// Set TX power: 0x0F=+20dBm, 0x07=0dBm, 0x03=-6dBm, 0x00=-18dBm
static void setTxPower(uint8_t pwr) {
uint8_t rf_setup = readReg(0x06);
rf_setup = (rf_setup & 0xF9) | ((pwr & 0x03) << 1);
writeReg(0x06, rf_setup);
}
// Set max TX power
static void setMaxPower() {
setTxPower(0x03); // PA_MAX on NRF24L01+PA+LNA
}
// Set data rate: 0x00=1Mbps, 0x08=2Mbps, 0x20=250kbps
static void setDataRate(uint8_t rate) {
uint8_t rf_setup = readReg(0x06);
rf_setup = (rf_setup & 0xD7) | (rate & 0x28);
writeReg(0x06, rf_setup);
}
// Transmit packet
static bool transmit(const uint8_t* data, uint8_t len) {
if (len > 32) return false;
// Go to TX mode
uint8_t config = readReg(0x00);
writeReg(0x00, config & 0xFE); // Clear PRIM_RX
digitalWrite(NRF24_CE, LOW);
// Load TX FIFO
digitalWrite(NRF24_CSN, LOW);
SPI.transfer(0xA0); // W_TX_PAYLOAD
for (uint8_t i = 0; i < len; i++) {
SPI.transfer(data[i]);
}
digitalWrite(NRF24_CSN, HIGH);
// Start transmission
digitalWrite(NRF24_CE, HIGH);
delayMicroseconds(15);
digitalWrite(NRF24_CE, LOW);
// Wait for completion or timeout
uint32_t start = millis();
while (millis() - start < 5000) {
uint8_t status = getStatus();
if (status & 0x20) { // TX_DS = transmission complete
// Clear interrupt
writeReg(0x07, 0x20);
return true;
}
if (status & 0x10) { // MAX_RT = max retries reached
// Clear interrupt
writeReg(0x07, 0x10);
return false;
}
delay(1);
}
return false;
}
// Receive packet (non-blocking)
// Returns length of packet or 0 if none
static uint8_t receive(uint8_t* buffer, uint8_t max_len) {
uint8_t status = getStatus();
if (!(status & 0x40)) { // RX_DR = data ready
return 0;
}
// Read payload
uint8_t len = readReg(0x60); // RX_PL_WID
if (len > max_len) len = max_len;
digitalWrite(NRF24_CSN, LOW);
SPI.transfer(0x61); // R_RX_PAYLOAD
for (uint8_t i = 0; i < len; i++) {
buffer[i] = SPI.transfer(0x00);
}
digitalWrite(NRF24_CSN, HIGH);
// Clear RX FIFO
writeReg(0x07, 0x40); // Clear RX_DR flag
return len;
}
// Get signal strength (RSSI estimation)
// No built-in RSSI on NRF24, so estimate from carrier detect
static int8_t getRSSI() {
uint8_t cd = readReg(0x09); // CD (carrier detect)
if (cd & 0x01) {
return -50; // Signal present
}
return -90; // No signal
}
// Enter listening (RX) mode
static void listenMode() {
digitalWrite(NRF24_CE, LOW);
uint8_t config = readReg(0x00);
writeReg(0x00, config | 0x01); // PRIM_RX
digitalWrite(NRF24_CE, HIGH);
}
// Sleep mode (low power)
static void sleep() {
digitalWrite(NRF24_CE, LOW);
uint8_t config = readReg(0x00);
writeReg(0x00, config & 0xFD); // PWR_UP = 0
}
static void wakeup() {
uint8_t config = readReg(0x00);
writeReg(0x00, config | 0x02); // PWR_UP
delay(5);
digitalWrite(NRF24_CE, HIGH);
}
// Promiscuous mode (Goodspeed) - capture all 2.4GHz packets
static void enablePromiscuous() {
// Disable address matching
writeReg(0x02, 0x01); // EN_RXADDR = pipe 0 only
writeReg(0x03, 0x03); // SETUP_AW = 5 bytes
// Set minimal RX address (all zeros)
uint8_t addr[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
setRxAddress(0, 0x0000000000LL);
listenMode();
}
// Spectrum scanner - sweep channels and measure signal
static uint8_t scanChannel(uint8_t channel) {
setChannel(channel);
delay(40);
return readReg(0x09) & 0x01; // CD bit = carrier detect
}
private:
static uint8_t getStatus() {
digitalWrite(NRF24_CSN, LOW);
uint8_t status = SPI.transfer(0xFF);
digitalWrite(NRF24_CSN, HIGH);
return status;
}
static uint8_t readReg(uint8_t reg) {
digitalWrite(NRF24_CSN, LOW);
SPI.transfer(reg & 0x1F); // Max 5 bits
uint8_t val = SPI.transfer(0x00);
digitalWrite(NRF24_CSN, HIGH);
return val;
}
static void writeReg(uint8_t reg, uint8_t val) {
digitalWrite(NRF24_CSN, LOW);
SPI.transfer((reg & 0x1F) | 0x20); // Write flag
SPI.transfer(val);
digitalWrite(NRF24_CSN, HIGH);
}
static void setRxAddress(uint8_t pipe, uint64_t addr) {
uint8_t reg = 0x0A + pipe; // RX_ADDR_P0-P5
digitalWrite(NRF24_CSN, LOW);
SPI.transfer(reg | 0x20);
for (int i = 0; i < 5; i++) {
SPI.transfer((addr >> (i * 8)) & 0xFF);
}
digitalWrite(NRF24_CSN, HIGH);
}
static void setTxAddress(uint64_t addr) {
uint8_t reg = 0x10; // TX_ADDR
digitalWrite(NRF24_CSN, LOW);
SPI.transfer(reg | 0x20);
for (int i = 0; i < 5; i++) {
SPI.transfer((addr >> (i * 8)) & 0xFF);
}
digitalWrite(NRF24_CSN, HIGH);
}
};
#endif // RADIO_NRF24_H

126
include/touch_ft6336.h Normal file
View File

@@ -0,0 +1,126 @@
#ifndef TOUCH_FT6336_H
#define TOUCH_FT6336_H
#include <Wire.h>
#include "board_config.h"
// FT6336 Capacitive Touch Controller
class TouchFT6336 {
public:
struct TouchPoint {
uint16_t x;
uint16_t y;
uint8_t pressure;
bool pressed;
};
// Initialize I2C touch controller
static bool begin() {
// Hardware reset (active low)
pinMode(TOUCH_RST, OUTPUT);
digitalWrite(TOUCH_RST, LOW);
delay(10);
digitalWrite(TOUCH_RST, HIGH);
delay(300);
pinMode(TOUCH_INT, INPUT_PULLUP);
Wire.begin(TOUCH_SDA, TOUCH_SCL, 400000);
delay(100);
// Verify FT6336 is present (chip ID reg 0xA3 = 0x64 on FT6336G)
uint8_t chipID = readReg(0xA3);
Serial.printf("[Touch] Chip ID: 0x%02X\n", chipID);
if (chipID == 0x00 || chipID == 0xFF) {
return false; // Not responding
}
// Reset to defaults
writeReg(0xFC, 0x01);
delay(300);
// Set to normal mode, threshold
writeReg(0x80, 0x00); // Normal mode
writeReg(0x88, 40); // Touch threshold
return true;
}
// Read touch point (single finger)
static TouchPoint readTouch() {
TouchPoint tp = {0, 0, 0, false};
Wire.beginTransmission(TOUCH_ADDR);
Wire.write(0x02);
if (Wire.endTransmission() != 0) return tp;
// Read 5 bytes: status + X/Y coordinates
if (Wire.requestFrom((uint8_t)TOUCH_ADDR, (uint8_t)5) != 5) {
return tp;
}
uint8_t status = Wire.read();
uint8_t x_hi = Wire.read();
uint8_t x_lo = Wire.read();
uint8_t y_hi = Wire.read();
uint8_t y_lo = Wire.read();
// Extract touch count (bits 3-0)
uint8_t touch_count = status & 0x0F;
if (touch_count > 0) {
tp.pressed = true;
tp.x = ((x_hi & 0x0F) << 8) | x_lo;
tp.y = ((y_hi & 0x0F) << 8) | y_lo;
tp.pressure = 255; // Capacitive doesn't have pressure, default max
}
return tp;
}
// Calibrate touch (4-point corners)
static void calibrate() {
// FT6336 typically doesn't need manual calibration
// It auto-calibrates on startup
// If needed, implement 4-point calibration here
}
// Get firmware version
static uint8_t getFirmwareVersion() {
return readReg(0xA6);
}
// Power modes
static void setPowerMode(uint8_t mode) {
// 0 = Active, 1 = Monitor, 3 = Sleep
writeReg(0xA5, mode);
}
static void sleep() {
setPowerMode(3);
}
static void wakeup() {
setPowerMode(0);
delay(50);
}
private:
static uint8_t readReg(uint8_t reg) {
Wire.beginTransmission(TOUCH_ADDR);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom((uint8_t)TOUCH_ADDR, (uint8_t)1);
return Wire.read();
}
static void writeReg(uint8_t reg, uint8_t value) {
Wire.beginTransmission(TOUCH_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}
};
#endif // TOUCH_FT6336_H