Files
patriandClaude Opus 5.5 c9fb70ad10 12-OBD-Speed: choose the background GIF from the SD card
- background page (4th tile): built-in or any GIF in /gifs on the SD card
- chosen GIF is copied into a raw 11.9 MB 'bggif' flash partition (header
  written last) and played memory-mapped from flash; SD only needed when
  choosing or when the file changed (checked at boot in the background)
- own partitions.csv: 4 MB app + bggif + NVS
- tools/gif_to_360.py: convert GIFs to 360x360 keeping frame timing;
  converted output (12-OBD-Speed/sd/) is git-ignored

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:58:44 +02:00

16 KiB
Raw Permalink Blame History

JC3636W518EN — Project Notes

Vendor kit and documentation for the JC3636W518 (Shenzhen Jingcai / JCZN, Guition), a round 1.8" ESP32-S3 touch-display puck. Goal: write a new custom program for this device.

Working agreement (from the user)

  • Before starting any task, ask questions first and make sure requirements are clearly understood.
  • While working, describe what is being done at each step.
  • Keep this file updated with new findings.
  • The folder is a git repo (local only, no remote). Commit 9ee85bf = original kit snapshot before any cleanup.

Hardware summary

Item Detail
MCU ESP32-S3R8 (dual-core LX7 @ 240 MHz, 512 KB SRAM, 8 MB OPI PSRAM in-package)
Flash 16 MB confirmed (esptool flash_id: mfr 0x85, dev 0x2018, quad mode). Chip rev v0.2, MAC f0:9e:9e:1e:60:40
Radio WiFi + BLE 5 only — NO Bluetooth Classic (ESP32-S3 has no BR/EDR, so no SPP / BluetoothSerial)
Connection Board shows up on COM21 on this PC. esptool: %LOCALAPPDATA%\Arduino15\packages\esp32\tools\esptool_py\4.6\esptool.exe
Display 1.8" round IPS, 360×360, driver ST77916 over QSPI, RGB565 used (panel is 262K/18-bit)
Touch CST816S capacitive, I2C (addr 0x15), single touch + gestures
Audio out PCM5100A I2S DAC → 3.5 mm line-out jack (no speaker amp on board)
Microphone MSM261S4030H0R I2S MEMS mic ("pickup port")
Storage microSD (TF) slot, 4-bit SD_MMC
Button BOOT button on GPIO0 (only user button)
Power 5 V USB-C (~180 mA), plus Qi wireless receiver (standalone, no MCU signals)
USB Native USB (GPIO19/20) — USB-CDC serial, no separate USB-UART chip
Expansion CN2 4-pin connector: 5V, GPIO39, GPIO38, GND (the only free GPIOs)
Size 58 × 58 × 11 mm metal case, active area 45.68 × 45.68 mm

Pin map (from pincfg.h, cross-checked against schematic)

LCD QSPI : CS=10  SCK=9  D0=11 D1=12 D2=13 D3=14  RST=47  BL=15 (PWM, LEDC, via AO3400 MOSFET)
           LCD_TE is on the FPC; schematic net near GPIO47/RST is hard to read — treat TE as unused
Touch I2C: SDA=7  SCL=8  INT=41  RST=40
SD_MMC   : CLK=3  CMD=4  D0=2  D1=1  D2=6  D3=5
DAC I2S  : BCK=18 WS/LRCK=16 DOUT=17  MCK=21 (code uses -1, PCM5100A PLLs from BCK)  MUTE/XSMT=48 (LOW = mute)
Mic I2S  : SCK=42 WS=45 SD=46
Button   : GPIO0 (BOOT)
Free     : GPIO38, GPIO39 (on CN2 header)
USB      : D-=19 D+=20

PlatformIO setup (preferred build system, since 2026-10-23)

  • All projects are PlatformIO projects (platformio.ini, src/main.cpp, include/). They no longer open in the Arduino IDE.
  • PlatformIO IDE extension is installed in VS Code. CLI: %USERPROFILE%\.platformio\penv\Scripts\pio.exe (Core 6.2.0, not on PATH).
  • JC3636W518 (ESP32-S3): platform https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip (pioarduino = Arduino core 3.0.5; the official PIO espressif32 only has core 2.x, which ESP32_Display_Panel 0.1.4 can't use). Board esp32-s3-devkitc-1 + board_build.arduino.memory_type = qio_opi, flash_mode = qio, board_upload.flash_size = 16MB, partitions = huge_app.csv, -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=1, upload/monitor COM21 @115200. All of this lives in shared/jc3636w518.ini; a project's platformio.ini only does extra_configs + [shared] dir + extends = jc3636w518 (see shared/README.md). Libraries: lib_deps = lvgl/lvgl@9.6.0, lib_extra_dirs → shared/lib (board library) + vendor 2-Program Example/Demo_Arduino/library (ESP32_Display_Panel, ESP32_IO_Expander; its lvgl 8 is not used). LVGL config: -DLV_CONF_INCLUDE_SIMPLE -I${shared.dir}/include — the -I is required, otherwise the LVGL library compiles without lv_conf.h (symptom: undefined reference to lv_gif_create).
  • (Classic ESP32 projects — the 2024 BT-Classic→WiFi bridge and ELMulator — were removed 2026-10-23; they used espressif32@6.10.0, board esp32dev. In git history.)
  • New JC3636W518 project: follow shared/README.md (3-line platformio.ini + #include "jc3636w518.h").
  • Editing files with scripts: keep each file's line endings (most are LF, some main.cpp are CRLF) — Python open(..., newline="").
  • When converting .ino → main.cpp: add #include <Arduino.h> and prototypes for functions used before their definition; don't #include .c files that are also in src/.

Software stack used by the vendor demo

  • Arduino IDE + arduino-esp32 core ≥ 3.0.1 (core 3.0.5 already installed on this PC under %LOCALAPPDATA%\Arduino15).
  • Libraries (bundled in 2-Program Example/Demo_Arduino/library/):
    • ESP32_Display_Panel 0.1.4 (Espressif) — ST77916 QSPI LCD + CST816S touch + backlight
    • ESP32_IO_Expander 0.0.2 (dependency)
    • lvgl 8.3.11 bundled (instructions say "8.4.0 and below"; LVGL 9 is NOT supported by the demo)
  • lv_conf.h must be copied next to the lvgl library folder (Documents\Arduino\libraries\lv_conf.h); for the LVGL demos, move lvgl/demos into lvgl/src/.
  • Key lv_conf.h settings: LV_COLOR_DEPTH 16, LV_COLOR_16_SWAP 1, LV_MEM_CUSTOM 1 (uses malloc → can use PSRAM), LV_TICK_CUSTOM 1, LV_USE_GIF 1, LV_USE_PNG 1, LV_USE_SJPG 1, Montserrat 48 enabled.
  • None of these libraries are currently in Documents\Arduino\libraries (only unrelated Adafruit libs). No arduino-cli / PlatformIO / ESP-IDF installed. Python 3.12 and git are available.

Arduino board settings (from Arduino set up.png)

Board ESP32S3 Dev Module, USB CDC On Boot Enabled, CPU 240 MHz, Flash QIO 120 MHz, Flash Size 16 MB, Partition Huge APP (3MB No OTA/1MB SPIFFS), PSRAM OPI PSRAM, Upload Mode UART0/Hardware CDC, Upload speed 921600. If upload fails: hold BOOT (GPIO0) while plugging in / resetting.

Display driver pattern (shared board library shared/lib/JC3636W518, was the vendor scr_st77916.h)

scr_lvgl_init() does everything: LEDC backlight → CST816S on I2C @400 kHz → QSPI bus @80 MHz (vendor: 50) → ESP_PanelLcd_ST77916(bus, 16bpp, RST) → invertColor(true) → lv_init() + tick → LVGL display with two 72-row draw buffers (2 × ~52 KB internal RAM) → flush callback with DMA-done callback → touch indev. Returns the lv_display_t*. Helpers: setRotation(0..3) / getRotation() (panel rotates via MADCTL swap/mirror like the vendor table; touch converted in software in touchpad_read; verified by the user at all 4 angles, 2026-10-23), set_brightness(0..100), screen_switch(bool); perf_monitor.h prints FPS/refresh/other/CPU once per second. Display color format is RGB565_SWAPPED (ST77916 byte order, set in lv_conf.h).

Folder contents

Folder What it is
1-Instructions Spec PDF (duplicate), AIDA64 remote-sensor layout aida_remote_1.85.rslcd, MJPEG conversion tool (Windows .NET, converts video to 360×360 MJPEG 25 fps)
2-Program Example/Demo_Arduino Vendor only: library/ (3 libs), lv_conf.h (vendor original), sd/ (SD listing example; its lv_conf.h has FATFS edits), docs/ (setup screenshots + compile instructions). The vendor's original LVGL demo sketch no longer exists unmodified.
tools gif_to_360.py — converts GIFs to 360×360 for the device (Pillow; scale to cover + center crop, keeps frame timing)
shared Shared code for all JC3636W518 projects: jc3636w518.ini (PlatformIO base config), include/lv_conf.h (LVGL 9.6), lib/JC3636W518 (board library). See its README.md
12-OBD-Speed The new program — working in the car (2026-10-23): speed from the ELM327 over BLE as big number + arc gauge on a GIF background, battery-voltage arc in the bottom gap, idle/screen-off mode (both verified by the user); swipe left for the adapter menu (scan, select → remembered in NVS, auto-reconnect every 3 s), again for settings (rotation, idle/screen-off timeout), again for the background (GIF from SD /gifs copied into an 11.9 MB raw flash partition, own partitions.csv). See its README.md
10-GIF-Tests GIF playback experiments (was 10-OBD-Display-2024): display_gif_embedded, display_gif_sd, assets/gifs. The 2024 bridge/ELMulator/WiFi speed sketch, the broken LVGL 9 archive and the BLE scanner tool (11-BLE-Scanner: on-screen scan, GATT listing, ELM test, LittleFS log) were removed 2026-10-23 — all in git history
3-Specification Spec sheet (factory firmware features, interface photo)
4-Chip manual ESP32-S3R8 datasheet, PCM5100A datasheet
5-Dimension Mechanical drawings (JPG + DWG)
6-Schematic diagram 5 pages: power/USB/SD/mic, ESP32-S3, DAC, LCD FPC, Qi receiver
7-User's manual "Getting started" — Arduino IDE setup, library install steps (16 pages)
8-Tool Network debugging assistant, SSCOM serial tool (Chinese Windows tools, .rar)
9-Burn Espressif Flash Download Tool 3.9.3, factory demo 1.85_demo.bin (1.7 MB, flash at 0x0), screenshots. logs/ are old (2022) logs from other boards — ignore

Factory firmware (for reference)

AIDA64 secondary screen (18 styles), mic spectrum, MP3 player (/music on SD), photo frame (/pic, 360×360 JPEG), MJPEG player (/mjpeg), weather, theme clocks, WiFi provisioning AP "My App"/12345678 + web config at device IP. Re-flash it with Flash Download Tool: ESP32-S3, 1.85_demo.bin @ 0x0.

Car battery / idle

Cheap ELM327 clones draw ~20–50 mA permanently (OBD pin 16 = always 12 V); the display ~180 mA if its USB stays powered. 12-OBD-Speed idle mode stops OBD polling after N min without data so the car's ECUs can sleep; ATRV (adapter-internal) every 30 s detects an engine start (> 13.2 V).

OBD adapter (user's)

AliExpress "ELM327 Mini V2.1 Bluetooth OBD Dual Mode 5.1" — dual mode = Bluetooth Classic SPP and BLE, so the ESP32-S3 should be able to connect directly over BLE (no bridge ESP32). Verified in the car (2026-10-23, with the since-removed BLE scanner tool; log in 12-OBD-Speed/docs/2026-10-23_ble_scanner_car_test.txt):

  • BLE name OBDII, MAC 1c:a1:35:69:8d:c5, advertises service 0xFFF0. Characteristics: FFF1 [W, WnR, Notify], FFF2 [W, WnR]. Working setup: subscribe to notifications on FFF1, write commands ("... ") to FFF1 (write without response) — FFF2 is the usual write characteristic on such clones and should work too.
  • Replies: ATZ → ELM327 v2.1 (echo on by default → send ATE0), ATRV → 12.9V, 0100 → 41 00 BE 3E A8 13.
  • Supported PIDs 01–20 (from BE 3E A8 13): 01 monitor status, 03 fuel system, 04 engine load, 05 coolant temp, 06/07 fuel trim B1, 0B intake manifold pressure (MAP), 0C RPM, 0D speed, 0E timing advance, 0F intake air temp, 11 throttle, 13 O2 sensors, 15 O2 sensor 2, 1C OBD standard, 1F run time, 20 → more PIDs in 21–40. No 10 (MAF) → speed-density engine.
  • Adapter is powered from the car's OBD port (pin 16, 12 V).

Gotchas / observations

  • LVGL 8 GIF bug (patched locally, only relevant if LVGL 8 is used again): LVGL 8.3.11 gifdec.c (gd_open_gif) filled the canvas opaque with the GIF's background color. GIFs with a transparent background (e.g. loligod.gif, bg = RGB 28,29,49) showed that dark gray except where "restore to background" frames had cleared to transparent (→ screen black). Patched in the vendor copy 2-Program Example/Demo_Arduino/library/lvgl/src/extra/libs/gif/gifdec.c: initial canvas alpha 0x00 (marked LOCAL PATCH). Re-apply if LVGL is ever replaced/upgraded.
  • Most GIFs in assets/gifs are optimized (transparent unchanged pixels, disposal 1/2); loligod.gif has a fully transparent background (~84 % of each frame).
  • GIF performance (360×360 loligod, embedded, measured with a monitor_cb FPS counter in display_gif_embedded): baseline 15.4 fps (decode 27 ms + refresh 31 ms) → no LVGL log / no per-loop prints: no change → vTaskDelay(1): 16.5 → double draw buffer (2×72 rows internal RAM): 19.5 (refresh 21 ms) → -O2 instead of -Os: 20.6 fps (refresh 19 ms, decode ~24 ms). GIF's own rate is 23.7 fps. From SD card it was only ~1.4 fps. Remaining limit is LVGL's single-threaded GIF decode; going further would need decoding on the other core or a lighter format (e.g. pre-converted frames/MJPEG).
  • SD card (user's 487 MB SDSC) intermittently fails to init (sdmmc_init_ocr ... 0x107) after resets; once stuck only a power cycle helps (card VDD is hard-wired to 3V3). Recommend a newer SDHC card.
  • Reading boot logs: USB-CDC — reset via DTR=0/RTS pulse, then reopen the port (it re-enumerates).
  • LVGL 9.6.0 port (display_gif_embedded, 2026-10-23): 23.4 fps (= the GIF's own 23.7 fps; decode ~12 ms, refresh ~24 ms) vs 20.6 fps tuned LVGL 8.3. LVGL 9.6 replaced gifdec with bitbank2 AnimatedGIF (2× faster decode, correct transparency — no local patch needed). Port notes: lib_deps = lvgl/lvgl@9.6.0 (takes priority over the vendor lvgl 8 in lib_extra_dirs); lv_conf.h generated from the 9.6 template (CLIB malloc, refresh 15 ms, GIF on, examples off); in 9.6 public headers live in include/lvgl/ and LV_COLOR_DEPTH is deprecated → LV_COLOR_FORMAT_DEFAULT (RGB565); no LV_COLOR_16_SWAP → lv_draw_rgb565_swap() in the flush cb; no LV_TICK_CUSTOM → lv_tick_set_cb(millis); no monitor_cb → display events LV_EVENT_REFR_START/RENDER_START/REFR_READY; images are lv_image_dsc_t with LV_IMAGE_HEADER_MAGIC. lv_gif_set_color_format() switches the GIF buffer format (used, see below).
  • LVGL 9 tuning (display_gif_embedded, CPU at full 23.8 fps): start 86 % → GIF in display color format 66 % → render RGB565_SWAPPED (no swap pass) 60 % → QSPI 80 MHz 55 % → IRAM for hot LVGL code 53 %. No effect: LV_GIF_CACHE_DECODE_DATA (not used anywhere in 9.6), 120-row buffers. AnimatedGIF "turbo" mode can't be used: it only outputs in COOKED draw mode, LVGL uses RAW.
  • All JC3636W518 LVGL projects now use LVGL 9.6.0 + the shared library (10-GIF-Tests/*, 12-OBD-Speed). SD drive A: = LV_USE_FS_STDIO on /sdcard/.
  • 80 MHz QSPI and RGB565_SWAPPED rendering: visually confirmed OK by the user (colors correct, no glitches).
  • Display is round: corners of the 360×360 framebuffer are not visible — design UI within a 360 px circle.
  • Compiling a 13.9 MB GIF into the firmware will not fit "Huge APP" (3 MB); large media should live on SD (or in a custom partition).
  • display_gif_sd setup() returns early if the SD card fails to mount (black screen); display_gif_embedded starts its GIF first.
  • Draw buffer is in internal RAM; PSRAM is available for larger buffers / images.
  • LittleFS works on the spiffs partition of huge_app.csv (~900 KB); the first mount after flashing formats it (prints Corrupted dir pair / mount failed (-84) and some task_wdt ... task not found once — harmless).
  • BLE (NimBLE) in the user's home: ~12 devices visible within 10 s.
  • Big fonts: generate with npx lv_font_conv (Node 22 installed) from lvgl/scripts/generators/built_in_font/Montserrat-Medium.ttf; -DLV_LVGL_H_INCLUDE_SIMPLE (in shared ini) makes the generated #include work.
  • Binary assets (GIFs): board_build.embed_files = data/x.gif → extern const uint8_t x_start[] asm("_binary_data_x_gif_start") (no huge C arrays).
  • Python edit scripts: never pass C strings containing \n through a bash heredoc into Python — it turned into a real line break twice; write the script with the Write tool instead.
  • Serial output from the background task (core 0) is often lost, not only right after reset; don't rely on it as proof. Serial output right after a reset can be lost/garbled (USB-CDC re-enumeration, prints from two cores interleave) — don't conclude a task isn't running from a missing early line.
  • Preferences prints nvs_open failed: NOT_FOUND the first time a namespace is read before it was written — harmless.