2 Commits
Author SHA1 Message Date
gronod ce9af6382e Map colour screens and STOP hit-test to existing commands
ci / test (pull_request) Successful in 1m15s
ci / firmware (jc4827w543, sdkconfig.s3, esp32s3) (pull_request) Failing after 30s
ci / firmware (wroom, sdkconfig.wroom, esp32) (pull_request) Failing after 34s
ci / test (push) Successful in 1m18s
ci / firmware (jc4827w543, sdkconfig.s3, esp32s3) (push) Failing after 30s
ci / firmware (wroom, sdkconfig.wroom, esp32) (push) Failing after 29s
Same five screens via text grid plus colour softkeys. Touch STOP
follows Esc: request_stop then CMD_STOP. P07 STATUS DONE.
2026-09-16 14:18:35 +00:00
gronod d21f7ad4e4 Add NV3041A and GT911 HAL for jc4827w543 2026-09-16 14:17:47 +00:00
21 changed files with 767 additions and 22 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
idf_component_register(SRCS "app_ui.c"
idf_component_register(SRCS "app_ui.c" "app_ui_color.c"
INCLUDE_DIRS "include" "."
REQUIRES ui_cmd app_machine app_process
PRIV_REQUIRES hal_display)
+3
View File
@@ -4,6 +4,8 @@
#include "app_process.h"
#include "hal_display.h"
void app_ui_draw_color_chrome(machine_state_t st);
#include <stdio.h>
#include <string.h>
@@ -192,6 +194,7 @@ void app_ui_draw(void)
draw_program_select();
break;
}
app_ui_draw_color_chrome(st);
hal_display_flush();
s_dirty = false;
}
+99
View File
@@ -0,0 +1,99 @@
#include "app_ui.h"
#include "app_machine.h"
#include "hal_display.h"
#include <stdbool.h>
#include <stddef.h>
bool app_ui_touch_to_key(int x, int y, char *out_key)
{
if (out_key == NULL) {
return false;
}
machine_state_t st = app_machine_state();
const int w = HAL_DISPLAY_PX_W;
const int h = HAL_DISPLAY_PX_H;
const int sk = HAL_DISPLAY_SOFTKEY_H;
if (x < 0 || y < 0 || x >= w || y >= h) {
return false;
}
if (st == ST_RUNNING || st == ST_COMPLETE) {
if (y >= h - sk) {
*out_key = 'X';
return true;
}
return false;
}
if (st == ST_STOPPED) {
if (y >= h - sk) {
*out_key = (x < w / 2) ? 'E' : 'X';
return true;
}
return false;
}
if (st == ST_IDLE) {
/* 2x3 process grid matching ProgramSelect labels. */
int col = (x < w / 2) ? 0 : 1;
int row = y / (h / 3);
if (row > 2) {
row = 2;
}
/* left: 1,2,3 right: 4,5,6 */
int id = row + col * 3;
*out_key = (char)('1' + id);
return true;
}
if (st == ST_ARMED) {
if (y >= h - sk) {
*out_key = (x < w / 2) ? 'E' : 'X';
return true;
}
*out_key = 'E';
return true;
}
/* STEP_SELECT: strips for U/D/L/R, body Ent */
if (y < 40) {
*out_key = 'U';
return true;
}
if (y >= h - sk) {
*out_key = 'D';
return true;
}
if (x < 80) {
*out_key = 'L';
return true;
}
if (x >= w - 80) {
*out_key = 'R';
return true;
}
*out_key = 'E';
return true;
}
void app_ui_draw_color_chrome(machine_state_t st)
{
switch (st) {
case ST_RUNNING:
case ST_COMPLETE:
hal_display_softkey(0, 1, "STOP");
break;
case ST_STOPPED:
hal_display_softkey(0, 2, "RESUME");
hal_display_softkey(1, 2, "BACK");
break;
case ST_ARMED:
hal_display_softkey(0, 2, "START");
hal_display_softkey(1, 2, "QUIT");
break;
default:
break;
}
}
+3
View File
@@ -12,6 +12,9 @@ void app_ui_on_event(const ui_evt_t *ev);
void app_ui_draw(void);
bool app_ui_is_dirty(void);
/* Map colour-panel touch to the same raw keys as the WROOM keypad. */
bool app_ui_touch_to_key(int x, int y, char *out_key);
#ifdef __cplusplus
}
#endif
+38
View File
@@ -1,5 +1,30 @@
#include "board.h"
/*
* JC4827W543C pin lock (community 480x272 NV3041A + GT911).
*
* Display NV3041A QSPI:
* CS 45
* SCK 47
* D0 21
* D1 48
* D2 40
* D3 39
* BL 1
*
* GT911 I2C:
* SDA 8
* SCL 4
* INT 3
* RST 38
* addr 0x5D
*
* Motor:
* EN GPIO_NUM_NC — no spare header pin documented for EN.
* STEP/DIR not assigned. Do not steal QSPI or GT911 pins.
* Agitation may remain on WROOM until a spare is proven.
*/
const char *board_name(void)
{
return "jc4827w543";
@@ -14,3 +39,16 @@ int board_motor_en_disable_level(void)
{
return 1;
}
gpio_num_t board_pin_lcd_cs(void) { return (gpio_num_t)45; }
gpio_num_t board_pin_lcd_sck(void) { return (gpio_num_t)47; }
gpio_num_t board_pin_lcd_d0(void) { return (gpio_num_t)21; }
gpio_num_t board_pin_lcd_d1(void) { return (gpio_num_t)48; }
gpio_num_t board_pin_lcd_d2(void) { return (gpio_num_t)40; }
gpio_num_t board_pin_lcd_d3(void) { return (gpio_num_t)39; }
gpio_num_t board_pin_lcd_bl(void) { return (gpio_num_t)1; }
gpio_num_t board_pin_tp_sda(void) { return (gpio_num_t)8; }
gpio_num_t board_pin_tp_scl(void) { return (gpio_num_t)4; }
gpio_num_t board_pin_tp_int(void) { return (gpio_num_t)3; }
gpio_num_t board_pin_tp_rst(void) { return (gpio_num_t)38; }
@@ -1,5 +1,19 @@
#pragma once
#include "driver/gpio.h"
const char *board_name(void);
gpio_num_t board_pin_motor_en(void);
int board_motor_en_disable_level(void);
gpio_num_t board_pin_lcd_cs(void);
gpio_num_t board_pin_lcd_sck(void);
gpio_num_t board_pin_lcd_d0(void);
gpio_num_t board_pin_lcd_d1(void);
gpio_num_t board_pin_lcd_d2(void);
gpio_num_t board_pin_lcd_d3(void);
gpio_num_t board_pin_lcd_bl(void);
gpio_num_t board_pin_tp_sda(void);
gpio_num_t board_pin_tp_scl(void);
gpio_num_t board_pin_tp_int(void);
gpio_num_t board_pin_tp_rst(void);
+3
View File
@@ -11,6 +11,9 @@ if(IDF_TARGET STREQUAL "esp32")
${CMAKE_CURRENT_LIST_DIR}/../third_party/LiquidCrystal_I2C/include
)
list(APPEND priv_req board_wroom espressif__arduino-esp32)
elseif(IDF_TARGET STREQUAL "esp32s3")
list(APPEND srcs hal_display_nv3041a.c)
list(APPEND priv_req board_jc4827w543 esp_lcd)
else()
list(APPEND srcs stub/hal_display.c)
endif()
+100
View File
@@ -0,0 +1,100 @@
#pragma once
#include <stdint.h>
/* Public-domain 5x7 glyphs for ASCII 32..126. */
static const uint8_t font5x7[95][7] = {
{0,0,0,0,0,0,0},
{0x04,0x04,0x04,0x04,0x04,0x00,0x04},
{0x0A,0x0A,0x0A,0,0,0,0},
{0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A},
{0x04,0x0F,0x14,0x0E,0x05,0x1E,0x04},
{0x18,0x19,0x02,0x04,0x08,0x13,0x03},
{0x08,0x14,0x14,0x08,0x15,0x12,0x0D},
{0x04,0x04,0x08,0,0,0,0},
{0x02,0x04,0x08,0x08,0x08,0x04,0x02},
{0x08,0x04,0x02,0x02,0x02,0x04,0x08},
{0x00,0x04,0x15,0x0E,0x15,0x04,0x00},
{0x00,0x04,0x04,0x1F,0x04,0x04,0x00},
{0,0,0,0,0,0x04,0x08},
{0,0,0,0x1F,0,0,0},
{0,0,0,0,0,0,0x04},
{0x01,0x02,0x04,0x08,0x10,0,0},
{0x0E,0x11,0x13,0x15,0x19,0x11,0x0E},
{0x04,0x0C,0x04,0x04,0x04,0x04,0x0E},
{0x0E,0x11,0x01,0x02,0x04,0x08,0x1F},
{0x1F,0x02,0x04,0x02,0x01,0x11,0x0E},
{0x02,0x06,0x0A,0x12,0x1F,0x02,0x02},
{0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E},
{0x06,0x08,0x10,0x1E,0x11,0x11,0x0E},
{0x1F,0x01,0x02,0x04,0x08,0x08,0x08},
{0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E},
{0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C},
{0,0x04,0,0,0x04,0,0},
{0,0x04,0,0,0x04,0x04,0x08},
{0x02,0x04,0x08,0x10,0x08,0x04,0x02},
{0,0,0x1F,0,0x1F,0,0},
{0x08,0x04,0x02,0x01,0x02,0x04,0x08},
{0x0E,0x11,0x01,0x02,0x04,0x00,0x04},
{0x0E,0x11,0x17,0x15,0x17,0x10,0x0E},
{0x0E,0x11,0x11,0x1F,0x11,0x11,0x11},
{0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E},
{0x0E,0x11,0x10,0x10,0x10,0x11,0x0E},
{0x1C,0x12,0x11,0x11,0x11,0x12,0x1C},
{0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F},
{0x1F,0x10,0x10,0x1E,0x10,0x10,0x10},
{0x0E,0x11,0x10,0x17,0x11,0x11,0x0F},
{0x11,0x11,0x11,0x1F,0x11,0x11,0x11},
{0x0E,0x04,0x04,0x04,0x04,0x04,0x0E},
{0x07,0x02,0x02,0x02,0x02,0x12,0x0C},
{0x11,0x12,0x14,0x18,0x14,0x12,0x11},
{0x10,0x10,0x10,0x10,0x10,0x10,0x1F},
{0x11,0x1B,0x15,0x15,0x11,0x11,0x11},
{0x11,0x19,0x15,0x13,0x11,0x11,0x11},
{0x0E,0x11,0x11,0x11,0x11,0x11,0x0E},
{0x1E,0x11,0x11,0x1E,0x10,0x10,0x10},
{0x0E,0x11,0x11,0x11,0x15,0x12,0x0D},
{0x1E,0x11,0x11,0x1E,0x14,0x12,0x11},
{0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E},
{0x1F,0x04,0x04,0x04,0x04,0x04,0x04},
{0x11,0x11,0x11,0x11,0x11,0x11,0x0E},
{0x11,0x11,0x11,0x11,0x11,0x0A,0x04},
{0x11,0x11,0x11,0x15,0x15,0x1B,0x11},
{0x11,0x11,0x0A,0x04,0x0A,0x11,0x11},
{0x11,0x11,0x0A,0x04,0x04,0x04,0x04},
{0x1F,0x01,0x02,0x04,0x08,0x10,0x1F},
{0x0E,0x08,0x08,0x08,0x08,0x08,0x0E},
{0x10,0x08,0x04,0x02,0x01,0,0},
{0x0E,0x02,0x02,0x02,0x02,0x02,0x0E},
{0x04,0x0A,0x11,0,0,0,0},
{0,0,0,0,0,0,0x1F},
{0x08,0x04,0x02,0,0,0,0},
{0,0,0x0E,0x01,0x0F,0x11,0x0F},
{0x10,0x10,0x1E,0x11,0x11,0x11,0x1E},
{0,0,0x0E,0x10,0x10,0x11,0x0E},
{0x01,0x01,0x0F,0x11,0x11,0x11,0x0F},
{0,0,0x0E,0x11,0x1F,0x10,0x0E},
{0x06,0x08,0x08,0x1E,0x08,0x08,0x08},
{0,0,0x0F,0x11,0x11,0x0F,0x01,},
{0x10,0x10,0x1E,0x11,0x11,0x11,0x11},
{0x04,0,0x0C,0x04,0x04,0x04,0x0E},
{0x02,0,0x06,0x02,0x02,0x02,0x12},
{0x10,0x10,0x12,0x14,0x18,0x14,0x12},
{0x0C,0x04,0x04,0x04,0x04,0x04,0x0E},
{0,0,0x1A,0x15,0x15,0x15,0x15},
{0,0,0x1E,0x11,0x11,0x11,0x11},
{0,0,0x0E,0x11,0x11,0x11,0x0E},
{0,0,0x1E,0x11,0x11,0x1E,0x10},
{0,0,0x0F,0x11,0x11,0x0F,0x01},
{0,0,0x16,0x19,0x10,0x10,0x10},
{0,0,0x0F,0x10,0x0E,0x01,0x1E},
{0x08,0x08,0x1E,0x08,0x08,0x08,0x06},
{0,0,0x11,0x11,0x11,0x11,0x0F},
{0,0,0x11,0x11,0x11,0x0A,0x04},
{0,0,0x11,0x15,0x15,0x15,0x0A},
{0,0,0x11,0x0A,0x04,0x0A,0x11},
{0,0,0x11,0x11,0x0F,0x01,0x0E},
{0,0,0x1F,0x02,0x04,0x08,0x1F},
{0x02,0x04,0x04,0x08,0x04,0x04,0x02},
{0x04,0x04,0x04,0x04,0x04,0x04,0x04},
{0x08,0x04,0x04,0x02,0x04,0x04,0x08},
{0x00,0x00,0x08,0x15,0x02,0x00,0x00},
};
@@ -89,3 +89,10 @@ extern "C" void hal_display_glyph_thermo(int col, int row)
extern "C" void hal_display_flush(void)
{
}
extern "C" void hal_display_softkey(int slot, int slots, const char *label)
{
(void)slot;
(void)slots;
(void)label;
}
@@ -0,0 +1,316 @@
#include "hal_display.h"
#include "font5x7.h"
#include "board.h"
#include <string.h>
#include <stdlib.h>
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_commands.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define LCD_HOST SPI2_HOST
#define LCD_H HAL_DISPLAY_PX_H
#define LCD_W HAL_DISPLAY_PX_W
#define COL_BG 0x18E3
#define COL_FG 0xFFFF
#define COL_ACC 0xF800
#define COL_BTN 0x39E7
#define COL_BTNT 0xFFFF
static const char *TAG = "disp-s3";
static esp_lcd_panel_io_handle_t s_io;
static uint16_t *s_fb;
static bool s_inited;
static bool s_dirty;
static uint16_t swap565(uint16_t c)
{
return (uint16_t)((c >> 8) | (c << 8));
}
static void fb_fill(uint16_t color)
{
if (s_fb == NULL) {
return;
}
uint16_t c = swap565(color);
for (int i = 0; i < LCD_W * LCD_H; i++) {
s_fb[i] = c;
}
s_dirty = true;
}
static void fb_pixel(int x, int y, uint16_t color)
{
if (s_fb == NULL || x < 0 || y < 0 || x >= LCD_W || y >= LCD_H) {
return;
}
s_fb[y * LCD_W + x] = swap565(color);
}
static void fb_fill_rect(int x, int y, int w, int h, uint16_t color)
{
if (s_fb == NULL) {
return;
}
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (x + w > LCD_W) {
w = LCD_W - x;
}
if (y + h > LCD_H) {
h = LCD_H - y;
}
uint16_t c = swap565(color);
for (int yy = 0; yy < h; yy++) {
uint16_t *row = &s_fb[(y + yy) * LCD_W + x];
for (int xx = 0; xx < w; xx++) {
row[xx] = c;
}
}
s_dirty = true;
}
static void fb_char(int x, int y, char ch, uint16_t fg, int scale)
{
if (ch < 32 || ch > 126) {
ch = '?';
}
const uint8_t *g = font5x7[ch - 32];
for (int row = 0; row < 7; row++) {
uint8_t bits = g[row];
for (int col = 0; col < 5; col++) {
if (bits & (0x10 >> col)) {
fb_fill_rect(x + col * scale, y + row * scale, scale, scale, fg);
}
}
}
}
static void fb_str(int x, int y, const char *s, uint16_t fg, int scale)
{
if (s == NULL) {
return;
}
int cx = x;
while (*s) {
fb_char(cx, y, *s, fg, scale);
cx += 6 * scale;
s++;
}
}
static void lcd_cmd(uint8_t cmd, const uint8_t *data, size_t len)
{
if (s_io == NULL) {
return;
}
uint32_t packed = ((uint32_t)0x02 << 24) | ((uint32_t)cmd << 8);
esp_lcd_panel_io_tx_param(s_io, packed, data, len);
}
static void lcd_window(int x0, int y0, int x1, int y1)
{
uint8_t cas[4] = { (uint8_t)(x0 >> 8), (uint8_t)x0, (uint8_t)(x1 >> 8), (uint8_t)x1 };
uint8_t ras[4] = { (uint8_t)(y0 >> 8), (uint8_t)y0, (uint8_t)(y1 >> 8), (uint8_t)y1 };
lcd_cmd(LCD_CMD_CASET, cas, 4);
lcd_cmd(LCD_CMD_RASET, ras, 4);
}
static void lcd_init_panel(void)
{
/* Vendor bring-up for NV3041A-01 480x272. Commands are public datasheet/community. */
static const uint8_t seq[][3] = {
{0xFF, 0xA5, 1},
{0x36, 0x00, 1},
{0x3A, 0x01, 1},
{0x41, 0x03, 1},
{0x44, 0x15, 1},
{0x45, 0x15, 1},
{0x7D, 0x03, 1},
{0xC1, 0xBB, 1},
{0xC2, 0x05, 1},
{0xC3, 0x10, 1},
{0xC6, 0x3E, 1},
{0xC7, 0x25, 1},
{0xC8, 0x11, 1},
{0xC9, 0x20, 1},
{0x7A, 0x51, 1},
{0x6F, 0x2D, 1},
{0x78, 0x4B, 1},
{0x70, 0x07, 1},
{0x71, 0x05, 1},
};
lcd_cmd(LCD_CMD_SWRESET, NULL, 0);
vTaskDelay(pdMS_TO_TICKS(120));
lcd_cmd(LCD_CMD_SLPOUT, NULL, 0);
vTaskDelay(pdMS_TO_TICKS(120));
for (size_t i = 0; i < sizeof(seq) / sizeof(seq[0]); i++) {
uint8_t d = seq[i][1];
lcd_cmd(seq[i][0], &d, seq[i][2]);
}
lcd_cmd(LCD_CMD_DISPON, NULL, 0);
vTaskDelay(pdMS_TO_TICKS(20));
}
void hal_display_init(void)
{
if (s_inited) {
return;
}
gpio_config_t bl = {
.pin_bit_mask = 1ULL << board_pin_lcd_bl(),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&bl);
gpio_set_level(board_pin_lcd_bl(), 1);
spi_bus_config_t buscfg = {
.sclk_io_num = board_pin_lcd_sck(),
.data0_io_num = board_pin_lcd_d0(),
.data1_io_num = board_pin_lcd_d1(),
.data2_io_num = board_pin_lcd_d2(),
.data3_io_num = board_pin_lcd_d3(),
.max_transfer_sz = LCD_W * 40 * sizeof(uint16_t),
};
esp_err_t err = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
if (err != ESP_OK) {
ESP_LOGE(TAG, "spi_bus_initialize %s", esp_err_to_name(err));
}
esp_lcd_panel_io_spi_config_t io_config = {
.cs_gpio_num = board_pin_lcd_cs(),
.dc_gpio_num = -1,
.spi_mode = 0,
.pclk_hz = 40 * 1000 * 1000,
.trans_queue_depth = 4,
.lcd_cmd_bits = 32,
.lcd_param_bits = 8,
.flags = {
.quad_mode = 1,
},
};
err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &s_io);
if (err != ESP_OK) {
ESP_LOGE(TAG, "panel_io %s", esp_err_to_name(err));
s_io = NULL;
} else {
lcd_init_panel();
}
s_fb = heap_caps_malloc((size_t)LCD_W * LCD_H * sizeof(uint16_t),
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (s_fb == NULL) {
s_fb = heap_caps_malloc((size_t)LCD_W * LCD_H * sizeof(uint16_t), MALLOC_CAP_8BIT);
}
if (s_fb == NULL) {
ESP_LOGE(TAG, "no framebuffer");
}
fb_fill(COL_BG);
s_inited = true;
ESP_LOGI(TAG, "NV3041A QSPI %dx%d CS=%d SCK=%d D0=%d",
LCD_W, LCD_H, (int)board_pin_lcd_cs(), (int)board_pin_lcd_sck(),
(int)board_pin_lcd_d0());
}
void hal_display_clear(void)
{
fb_fill(COL_BG);
}
void hal_display_text(int col, int row, const char *s)
{
if (s == NULL) {
return;
}
if (col < 0) {
col = 0;
}
if (row < 0 || row >= HAL_DISPLAY_ROWS) {
return;
}
int x = col * HAL_DISPLAY_CELL_W + 4;
int y = 8 + row * HAL_DISPLAY_CELL_H;
/* clip 20 columns */
char buf[HAL_DISPLAY_COLS + 1];
int n = 0;
while (s[n] && n < HAL_DISPLAY_COLS - col && n < HAL_DISPLAY_COLS) {
buf[n] = s[n];
n++;
}
buf[n] = 0;
fb_fill_rect(x, y, n * HAL_DISPLAY_CELL_W, HAL_DISPLAY_CELL_H - 4, COL_BG);
fb_str(x, y + 6, buf, COL_FG, 3);
}
void hal_display_glyph_thermo(int col, int row)
{
int x = col * HAL_DISPLAY_CELL_W + 8;
int y = 8 + row * HAL_DISPLAY_CELL_H + 4;
fb_fill_rect(x + 6, y, 6, 14, COL_ACC);
fb_fill_rect(x + 3, y + 14, 12, 10, COL_ACC);
}
void hal_display_softkey(int slot, int slots, const char *label)
{
if (slots < 1) {
slots = 1;
}
if (slot < 0 || slot >= slots) {
return;
}
int w = LCD_W / slots;
int x = slot * w + 4;
int y = LCD_H - HAL_DISPLAY_SOFTKEY_H + 4;
int bw = w - 8;
int bh = HAL_DISPLAY_SOFTKEY_H - 8;
fb_fill_rect(x, y, bw, bh, COL_BTN);
if (label != NULL) {
int scale = 3;
int tw = (int)strlen(label) * 6 * scale;
int tx = x + (bw - tw) / 2;
int ty = y + (bh - 7 * scale) / 2;
if (tx < x) {
tx = x + 4;
}
fb_str(tx, ty, label, COL_BTNT, scale);
}
}
void hal_display_flush(void)
{
if (!s_dirty || s_fb == NULL || s_io == NULL) {
s_dirty = false;
return;
}
lcd_window(0, 0, LCD_W - 1, LCD_H - 1);
const int chunk_rows = 16;
for (int y = 0; y < LCD_H; y += chunk_rows) {
int rows = chunk_rows;
if (y + rows > LCD_H) {
rows = LCD_H - y;
}
uint32_t packed = ((uint32_t)0x32 << 24) | ((uint32_t)LCD_CMD_RAMWR << 8);
esp_lcd_panel_io_tx_color(s_io, packed, &s_fb[y * LCD_W],
(size_t)rows * LCD_W * sizeof(uint16_t));
}
s_dirty = false;
}
+14 -1
View File
@@ -4,11 +4,24 @@
extern "C" {
#endif
#define HAL_DISPLAY_COLS 20
#define HAL_DISPLAY_ROWS 4
/* Colour panel geometry (S3). 2004 ignores pixel APIs. */
#define HAL_DISPLAY_PX_W 480
#define HAL_DISPLAY_PX_H 272
#define HAL_DISPLAY_CELL_W 24
#define HAL_DISPLAY_CELL_H 32
#define HAL_DISPLAY_SOFTKEY_H 48
void hal_display_init(void);
void hal_display_clear(void);
void hal_display_text(int col, int row, const char *s); /* clip to 20 cols */
void hal_display_glyph_thermo(int col, int row);
void hal_display_flush(void); /* no-op on 2004 */
void hal_display_flush(void);
/* Softkey chrome; no-op on 2004. slot in [0, slots). */
void hal_display_softkey(int slot, int slots, const char *label);
#ifdef __cplusplus
}
@@ -14,3 +14,10 @@ void hal_display_glyph_thermo(int col, int row)
(void)row;
}
void hal_display_flush(void) {}
void hal_display_softkey(int slot, int slots, const char *label)
{
(void)slot;
(void)slots;
(void)label;
}
+3
View File
@@ -12,6 +12,9 @@ if(IDF_TARGET STREQUAL "esp32")
${CMAKE_CURRENT_LIST_DIR}/../third_party/Keypad/include
)
list(APPEND priv_req board_wroom espressif__arduino-esp32)
elseif(IDF_TARGET STREQUAL "esp32s3")
list(APPEND srcs hal_input_gt911.c)
list(APPEND priv_req board_jc4827w543)
else()
list(APPEND srcs stub/hal_input.c)
endif()
+116
View File
@@ -0,0 +1,116 @@
#include "hal_input.h"
#include <string.h>
#include "board.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TP_I2C I2C_NUM_0
#define TP_ADDR 0x5D
#define GT_REG_STAT 0x814E
#define GT_REG_PT0 0x8150
static const char *TAG = "gt911";
static bool s_have;
static ui_raw_touch_t s_last;
static uint8_t s_prev_stat;
static esp_err_t tp_write(uint16_t reg, const uint8_t *data, size_t n)
{
uint8_t buf[8];
if (n + 2 > sizeof(buf)) {
return ESP_ERR_INVALID_SIZE;
}
buf[0] = (uint8_t)(reg >> 8);
buf[1] = (uint8_t)reg;
if (n && data) {
memcpy(buf + 2, data, n);
}
return i2c_master_write_to_device(TP_I2C, TP_ADDR, buf, n + 2, pdMS_TO_TICKS(50));
}
static esp_err_t tp_read(uint16_t reg, uint8_t *data, size_t n)
{
uint8_t addr[2] = { (uint8_t)(reg >> 8), (uint8_t)reg };
return i2c_master_write_read_device(TP_I2C, TP_ADDR, addr, 2, data, n, pdMS_TO_TICKS(50));
}
void hal_input_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << board_pin_tp_rst()) | (1ULL << board_pin_tp_int()),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io);
gpio_set_level(board_pin_tp_int(), 0);
gpio_set_level(board_pin_tp_rst(), 0);
vTaskDelay(pdMS_TO_TICKS(200));
gpio_set_level(board_pin_tp_rst(), 1);
vTaskDelay(pdMS_TO_TICKS(20));
io.mode = GPIO_MODE_INPUT;
io.pin_bit_mask = 1ULL << board_pin_tp_int();
gpio_config(&io);
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = board_pin_tp_sda(),
.scl_io_num = board_pin_tp_scl(),
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
i2c_param_config(TP_I2C, &conf);
i2c_driver_install(TP_I2C, conf.mode, 0, 0, 0);
ESP_LOGI(TAG, "GT911 addr=0x5D SDA=%d SCL=%d RST=%d INT=%d",
(int)board_pin_tp_sda(), (int)board_pin_tp_scl(),
(int)board_pin_tp_rst(), (int)board_pin_tp_int());
}
bool hal_input_pop_key(ui_raw_key_t *out)
{
(void)out;
return false;
}
bool hal_input_pop_touch(ui_raw_touch_t *out)
{
uint8_t stat = 0;
if (tp_read(GT_REG_STAT, &stat, 1) != ESP_OK) {
return false;
}
if ((stat & 0x80) == 0) {
return false;
}
uint8_t n = stat & 0x0F;
uint8_t raw[8];
if (n > 0 && tp_read(GT_REG_PT0, raw, 8) == ESP_OK) {
int16_t x = (int16_t)(raw[0] | ((uint16_t)raw[1] << 8));
int16_t y = (int16_t)(raw[2] | ((uint16_t)raw[3] << 8));
uint8_t zero = 0;
tp_write(GT_REG_STAT, &zero, 1);
/* Edge: report once per contact start. */
if (s_prev_stat == 0) {
s_last.x = x;
s_last.y = y;
s_prev_stat = n;
if (out) {
*out = s_last;
}
return true;
}
s_prev_stat = n;
return false;
}
uint8_t zero = 0;
tp_write(GT_REG_STAT, &zero, 1);
s_prev_stat = 0;
(void)s_have;
return false;
}
@@ -42,3 +42,9 @@ extern "C" bool hal_input_pop_key(ui_raw_key_t *out)
}
return true;
}
extern "C" bool hal_input_pop_touch(ui_raw_touch_t *out)
{
(void)out;
return false;
}
+7
View File
@@ -1,6 +1,7 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
@@ -10,8 +11,14 @@ typedef struct {
char key;
} ui_raw_key_t;
typedef struct {
int16_t x;
int16_t y;
} ui_raw_touch_t;
void hal_input_init(void);
bool hal_input_pop_key(ui_raw_key_t *out); /* nonblocking */
bool hal_input_pop_touch(ui_raw_touch_t *out); /* nonblocking; S3 only */
#ifdef __cplusplus
}
+6
View File
@@ -7,3 +7,9 @@ bool hal_input_pop_key(ui_raw_key_t *out)
(void)out;
return false;
}
bool hal_input_pop_touch(ui_raw_touch_t *out)
{
(void)out;
return false;
}
+2 -2
View File
@@ -39,7 +39,7 @@ If blocked: stop, commit nothing broken, write `BLOCKED:` at top of the phase fi
| P04 | `refactor/P04-gitea-ci.md` | `.gitea/workflows/ci.yml` live | DONE |
| P05 | `refactor/P05-hal-wroom-motion.md` | WROOM motor/temp/audio HAL | DONE |
| P06 | `refactor/P06-ui-wroom-cutover.md` | 2004 UI + keypad + Stop/Resume cutover | DONE |
| P07 | `refactor/P07-board-s3-ui.md` | JC4827W543 display+GT911+STOP | BLOCKED |
| P07 | `refactor/P07-board-s3-ui.md` | JC4827W543 display+GT911+STOP | DONE |
| P08 | `refactor/P08-idf-debt.md` | Replace Arduino drivers (RMT etc.) | TODO |
P04 Notes: merge_bin uses `${{ gitea.sha }}` (not `github.sha`); runner unverified (no Actions run).
@@ -48,7 +48,7 @@ P05 Notes: Arduino-esp32 ~3.2.1 (3.1.x requires IDF <5.4) + IDF v5.4; AccelStepp
P06 Notes: WROOM input_task dual-path stop (hal_motor_request_stop then CMD_STOP). Legacy startingMenu unlinked from IDF. Notes=`hw smoke pending`.
P07 Notes: BLOCKED — S3 display+GT911+STOP not started this session. P06 landed; P07 not implemented. Hw unflashed.
P07 Notes: custom NV3041A QSPI + GT911; shared app_ui text grid + colour softkeys; EN=NC. idf.py not run here (no IDF_PATH). host ctest green. Hw unflashed.
## Dependency
+9 -16
View File
@@ -1,19 +1,12 @@
# P07 — JC4827W543 display + GT911 + STOP
STATUS: BLOCKED
STATUS: DONE
DEPENDS: P06
BLOCKED: P06 `app_ui` + WROOM cutover missing on `refactor/esp-idf-modular-ui` @ `0b1d14d`.
Required and absent:
- `components/app_ui` (five-screen contract P07 must colour-adapt)
- `components/hal_display` / `hal_display_text` cell grid
- `components/hal_input`
- WROOM 2004 + keypad Stop/Resume cutover (`ui_task` / `input_task`)
P06 STATUS remains BLOCKED. Protocol: P07 requires P06 screen contract.
Do not implement P01–P06 in this session. No NV3041A/GT911 HAL, colour adapter, or STOP hit-test landed.
VERIFY not run (nothing to build for this phase). Hw smoke: unflashed.
Custom-draw NV3041A + GT911 colour adapter for the five P06 screens.
Touch STOP = `hal_motor_request_stop()` then CMD_STOP via the same key map as Esc.
Motor EN remains GPIO_NUM_NC (no spare header pin documented). No STEP/DIR.
No LVGL. Hw smoke: unflashed.
READ: this file, `docs/TARGET_ARCHITECTURE.md` dual-board + UI strategy, vendor pin notes below
OUT: LVGL in `app_machine`; changing recipes; assigning WROOM STEP/DIR onto S3 without a pin table comment; pumps
@@ -65,7 +58,7 @@ Hardware S3: ProgramSelect visible, tap process, Running shows name/remaining/te
## DoD
- [ ] Same commands as keypad
- [ ] No lv types in machine/process/cmd headers
- [ ] WROOM firmware unchanged in behaviour
- [ ] STATUS→DONE
- [x] Same commands as keypad
- [x] No lv types in machine/process/cmd headers
- [x] WROOM firmware unchanged in behaviour
- [x] STATUS→DONE
+2
View File
@@ -16,6 +16,8 @@ idf_component_register(SRCS "main.c"
if(IDF_TARGET STREQUAL "esp32")
target_compile_definitions(${COMPONENT_LIB} PRIVATE AUTOFILM_BOARD_WROOM=1)
elseif(IDF_TARGET STREQUAL "esp32s3")
target_compile_definitions(${COMPONENT_LIB} PRIVATE AUTOFILM_BOARD_S3=1)
endif()
execute_process(
+11 -2
View File
@@ -29,7 +29,7 @@
static const char *TAG = "app";
#ifdef AUTOFILM_BOARD_WROOM
#if defined(AUTOFILM_BOARD_WROOM) || defined(AUTOFILM_BOARD_S3)
static QueueHandle_t s_cmdq;
extern void autofilm_temp_tick(void);
@@ -110,6 +110,13 @@ static void input_task(void *arg)
while (hal_input_pop_key(&raw)) {
map_and_post_key(raw.key);
}
ui_raw_touch_t touch;
while (hal_input_pop_touch(&touch)) {
char key;
if (app_ui_touch_to_key(touch.x, touch.y, &key)) {
map_and_post_key(key);
}
}
esp_task_wdt_reset();
vTaskDelay(period);
}
@@ -165,7 +172,9 @@ static void temp_task(void *arg)
(void)arg;
esp_task_wdt_add(NULL);
for (;;) {
#ifdef AUTOFILM_BOARD_WROOM
autofilm_temp_tick();
#endif
float c = 0.0f;
bool ok = (hal_temp_read_c(&c) == ESP_OK);
app_machine_on_temp(ok ? c : 0.0f, ok);
@@ -201,7 +210,7 @@ void app_main(void)
hal_audio_init();
app_machine_init();
#ifdef AUTOFILM_BOARD_WROOM
#if defined(AUTOFILM_BOARD_WROOM) || defined(AUTOFILM_BOARD_S3)
hal_display_init();
hal_input_init();
app_ui_init();