Skip to content

Add Alientek T90 (Nations N32L40x) BSP with GC9 colour display#2219

Open
vshuraeff wants to merge 23 commits into
Ralim:devfrom
vshuraeff:bringup/alientek-t90b
Open

Add Alientek T90 (Nations N32L40x) BSP with GC9 colour display#2219
vshuraeff wants to merge 23 commits into
Ralim:devfrom
vshuraeff:bringup/alientek-t90b

Conversation

@vshuraeff

@vshuraeff vshuraeff commented Jun 27, 2026

Copy link
Copy Markdown

Summary

Adds a new BSP target for the Alientek T90 soldering iron — a Nations N32L40x (Cortex-M4F) iron
with a JBC T245 / C245 cartridge, a GC9-family 160x40 RGB565 colour SPI LCD, a CH224Q USB-PD
sink and a QST QMA6100P accelerometer. Build with make model=T90 firmware-EN.

The T90 has no exposed SWD header and ships a closed HID "update-mode" bootloader, so the port was
brought up almost entirely by reverse-engineering the stock firmware. A small atktool utility
(Tools/atktool/) packs the .atk image and flashes over the HID bootloader; see the flashing guide.

Status — validated on real hardware

  • Boots; reads tip temperature; drives the heater; PID converges.
  • Settings persist across power cycles.
  • Accelerometer auto-rotation (R / L / Auto).
  • Native RGB565 colour soldering screen; every other screen (menus, warnings, no-tip icon, boot logo)
    renders through IronOS's normal mono pipeline.
  • Compact small-font menu help text suited to the wide/short panel.
image

Calibration caveat: the tip / Vin scale is reverse-engineered from the stock firmware and tracks the
factory closely, but a final fit against a reference thermometer is still pending — treat absolute
readings as approximate (room temperature currently reads at the fixed ~38 °C cold-junction anchor).

Reverse-engineering notes

  • First-boot fault — a deterministic UsageFault / INVSTATE was an inherited FPU exception
    frame
    : the hard-float factory bootloader branches into the app with CONTROL.FPCA still set, while
    this is a soft-float build on the FreeRTOS ARM_CM3 port that never saves FP context. Fixed in
    preRTOS.cpp (clear FPCA, disable FPCCR auto/lazy stacking, deny CP10/11 before the scheduler).
  • ADC channel map — N32L40x channel N samples PA(N-1); the front-end is Vin=ch2/PA1, NTC=ch3/PA2,
    tip=ch4/PA3 (injected group), current=ch5/PA4.
  • Calibration — the factory tip function returns an absolute temperature via a piecewise-linear
    lut_interp(opamp_input) with no live cold-junction term at soldering temperatures; reproduced as a
    uV→°C LUT in the IronOS delta model plus a fixed 38 °C cold junction (the factory's baked-in anchor).
  • Settings flash — the top of the die is bootloader-owned and wiped on power-cycle, so settings were
    relocated into the retained application-flash region (linker shortened to 76K).
  • Flash protocol — the .atk format and HID update protocol were recovered by byte-exact round-trip
    against factory images; the commit opcode is SetBoot (0x15).

More detail in source/Core/BSP/Alientek-T90/README.md.

Shared-core changes (for discussion)

Most of the change is confined to the new BSP, but a few small, flag-gated hooks touch shared code —
non-T90 builds are byte-identical:

  • FRAMEBUFFER_ROTATION (OLED.hpp) — when the BSP rotates the whole framebuffer in software the UI
    draws a single un-rotated layout: getRotation() is forced false and getRawRotation() exposes the
    true left/right-hand state (otherwise the per-screen rotation branches double-apply).
  • MENU_DESCRIPTION_SMALL_FONT — word-wraps menu descriptions and renders them in the small font under
    the setting value instead of the full-screen scrolling takeover (build-side support in
    make_translation.py).
  • getCurrentOperatingMode() (GUIThread.cpp) — a C accessor so the BSP colour renderer can read the
    active operating mode without depending on the deep UI enum header.

Testing

make model=T90 firmware-EN builds clean (FLASH ~49 %, RAM ~70 % of the N32L403 budget); the resulting
image was flashed and exercised on a physical T90 — boot, heating, settings persistence, auto-rotation,
the colour soldering screen and the menus.


#2195

vshuraeff added 22 commits June 24, 2026 20:04
Adds a new IronOS build target for the Alientek T90B soldering iron:
- Nations N32L40x (Cortex-M4, soft-float) vendor SDK + startup/linker (app base 0x08005000)
- BSP integration layer (Setup/BSP/IRQ/Power/ThermoModel/flash) on the N32 std-periph driver
- GC9-family 160x40 RGB565 SPI display via a GC9Display shim bound through OLED.hpp,
  rendering the mono 128x32 UI centered + transposed into the color panel
- CH224Q USB-PD over bit-bang I2C; T245 tip thermo model (placeholder calibration)
- Makefile family block (model=T90) + host-only .atk packaging rule

Builds clean: FLASH 29.8% (106K), RAM 82.4% (32K). Analog calibration values are
placeholders pending on-hardware measurement.
Safety / correctness fixes from the 5-corner (Codex + Antigravity) review:
- heater ADC blanking: TIM4 update now forces the heater off for the injected tip
  sample and the duty is re-applied in ADC_IRQHandler after conversion; TIM2 OC
  preload disabled so the blanking takes effect immediately (cross-engine BLOCKER)
- preStartChecks slow-PWM now represcales TIM2 (carrier), not TIM4 (schedule/safety)
- flash: settings and boot logo split into two separate 2K pages so a settings save
  no longer erases the logo; clamp save length to one page; app region 106K -> 104K
- cold-junction NTC reading scaled <<3 to match the 15-bit NTCHandleLookup (was stuck 45C)
- drop CUSTOM_MAX_TEMP_C so isTipDisconnected uses the ADC-rail max, not the 400C cap
  (avoids a false 'no tip' at high setpoints)
- CH224Q: do not report negotiated voltage until power_check confirms a live contract,
  so preStartChecks actually waits for PD to settle before heating
- setTipPWM writes duty before re-arming the safety enable, with a barrier
- fault handlers force the heater off before spinning
- getInputVoltageX10 guards divisor==0; GC9 SPI waits are bounded; panel index guarded

Builds clean: FLASH 30.4% (104K), RAM 82.4% (32K).
Lower-severity items from the concurrency/quality corners (Codex+Antigravity):
- bound the ADC ENDC/RDY/calibration poll loops with a timeout (no unbounded spin)
- flash.c uses the N32 FLASH_STS_CLRFLAG (incl. PVERR/EVERR), not the STM32F1 subset
- clarify the NVIC priority comment (numeric>=5 = FreeRTOS-safe zone; do not lower)
- fix two inherited comment typos

Build still clean: FLASH 30.4% (104K), RAM 82.4% (32K).
- add T90 to the CI build matrix (push.yml) + build.sh AVAILABLE_MODELS
- vendor a stdlib-only .atk packer (source/atk-pack.py) and wire .atk into
  firmware-% for T90 so the release build produces a flashable image in CI
  (replaces the host-only uvx path; ATK_NAME overridable)
- archive *.atk alongside *.hex/*.bin/*.dfu
- README: add T90 to Supported Hardware + downloads, with an experimental-port note
Two showstoppers found by validating against real T90 firmware images before flashing:

1. .atk format was wrong (T80P-style 32-byte ASCII header). The real T90 bootloader
   format is a 13-byte header (magic 08 02 0A 01 A0 20 + uint32 payload size + 3-byte
   build date) followed by the payload XOR 0xFF, with no payload checksum. Rewrote
   atk-pack.py to this format; verified by byte-exact round-trip against the official
   factory t90.atk and PanKleszcz's t90_msdmem.atk, and our T90_EN.atk now XOR-decodes
   to a valid vector table (SP 0x20006000, reset in the 0x08005000 app region).

2. SRAM is 24K, not 32K (confirmed: factory/msdmem stacks at SP=0x20006000). The build
   used 27K RAM (>24K) -> would hardfault on boot. Set the linker to 24K /
   _estack=0x20006000 and rewrote GC9Display to STREAM the RGB565 conversion on the fly
   instead of a 12.8K full-frame buffer. RAM now 14.2K/24K (57.7%), FLASH 30.3%/104K.

Result: T90_EN.{bin,atk} are now correctly sized and packaged for the real hardware.
Cross-checked against the N32L40x reference (PanKleszcz HS-02 BSP - note its panel is
ST7735, so MCU-level only) and the N32L43 datasheet AF table; our GC9 init was already
validated against the decompiled T90 stock firmware.

- SPI1 AF was a uniform AF5 on PB3+PB5 (wrong -> blank panel). N32L40x AF is per-pin:
  SCK PB3 = AF1, MOSI PB5 = AF0 (confirmed by T90 firmware register writes AND N32L43
  user-manual Table 5-23). Split the GPIO AF config in Setup.cpp + GC9Display.cpp.
- ADC: enable the 1 MHz calibration/bandgap clock (RCC_ConfigAdc1mClk) and the internal
  reference (ADC_EnableTempSensorVrefint); without them calibration/conversions feeding
  the PID are untrustworthy.
- GC9: add a ~120 ms power-on settle before the 0xFE/0xEF unlock (matches stock).
- preRTOS: __enable_irq() in case the bootloader hands off with interrupts masked.

Build clean: FLASH 30.4%/104K, RAM 57.7%/24K.
A small dependency-light C toolkit to work with the Alientek T90 firmware image format
and (experimentally) flash over USB, so the iron can be packed/flashed without the
official Windows updater.

- pack/unpack/info: pure C, no deps. Implements the reverse-engineered T90 .atk format
  (13-byte header magic 08 02 0A 01 A0 20 + uint32 size + 3-byte date; payload XOR 0xFF).
  Verified by byte-exact round-trip against the real factory t90.atk and t90_msdmem.atk.
- probe/flash: hidapi (auto-detected), implementing the Alientek HID bootloader protocol
  from the T80P reference (dozed-dev/atk-fw-util). T90 update-mode VID/PID + chunking are
  not fully confirmed, so flash is EXPERIMENTAL and gated behind --force, with --vid/--pid
  overrides and a recovery-first workflow documented in the README.
…, buttons)

The iron now boots into the IronOS UI. Fixes that make it run:

- preRTOS FPU frame: the update-mode bootloader hands control off with
  CONTROL.FPCA set (the factory firmware is hard-float), but this is a
  soft-float build on the FreeRTOS ARM_CM3 port, whose PendSV never saves an
  FP exception frame. With FPCA set the core stacked extended (FP) frames and
  armed lazy S0-S15 stacking into stack slots the port does not account for,
  clobbering a preempted task's saved return address -> deterministic
  UsageFault INVSTATE (EXC_RETURN 0xFFFFFFED). Clear FPCA, disable FPCCR
  ASPEN/LSPEN and leave CP10/CP11 denied so every exception uses the 8-word
  basic frame the port expects.
- preRTOS bring-up order: stop the leftover bootloader SysTick (+ clear the
  pending tick) so xPortSysTickHandler cannot fire before the scheduler, clear
  all NVIC enables/pendings (leftover bootloader IRQs), and defer __enable_irq()
  to the end of bring-up.
- linker: cap SRAM at the proven-real 20K (_estack 0x20005000); 0x20006000
  (24K) faults on this part. Reserve the HID-bootloader app-validity gate
  (8 bytes 00 00 00 00 00 FF FF FF at 0x08005800), the only thing the
  bootloader checks before jumping to the app.
- display: drop the unneeded AFIO clock; SPI1 alternate-function on the
  N32L40x is configured per-pin via AFL/AFH, not through AFIO remap.
- buttons: the panel is mounted rotated 180 deg from the reference (the
  comfortable right-hand grip), so map logical A/B to the opposite keys so the
  +/- buttons match their on-screen meaning.
…nsweep

- The post-END commit must use SetBoot (0x15), which finalizes the freshly
  flashed image (sets the app-valid flag) and reboots. SetStartAPP (0x16) only
  jumps to an already-valid resident app without finalizing, so a fresh flash
  committed with 0x16 never boots. Confirmed live on the device. Default the
  commit code to 0x15; add --commit <code> and --no-commit overrides.
- Add an fnsweep subcommand to probe the bootloader function-code space against
  addr 0xAF, plus a --dexor option, to support protocol reverse-engineering.
The T90 has a QST QMA6100P motion sensor on the same bit-bang I2C bus as the
CH224Q (PB6=SCL / PB7=SDA, 7-bit address 0x12, chip-id 0x90); the earlier
bring-up wrongly assumed there was no accelerometer. Wire it into the movement
framework so the iron sleeps when idle and wakes on motion.

- new Core/Drivers/QMA6100P.{hpp,cpp} + QMA6100P_defines.h: static-class driver
  in the SC7A20 mould. detect() = probe + chip-id 0x90; initalize() runs the QST
  init sequence (soft reset, wake/MCLK, +/-8g range, default ODR); getAxisReadings
  reads the 6 output bytes as 14-bit left-justified signed values; getOrientation
  derives hand/flat in software (mapping flagged for on-device tuning).
- MOVThread: add the ACCEL_QMA detect/read branches; add AccelType::QMA.
- Translations: add the "QMA" accelerometer name.
- T90 configuration.h: drop NO_ACCEL; enable ACCEL_QMA + ACCEL_I2CBB2 +
  I2C_SOFT_BUS_2 (the accelerometer talks over the I2CBB2 soft bus).
- Software_I2C.h: expose the PB6/PB7 soft-I2C macros for I2C_SOFT_BUS_2 too, not
  just CH224_SOFT_I2C (the two devices share the bus).
- postRTOS: call I2CBB2::init() after the scheduler starts (it creates a FreeRTOS
  static mutex).
- I2CBB2::init(): skip the STM32-HAL GPIO setup block on the N32L40X, where the
  SCL2/SDA2 pins are already configured open-drain in Setup.cpp via the std-periph
  driver and the STM32 HAL API does not exist; no change for other targets.

Verified on hardware: the debug menu reports ACC = QMA, the display sleeps when
the iron is set down and wakes when it is picked up.

Left/right-hand orientation mapping for display auto-rotate still needs on-device
tuning.
On the N32L40x, ADC channel N reads PA(N-1) (ADC_CH_2_PA1, ADC_CH_3_PA2, ...).
The earlier pinmap applied a ch_n=PA_n convention and shifted every analog signal
up by one pad: "Vin" actually sampled the NTC node, "tip" sampled the current
shunt (a bogus temperature that drove full-power runaway) and the real Vin pin
(PA1) was never configured. Corrected to Vin=ch2/PA1, NTC=ch3/PA2, tip=ch4/PA3
(injected group), current=ch5/PA4 (reserved). All five pads are now driven analog.

Also harden the shared single ADC:
- serialise the regular group with a static FreeRTOS mutex so the PID thread (Vin)
  and GUI thread (NTC) can no longer race the regular-rank reconfiguration;
- two-pass "settle" read that discards the first conversion (the rank is re-pointed
  every call, so the sample-and-hold still carries the previous channel's charge);
- phase the injected tip sample a settling holdoff after the heater blanks
  (TIM4 OC2REF -> TRGO) so the high-impedance thermocouple front-end has recovered;
- drop the redundant injected-length re-write from the regular read path: it lives
  in a separate register set once in MX_ADC_Init, and re-asserting it on every read
  could abort an in-flight TIM4-triggered tip conversion;
- remove the dead current-sense reader (tip presence now uses an idle-temperature
  test, not the floating current node).
Reverse-engineer the T245 (JBC C245) thermal front-end from the Alientek T90B
stock firmware instead of carrying Sequre S60 placeholders:

- uV -> degC curve (ThermoModel.cpp) taken from the factory op-amp-output lookup,
  expressed in the IronOS delta model and passed through InterpolateLookupTable;
- OP_AMP_GAIN_STAGE 250 and VOLTAGE_DIV 370 from the factory front-end (536/467
  were stale placeholders; the 536 gain halved the computed tip uV, so a hot tip
  read ~half temperature and the PID drove full power);
- getHandleTemperature() returns a fixed 38 degC. The factory LUT is ABSOLUTE and
  anchored at a ~38 degC cold junction; our delta table stores those breakpoints
  minus 38 and getTipInC() adds the handle term back, so a fixed 38 reproduces the
  factory absolute reading exactly. The on-board NTC runs through an uncharacterised
  divider, so a live reading just injected tens of degrees of error (the original
  cause of the overheating). TODO: restore a live cold junction by reversing the
  factory NTC curve once a reference thermometer is available.

Tip presence (isTipDisconnected) now uses an idle-temperature test: an open handle
reads the op-amp open-circuit output (~360 degC, steady) while idle, whereas a
fitted idle tip sits near ambient. The classic rail-high test is useless here (the
T90 open thermocouple does not rail) and the floating current node is unreliable.

configuration.h carries the full T90 model block: auto-rotate orientation default
(QMA6100P fitted), MAX_TEMP for the T245, the calibration constants above, the
flash-layout addresses, and the color-display / small-font / framebuffer-rotation
feature flags consumed by the UI commits that follow.
Settings never survived a power cycle at the old 0x0801F000 page: the top of the
128K die (~0x0801C954..0x08020000) is owned by the Alientek HID bootloader and its
metadata, so writes there read back in-session but are wiped on the next power-up.

Relocate the settings and logo pages just ABOVE the IronOS app, inside the region
the bootloader treats as application flash (proven to retain across power cycles -
the factory firmware's own code lives there):
  settings page: 0x08018000..0x08018800 (2K)
  logo page:     0x08018800..0x08019000 (2K)
The linker FLASH region is shortened to 0x08005000..0x08018000 (76K) so app code
can never reach them.

flash_save_buffer now acts on the N32 erase/program status: it bails and re-locks
on a write-protected or failed page erase, and stops on the first failed word
program, instead of programming into an un-erased or faulted page.
…n, small-font menus

The T90 has a GC9-family 160x40 RGB565 color LCD where IronOS expects a 1bpp mono
panel. The GC9 shim expands the mono framebuffer to color on the fly (no 12.8 KB
full-frame buffer fits the 20 KB SRAM).

- FRAMEBUFFER_ROTATION: the shim rotates the WHOLE framebuffer in software, so the
  UI must draw a single un-rotated layout. OLED::getRotation() is forced false under
  this flag while OLED::getRawRotation() exposes the true left/right-hand state for
  the shim and for rotation-aware reads; otherwise the per-screen getRotation()
  branches (mirrored icons, swapped positions) double-apply on top of the frame
  rotation and the home-screen icons scatter. draw_temperature_change keeps the +/-
  symbols on the side of the physical key per the true orientation.

- Native RGB565 "hero" soldering screen (ColorUI/ColorTheme): a heat-mapped big
  temperature, voltage/wattage/setpoint columns and a live power bar streamed
  straight to the panel. Other modes/screens fall back to IronOS's mono pipeline,
  so the no-tip icon, warnings, menus and boot logo render normally. renderActiveScreen
  reads the active mode via a new getCurrentOperatingMode() C accessor and only
  takes over for soldering with a tip fitted.

- The full-frame GC9 Transmit is serialised with a display mutex: OLED::setRotation()
  issues a full-frame Transmit from the higher-priority motion task and could preempt
  the GUI task mid-frame, interleaving the SPI stream and rebuilding the ColorUI
  render state under a reader.

- MENU_DESCRIPTION_SMALL_FONT renders scrolling help text small: the wide/short panel
  turns the default large font into an unreadable smear for long descriptions.
Upstream replaces the whole settings screen with the description scrolling sideways in the large font
after a few seconds of inactivity. On a wide/short panel (e.g. the T90 colour LCD) that hides the
setting name and value and leaves a two-button user without context (which key changes, exits, or
moves on), and the large font is barely readable there.

Under MENU_DESCRIPTION_SMALL_FONT the description is shown in the small font in the otherwise-empty
area under the setting value instead: the name, value and scrollbar stay visible and the buttons keep
their normal actions (no help-mode takeover). Long descriptions page vertically so nothing scrolls
sideways.

The descriptions were previously only ever encoded for the large-font symbol table, so rendering them
in the small font produced garbage (the indices point at different glyphs). make_translation.py now,
when MENU_DESCRIPTION_SMALL_FONT is defined, word-wraps the descriptions and encodes them with the
small-font table (and emits the small glyphs for their characters). `\n` always encodes to 0x01, so
the firmware splits the wrapped lines back out at render time. Non-T90 builds are byte-identical (the
flag gates every change).
…EADME

Add a BSP README for the Alientek T90 (hardware/pin/ADC map, the reverse-engineering approach, and the
key engineering decisions: corrected ADC channel map, factory-reversed tip calibration, fixed cold
junction, idle-temperature tip detection, retained-flash settings, the colour display + software
framebuffer rotation, and the compact menu help layout).

Add a flashing guide (atktool over the HID update-mode bootloader, the B+USB+power update-mode entry,
and recovery via the factory image).

Refresh the main README and docs index: the port is now validated on real hardware (boots, reads tip
temperature, heats, persists settings, auto-rotates, native colour screen) with calibration reversed
from the stock firmware, and link the new flashing guide.
Copy each wrapped help line a whole glyph at a time into the render buffer so a 0xF1..0xFF two-byte
glyph can never be split at the buffer boundary (the previous fixed-byte clamp could leave a dangling
lead byte), and document that the build-time wrap width counts display glyphs. Harmless in practice for
the Latin small font (lines stay well under the buffer) but robust for wider wraps / multi-byte glyphs.
Addresses review feedback on the compact small-font descriptions.
… glyph indices

Two fallouts of rendering menu descriptions in the small font:

- CJK languages (JA_JP, ZH_*, ...) failed to build: a description's CJK characters were pulled into the
  small-font symbol set, but CJK has no small glyph (large font only), so make_translation raised a
  missing-glyph KeyError. small_font_renderable() now keeps any description containing a character without
  a small glyph in the large font, as before; only fully small-renderable descriptions are wrapped and
  small-encoded.

- The native colour soldering screen showed the wrong unit letters (e.g. "9.2M"/"9.9k" instead of
  "9.2V"/"9.9W"): ColorUI hardcoded the small-font glyph indices for V / W / '.', which shifted once the
  descriptions were added to the small font. They are now derived at runtime from the IronOS small-symbol
  constants (encoded byte == font-table position + 2), so they track any font reshuffle.
`make check-style` (clang-format) covers the new T90 BSP and its bundled Nations std-periph driver, the
same as the other boards' vendor code. Apply clang-format so the check passes. No functional change.
The Alientek bootloader leaves SysTick + its interrupt, leftover NVIC IRQs and
possibly a DMA channel running on a normal power-up (but not on a direct
post-flash boot). settingsConstants[] lives in .data because one of its bounds
is runtime-initialised (TemperatureInF.max via HasFahrenheit), so that stray
activity zeroed QCIdealVoltage's min/max right after the Reset_Handler .data
copy. The resulting [0,0] bounds with an intact default of 90 made sanitise
reset the value on every boot -> loadSettings() returned dirty -> the "Certain
settings changed" warning on every power-on.

- SystemInit: stop SysTick, disable+clear all NVIC IRQs and DMA channels, then
  re-initialise .data (copy from flash) and .bss (re-zero) before the C++
  constructors run, so the corruption cannot survive into the app.
- Relocate the settings page to 0x0801B000, verified on hardware to retain all
  120 bytes across power-off (sentinel write + power-cycle read-back).
- flash_save_buffer: reset the iCache after programming so the saveSettings
  verify and the next-boot load read the freshly written cells, not stale lines.
The 2K logo flash page cannot hold a 160x40 colour frame, so the splash is
compiled in (BootLogoData.cpp, RGB565). showBootLogo streams it through the GC9
colour path honouring the LOGOTime setting (skip / N seconds / until a button),
and the GUI state machine shows it before the startup warnings.
…age move

Update the BSP README's settings-persistence note to the relocated 0x0801B000
page and add the bootloader-leftover (SysTick/NVIC/DMA) .data/.bss corruption
finding and the SystemInit fix.
@vshuraeff
vshuraeff marked this pull request as ready for review June 27, 2026 10:54

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not include files that are not required for the project to keep bloat down 🙏🏼

#include <stdint.h>

// clang-format off
extern const uint16_t t90BootLogoRGB565[6400] = {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than one time generating this, its preffered if this is generated in the Makefile so we only commit the source image data; and can derive this at compile time

//
// Re-implements the BSP.h + BSP_Power.h contract against the N32 std-periph driver, following
// the Sequre S60 algorithms (the only other std-periph / Cortex-M4 IronOS target) but with no
// ST HAL: every HAL_* call is replaced with the equivalent Nations driver call or register poke.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do NOT do this sorts of comparative comments, I know the LLM likes to make them but they do not really help. Its best to just document what is here rather than what isn't

#define ADC_MAX_READING (4096 * 8) // Maximum reading of the adc
#define ADC_VDD_MV 3300 // ADC max reading millivolts

// Deriving the Voltage div:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Floating comment?

@Ralim

Ralim commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Hia, Thank you so much for the PR.

A few questions / small ask.

  1. Which LLM is used for this?

You ignored the PR template and instead raised the PR with a very LLM-like message, as well as lots of the code comments read like an LLM. If an LLM is used please disclose this.

  1. Where did atktool come from?

Is it brand new, or is it from another project (So I know for the software licence)?

  1. Flashing docs

For the flashing doc, thank you for writing one 🙇🏼

-> Where does t90-factory.atk come from (can you link to it in the doc?)
-> Does the flashing work on windows? If it doesn't, Please be very clear at the top of the file.

I dont mind if it does or doesn't work on Windows, just want to make it very clear so we dont get issues raised

  1. Colourisation implementation

Please do not do colourisation or theme's in the BSP folder. All drawing logic needs to live in the UI/drawing folder.

As you are adding a colour screen, create a new folder colour_WidthxHeight and place the updated drawing logic in there. Update the code to mask out OLED.{cpp,hpp} and make a new driver for the screen.

This way you can link to your display driver instead of the oled file,and call functions directly to draw how you want.

  1. Boot logo

Traditionally, IronOS does not burn in a boot logo by default and that is up to the user to flash later on. It would be preferable to keep matching this setup. This way the boot logo can be submitted to IronOS-meta repo, and users can flash their own.

Some thought will need to be given to how to encode and store boot logos vs flash size for a colour display.

  1. Shared code changes

For the first two code changes; these should be raised in their own PR's for review.
I believe you may not need the framebuffer rotation change once you write your own display driver file instead of hijacking oled.

  1. PR Size

This PR is quite large, I've some a skim review of things that show up as soon as I open the file, but its hard to read each line when its this big.

Would you be willing to break this up into PR's that implement logical blocks / changes incrementally so that its not a 60k line delta to sift through.

  1. Readme

When updating the main readme, you dont need to add the LLM-speak long explanation, just mark it as experimental.

I hope that all makes sense 🙇🏼

@dogtopus

Copy link
Copy Markdown
Contributor

Also the atktool should probably do firmware flashing only, and be separated from this repo. This aligns better to what other platforms do (e.g. Pinecil).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants