# Display

The screen on a Kode Dot: an AMOLED with touch over MIPI-DSI, what brings it up, what LVGL needs from you, and the code to draw on it.

## What it is

The front of the Kode Dot is a 2.06-inch AMOLED, 410 pixels wide and 502
tall, with a capacitive touch layer over it. AMOLED means every pixel makes
its own light: black costs nothing, white costs the most, and brightness is
the biggest lever you have on battery life.

You draw on it with **LVGL**, version 9: you create labels, buttons and
images, and LVGL renders them into a buffer that the panel driver pushes to
the screen. `kode_bsp_init` does the bring-up, powers the panel, resets it,
sends the manufacturer's init sequence, starts LVGL and finds whichever touch
controller the board carries, so your code starts at "create a label".

## How it is connected

```mermaid
flowchart LR
  p4["ESP32-P4"] -- "MIPI-DSI, 1 lane" --> panel["CO5300 AMOLED"]
  p4 -- "GPIO7 reset" --> panel
  exp["TCA6408 P7"] -. "panel power" .-> panel
  p4 -- "I2C, 0x15 or 0x5A" --> touch["CST820 or CST916E"]
```

*Pixels over MIPI-DSI; power and reset through the expander and a pin; touch on the I2C bus*

The panel's controller is a Chipone CO5300 on a single MIPI-DSI lane. Its
power enable is bit `P7` of the TCA6408 expander, not a P4 pin, and its reset
is `GPIO7`, shared with the touch controller. Touch is on the shared I2C bus
at `0x15` or `0x5A` depending on which supplier made the panel; its interrupt
is bit `P6` of the same expander, so touch is polled.

## Specifications

| Thing | Value |
| --- | --- |
| Panel | 2.06-inch AMOLED, Chipone CO5300, 410 by 502, portrait, RGB565 |
| Bus | MIPI-DSI, one data lane. The DPHY runs from the P4's internal LDO channel 3 at 2.5 V |
| Power | Expander `P7`, active high. Off at boot |
| Reset | `GPIO7`, active low, shared by panel and touch |
| Touch | CST820 at `0x15` or CST916E at `0x5A`, found at start-up. Interrupt on expander `P6`, polled |
| UI | LVGL 9. `kode_bsp_display()` returns the display; take `kode_bsp_lvgl_lock()` around UI calls from your own task |
| One frame | About 400 KB at two bytes per pixel, in PSRAM |
| Cost | About 76 mA white at full brightness, about 3 mA at 25 % |
| Validated | `Kode-Dot-Val/03-DISPLAY`: panel, touch and LVGL on both panel suppliers |

## Gotchas

> **WARNING**
>
> **The screen is on and shows nothing.** LVGL does not run itself. It needs a
> tick source and something calling `lv_timer_handler` on a period.
> `kode_bsp_init` starts that task; if you bring the panel up yourself, start it
> too, or every widget you create stays invisible.

> **WARNING**
>
> **It looks dead at boot, and every status bit says it works.** Brightness
> zero on an AMOLED is a black screen. Set a brightness after display-on.

> **WARNING**
>
> **Touch stopped working when you added touch.** The reset line is the
> display's; a touch driver that pulses `GPIO7` restarts the panel. Give it
> `rst_gpio_num = -1` and `int_gpio_num = -1` and let LVGL poll, which is what
> `kode_bsp_init` sets up.

> **NOTE**
>
> **Rotate at bring-up, not in your UI.** `lv_display_set_rotation` on LVGL 9
> does not survive partial rendering into DMA buffers. Rotation belongs to the
> panel driver's configuration.

## Apps that use it

On the marketplace, apps that put this part to work. Install one from
`start/first-app` and see it before you write a line.

- https://apps.kode.diy/app/e68185ad-1979-4683-92d8-2a9aab7365ef
- https://apps.kode.diy/app/b20a29cd-7353-4bdb-86a0-6b844cf98e1a
- https://apps.kode.diy/app/f07fe1e5-d84c-492a-9fcf-71bb2b2d2232
- https://apps.kode.diy/app/e3e0f8b8-6043-45a3-9fde-69add83d4b5d
- https://apps.kode.diy/app/0c3389fc-16a6-4c31-bbf1-396227c34242
- https://apps.kode.diy/app/963762ed-8005-4342-94ec-4e8253e48bf3
- https://apps.kode.diy/app/236f65e9-55e6-4eed-946f-bd3051513957
- https://apps.kode.diy/app/0a8a1179-088e-4fae-a2e5-b97a0a0923e1

## Example code

Put a line of text in the middle of the screen and move it to wherever you
touch. You see the text, and it follows your finger.

```c
/*
 * display_touch.c - 2026-09-18 - from Kode with love
 *
 * Draws a label in the middle of the Kode Dot's screen and moves it to the
 * last point touched. Brings the board up, which starts LVGL and the touch
 * controller, then creates the label and a timer that reads the touch state
 * thirty times a second. The text follows your finger.
 */
#include "kode.h"

static lv_obj_t *s_label;

/* Runs inside the LVGL task: read the one input device and move the label to it. */
static void follow_touch(lv_timer_t *timer)
{
    (void)timer;
    lv_indev_t *touch = lv_indev_get_next(NULL);
    if (!touch || lv_indev_get_state(touch) != LV_INDEV_STATE_PRESSED) return;
    lv_point_t p;
    lv_indev_get_point(touch, &p);
    lv_obj_set_pos(s_label, p.x - lv_obj_get_width(s_label) / 2, p.y - 12);
}

/* Board and LVGL, then the label and its timer, under the LVGL lock. */
void app_main(void)
{
    ESP_ERROR_CHECK(kode_bsp_init());

    kode_bsp_lvgl_lock();
    s_label = lv_label_create(lv_display_get_screen_active(kode_bsp_display()));
    lv_label_set_text(s_label, "Hello, Kode Dot");
    lv_obj_set_style_text_font(s_label, &lv_font_montserrat_16, 0);
    lv_obj_center(s_label);
    lv_timer_create(follow_touch, 33, NULL);
    kode_bsp_lvgl_unlock();
}
```

Compiled on the date in the header against the `kode` component with
ESP-IDF v6.1. **TBD**: a run on a V1 board.

## In the SDK

- [`kdrv_display.c`](https://github.com/kodediy/kode-sdk/blob/main/components/kdrv/kdrv_display.c): the panel bring-up in `kode-sdk`.
- [`kdrv_cst820.c`](https://github.com/kodediy/kode-sdk/blob/main/components/kdrv/kdrv_cst820.c): the touch controller.
