Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions boards/onx3248g035.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi",
"partitions": "default_16MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_ONX3248G035",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32_s3r8n16"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "OpenNextion ONX3248G035 (ESP32-S3R8, 16MB Flash)",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 460800
},
"url": "https://github.com/OpenNextion",
"vendor": "OpenNextion"
}
52 changes: 39 additions & 13 deletions include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

namespace config {

#if defined(BOARD_ONX3248G035)
constexpr bool kBoardOnx3248g035 = true;
#else
constexpr bool kBoardOnx3248g035 = false;
#endif

// --- Wi-Fi portal ---
constexpr char kPortalApName[] = "PlaneRadar-Setup";
constexpr char kPortalIp[] = "192.168.4.1";
Expand All @@ -23,26 +29,46 @@ constexpr unsigned long kWifiDownGraceMs = 4000;
/** Minimum interval between background reconnect tries. */
constexpr unsigned long kWifiReconnectIntervalMs = 15000;

// --- BOOT button (ESP32-C3 Super Mini, active LOW) ---
constexpr gpio_num_t kBootPin = GPIO_NUM_9;
// --- User input (active LOW when present) ---
constexpr bool kHasBootButton = true;
constexpr bool kBootTapChangesRange = true;
constexpr gpio_num_t kBootPin =
kBoardOnx3248g035 ? GPIO_NUM_0 : GPIO_NUM_9;
constexpr unsigned long kBootResetHoldMs = 3000UL;
/** Ignore BOOT taps shorter than this (debounce). */
constexpr unsigned long kBootTapMinMs = 40UL;

// --- Display: GC9A01 1.28" round 240×240 (SPI) ---
constexpr gpio_num_t kDisplayPinRst = GPIO_NUM_0;
constexpr gpio_num_t kDisplayPinCs = GPIO_NUM_1;
constexpr gpio_num_t kDisplayPinDc = GPIO_NUM_10;
constexpr gpio_num_t kDisplayPinMosi = GPIO_NUM_3; // display SDA
constexpr gpio_num_t kDisplayPinSclk = GPIO_NUM_4; // display SCL
// --- Display ---
constexpr gpio_num_t kDisplayPinRst =
kBoardOnx3248g035 ? GPIO_NUM_NC : GPIO_NUM_0;
constexpr gpio_num_t kDisplayPinCs =
kBoardOnx3248g035 ? GPIO_NUM_2 : GPIO_NUM_1;
constexpr gpio_num_t kDisplayPinDc =
kBoardOnx3248g035 ? GPIO_NUM_3 : GPIO_NUM_10;
constexpr gpio_num_t kDisplayPinMosi =
kBoardOnx3248g035 ? GPIO_NUM_1 : GPIO_NUM_3;
constexpr gpio_num_t kDisplayPinSclk =
kBoardOnx3248g035 ? GPIO_NUM_5 : GPIO_NUM_4;
constexpr gpio_num_t kDisplayPinBl =
kBoardOnx3248g035 ? GPIO_NUM_6 : GPIO_NUM_NC;

constexpr int kDisplayWidth = kBoardOnx3248g035 ? 320 : 240;
constexpr int kDisplayHeight = kBoardOnx3248g035 ? 480 : 240;

constexpr int kDisplayWidth = 240;
constexpr int kDisplayHeight = 240;
constexpr int kRadarViewportSize = kBoardOnx3248g035 ? kDisplayWidth :
(kDisplayWidth < 240 ? kDisplayWidth : 240);
constexpr int kRadarViewportX = (kDisplayWidth - kRadarViewportSize) / 2;
constexpr int kRadarViewportY =
kBoardOnx3248g035 ? 0 : (kDisplayHeight - kRadarViewportSize) / 2;
constexpr bool kFrameSpriteUsePsram = kBoardOnx3248g035;
constexpr bool kRadarInfoPanelEnabled = kBoardOnx3248g035;
constexpr int kRadarInfoPanelY = 330;

constexpr uint32_t kDisplaySpiWriteHz = 40000000;
// GC9A01 modules often need invert + BGR for correct black/green output
constexpr bool kDisplayInvert = true;
constexpr uint32_t kDisplaySpiWriteHz =
kBoardOnx3248g035 ? 40000000 : 40000000;
constexpr bool kDisplayInvert = kBoardOnx3248g035 ? false : true;
constexpr bool kDisplayRgbOrder = true;
constexpr uint8_t kDisplayRotation = 0;

// --- Radar center defaults (overridden via WiFi setup portal) ---
constexpr double kDefaultRadarLat = 52.3676;
Expand Down
3 changes: 3 additions & 0 deletions include/hardware/board_support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void boardInitBeforeDisplay();
1 change: 1 addition & 0 deletions include/hardware/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
extern LGFX tft;

void displayInit();
void displayRadarBackground();
19 changes: 18 additions & 1 deletion include/hardware/lgfx_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@

#include "config.h"

/** LovyanGFX device: GC9A01 on SPI. Pin values come from config.h. */
/** LovyanGFX device. Panel selection and pins come from config.h build flags. */
class LGFX : public lgfx::LGFX_Device {
lgfx::Bus_SPI _bus;
#if defined(BOARD_ONX3248G035)
lgfx::Panel_ST7796 _panel;
#else
lgfx::Panel_GC9A01 _panel;
#endif

public:
LGFX() {
Expand All @@ -27,8 +31,21 @@ class LGFX : public lgfx::LGFX_Device {
auto cfg = _panel.config();
cfg.pin_cs = static_cast<int>(config::kDisplayPinCs);
cfg.pin_rst = static_cast<int>(config::kDisplayPinRst);
cfg.pin_busy = -1;
cfg.panel_width = config::kDisplayWidth;
cfg.panel_height = config::kDisplayHeight;
cfg.memory_width = config::kDisplayWidth;
cfg.memory_height = config::kDisplayHeight;
cfg.offset_x = 0;
cfg.offset_y = 0;
cfg.offset_rotation = 0;
cfg.dummy_read_pixel = 8;
cfg.dummy_read_bits = 1;
cfg.readable = false;
cfg.invert = config::kDisplayInvert;
cfg.rgb_order = config::kDisplayRgbOrder;
cfg.dlen_16bit = false;
cfg.bus_shared = false;
_panel.config(cfg);
}
setPanel(&_panel);
Expand Down
3 changes: 3 additions & 0 deletions include/ui/radar_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ void radarDisplayDraw();
/** Redraw aircraft only (blits cached grid; no full-screen clear). */
void radarDisplayRefreshAircraft();

/** Refresh dynamic info panel without refetching ADS-B data. */
void radarDisplayRefreshInfo();

} // namespace ui
58 changes: 36 additions & 22 deletions include/ui/radar_theme.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,79 @@

#include <cstdint>

#include "config.h"

namespace ui::radar {

constexpr int kSize = 240;
constexpr int kCenterX = kSize / 2;
constexpr int kCenterY = kSize / 2;
constexpr int kSize = config::kRadarViewportSize;
constexpr int kBaseSize = 240;
constexpr int kOffsetX = config::kRadarViewportX;
constexpr int kOffsetY = config::kRadarViewportY;
constexpr int kRight = kOffsetX + kSize - 1;
constexpr int kBottom = kOffsetY + kSize - 1;
constexpr int kCenterX = kOffsetX + kSize / 2;
constexpr int kCenterY = kOffsetY + kSize / 2;

// Keep the original 240×240 radar geometry unchanged on existing boards.
// Larger rectangular boards scale these pixel constants from the 240 px baseline.
constexpr int scalePx(int px) { return (px * kSize + kBaseSize / 2) / kBaseSize; }
constexpr float scalePx(float px) {
return px * static_cast<float>(kSize) / static_cast<float>(kBaseSize);
}

/** Outermost grid ring (inside edge labels). */
constexpr int kGridOuterRadius = 107;
constexpr int kGridOuterRadius = scalePx(107);

/** N: offset from top edge (top_center, negative = up). */
constexpr int kCardinalNorthOffsetY = -1;
constexpr int kCardinalNorthOffsetY = scalePx(-1);
/** S: offset from bottom edge (bottom_center, positive = down). */
constexpr int kCardinalSouthOffsetY = 3;
constexpr int kCardinalSouthOffsetY = scalePx(3);

/** Gap between scale label right edge and outer ring on the east spoke (px). */
constexpr int kScaleGapFromOuterRing = 6;
constexpr int kScaleGapFromOuterRing = scalePx(6);

/** Target cap height (px) for N/S/E/W. */
constexpr int kCardinalLabelHeightPx = 14;
constexpr int kCardinalLabelHeightPx = scalePx(14);
/** Scale label is this many px shorter than cardinals. */
constexpr int kScaleBelowCardinalPx = 3;
constexpr int kScaleBelowCardinalPx = scalePx(3);

constexpr int kRingCount = 4;

/** Shared grid stroke: drawWideLine half-width (~2 px total); rings use the same px count. */
constexpr float kGridStrokeHalfWidth = 1.0f;
constexpr float kGridStrokeHalfWidth = scalePx(1.0f);

constexpr int kCenterDotRadius = 2;
constexpr int kCenterDotRadius = scalePx(2);

/** Filled aircraft symbol (nose triangle). */
constexpr int kAircraftNoseLenPx = 8;
constexpr int kAircraftTailLenPx = 3;
constexpr int kAircraftTailHalfPx = 4;
constexpr int kAircraftNoseLenPx = scalePx(8);
constexpr int kAircraftTailLenPx = scalePx(3);
constexpr int kAircraftTailHalfPx = scalePx(4);
/** Track vector: ground distance covered in this many seconds at current gs. */
constexpr float kAircraftTrackHorizonSec = 60.0f;
/** Minimum visible vector when gs > 0 (px). */
constexpr int kAircraftSpeedLineMinPx = 2;
constexpr int kAircraftSpeedLineMinPx = scalePx(2);
/** Track line length uses this outer_km, not the active range preset. */
constexpr float kAircraftTrackRefOuterKm = 13.3f;
/** Shorter than full 60 s horizon at ref scale; ×1.5 length boost applied. */
constexpr float kAircraftTrackLengthScale = 1.5f / 5.0f;
/** drawWideLine half-width for speed vectors (~2 px total). */
constexpr float kAircraftTrackLineHalfWidth = 1.0f;
constexpr float kAircraftTrackLineHalfWidth = scalePx(1.0f);

constexpr float kRunwayLineWidthPx = 2.0f;
constexpr float kRunwayLineWidthPx = scalePx(2.0f);
constexpr float kRunwayLineHalfWidth = kRunwayLineWidthPx * 0.5f;
constexpr int kRunwayLabelHeightPx = kCardinalLabelHeightPx;
constexpr int kRunwayLabelGapPx = 3;
constexpr int kRunwayLabelGapPx = scalePx(3);
/** Gap from triangle edge to tag block (px). */
constexpr int kAircraftLabelGapPx = 1;
constexpr int kAircraftLabelGapPx = scalePx(1);
/** Keep symbol centroid inside outer ring by at least this inset (px). */
constexpr int kAircraftInsideRingInsetPx =
kAircraftNoseLenPx + kAircraftTailHalfPx + 1;

/** Beyond-ring traffic: bearing cues on screen rim (correct direction, fixed radius). */
constexpr int kBeyondRingDotRadiusPx = 4;
constexpr int kBeyondRingScreenMarginPx = 2;
constexpr int kBeyondRingDotRadiusPx = scalePx(4);
constexpr int kBeyondRingScreenMarginPx = scalePx(2);
/** Target cap height (px) for aircraft tags (bold, slightly above scale label). */
constexpr int kAircraftTagLabelHeightPx = 13;
constexpr int kAircraftTagLabelHeightPx = scalePx(13);

/** RGB565 palette targets (applied in initPalette). */
constexpr uint8_t kBgR = 4;
Expand Down
18 changes: 18 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,21 @@ lib_deps =
lovyan03/LovyanGFX@^1.2.7
tzapu/WiFiManager@^2.0.17
bblanchon/ArduinoJson@^7.4.2

[env:onx3248g035]
platform = espressif32@6.5.0
board = onx3248g035
framework = arduino
monitor_speed = 115200
board_build.partitions = partitions/plane_radar.csv
extra_scripts = post:scripts/merge_firmware.py
board_build.embed_files = data/ui_font.vlw
build_flags =
-std=gnu++17
-DWM_NODEBUG
-DWM_MDNS
-DBOARD_ONX3248G035
lib_deps =
lovyan03/LovyanGFX@^1.2.7
tzapu/WiFiManager@^2.0.17
bblanchon/ArduinoJson@^7.4.2
73 changes: 73 additions & 0 deletions src/hardware/board_support.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "hardware/board_support.h"

#include <Arduino.h>
#include <Wire.h>

#include "config.h"

namespace {

#if defined(BOARD_ONX3248G035)

constexpr uint8_t kPcf8574Addr = 0x38;
constexpr int kPcfExioLcdRst = 6;
constexpr int kPcfExioSdCs = 7;
constexpr int kBoardI2cSda = 8;
constexpr int kBoardI2cScl = 7;

uint8_t s_pcf_shadow = 0xFF;
bool s_pcf_ready = false;

bool pcfWrite(uint8_t value) {
Wire.beginTransmission(kPcf8574Addr);
Wire.write(value);
const uint8_t error = Wire.endTransmission();
if (error != 0) {
Serial.printf("board: PCF8574 write failed (%u)\n", error);
return false;
}
s_pcf_shadow = value;
return true;
}

bool pcfSetOutput(int bit, bool high) {
const uint8_t mask = static_cast<uint8_t>(1u << bit);
const uint8_t next = high ? (s_pcf_shadow | mask) : (s_pcf_shadow & ~mask);
return pcfWrite(next);
}

void initOnx3248g035IoExpander() {
if (s_pcf_ready) {
return;
}

Wire.begin(kBoardI2cSda, kBoardI2cScl);
Wire.setClock(400000);

s_pcf_ready = pcfWrite(0xFF);
if (!s_pcf_ready) {
Serial.println("board: continuing without PCF8574 init");
return;
}

pcfSetOutput(kPcfExioSdCs, true);
pcfSetOutput(kPcfExioLcdRst, false);
delay(200);
pcfSetOutput(kPcfExioLcdRst, true);
delay(200);
}

#endif

} // namespace

void boardInitBeforeDisplay() {
if (config::kDisplayPinBl != GPIO_NUM_NC) {
pinMode(config::kDisplayPinBl, OUTPUT);
digitalWrite(config::kDisplayPinBl, LOW);
}

#if defined(BOARD_ONX3248G035)
initOnx3248g035IoExpander();
#endif
}
12 changes: 11 additions & 1 deletion src/hardware/display.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
#include "hardware/display.h"

#include <Arduino.h>

#include "config.h"
#include "hardware/board_support.h"
#include "hardware/display_font.h"

LGFX tft;

void displayInit() {
boardInitBeforeDisplay();
tft.init();
tft.setRotation(0);
tft.setRotation(config::kDisplayRotation);
if (config::kDisplayPinBl != GPIO_NUM_NC) {
digitalWrite(config::kDisplayPinBl, HIGH);
}
tft.setBrightness(255);
tft.setTextWrap(false);
displayFontInit();
}

void displayRadarBackground() { tft.fillScreen(config::kColorBlack); }
Loading