mirror of
https://github.com/pelgraine/Meck.git
synced 2026-03-28 17:42:44 +01:00
Compare commits
1 Commits
t5s3-1
...
crowpanel-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb778780f3 |
43
boards/crowpanel.json
Normal file
43
boards/crowpanel.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi",
|
||||
"partitions": "default_16MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Elecrow CrowPanel (ESP32-S3 16MB Flash, 8MB PSRAM)",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 524288,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"monitor": {
|
||||
"speed": 115200
|
||||
},
|
||||
"url": "https://www.elecrow.com/crowpanel-advance-hmi-intelligent-screen-esp32-ai-display.html",
|
||||
"vendor": "Elecrow"
|
||||
}
|
||||
@@ -343,6 +343,18 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Non-T-Deck ESP32 targets (CrowPanel, etc.) ---
|
||||
// Variables declared inside the LilyGo_TDeck_Pro block above that are
|
||||
// referenced unconditionally in setup()/loop() need parallel declarations.
|
||||
#if !defined(LilyGo_TDeck_Pro) && defined(ESP32)
|
||||
CPUPowerManager cpuPower;
|
||||
#define AGC_RESET_INTERVAL_MS 500
|
||||
static unsigned long lastAGCReset = 0;
|
||||
static bool readerMode = false;
|
||||
static bool notesMode = false;
|
||||
static bool audiobookMode = false;
|
||||
#endif
|
||||
|
||||
// Believe it or not, this std C function is busted on some platforms!
|
||||
static uint32_t _atoi(const char* sp) {
|
||||
uint32_t n = 0;
|
||||
@@ -749,8 +761,8 @@ void setup() {
|
||||
initKeyboard();
|
||||
#endif
|
||||
|
||||
// Initialize touch input (CST328)
|
||||
#ifdef HAS_TOUCHSCREEN
|
||||
// Initialize touch input (CST328 — T-Deck Pro only; CrowPanel uses GT911 via LovyanGFX)
|
||||
#if defined(HAS_TOUCHSCREEN) && defined(CST328_PIN_INT)
|
||||
if (touchInput.begin(CST328_PIN_INT)) {
|
||||
MESH_DEBUG_PRINTLN("setup() - Touch input initialized");
|
||||
} else {
|
||||
@@ -910,7 +922,7 @@ void loop() {
|
||||
cpuPower.loop();
|
||||
|
||||
// Audiobook: service audio decode regardless of which screen is active
|
||||
#ifndef HAS_4G_MODEM
|
||||
#if defined(LilyGo_TDeck_Pro) && !defined(HAS_4G_MODEM)
|
||||
{
|
||||
AudiobookPlayerScreen* abPlayer =
|
||||
(AudiobookPlayerScreen*)ui_task.getAudiobookScreen();
|
||||
@@ -1110,10 +1122,12 @@ void loop() {
|
||||
#endif
|
||||
rtc_clock.tick();
|
||||
// Periodic AGC reset - re-assert boosted RX gain to prevent sensitivity drift
|
||||
#if defined(LilyGo_TDeck_Pro)
|
||||
if ((millis() - lastAGCReset) >= AGC_RESET_INTERVAL_MS) {
|
||||
radio_reset_agc();
|
||||
lastAGCReset = millis();
|
||||
}
|
||||
#endif
|
||||
// Handle T-Deck Pro keyboard input
|
||||
#if defined(LilyGo_TDeck_Pro)
|
||||
handleKeyboardInput();
|
||||
|
||||
@@ -1,107 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
// =============================================================================
|
||||
// GxEPDDisplay STUB for CrowPanel (and other non-e-ink LGFX targets)
|
||||
//
|
||||
// This file shadows src/helpers/ui/GxEPDDisplay.h to prevent the LovyanGFX vs
|
||||
// Adafruit_GFX GFXfont type collision at link time. MapScreen.h unconditionally
|
||||
// includes GxEPDDisplay.h and uses a GxEPDDisplay* member — this stub provides
|
||||
// the minimal API so that compilation and linking succeed.
|
||||
//
|
||||
// On CrowPanel the map screen is inert (no SD card when function switch is in
|
||||
// WM mode, no keyboard to navigate to it). The _einkDisplay pointer in
|
||||
// MapScreen will be a bad cast but is null-checked before every draw call.
|
||||
// =============================================================================
|
||||
|
||||
#define ENABLE_GxEPD2_GFX 0
|
||||
#include <helpers/ui/DisplayDriver.h>
|
||||
|
||||
#include <GxEPD2_BW.h>
|
||||
#include <GxEPD2_3C.h>
|
||||
#include <GxEPD2_4C.h>
|
||||
#include <GxEPD2_7C.h>
|
||||
#include <Fonts/FreeSans9pt7b.h>
|
||||
#include <Fonts/FreeSansBold12pt7b.h>
|
||||
#include <Fonts/FreeSans18pt7b.h>
|
||||
|
||||
// Inline CRC32 for frame change detection (replaces bakercp/CRC32
|
||||
// to avoid naming collision with PNGdec's bundled CRC32.h)
|
||||
class FrameCRC32 {
|
||||
uint32_t _crc = 0xFFFFFFFF;
|
||||
public:
|
||||
void reset() { _crc = 0xFFFFFFFF; }
|
||||
template<typename T> void update(T val) {
|
||||
const uint8_t* p = (const uint8_t*)&val;
|
||||
for (size_t i = 0; i < sizeof(T); i++) {
|
||||
_crc ^= p[i];
|
||||
for (int b = 0; b < 8; b++)
|
||||
_crc = (_crc >> 1) ^ (0xEDB88320 & -(int32_t)(_crc & 1));
|
||||
}
|
||||
}
|
||||
template<typename T> void update(const T* data, size_t len) {
|
||||
const uint8_t* p = (const uint8_t*)data;
|
||||
for (size_t i = 0; i < len * sizeof(T); i++) {
|
||||
_crc ^= p[i];
|
||||
for (int b = 0; b < 8; b++)
|
||||
_crc = (_crc >> 1) ^ (0xEDB88320 & -(int32_t)(_crc & 1));
|
||||
}
|
||||
}
|
||||
uint32_t finalize() { return _crc ^ 0xFFFFFFFF; }
|
||||
};
|
||||
|
||||
#include "DisplayDriver.h"
|
||||
// GxEPD color constants referenced by MapScreen.h
|
||||
#ifndef GxEPD_BLACK
|
||||
#define GxEPD_BLACK 0
|
||||
#define GxEPD_WHITE 1
|
||||
#endif
|
||||
|
||||
class GxEPDDisplay : public DisplayDriver {
|
||||
|
||||
#if defined(EINK_DISPLAY_MODEL)
|
||||
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> display;
|
||||
const float scale_x = EINK_SCALE_X;
|
||||
const float scale_y = EINK_SCALE_Y;
|
||||
const float offset_x = EINK_X_OFFSET;
|
||||
const float offset_y = EINK_Y_OFFSET;
|
||||
#else
|
||||
GxEPD2_BW<GxEPD2_150_BN, 200> display;
|
||||
const float scale_x = 1.5625f;
|
||||
const float scale_y = 1.5625f;
|
||||
const float offset_x = 0;
|
||||
const float offset_y = 10;
|
||||
#endif
|
||||
bool _init = false;
|
||||
bool _isOn = false;
|
||||
uint16_t _curr_color;
|
||||
FrameCRC32 display_crc;
|
||||
int last_display_crc_value = 0;
|
||||
|
||||
public:
|
||||
#if defined(EINK_DISPLAY_MODEL)
|
||||
GxEPDDisplay() : DisplayDriver(128, 128), display(EINK_DISPLAY_MODEL(PIN_DISPLAY_CS, PIN_DISPLAY_DC, PIN_DISPLAY_RST, PIN_DISPLAY_BUSY)) {}
|
||||
#else
|
||||
GxEPDDisplay() : DisplayDriver(128, 128), display(GxEPD2_150_BN(DISP_CS, DISP_DC, DISP_RST, DISP_BUSY)) {}
|
||||
#endif
|
||||
GxEPDDisplay() : DisplayDriver(128, 128) {}
|
||||
|
||||
bool begin();
|
||||
// --- MapScreen raw pixel API (stubs) ---
|
||||
void drawPixelRaw(int16_t x, int16_t y, uint16_t color) {}
|
||||
int16_t rawWidth() { return 0; }
|
||||
int16_t rawHeight() { return 0; }
|
||||
void drawTextRaw(int16_t x, int16_t y, const char* text, uint16_t color) {}
|
||||
void invalidateFrameCRC() {}
|
||||
|
||||
bool isOn() override {return _isOn;};
|
||||
void turnOn() override;
|
||||
void turnOff() override;
|
||||
void clear() override;
|
||||
void startFrame(Color bkg = DARK) override;
|
||||
void setTextSize(int sz) override;
|
||||
void setColor(Color c) override;
|
||||
void setCursor(int x, int y) override;
|
||||
void print(const char* str) override;
|
||||
void fillRect(int x, int y, int w, int h) override;
|
||||
void drawRect(int x, int y, int w, int h) override;
|
||||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override;
|
||||
uint16_t getTextWidth(const char* str) override;
|
||||
void endFrame() override;
|
||||
|
||||
// --- Raw pixel access for MapScreen (bypasses scaling) ---
|
||||
void drawPixelRaw(int16_t x, int16_t y, uint16_t color) {
|
||||
display.drawPixel(x, y, color);
|
||||
}
|
||||
int16_t rawWidth() { return display.width(); }
|
||||
int16_t rawHeight() { return display.height(); }
|
||||
|
||||
// Draw text at raw (unscaled) physical coordinates using built-in 5x7 font
|
||||
void drawTextRaw(int16_t x, int16_t y, const char* text, uint16_t color) {
|
||||
display.setFont(NULL); // Built-in 5x7 font
|
||||
display.setTextSize(1);
|
||||
display.setTextColor(color);
|
||||
display.setCursor(x, y);
|
||||
display.print(text);
|
||||
}
|
||||
|
||||
// Force endFrame() to push to display even if CRC unchanged
|
||||
// (needed because drawPixelRaw bypasses CRC tracking)
|
||||
void invalidateFrameCRC() { last_display_crc_value = 0; }
|
||||
// --- DisplayDriver pure virtuals (no-op implementations) ---
|
||||
bool isOn() override { return false; }
|
||||
void turnOn() override {}
|
||||
void turnOff() override {}
|
||||
void clear() override {}
|
||||
void startFrame(Color bkg = DARK) override {}
|
||||
void setTextSize(int sz) override {}
|
||||
void setColor(Color c) override {}
|
||||
void setCursor(int x, int y) override {}
|
||||
void print(const char* str) override {}
|
||||
void fillRect(int x, int y, int w, int h) override {}
|
||||
void drawRect(int x, int y, int w, int h) override {}
|
||||
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override {}
|
||||
uint16_t getTextWidth(const char* str) override { return 0; }
|
||||
void endFrame() override {}
|
||||
};
|
||||
74
variants/crowpanel_70/CrowPanel70Board.h
Normal file
74
variants/crowpanel_70/CrowPanel70Board.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
|
||||
// CrowPanel 7.0" Advance Series
|
||||
//
|
||||
// V1.0: PCA9557/TCA9534 I/O expander at 0x18 for display power/reset/backlight
|
||||
// V1.2: STC8H1K28 MCU at 0x30 (6-step brightness)
|
||||
// V1.3: STC8H1K28 MCU at 0x30 (246-step brightness)
|
||||
//
|
||||
// I2C bus (shared between touch GT911, RTC at 0x51, and I/O controller)
|
||||
// SDA = IO15, SCL = IO16
|
||||
//
|
||||
// Function select switch (active-low DIP switches on PCB rear):
|
||||
// S1=0 S0=0 → MIC & SPK (IO4/5/6 to speaker, IO19/20 to mic)
|
||||
// S1=0 S0=1 → WM (IO4/5/6 + IO19/20 to wireless module) ← REQUIRED FOR LORA
|
||||
// S1=1 S0=1 → MIC & TF Card (IO4/5/6 to SD, IO19/20 to mic)
|
||||
|
||||
#define PIN_BOARD_SDA 15
|
||||
#define PIN_BOARD_SCL 16
|
||||
|
||||
// Touch pins (GT911 at 0x5D)
|
||||
#define PIN_TOUCH_SDA PIN_BOARD_SDA
|
||||
#define PIN_TOUCH_SCL PIN_BOARD_SCL
|
||||
#define PIN_TOUCH_INT 1 // IO1_TP_INT (active low pulse, not level-based)
|
||||
#define PIN_TOUCH_RST -1 // Controlled via STC8H1K28 P1.7 (v1.3) or TCA9534 (v1.0)
|
||||
|
||||
// STC8H1K28 I2C address (v1.2/v1.3 only)
|
||||
#define STC8H_ADDR 0x30
|
||||
|
||||
class CrowPanel70Board : public ESP32Board {
|
||||
public:
|
||||
void begin() {
|
||||
// NOTE: Wire.begin(SDA, SCL) is called in target.cpp radio_init() BEFORE
|
||||
// lcd.init(), to ensure correct init order for v1.3 STC8H1K28 backlight.
|
||||
// Do NOT call Wire.begin() here — it would conflict with LovyanGFX's
|
||||
// internal Wire usage for the GT911 touch controller.
|
||||
|
||||
ESP32Board::begin();
|
||||
}
|
||||
|
||||
const char* getManufacturerName() const override {
|
||||
#ifdef CROWPANEL_V13
|
||||
return "CrowPanel 7.0 V1.3";
|
||||
#else
|
||||
return "CrowPanel 7.0";
|
||||
#endif
|
||||
}
|
||||
|
||||
// --- STC8H1K28 control (v1.3) ---
|
||||
// These are safe to call on all versions; on v1.0 they'll talk to
|
||||
// a nonexistent I2C device and silently fail.
|
||||
|
||||
void setBacklightBrightness(uint8_t level) {
|
||||
// 0 = max, 244 = min, 245 = off
|
||||
Wire.beginTransmission(STC8H_ADDR);
|
||||
Wire.write(level);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
void buzzerOn() {
|
||||
Wire.beginTransmission(STC8H_ADDR);
|
||||
Wire.write(246);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
void buzzerOff() {
|
||||
Wire.beginTransmission(STC8H_ADDR);
|
||||
Wire.write(247);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
};
|
||||
33
variants/crowpanel_70/CrowPanel70Display.h
Normal file
33
variants/crowpanel_70/CrowPanel70Display.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <helpers/ui/LGFXDisplay.h>
|
||||
#include "LGFX_CrowPanel70.h"
|
||||
|
||||
// Custom display class for CrowPanel 7.0" that handles native landscape touch coordinates
|
||||
class CrowPanel70Display : public LGFXDisplay {
|
||||
public:
|
||||
CrowPanel70Display(int w, int h, LGFX_Device &disp) : LGFXDisplay(w, h, disp) {}
|
||||
|
||||
// Override getTouch for native landscape display (800x480)
|
||||
// The 7" panel is natively landscape, unlike the 3.5" which is portrait rotated
|
||||
//
|
||||
// GT911 touch coords are in physical space (0-799, 0-479).
|
||||
// We map them to logical space using:
|
||||
// display->width()/height() = physical LGFX dimensions (800, 480)
|
||||
// width()/height() = logical DisplayDriver dimensions (128, 64)
|
||||
bool getTouch(int *x, int *y) override {
|
||||
lgfx::v1::touch_point_t point;
|
||||
int touch_count = display->getTouch(&point);
|
||||
|
||||
if (touch_count > 0) {
|
||||
// Physical touch → logical coords
|
||||
*x = point.x * width() / display->width();
|
||||
*y = point.y * height() / display->height();
|
||||
return true;
|
||||
}
|
||||
|
||||
*x = -1;
|
||||
*y = -1;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
204
variants/crowpanel_70/LGFX_CrowPanel70.h
Normal file
204
variants/crowpanel_70/LGFX_CrowPanel70.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
|
||||
#define LGFX_USE_V1
|
||||
#include <LovyanGFX.hpp>
|
||||
|
||||
#ifndef CROWPANEL_V13
|
||||
// V1.0 uses TCA9534 I/O expander at 0x18
|
||||
#include <TCA9534.h>
|
||||
#endif
|
||||
|
||||
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
|
||||
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
|
||||
|
||||
// CrowPanel 7.0" uses RGB parallel interface (16-bit) with:
|
||||
// V1.0: TCA9534 I/O expander at 0x18 for display power/reset/backlight
|
||||
// V1.2/V1.3: STC8H1K28 MCU at 0x30 for backlight, buzzer, speaker control
|
||||
//
|
||||
// Display: 800x480 native landscape, ST7277 driver IC
|
||||
// Touch: GT911 at 0x5D on I2C (SDA=15, SCL=16)
|
||||
//
|
||||
// STC8H1K28 (v1.3) command byte reference:
|
||||
// 0 = backlight max brightness
|
||||
// 1-244 = backlight dimmer (244 = dimmest)
|
||||
// 245 = backlight off
|
||||
// 246 = buzzer on
|
||||
// 247 = buzzer off
|
||||
// 248 = speaker amp on
|
||||
// 249 = speaker amp off
|
||||
|
||||
#define STC8H_I2C_ADDR 0x30
|
||||
|
||||
class LGFX_CrowPanel70 : public lgfx::LGFX_Device {
|
||||
lgfx::Bus_RGB _bus_instance;
|
||||
lgfx::Panel_RGB _panel_instance;
|
||||
lgfx::Touch_GT911 _touch_instance;
|
||||
|
||||
#ifndef CROWPANEL_V13
|
||||
TCA9534 _ioex;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static constexpr uint16_t SCREEN_WIDTH = 800;
|
||||
static constexpr uint16_t SCREEN_HEIGHT = 480;
|
||||
|
||||
// --- STC8H1K28 helper (v1.3) ---
|
||||
static void sendSTC8Command(uint8_t cmd) {
|
||||
Wire.beginTransmission(STC8H_I2C_ADDR);
|
||||
Wire.write(cmd);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
// Set backlight brightness: 0 = max, 244 = min, 245 = off
|
||||
static void setBacklight(uint8_t brightness) {
|
||||
#ifdef CROWPANEL_V13
|
||||
sendSTC8Command(brightness);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void buzzerOn() { sendSTC8Command(246); }
|
||||
static void buzzerOff() { sendSTC8Command(247); }
|
||||
static void speakerOn() { sendSTC8Command(248); }
|
||||
static void speakerOff() { sendSTC8Command(249); }
|
||||
|
||||
bool init_impl(bool use_reset, bool use_clear) override {
|
||||
|
||||
#ifdef CROWPANEL_V13
|
||||
// ---- V1.3: All I2C + GPIO init done externally in target.cpp ----
|
||||
// Wire.begin(), STC8H backlight, and GPIO1 touch reset pulse are
|
||||
// called BEFORE lcd.init() to avoid Wire double-init conflicts and
|
||||
// ensure the display is powered before Panel_RGB allocates framebuffer.
|
||||
|
||||
#else
|
||||
// ---- V1.0: TCA9534 init ----
|
||||
_ioex.attach(Wire);
|
||||
_ioex.setDeviceAddress(0x18);
|
||||
|
||||
// Configure TCA9534 pins as outputs
|
||||
_ioex.config(1, TCA9534::Config::OUT); // Display power
|
||||
_ioex.config(2, TCA9534::Config::OUT); // Display reset
|
||||
_ioex.config(3, TCA9534::Config::OUT); // Not used
|
||||
_ioex.config(4, TCA9534::Config::OUT); // Backlight
|
||||
|
||||
// Power on display
|
||||
_ioex.output(1, TCA9534::Level::H);
|
||||
|
||||
// Reset sequence
|
||||
pinMode(1, OUTPUT);
|
||||
digitalWrite(1, LOW);
|
||||
_ioex.output(2, TCA9534::Level::L);
|
||||
delay(20);
|
||||
_ioex.output(2, TCA9534::Level::H);
|
||||
delay(100);
|
||||
pinMode(1, INPUT);
|
||||
|
||||
// Turn on backlight
|
||||
_ioex.output(4, TCA9534::Level::H);
|
||||
#endif
|
||||
|
||||
return LGFX_Device::init_impl(use_reset, use_clear);
|
||||
}
|
||||
|
||||
LGFX_CrowPanel70(void) {
|
||||
// Panel configuration
|
||||
{
|
||||
auto cfg = _panel_instance.config();
|
||||
cfg.memory_width = SCREEN_WIDTH;
|
||||
cfg.memory_height = SCREEN_HEIGHT;
|
||||
cfg.panel_width = SCREEN_WIDTH;
|
||||
cfg.panel_height = SCREEN_HEIGHT;
|
||||
cfg.offset_x = 0;
|
||||
cfg.offset_y = 0;
|
||||
cfg.offset_rotation = 0; // Panel_RGB: rotation not supported via offset_rotation
|
||||
// Display renders in portrait (480x800 physical). Landscape rotation will
|
||||
// be handled by swapping the CrowPanel70Display coordinate mapping.
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
// Panel detail configuration
|
||||
{
|
||||
auto cfg = _panel_instance.config_detail();
|
||||
cfg.use_psram = 1; // Use PSRAM for frame buffer
|
||||
_panel_instance.config_detail(cfg);
|
||||
}
|
||||
|
||||
// RGB bus configuration
|
||||
// Pin mapping is identical across all CrowPanel 7" hardware versions
|
||||
{
|
||||
auto cfg = _bus_instance.config();
|
||||
cfg.panel = &_panel_instance;
|
||||
|
||||
// Blue (B3-B7 on panel, 5 bits)
|
||||
cfg.pin_d0 = 21; // B3
|
||||
cfg.pin_d1 = 47; // B4
|
||||
cfg.pin_d2 = 48; // B5
|
||||
cfg.pin_d3 = 45; // B6
|
||||
cfg.pin_d4 = 38; // B7
|
||||
|
||||
// Green (G2-G7 on panel, 6 bits)
|
||||
cfg.pin_d5 = 9; // G2
|
||||
cfg.pin_d6 = 10; // G3
|
||||
cfg.pin_d7 = 11; // G4
|
||||
cfg.pin_d8 = 12; // G5
|
||||
cfg.pin_d9 = 13; // G6
|
||||
cfg.pin_d10 = 14; // G7
|
||||
|
||||
// Red (R3-R7 on panel, 5 bits)
|
||||
cfg.pin_d11 = 7; // R3
|
||||
cfg.pin_d12 = 17; // R4
|
||||
cfg.pin_d13 = 18; // R5
|
||||
cfg.pin_d14 = 3; // R6
|
||||
cfg.pin_d15 = 46; // R7
|
||||
|
||||
// Control pins
|
||||
cfg.pin_henable = 42; // DE (Data Enable)
|
||||
cfg.pin_vsync = 41; // VSYNC
|
||||
cfg.pin_hsync = 40; // HSYNC
|
||||
cfg.pin_pclk = 39; // Pixel clock (DCLK)
|
||||
|
||||
// Timing configuration (14MHz pixel clock for 7" display)
|
||||
cfg.freq_write = 14000000;
|
||||
|
||||
// Horizontal timing
|
||||
cfg.hsync_polarity = 0;
|
||||
cfg.hsync_front_porch = 8;
|
||||
cfg.hsync_pulse_width = 4;
|
||||
cfg.hsync_back_porch = 8;
|
||||
|
||||
// Vertical timing
|
||||
cfg.vsync_polarity = 0;
|
||||
cfg.vsync_front_porch = 8;
|
||||
cfg.vsync_pulse_width = 4;
|
||||
cfg.vsync_back_porch = 8;
|
||||
|
||||
// Clock configuration
|
||||
cfg.pclk_idle_high = 1;
|
||||
cfg.pclk_active_neg = 0;
|
||||
|
||||
_bus_instance.config(cfg);
|
||||
}
|
||||
_panel_instance.setBus(&_bus_instance);
|
||||
|
||||
// Touch configuration (GT911 at 0x5D)
|
||||
{
|
||||
auto cfg = _touch_instance.config();
|
||||
cfg.x_min = 0;
|
||||
cfg.x_max = SCREEN_WIDTH - 1;
|
||||
cfg.y_min = 0;
|
||||
cfg.y_max = SCREEN_HEIGHT - 1;
|
||||
cfg.pin_int = -1; // IO1 is TP_INT but we poll, not interrupt-driven
|
||||
cfg.pin_rst = -1; // Reset via STC8H1K28 (v1.3) or TCA9534 (v1.0)
|
||||
cfg.bus_shared = true;
|
||||
cfg.offset_rotation = 0; // Match panel config
|
||||
cfg.i2c_port = 0;
|
||||
cfg.i2c_addr = 0x5D;
|
||||
cfg.pin_sda = 15;
|
||||
cfg.pin_scl = 16;
|
||||
cfg.freq = 400000;
|
||||
_touch_instance.config(cfg);
|
||||
_panel_instance.setTouch(&_touch_instance);
|
||||
}
|
||||
|
||||
setPanel(&_panel_instance);
|
||||
}
|
||||
};
|
||||
70
variants/crowpanel_70/cpupowermanager.h
Normal file
70
variants/crowpanel_70/cpupowermanager.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// CPU Frequency Scaling for ESP32-S3
|
||||
//
|
||||
// Typical current draw (CPU only, rough):
|
||||
// 240 MHz ~70-80 mA
|
||||
// 160 MHz ~50-60 mA
|
||||
// 80 MHz ~30-40 mA
|
||||
//
|
||||
// SPI peripherals and UART use their own clock dividers from the APB clock,
|
||||
// so LoRa, e-ink, and GPS serial all work fine at 80MHz.
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#ifndef CPU_FREQ_IDLE
|
||||
#define CPU_FREQ_IDLE 80 // MHz — normal mesh listening
|
||||
#endif
|
||||
|
||||
#ifndef CPU_FREQ_BOOST
|
||||
#define CPU_FREQ_BOOST 240 // MHz — heavy processing
|
||||
#endif
|
||||
|
||||
#ifndef CPU_BOOST_TIMEOUT_MS
|
||||
#define CPU_BOOST_TIMEOUT_MS 10000 // 10 seconds
|
||||
#endif
|
||||
|
||||
class CPUPowerManager {
|
||||
public:
|
||||
CPUPowerManager() : _boosted(false), _boost_started(0) {}
|
||||
|
||||
void begin() {
|
||||
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||
_boosted = false;
|
||||
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (_boosted && (millis() - _boost_started >= CPU_BOOST_TIMEOUT_MS)) {
|
||||
setIdle();
|
||||
}
|
||||
}
|
||||
|
||||
void setBoost() {
|
||||
if (!_boosted) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_BOOST);
|
||||
_boosted = true;
|
||||
MESH_DEBUG_PRINTLN("CPU power: boosted to %d MHz", CPU_FREQ_BOOST);
|
||||
}
|
||||
_boost_started = millis();
|
||||
}
|
||||
|
||||
void setIdle() {
|
||||
if (_boosted) {
|
||||
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||
_boosted = false;
|
||||
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
bool isBoosted() const { return _boosted; }
|
||||
uint32_t getFrequencyMHz() const { return getCpuFrequencyMhz(); }
|
||||
|
||||
private:
|
||||
bool _boosted;
|
||||
unsigned long _boost_started;
|
||||
};
|
||||
|
||||
#endif // ESP32
|
||||
44
variants/crowpanel_70/pins_arduino.h
Normal file
44
variants/crowpanel_70/pins_arduino.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// USB Serial (native USB CDC on ESP32-S3)
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
// I2C (shared between touch GT911, RTC PCF85063 at 0x51, STC8H1K28 at 0x30)
|
||||
static const uint8_t SDA = 15;
|
||||
static const uint8_t SCL = 16;
|
||||
|
||||
// Default SPI — mapped to wireless module slot (active when function switch = WM)
|
||||
// V1.3 LoRa pin mapping (confirmed from board silkscreen):
|
||||
// NSS=19, DIO1=20, RESET=8, BUSY=2, SPI on 4/5/6
|
||||
// V1.0 LoRa pin mapping (original):
|
||||
// NSS=0, DIO1=20, RESET=19, BUSY=2, SPI on 4/5/6
|
||||
#ifdef CROWPANEL_V13
|
||||
static const uint8_t SS = 20; // LoRa NSS (V1.3: GPIO20, confirmed by SPI probe)
|
||||
#else
|
||||
static const uint8_t SS = 0; // LoRa NSS (V1.0: on boot strapping pin)
|
||||
#endif
|
||||
static const uint8_t MOSI = 6; // LoRa MOSI / SD MOSI / I2S LRCLK (shared)
|
||||
static const uint8_t MISO = 4; // LoRa MISO / SD MISO / I2S SDIN (shared)
|
||||
static const uint8_t SCK = 5; // LoRa SCK / SD CLK / I2S BCLK (shared)
|
||||
|
||||
// Analog pins
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
|
||||
// Touch pins
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
143
variants/crowpanel_70/platformio.ini
Normal file
143
variants/crowpanel_70/platformio.ini
Normal file
@@ -0,0 +1,143 @@
|
||||
; =============================================================================
|
||||
; CrowPanel 7.0" Advance Series — shared base configuration
|
||||
; =============================================================================
|
||||
; Display: 800x480 IPS, ST7277 driver, RGB parallel 16-bit
|
||||
; Touch: GT911 at I2C 0x5D
|
||||
; MCU: ESP32-S3-WROOM-1-N16R8 (16MB flash, 8MB OPSRAM)
|
||||
;
|
||||
; IMPORTANT: The PCB function select DIP switch (rear of board) MUST be set
|
||||
; to WM mode (S0=1, S1=0) for LoRa wireless module operation.
|
||||
; =============================================================================
|
||||
|
||||
[crowpanel_70_base]
|
||||
extends = esp32_base
|
||||
board = crowpanel
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-I variants/crowpanel_70
|
||||
-D CROWPANEL_70
|
||||
; Route Arduino Serial to UART0 (CH340 USB bridge) instead of native USB CDC.
|
||||
; The crowpanel.json board def sets CDC_ON_BOOT=1 but the user monitors via
|
||||
; the CH340 port (/dev/cu.wchusbserial*), not the native USB-C port.
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=0
|
||||
; I2C pins (shared: touch, RTC, I/O controller)
|
||||
-D PIN_BOARD_SDA=15
|
||||
-D PIN_BOARD_SCL=16
|
||||
; Radio configuration (common across versions)
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||
-D SX126X_DIO3_TCXO_VOLTAGE=3.3
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D LORA_TX_POWER=22
|
||||
; SPI bus pins (same all versions — active when function switch = WM)
|
||||
-D P_LORA_MOSI=6
|
||||
-D P_LORA_MISO=4
|
||||
-D P_LORA_SCLK=5
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/crowpanel_70>
|
||||
+<helpers/sensors>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
; CRITICAL: Use LovyanGFX 1.2.0 — v1.2.7 breaks 7" RGB panel init!
|
||||
lovyan03/LovyanGFX@1.2.0
|
||||
; PNGdec for map tile rendering
|
||||
bitbank2/PNGdec@^1.1.1
|
||||
; NOTE: GxEPD2 + Adafruit GFX NOT needed — CrowPanel variant shadows
|
||||
; GxEPDDisplay.h with a stub to avoid LovyanGFX/Adafruit_GFX type collision
|
||||
|
||||
; =============================================================================
|
||||
; V1.3 hardware (current production — STC8H1K28 at 0x30)
|
||||
; =============================================================================
|
||||
; LoRa pins confirmed from V1.3 board silkscreen (wireless module connector):
|
||||
; IO19=NSS, IO20=DIO1, IO8=RESET, IO2=BUSY
|
||||
; IO8 was freed when buzzer moved from direct GPIO to STC8H1K28
|
||||
; =============================================================================
|
||||
|
||||
[crowpanel_70_v13]
|
||||
extends = crowpanel_70_base
|
||||
build_flags =
|
||||
${crowpanel_70_base.build_flags}
|
||||
-D CROWPANEL_V13
|
||||
; V1.3 LoRa pin mapping (confirmed by SPI probe: NSS=GPIO20, DIO1=GPIO19)
|
||||
-D P_LORA_NSS=20
|
||||
-D P_LORA_DIO_1=19
|
||||
-D P_LORA_RESET=8
|
||||
-D P_LORA_BUSY=2
|
||||
lib_deps =
|
||||
${crowpanel_70_base.lib_deps}
|
||||
; No TCA9534 needed — V1.3 uses STC8H1K28 controlled via raw I2C writes
|
||||
|
||||
[env:crowpanel_70_v13_companion_radio_ble]
|
||||
extends = crowpanel_70_v13
|
||||
build_flags =
|
||||
${crowpanel_70_v13.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D DISPLAY_CLASS=CrowPanel70Display
|
||||
; Scale factors: 800x480 physical -> 128x64 logical
|
||||
; 800/128 = 6.25, 480/64 = 7.5
|
||||
-D DISPLAY_SCALE_X=6.25
|
||||
-D DISPLAY_SCALE_Y=7.5
|
||||
-D AUTO_OFF_MILLIS=30000
|
||||
-D HAS_TOUCH_SCREEN=1
|
||||
-D NO_BATTERY_INDICATOR=1
|
||||
-D MESH_DEBUG=1
|
||||
build_src_filter = ${crowpanel_70_v13.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<helpers/ui/LGFXDisplay.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${crowpanel_70_v13.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
; =============================================================================
|
||||
; V1.0 hardware (original — TCA9534/PCA9557 at 0x18)
|
||||
; =============================================================================
|
||||
; LoRa pins for V1.0:
|
||||
; IO0=NSS (boot pin!), IO20=DIO1, IO19=RESET, IO2=BUSY
|
||||
; =============================================================================
|
||||
|
||||
[crowpanel_70_v10]
|
||||
extends = crowpanel_70_base
|
||||
build_flags =
|
||||
${crowpanel_70_base.build_flags}
|
||||
; V1.0 LoRa pin mapping
|
||||
-D P_LORA_NSS=0
|
||||
-D P_LORA_DIO_1=20
|
||||
-D P_LORA_RESET=19
|
||||
-D P_LORA_BUSY=2
|
||||
lib_deps =
|
||||
${crowpanel_70_base.lib_deps}
|
||||
; TCA9534 I/O expander for display power control (V1.0 only)
|
||||
hideakitai/TCA9534@0.1.1
|
||||
|
||||
[env:crowpanel_70_v10_companion_radio_ble]
|
||||
extends = crowpanel_70_v10
|
||||
build_flags =
|
||||
${crowpanel_70_v10.build_flags}
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D DISPLAY_CLASS=CrowPanel70Display
|
||||
-D DISPLAY_SCALE_X=6.25
|
||||
-D DISPLAY_SCALE_Y=7.5
|
||||
-D AUTO_OFF_MILLIS=30000
|
||||
-D HAS_TOUCH_SCREEN=1
|
||||
-D NO_BATTERY_INDICATOR=1
|
||||
-D MESH_DEBUG=1
|
||||
build_src_filter = ${crowpanel_70_v10.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<helpers/ui/LGFXDisplay.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${crowpanel_70_v10.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
227
variants/crowpanel_70/target.cpp
Normal file
227
variants/crowpanel_70/target.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
|
||||
CrowPanel70Board board;
|
||||
|
||||
// SPI bus for LoRa — use HSPI (SPI3_HOST). Pass -1 for SS so RadioLib
|
||||
// can manually toggle NSS via digitalWrite (hardware CS conflicts with this).
|
||||
static SPIClass spi(HSPI);
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
|
||||
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
ESP32RTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
|
||||
EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
LGFX_CrowPanel70 lcd;
|
||||
CrowPanel70Display display(800, 480, lcd);
|
||||
#ifndef HAS_TOUCH_SCREEN
|
||||
TouchButton user_btn(&display);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool radio_init() {
|
||||
delay(1000);
|
||||
|
||||
#ifdef CROWPANEL_V13
|
||||
Serial.println("\n\n=== CrowPanel 7.0 V1.3 MeshCore ===");
|
||||
#else
|
||||
Serial.println("\n\n=== CrowPanel 7.0 MeshCore ===");
|
||||
#endif
|
||||
Serial.println("Initializing...");
|
||||
Serial.printf(" LoRa pins: NSS=%d DIO1=%d RST=%d BUSY=%d\n",
|
||||
P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
|
||||
Serial.printf(" SPI pins: MOSI=%d MISO=%d SCK=%d\n",
|
||||
P_LORA_MOSI, P_LORA_MISO, P_LORA_SCLK);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#ifdef CROWPANEL_V13
|
||||
Serial.println("Step 1: STC8H1K28 backlight ON...");
|
||||
Wire.beginTransmission(0x30);
|
||||
Wire.write(0);
|
||||
Wire.endTransmission();
|
||||
#endif
|
||||
|
||||
Serial.println("Step 2: Touch reset pulse...");
|
||||
pinMode(1, OUTPUT);
|
||||
digitalWrite(1, LOW);
|
||||
delay(120);
|
||||
pinMode(1, INPUT);
|
||||
|
||||
// NO ST7277 SPI init — MADCTL breaks RGB mode on this panel.
|
||||
// Rotation handled by LovyanGFX offset_rotation in constructor.
|
||||
|
||||
Serial.println("Step 3: lcd.init()...");
|
||||
lcd.init();
|
||||
Serial.printf(" LGFX: %dx%d\n", lcd.width(), lcd.height());
|
||||
|
||||
Serial.println("Step 4: display.begin()...");
|
||||
display.begin();
|
||||
#ifndef HAS_TOUCH_SCREEN
|
||||
user_btn.begin();
|
||||
#endif
|
||||
Serial.println("Display ready");
|
||||
|
||||
// Orientation test
|
||||
lcd.fillScreen(0x0000);
|
||||
int w = lcd.width();
|
||||
int h = lcd.height();
|
||||
lcd.fillRect(0, 0, 60, 40, 0xF800);
|
||||
lcd.fillRect(w-60, 0, 60, 40, 0x07E0);
|
||||
lcd.fillRect(0, h-40, 60, 40, 0x001F);
|
||||
lcd.fillRect(w-60, h-40, 60, 40, 0xFFE0);
|
||||
lcd.setTextColor(0xFFFF);
|
||||
lcd.setTextSize(2);
|
||||
lcd.setCursor(80, 10);
|
||||
lcd.printf("LGFX: %dx%d", w, h);
|
||||
lcd.setCursor(80, 40);
|
||||
lcd.print("CrowPanel 7.0 V1.3 + Meck");
|
||||
lcd.setCursor(80, 70);
|
||||
lcd.print("RED=TL GRN=TR BLU=BL YEL=BR");
|
||||
lcd.setCursor(80, 100);
|
||||
lcd.print("Testing LoRa...");
|
||||
#endif
|
||||
|
||||
Serial.println("Initializing RTC...");
|
||||
fallback_clock.begin();
|
||||
rtc_clock.begin(Wire);
|
||||
|
||||
// --- LoRa SPI diagnostic: bitbang to bypass peripheral ---
|
||||
Serial.println("Initializing LoRa radio...");
|
||||
Serial.println(" Hardware SPI returned all zeros — trying bitbang to test physical wiring\n");
|
||||
|
||||
// Reset radio
|
||||
pinMode(P_LORA_RESET, OUTPUT);
|
||||
digitalWrite(P_LORA_RESET, LOW);
|
||||
delay(20);
|
||||
digitalWrite(P_LORA_RESET, HIGH);
|
||||
delay(100);
|
||||
pinMode(P_LORA_BUSY, INPUT);
|
||||
unsigned long bs = millis();
|
||||
while (digitalRead(P_LORA_BUSY) && (millis() - bs < 1000)) delay(1);
|
||||
Serial.printf(" BUSY after reset: %s (%ldms)\n",
|
||||
digitalRead(P_LORA_BUSY) ? "HIGH" : "LOW", millis() - bs);
|
||||
|
||||
// Configure pins as GPIO (not SPI peripheral)
|
||||
pinMode(P_LORA_SCLK, OUTPUT); // GPIO5 = SCK
|
||||
pinMode(P_LORA_MOSI, OUTPUT); // GPIO6 = MOSI
|
||||
pinMode(P_LORA_MISO, INPUT); // GPIO4 = MISO
|
||||
pinMode(P_LORA_NSS, OUTPUT); // GPIO20 = NSS
|
||||
digitalWrite(P_LORA_SCLK, LOW);
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
|
||||
// Bitbang SPI Mode 0: CPOL=0, CPHA=0
|
||||
// Clock idle LOW, data sampled on rising edge
|
||||
auto bbTransfer = [](uint8_t tx) -> uint8_t {
|
||||
uint8_t rx = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
// Set MOSI
|
||||
digitalWrite(P_LORA_MOSI, (tx >> i) & 1);
|
||||
delayMicroseconds(2);
|
||||
// Rising edge — slave clocks in MOSI, we read MISO
|
||||
digitalWrite(P_LORA_SCLK, HIGH);
|
||||
delayMicroseconds(2);
|
||||
if (digitalRead(P_LORA_MISO)) rx |= (1 << i);
|
||||
// Falling edge
|
||||
digitalWrite(P_LORA_SCLK, LOW);
|
||||
delayMicroseconds(2);
|
||||
}
|
||||
return rx;
|
||||
};
|
||||
|
||||
// Read register 0x0320 via bitbang
|
||||
Serial.println(" Bitbang SPI read of reg 0x0320:");
|
||||
digitalWrite(P_LORA_NSS, LOW);
|
||||
delayMicroseconds(50);
|
||||
uint8_t b0 = bbTransfer(0x1D); // ReadRegister
|
||||
uint8_t b1 = bbTransfer(0x03); // Addr high
|
||||
uint8_t b2 = bbTransfer(0x20); // Addr low
|
||||
uint8_t b3 = bbTransfer(0x00); // NOP (status)
|
||||
uint8_t b4 = bbTransfer(0x00); // Register value
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
|
||||
Serial.printf(" SCK=%d MOSI=%d MISO=%d NSS=%d\n",
|
||||
P_LORA_SCLK, P_LORA_MOSI, P_LORA_MISO, P_LORA_NSS);
|
||||
Serial.printf(" bytes: %02X %02X %02X %02X %02X\n", b0, b1, b2, b3, b4);
|
||||
Serial.printf(" val=0x%02X %s\n", b4,
|
||||
b4 == 0x58 ? "<<< SX1262 FOUND via bitbang!" :
|
||||
(b4 == 0xFF ? "(no response)" :
|
||||
(b4 == 0x00 ? "(zeros — physical wiring issue)" : "")));
|
||||
|
||||
// Also try with MISO and MOSI swapped
|
||||
Serial.println("\n Bitbang with MISO=6, MOSI=4 (swapped):");
|
||||
pinMode(6, INPUT); // Now MISO
|
||||
pinMode(4, OUTPUT); // Now MOSI
|
||||
digitalWrite(4, LOW);
|
||||
|
||||
// Reset again
|
||||
digitalWrite(P_LORA_RESET, LOW);
|
||||
delay(20);
|
||||
digitalWrite(P_LORA_RESET, HIGH);
|
||||
delay(100);
|
||||
bs = millis();
|
||||
while (digitalRead(P_LORA_BUSY) && (millis() - bs < 500)) delay(1);
|
||||
|
||||
auto bbTransfer2 = [](uint8_t tx) -> uint8_t {
|
||||
uint8_t rx = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
digitalWrite(4, (tx >> i) & 1); // MOSI on GPIO4
|
||||
delayMicroseconds(2);
|
||||
digitalWrite(5, HIGH); // SCK
|
||||
delayMicroseconds(2);
|
||||
if (digitalRead(6)) rx |= (1 << i); // MISO on GPIO6
|
||||
digitalWrite(5, LOW);
|
||||
delayMicroseconds(2);
|
||||
}
|
||||
return rx;
|
||||
};
|
||||
|
||||
digitalWrite(P_LORA_NSS, LOW);
|
||||
delayMicroseconds(50);
|
||||
b0 = bbTransfer2(0x1D);
|
||||
b1 = bbTransfer2(0x03);
|
||||
b2 = bbTransfer2(0x20);
|
||||
b3 = bbTransfer2(0x00);
|
||||
b4 = bbTransfer2(0x00);
|
||||
digitalWrite(P_LORA_NSS, HIGH);
|
||||
|
||||
Serial.printf(" SCK=5 MOSI=4 MISO=6 NSS=%d\n", P_LORA_NSS);
|
||||
Serial.printf(" bytes: %02X %02X %02X %02X %02X\n", b0, b1, b2, b3, b4);
|
||||
Serial.printf(" val=0x%02X %s\n", b4,
|
||||
b4 == 0x58 ? "<<< SX1262 FOUND (MISO/MOSI swapped)!" :
|
||||
(b4 == 0xFF ? "(no response)" :
|
||||
(b4 == 0x00 ? "(zeros)" : "")));
|
||||
|
||||
// Restore pins for hardware SPI attempt
|
||||
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI, -1);
|
||||
bool result = radio.std_init(&spi);
|
||||
if (result) {
|
||||
Serial.println("LoRa: OK!");
|
||||
#ifdef DISPLAY_CLASS
|
||||
lcd.setTextColor(0x07E0);
|
||||
lcd.setCursor(80, 130);
|
||||
lcd.print("LoRa: OK!");
|
||||
#endif
|
||||
} else {
|
||||
Serial.println("LoRa: FAILED");
|
||||
#ifdef DISPLAY_CLASS
|
||||
lcd.setTextColor(0xF800);
|
||||
lcd.setCursor(80, 130);
|
||||
lcd.print("LoRa: FAILED");
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t radio_get_rng_seed() { return radio.random(0x7FFFFFFF); }
|
||||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
radio.setFrequency(freq); radio.setSpreadingFactor(sf);
|
||||
radio.setBandwidth(bw); radio.setCodingRate(cr);
|
||||
}
|
||||
void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); }
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng);
|
||||
}
|
||||
39
variants/crowpanel_70/target.h
Normal file
39
variants/crowpanel_70/target.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "variant.h" // Board-specific defines (HAS_GPS, GPS_BAUDRATE, etc.)
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <CrowPanel70Board.h>
|
||||
#include <helpers/radiolib/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/sensors/EnvironmentSensorManager.h>
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
#include <LGFX_CrowPanel70.h>
|
||||
#include <CrowPanel70Display.h>
|
||||
#ifndef HAS_TOUCH_SCREEN
|
||||
#include <TouchButton.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern CrowPanel70Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern EnvironmentSensorManager sensors;
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
extern LGFX_CrowPanel70 lcd;
|
||||
extern CrowPanel70Display display;
|
||||
#ifndef HAS_TOUCH_SCREEN
|
||||
extern TouchButton user_btn;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool radio_init();
|
||||
uint32_t radio_get_rng_seed();
|
||||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
|
||||
void radio_set_tx_power(uint8_t dbm);
|
||||
mesh::LocalIdentity radio_new_identity();
|
||||
150
variants/crowpanel_70/variant.h
Normal file
150
variants/crowpanel_70/variant.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
|
||||
// =============================================================================
|
||||
// CrowPanel 7.0" Advance Series (V1.0 / V1.3)
|
||||
// ESP32-S3-WROOM-1-N16R8 (16MB Flash, 8MB OPI-PSRAM)
|
||||
// 800x480 IPS display, ST7277 driver, RGB parallel 16-bit
|
||||
// GT911 capacitive touch at I2C 0x5D
|
||||
// =============================================================================
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// I2C Bus (shared: touch GT911, RTC PCF85063 at 0x51, STC8H1K28 at 0x30)
|
||||
// -----------------------------------------------------------------------------
|
||||
#define I2C_SDA 15
|
||||
#define I2C_SCL 16
|
||||
#define PIN_BOARD_SDA I2C_SDA
|
||||
#define PIN_BOARD_SCL I2C_SCL
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Display
|
||||
// -----------------------------------------------------------------------------
|
||||
#define LCD_HOR_SIZE 800
|
||||
#define LCD_VER_SIZE 480
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LoRa Radio (SX1262) — via wireless module connector
|
||||
// Pin mapping depends on hardware version (defined in platformio.ini):
|
||||
// V1.3: NSS=19, DIO1=20, RESET=8, BUSY=2, SPI 4/5/6
|
||||
// V1.0: NSS=0, DIO1=20, RESET=19, BUSY=2, SPI 4/5/6
|
||||
//
|
||||
// IMPORTANT: Function select DIP switch must be set to WM mode (S0=1, S1=0)
|
||||
// for the SPI bus to be routed to the wireless module connector.
|
||||
// -----------------------------------------------------------------------------
|
||||
#define USE_SX1262
|
||||
#define USE_SX1268
|
||||
|
||||
// P_LORA_* pins are defined in platformio.ini per hardware version
|
||||
// RadioLib/MeshCore compat aliases (only define if not already set by platformio)
|
||||
#ifndef P_LORA_NSS
|
||||
#ifdef CROWPANEL_V13
|
||||
#define P_LORA_NSS 20
|
||||
#define P_LORA_DIO_1 19
|
||||
#define P_LORA_RESET 8
|
||||
#define P_LORA_BUSY 2
|
||||
#else
|
||||
#define P_LORA_NSS 0
|
||||
#define P_LORA_DIO_1 20
|
||||
#define P_LORA_RESET 19
|
||||
#define P_LORA_BUSY 2
|
||||
#endif
|
||||
#endif
|
||||
#ifndef P_LORA_SCLK
|
||||
#define P_LORA_SCLK 5
|
||||
#define P_LORA_MISO 4
|
||||
#define P_LORA_MOSI 6
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GPS — not present on CrowPanel
|
||||
// Define a fallback GPS_BAUDRATE so that serial CLI code compiles cleanly
|
||||
// (the CLI GPS commands will be inert since HAS_GPS is not defined)
|
||||
// -----------------------------------------------------------------------------
|
||||
// #define HAS_GPS — intentionally NOT defined
|
||||
#ifndef GPS_BAUDRATE
|
||||
#define GPS_BAUDRATE 9600
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SD Card — shares SPI bus with LoRa and speaker via function switch
|
||||
// When function switch is in WM mode, SD card is NOT accessible.
|
||||
// SD support can be enabled for standalone (non-LoRa) use with S1=1,S0=1
|
||||
// -----------------------------------------------------------------------------
|
||||
// #define HAS_SDCARD — not defined by default (conflicts with LoRa SPI)
|
||||
|
||||
// SDCARD_CS dummy: NotesScreen, TextReaderScreen, EpubZipReader reference this
|
||||
// unconditionally. -1 makes digitalWrite() a no-op on ESP32.
|
||||
#ifndef SDCARD_CS
|
||||
#define SDCARD_CS -1
|
||||
#endif
|
||||
|
||||
// E-ink display: not present. GxEPDDisplay.h is shadowed by a stub in the
|
||||
// variant include path to avoid LovyanGFX/Adafruit_GFX type collision.
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Battery — CrowPanel has a battery connector but no fuel gauge IC
|
||||
// Battery charging is handled by onboard circuit with CHG LED indicator
|
||||
// -----------------------------------------------------------------------------
|
||||
// #define HAS_BQ27220 — not present
|
||||
#define NO_BATTERY_INDICATOR 1
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Audio — I2S speaker (shared pins, only when function switch = MIC&SPK)
|
||||
// Not usable simultaneously with LoRa — commented out for reference
|
||||
// -----------------------------------------------------------------------------
|
||||
// #define BOARD_I2S_SDIN 4 // IO4 (shared with SPI MISO)
|
||||
// #define BOARD_I2S_BCLK 5 // IO5 (shared with SPI SCK)
|
||||
// #define BOARD_I2S_LRCLK 6 // IO6 (shared with SPI MOSI)
|
||||
|
||||
// MIC (v1.3: LMD3526B261 PDM mic)
|
||||
// #define BOARD_MIC_DATA 20 // IO20 (shared with LoRa DIO1)
|
||||
// #define BOARD_MIC_CLOCK 19 // IO19 (shared with LoRa NSS)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Touch Screen
|
||||
// -----------------------------------------------------------------------------
|
||||
#define HAS_TOUCHSCREEN 1
|
||||
|
||||
// Touch controller: GT911 at 0x5D
|
||||
// INT = IO1 (active-low pulse, used for GT911 address selection at boot)
|
||||
// RST = STC8H1K28 P1.7 (v1.3) or TCA9534 pin 2 (v1.0)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Keyboard — not present (touchscreen-only device)
|
||||
// -----------------------------------------------------------------------------
|
||||
// #define HAS_PHYSICAL_KEYBOARD — not present
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Buttons — CrowPanel is touchscreen-only
|
||||
// -----------------------------------------------------------------------------
|
||||
// BOOT button (GPIO0) is for programming only, not a user button.
|
||||
// Do NOT define PIN_USER_BTN — it would enable TouchButton code in UITask.cpp
|
||||
// but the TouchButton object is not instantiated (HAS_TOUCH_SCREEN suppresses it).
|
||||
// #define BUTTON_PIN 0
|
||||
// #define PIN_USER_BTN 0
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// UART
|
||||
// -----------------------------------------------------------------------------
|
||||
// UART0: TX=IO43, RX=IO44 (also USB CDC)
|
||||
// UART1: TX=IO20, RX=IO19 (shared with LoRa DIO1/NSS when in WM mode!)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RTC — PCF85063 at I2C 0x51 (confirmed from board silkscreen)
|
||||
// -----------------------------------------------------------------------------
|
||||
// AutoDiscoverRTCClock will find it automatically on the I2C bus
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// STC8H1K28 I/O Controller (V1.2/V1.3 only)
|
||||
// I2C slave at address 0x30
|
||||
// Command bytes:
|
||||
// 0 = backlight max brightness
|
||||
// 1-244 = backlight dimmer
|
||||
// 245 = backlight off
|
||||
// 246 = buzzer on
|
||||
// 247 = buzzer off
|
||||
// 248 = speaker amp on
|
||||
// 249 = speaker amp off
|
||||
// -----------------------------------------------------------------------------
|
||||
#ifdef CROWPANEL_V13
|
||||
#define STC8H_I2C_ADDR 0x30
|
||||
#endif
|
||||
Reference in New Issue
Block a user