# 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 ` 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.