Kryon / Documentation / ESP32

ESP32 Implementation

Running Kryon on ESP32 microcontrollers

Overview

Kryon runs natively on ESP32 microcontrollers via the kryesp implementation. This provides a complete UI runtime with CoAP server, WiFi connectivity, and LCD display support.

Features

  • Native ESP-IDF implementation
  • ILI9341 LCD support (240x320, SPI)
  • CoAP server via libcoap
  • WiFi connectivity (Station mode)
  • FreeRTOS threading
  • Thread-safe VFS updates

Hardware Requirements

Supported Boards

  • ESP32-S3: Recommended - More RAM, faster
  • ESP32-C3: Supported - Lower cost, less RAM
  • ESP32 (original): Supported

Minimum Specifications

  • Flash: 4MB+
  • RAM: 400KB+ free
  • WiFi required for CoAP

Pin Configuration

Default pin configuration for ILI9341 LCD:

FunctionPinDescription
LCD_SCLKGPIO 14SPI Clock
LCD_MOSIGPIO 13SPI Data
LCD_MISOGPIO 12SPI MISO (not used for display)
LCD_CSGPIO 15Chip Select
LCD_DCGPIO 2Data/Command
LCD_RST-1Reset (use shared reset)
LCD_BCKLGPIO 27Backlight (PWM)

Modifying Pins

Edit hardware.h to change pin assignments:

#define LCD_SCLK_PIN 14
#define LCD_MOSI_PIN 13
#define LCD_MISO_PIN 12
#define LCD_CS_PIN   15
#define LCD_DC_PIN    2
#define LCD_BCKL_PIN 27

Display Support

ILI9341 (Recommended)

  • Resolution: 240x320
  • Interface: SPI
  • Color: RGB565 (16-bit)
  • Touch: Not supported (planned)

Other Displays

Other SPI displays can be added by implementing the ESP LCD panel interface.

Building

Prerequisites

# Install ESP-IDF
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html

# Set up environment
. ~/esp/esp-idf/export.sh

Build

cd kryesp
idf.py build

Flash

idf.py -p /dev/ttyUSB0 flash

Monitor

idf.py -p /dev/ttyUSB0 monitor

WiFi Configuration

Method 1: Hardcoded (Development)

Edit main/main.c:

#define WIFI_SSID "YourNetwork"
#define WIFI_PASS "YourPassword"

Method 2: NVS (Production)

# Set WiFi via NVS
esptool.py --port /dev/ttyUSB0 write_nv_config --ssid "YourNetwork" --pass "YourPassword"

WiFi Events

  • On connect: CoAP server starts
  • On disconnect: CoAP server stops
  • Auto-reconnect enabled

CoAP Server

Default Configuration

  • Port: 5683 (standard CoAP)
  • Protocol: UDP
  • Security: None (add DTLS for production)

Endpoints

See CoAP documentation for endpoint format.

Example Usage

# Read circle radius
coap-client get coap://esp32.local/0/radius

# Set circle radius to 100
echo -n -e "\x00\x64" | coap-client put coap://esp32.local/0/radius

Loading .krb Files

Method 1: SPIFFS (Recommended)

# Format SPIFFS (first time only)
idf.py erase-flash
idf.py flash

# Upload .krb file
esptool.py --port /dev/ttyUSB0 write_spi_flash 0x300000 ui.krb

# Or use SPIFFS upload tool
idf.py spi-flash

Method 2: Embedded

Embed .krb as C array:

# Convert .krb to C header
xxd -i ui.krb > ui_krb.h

# In main.c
extern const unsigned char ui_krb[];
extern const unsigned int ui_krb_len;

load_krb_from_memory(ui_krb, ui_krb_len);

Method 3: HTTP Download

Download .krb from URL on boot:

esp_http_client_config_t config = {
    .url = "http://server.com/ui.krb",
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// Download and load...

Architecture

Thread Layout

  • Main Task: UI rendering and event handling
  • CoAP Task: Network request handling
  • FreeRTOS Queue: Thread-safe UI updates

Memory Layout

ComponentESP32-S3ESP32-C3
FreeRTOS Queue512 bytes512 bytes
VFS Table~1KB~1KB
CoAP Context~8KB~8KB
Network Buffers~4KB~4KB
Total Overhead~13.5KB~13.5KB

Troubleshooting

Display Not Working

  • Check SPI pin connections
  • Verify 3.3V power supply
  • Test with known-good display
  • Check for backlight (screen may be on but dark)

WiFi Not Connecting

  • Verify SSID and password
  • Check 2.4GHz only (ESP32 doesn't support 5GHz)
  • Verify antenna connection
  • Check router logs

CoAP Not Responding

  • Confirm device has IP address
  • Check port 5683 is not blocked
  • Try from same network first
  • Check firewall settings

Out of Memory

  • Reduce MAX_COAP_RESOURCES in VFSRegistry.h
  • Use smaller .krb files
  • Reduce CoAP task stack size

Example Projects

Basic Circle

const meta {
    title "Circle"
}

var env {
    w 240
    h 320
}

def window {
    write /dev/wctl [ new 0 0 %size[0] %size[1] %title ]
}

def circle {
    write /dev/draw [ circ %center[0] %center[1] ./radius %color ]
}

window {
    title /meta/title
    size [ /env/w /env/h ]

    circle {
        center [ 120 160 ]
        radius 50
        color 0xFF0000
    }
}

Animated Circle

const meta {
    title "Animated"
}

var env {
    w 240
    h 320
}

def circle {
    write /dev/draw [ circ %center[0] %center[1] ./radius %color ]
    %logic
}

window {
    title /meta/title
    speed 50
    size [ /env/w /env/h ]

    circle {
        center [ 120 160 ]
        radius 40
        color 0xFF0000
        dir 1

        logic {
            listen /dev/timer {
                tick ../speed {
                    write ./radius [ + ./radius ./dir ]
                    if [ >= ./radius 60 ] { write ./dir -1 }
                    if [ <= ./radius 40 ] { write ./dir 1 }
                }
            }
        }
    }
}

Control from Python

from aiocoap import *

async def animate():
    ctx = await Context.create_client_context()

    # Set radius to 100
    payload = (100).to_bytes(2, 'big')
    req = Message(code=PUT, uri="coap://esp32.local/0/radius", payload=payload)
    await ctx.request(req).response

    # Read radius
    req = Message(code=GET, uri="coap://esp32.local/0/radius")
    resp = await ctx.request(req).response
    value = int.from_bytes(resp.payload, 'big')
    print(f"Radius: {value}")

asyncio.run(animate())

Production Checklist

  • ✔ Enable DTLS for security
  • ✔ Implement IP whitelist
  • ✔ Use NVS for WiFi credentials
  • ✔ Add error handling for display
  • ✔ Implement watchdog timer
  • ✔ Add OTA updates
  • ✔ Test power consumption
  • ✔ Verify CoAP reliability