Compare commits

...
5 Commits
Author SHA1 Message Date
maddiebaka 160ee1ce2f Finish numberstack code UI 2026-04-25 15:41:45 -04:00
maddiebaka 0a56a1ec1c Layout main UI screen 2026-03-12 12:28:06 -04:00
maddiebaka 4876d31648 Implement PID and telemetry 2026-03-12 11:12:38 -04:00
maddiebaka 9fe77e4e41 Adjust Kconfig values for new CYD variant 2026-03-12 11:10:57 -04:00
maddiebaka f02d138ffa Finish display impl, write UI widget, refactor 2025-12-31 11:56:54 -05:00
11 changed files with 461 additions and 85 deletions
+10 -1
View File
@@ -66,6 +66,14 @@ dependencies:
registry_url: https://components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 2.7.0 version: 2.7.0
hayschan/autopid_for_esp_idf:
component_hash:
abb01ee57353d22df70c27840ddec00b90544540c9a78ec18932737d7fbb7878
dependencies: []
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.2
idf: idf:
source: source:
type: idf type: idf
@@ -82,8 +90,9 @@ direct_dependencies:
- atanisoft/esp_lcd_touch_xpt2046 - atanisoft/esp_lcd_touch_xpt2046
- espressif/esp_lcd_ili9341 - espressif/esp_lcd_ili9341
- espressif/esp_lvgl_port - espressif/esp_lvgl_port
- hayschan/autopid_for_esp_idf
- idf - idf
- lvgl/lvgl - lvgl/lvgl
manifest_hash: d2b549f17124d3d38e4add4614fb10956ada1a576e4c5f6a623ea3756bcd3ba5 manifest_hash: 5e3bb9d0c782ebaa9ecdc3b0310e8f13b698e0fd5071cab2a465725e2a33722b
target: esp32 target: esp32
version: 2.0.0 version: 2.0.0
+2 -2
View File
@@ -1,4 +1,4 @@
idf_component_register(SRCS "spincoat-plater-firmware.c" "dshot_esc_encoder.c" "display.c" idf_component_register(SRCS "spincoat-plater-firmware.cpp" "dshot_esc_encoder.c" "display.c" "motor.c" "ui.c"
PRIV_REQUIRES esp_driver_rmt esp_driver_gpio esp_driver_uart esp_driver_spi PRIV_REQUIRES esp_driver_rmt esp_driver_gpio esp_driver_uart esp_driver_spi
esp_lcd unity lvgl esp_lvgl_port esp_lcd_touch_xpt2046 esp_lcd unity lvgl esp_lvgl_port esp_lcd_touch_xpt2046 autopid_for_esp_idf
INCLUDE_DIRS ".") INCLUDE_DIRS ".")
+9 -3
View File
@@ -1,12 +1,12 @@
menu "Pin Mapping Configuration" menu "Pin Mapping Configuration"
config ESC_CTRL_PIN config ESC_CTRL_PIN
int "ESC control GPIO pin" int "ESC control GPIO pin"
default 22 default 18
help help
This is the pin used for sending DSHOT packets to the ESC. This is the pin used for sending DSHOT packets to the ESC.
config TELEMETRY_RX_PIN config TELEMETRY_RX_PIN
int "ESC telemetry uart rx pin" int "ESC telemetry uart rx pin"
default 27 default 19
help help
This is the pin used for receiving UART telemetry from the ESC. This is the pin used for receiving UART telemetry from the ESC.
config TFT_MISO config TFT_MISO
@@ -39,7 +39,13 @@ menu "Pin Mapping Configuration"
default 21 default 21
help help
This is the pin for backlight control on the SPI LCD. This is the pin for backlight control on the SPI LCD.
config MOTOR_POLECOUNT
int "Pole count of motor driven by ESC"
default 6
help
This is the number of magnetic poles on your motor, which is used to calculate actual RPM
from electrical RPM.
config TOUCH_IRQ config TOUCH_IRQ
int "SPI touchscreen IRQ pin" int "SPI touchscreen IRQ pin"
+3 -7
View File
@@ -58,7 +58,6 @@ void touch_driver_read(lv_indev_t * indev, lv_indev_data_t * data) {
bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, strength, &count, 1); bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, strength, &count, 1);
if(touchpad_pressed == true) { if(touchpad_pressed == true) {
//ESP_LOGI(TAG, "Touchpad pressed from LVGL..\n");
data->point.x = x[0]; data->point.x = x[0];
data->point.y = y[0]; data->point.y = y[0];
data->state = LV_INDEV_STATE_PRESSED; data->state = LV_INDEV_STATE_PRESSED;
@@ -73,8 +72,6 @@ void touch_driver_read(lv_indev_t * indev, lv_indev_data_t * data) {
static void btn_event_cb(lv_event_t * e) { static void btn_event_cb(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e); lv_event_code_t code = lv_event_get_code(e);
//ESP_LOGI(TAG, "Button callback fired.");
if(code == LV_EVENT_CLICKED) { if(code == LV_EVENT_CLICKED) {
ESP_LOGI(TAG, "LVGL button pressed."); ESP_LOGI(TAG, "LVGL button pressed.");
} }
@@ -132,8 +129,8 @@ void init_touchscreen_xpt2046(void) {
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(TOUCH_SPI_HOST, &tp_io_config, &tp_io_handle)); ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(TOUCH_SPI_HOST, &tp_io_config, &tp_io_handle));
esp_lcd_touch_config_t tp_cfg = { esp_lcd_touch_config_t tp_cfg = {
.x_max = TFT_HRES, .x_max = TFT_VRES,
.y_max = TFT_VRES, .y_max = TFT_HRES,
.rst_gpio_num = -1, .rst_gpio_num = -1,
.int_gpio_num = -1, .int_gpio_num = -1,
.flags = { .flags = {
@@ -227,7 +224,7 @@ void init_lvgl_display(void) {
}, },
.flags = { .flags = {
.buff_dma = false, .buff_dma = false,
.swap_bytes = false, .swap_bytes = true,
} }
}; };
disp_handle = lvgl_port_add_disp(&disp_cfg); disp_handle = lvgl_port_add_disp(&disp_cfg);
@@ -253,5 +250,4 @@ void init_display(void) {
init_touchscreen_xpt2046(); init_touchscreen_xpt2046();
init_lvgl_display(); init_lvgl_display();
init_input(); init_input();
lv_example_btn_1();
} }
+1 -6
View File
@@ -11,12 +11,7 @@
#define TFT_VRES CONFIG_TFT_VRES #define TFT_VRES CONFIG_TFT_VRES
#define TFT_BPP CONFIG_TFT_BPP #define TFT_BPP CONFIG_TFT_BPP
#define LVGL_BUF_SIZE TFT_VRES * TFT_HRES / 10 * (TFT_BPP / 8) #define LVGL_BUF_SIZE ((TFT_VRES * TFT_HRES) / 10) * (TFT_BPP / 8)
/**
* Draws a test bitmap of stripes of colors to the LCD.
*/
void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle);
/** /**
* Initializes the display TFT and touchscreen. * Initializes the display TFT and touchscreen.
+1
View File
@@ -18,3 +18,4 @@ dependencies:
lvgl/lvgl: ^9.4.0 lvgl/lvgl: ^9.4.0
espressif/esp_lvgl_port: ^2.3.0 espressif/esp_lvgl_port: ^2.3.0
atanisoft/esp_lcd_touch_xpt2046: ^1.0.2 atanisoft/esp_lcd_touch_xpt2046: ^1.0.2
hayschan/autopid_for_esp_idf: ^1.0.2
@@ -1,45 +1,17 @@
/* #include "motor.h"
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <inttypes.h> #include "driver/spi_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_log.h"
#include "driver/rmt_tx.h" #include "driver/rmt_tx.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "driver/gpio.h" #include "esp_log.h"
#include "hal/spi_types.h"
#include "esp_lcd_panel_ops.h"
#include "driver/spi_common.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_commands.h"
#include "esp_lcd_ili9341.h"
#include "dshot_esc_encoder.h" #include "dshot_esc_encoder.h"
#include "display.h"
#if CONFIG_IDF_TARGET_ESP32H2
#define DSHOT_ESC_RESOLUTION_HZ 32000000 // 32MHz resolution, DSHot protocol needs a relative high resolution
#else
#define DSHOT_ESC_RESOLUTION_HZ 40000000 // 40MHz resolution, DSHot protocol needs a relative high resolution
#endif
#define GPIO_ESC_CTRL CONFIG_ESC_CTRL_PIN #define GPIO_ESC_CTRL CONFIG_ESC_CTRL_PIN
#define GPIO_ESC_RX CONFIG_TELEMETRY_RX_PIN #define GPIO_ESC_RX CONFIG_TELEMETRY_RX_PIN
#define UART_NUM UART_NUM_2
#define ESP_INTR_FLAG_DEFAULT 0 #define ESP_INTR_FLAG_DEFAULT 0
static const char *TAG = "spincoat-plater-firmware"; static const char *TAG = "spincoat-plater-firmware/motor";
static QueueHandle_t uart_queue = NULL; static QueueHandle_t uart_queue = NULL;
const int uart_buffer_size = (1024 * 2); const int uart_buffer_size = (1024 * 2);
@@ -53,7 +25,7 @@ rmt_transmit_config_t tx_config = {
dshot_esc_throttle_t throttle = { dshot_esc_throttle_t throttle = {
.throttle = 0, .throttle = 0,
.telemetry_req = false, // telemetry is not supported in this example .telemetry_req = false,
}; };
/** /**
@@ -130,9 +102,9 @@ void init_telemetry_uart_rx(void) {
.rx_flow_ctrl_thresh = 122, .rx_flow_ctrl_thresh = 122,
}; };
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0)); ESP_ERROR_CHECK(uart_driver_install(ESC_UART_NUM, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0));
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config)); ESP_ERROR_CHECK(uart_param_config(ESC_UART_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_PIN_NO_CHANGE, GPIO_ESC_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); ESP_ERROR_CHECK(uart_set_pin(ESC_UART_NUM, UART_PIN_NO_CHANGE, GPIO_ESC_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
} }
@@ -173,56 +145,64 @@ uint8_t get_crc8(uint8_t *Buf, uint8_t BufLen){
* Parse the KISS telemetry frame and check the crc8 * Parse the KISS telemetry frame and check the crc8
* TODO: Do more with the data than print it * TODO: Do more with the data than print it
*/ */
void parse_telemetry(void) { bool parse_telemetry(esc_telemetry_t * telemetry) {
uint8_t frame_size = 10; uint8_t frame_size = 10;
uint8_t data[128]; uint8_t data[128];
// get data // get data
uint8_t length = uart_read_bytes(UART_NUM, data, frame_size, 100); uint8_t length = uart_read_bytes(ESC_UART_NUM, data, frame_size, 100);
uart_flush(UART_NUM); uart_flush(ESC_UART_NUM);
if(length < 10) return false;
if(length < 10) return;
// chop out just the payload // chop out just the payload
uint8_t payload[128]; uint8_t payload[128];
uint8_t payload_length = (frame_size - 1); uint8_t payload_length = (frame_size - 1);
for(uint8_t i = 0; i < payload_length; i++) { for(uint8_t i = 0; i < payload_length; i++) {
payload[i] = data[i]; payload[i] = data[i];
} }
// calculate the crc8 // calculate the crc8
uint8_t expected_crc8 = get_crc8(payload, payload_length); uint8_t expected_crc8 = get_crc8(payload, payload_length);
uint8_t received_crc8 = (uint8_t) data[frame_size - 1]; uint8_t received_crc8 = (uint8_t) data[frame_size - 1];
if(expected_crc8 != received_crc8) return; if(expected_crc8 != received_crc8) return false;
for(uint8_t i = 0; i < length; i++) {
printf("%d - %d\n", i, data[i]); telemetry->temperature = payload[0];
} telemetry->voltage = (payload[1] << 8) + payload[2];
printf("--------------------\n"); telemetry->current = (payload[3] << 8) + payload[4];
printf("expected: %d\n", expected_crc8); telemetry->consumption = (payload[5] << 8) + payload[6];
printf("received: %d\n", received_crc8); telemetry->rpm = (payload[7] << 8) + payload[8];
printf("======================\n");
return true;
} }
void app_main(void) { /**
init_display(); * Helper function that sets up the RMT, creates the telemetry task, and
* initializes the telemetry UART.
*/
void init_motor(void) {
init_rmt_esc_tx(); init_rmt_esc_tx();
throttle.throttle = 300; throttle.throttle = 300;
xTaskCreate(&v_telemetry_packet_func, "v_telemetry_packet_func", 2048, NULL, 1, NULL); xTaskCreate(&v_telemetry_packet_func, "v_telemetry_packet_func", 2048, NULL, 1, NULL);
init_telemetry_uart_rx(); init_telemetry_uart_rx();
}
while(1) {
send_dshot_packet(); /**
* Returns the current throttle value
uint8_t length = 0; */
ESP_ERROR_CHECK(uart_get_buffered_data_len(UART_NUM, (size_t*)&length)); uint16_t get_throttle() {
return throttle.throttle;
if(length >= 10) { }
parse_telemetry();
} /**
} * Updates the throttle value.
*
* \arg \c throttle The new throttle value between 0-2048
*/
void update_throttle(uint16_t thr) {
throttle.throttle = thr;
} }
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#if CONFIG_IDF_TARGET_ESP32H2
#define DSHOT_ESC_RESOLUTION_HZ 32000000 // 32MHz resolution, DSHot protocol needs a relative high resolution
#else
#define DSHOT_ESC_RESOLUTION_HZ 40000000 // 40MHz resolution, DSHot protocol needs a relative high resolution
#endif
#define ESC_UART_NUM UART_NUM_2
/**
* Struct for returning telemetry data
*/
typedef struct {
uint16_t temperature;
uint16_t voltage;
uint16_t current;
uint16_t consumption;
uint16_t rpm;
} esc_telemetry_t;
void init_rmt_esc_tx(void);
void send_dshot_packet(void);
bool parse_telemetry(esc_telemetry_t * telemetry);
void init_motor(void);
uint16_t get_throttle(void);
void update_throttle(uint16_t throttle);
+104
View File
@@ -0,0 +1,104 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "esp_lcd_panel_ops.h"
#include "driver/spi_common.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_commands.h"
#include "esp_lcd_ili9341.h"
#include "lvgl.h"
#include "AutoPID-for-ESP-IDF.h"
extern "C" {
#include "dshot_esc_encoder.h"
#include "display.h"
#include "ui.h"
#include "motor.h"
}
static const char *TAG = "spincoat-plater-firmware";
static double throttle = 0;
static double zero_offset = 100; // Value to offset the throttle by, to skip the
// command values
static double OUTPUT_MIN = 0;
static double OUTPUT_MAX = 1948;
static double RPM_MAX = 250;
static double BANG_BANG_THRESHOLD = RPM_MAX + 200;
static double KP = 0.015;
static double KI = 0.8;
static double KD = 0.0;
static bool motor_running = false;
extern "C" void app_main(void) {
srand((unsigned int)esp_timer_get_time());
esc_telemetry_t telemetry;
double target = 100;
double real_rpm = 0;
AutoPID myPID(&real_rpm, &target, &throttle, OUTPUT_MIN, OUTPUT_MAX, KP, KI, KD);
myPID.setTimeStep(100);
myPID.setBangBang(400); // if you mess with this, you may melt your circuit
init_display();
vTaskDelay(pdMS_TO_TICKS(100));
build_ui();
init_motor();
update_throttle(throttle);
while(1) {
send_dshot_packet();
uint8_t length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(ESC_UART_NUM, (size_t*)&length));
if(length >= 10) {
if(parse_telemetry(&telemetry)) {
real_rpm = telemetry.rpm / (uint16_t) CONFIG_MOTOR_POLECOUNT;
myPID.run();
update_throttle(throttle + zero_offset);
update_rpm_readout(real_rpm);
ESP_LOGI(TAG, "eRPM: %d, RPM: %.2f, SetPoint: %.2f, Output: %.2f",
telemetry.rpm,
real_rpm,
target,
throttle + zero_offset); // Log the values
if(myPID.atSetPoint(10)) {
ESP_LOGI(TAG, "At setpoint.");
}
vTaskDelay(pdMS_TO_TICKS(100));
if(real_rpm >= 30) {
motor_running = true;
} else {
motor_running = false;
}
}
}
}
}
+217
View File
@@ -0,0 +1,217 @@
#include "ui.h"
// #include "lv_font.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "motor.h"
#define BTN_INCREMENT 100
#define MAX_RPM_SETTING 200
#define MAX_COAT_TIME_SETTING 30
#define MIN_RPM_SETTING 50
#define MIN_COAT_TIME_SETTING 5
#define RPM_STEPSIZE 25
#define COAT_TIME_STEPSIZE 5
static const char *TAG = "spincoat-plater-firmware/ui";
static lv_obj_t * rpm_label = NULL;
static lv_obj_t * coat_time_label = NULL;
static lv_obj_t * run_stop_label = NULL;
static uint16_t rpm_setting = 100; // rpm
static uint16_t coat_time_setting = 5; // seconds
static coat_state motor_state = STOPPED;
/**
* Callback for pressing the rpm increase button
*/
void rpm_increase_cb(lv_event_t * e) {
ESP_LOGI(TAG, "RPM increase callback called.");
if(rpm_setting < MAX_RPM_SETTING) {
rpm_setting += RPM_STEPSIZE;
lv_lock();
lv_label_set_text_fmt(rpm_label, "%d", rpm_setting);
lv_unlock();
}
}
/**
* Callback for pressing the rpm decrease button
*/
void rpm_decrease_cb(lv_event_t * e) {
ESP_LOGI(TAG, "RPM decrease callback called.");
if(rpm_setting > MIN_RPM_SETTING) {
rpm_setting -= RPM_STEPSIZE;
lv_lock();
lv_label_set_text_fmt(rpm_label, "%d", rpm_setting);
lv_unlock();
}
}
/**
* Callback for pressing the coat time increase button
*/
void coat_increase_cb(lv_event_t * e) {
ESP_LOGI(TAG, "Coat time increase callback called.");
if(coat_time_setting < MAX_COAT_TIME_SETTING) {
coat_time_setting += COAT_TIME_STEPSIZE;
lv_lock();
lv_label_set_text_fmt(coat_time_label, "%d", coat_time_setting);
lv_unlock();
}
}
/**
* Callback for pressing the coat time decrease button
*/
void coat_decrease_cb(lv_event_t * e) {
ESP_LOGI(TAG, "Coat time decrease callback called.");
if(coat_time_setting > MIN_COAT_TIME_SETTING) {
coat_time_setting -= COAT_TIME_STEPSIZE;
lv_lock();
lv_label_set_text_fmt(coat_time_label, "%d", coat_time_setting);
lv_unlock();
}
}
void run_stop_cb(lv_event_t * e) {
ESP_LOGI(TAG, "Run/stop callback called.");
if(run_stop_label == NULL) return;
lv_label_set_text(run_stop_label, "Stop");
}
/**
* Factory function that creates a "numberstack" widget and returns the label for it
*/
lv_obj_t * build_numberstack(lv_obj_t * parent,
const char * label_text,
lv_obj_t ** label_value,
ns_btn_cb_t top_btn_cb,
ns_btn_cb_t bottom_btn_cb,
uint16_t start_value,
uint16_t stepsize) {
lv_obj_t * container = lv_obj_create(parent);
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(container,
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_all(container, 0, 0);
/* -------- Top label -------- */
lv_obj_t * label_cont = lv_obj_create(container);
lv_obj_remove_flag(label_cont, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_flex_grow(label_cont, 1);
lv_obj_set_width(label_cont, lv_pct(100));
lv_obj_t * label = lv_label_create(label_cont);
lv_label_set_text(label, label_text);
lv_obj_set_style_bg_opa(label, LV_OPA_COVER, 0);
lv_obj_set_style_text_font(label, &lv_font_montserrat_12, 0);
lv_obj_center(label);
/* -------- Top button -------- */
lv_obj_t * btn_top = lv_button_create(container);
lv_obj_t * lbl_top = lv_label_create(btn_top);
lv_label_set_text_fmt(lbl_top, "+%d", stepsize);
lv_obj_add_event_cb(btn_top, top_btn_cb, LV_EVENT_CLICKED, (void *) 1);
lv_obj_set_flex_grow(btn_top, 1);
lv_obj_set_width(btn_top, lv_pct(100));
lv_obj_center(lbl_top);
/* -------- Center label -------- */
lv_obj_t * label2_cont = lv_obj_create(container);
lv_obj_remove_flag(label2_cont, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_flex_grow(label2_cont, 1);
lv_obj_set_width(label2_cont, lv_pct(100));
* label_value = lv_label_create(label2_cont);
lv_label_set_text_fmt(*label_value, "%d", start_value);
lv_obj_set_style_bg_opa(*label_value, LV_OPA_COVER, 0);
lv_obj_set_style_text_font(*label_value, &lv_font_montserrat_12, 0);
lv_obj_center(*label_value);
/* -------- Bottom button -------- */
lv_obj_t * btn_bottom = lv_button_create(container);
lv_obj_t * lbl_bottom = lv_label_create(btn_bottom);
lv_label_set_text_fmt(lbl_bottom, "-%d", stepsize);
lv_obj_add_event_cb(btn_bottom, bottom_btn_cb, LV_EVENT_CLICKED, (void *) 2);
lv_obj_set_flex_grow(btn_bottom, 1);
lv_obj_set_width(btn_bottom, lv_pct(100));
lv_obj_center(lbl_bottom);
return container;
}
void update_rpm_readout(uint16_t rpm) {
if(rpm_label != NULL) {
lv_label_set_text_fmt(rpm_label, "RPM: %d", rpm);
}
};
void build_ui(void) {
lv_obj_t * container = lv_obj_create(lv_screen_active());
lv_obj_set_size(container, lv_pct(100), lv_pct(100));
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(container,
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_t * numberstacks_container = lv_obj_create(container);
lv_obj_set_size(numberstacks_container , lv_pct(100), lv_pct(70));
lv_obj_set_flex_flow(numberstacks_container, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(numberstacks_container,
LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
lv_obj_t * rpm_container = build_numberstack(numberstacks_container, "RPM", &rpm_label,
rpm_increase_cb, rpm_decrease_cb, rpm_setting, RPM_STEPSIZE);
lv_obj_t * coat_time_container = build_numberstack(numberstacks_container, "Coat Time",
&coat_time_label, coat_increase_cb, coat_decrease_cb, coat_time_setting, COAT_TIME_STEPSIZE);
lv_obj_set_size(rpm_container, lv_pct(48), lv_pct(96));
lv_obj_set_size(coat_time_container, lv_pct(48), lv_pct(96));
lv_obj_set_style_pad_all(container, 0, 0);
lv_obj_set_style_pad_all(numberstacks_container, 0, 0);
lv_obj_set_style_pad_all(rpm_container, 0, 0);
lv_obj_set_style_pad_all(coat_time_container, 0, 0);
/* -------- Run/Stop button -------- */
lv_obj_t * btn_run_stop = lv_button_create(container);
lv_obj_t * lbl_run_stop = lv_label_create(btn_run_stop);
run_stop_label = lbl_run_stop;
lv_label_set_text(lbl_run_stop, "Run");
lv_obj_center(lbl_run_stop);
lv_obj_add_event_cb(btn_run_stop, run_stop_cb, LV_EVENT_CLICKED, NULL);
lv_obj_set_size(btn_run_stop, lv_pct(96), lv_pct(18));
lv_obj_set_style_pad_all(btn_run_stop, 0, 0);
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "lvgl.h"
#include "motor.h"
/** Numberstack button callback type */
typedef void (*ns_btn_cb_t)(lv_event_t *);
typedef struct {
ns_btn_cb_t top_cb;
ns_btn_cb_t bottom_cb;
} ns_widget_ctx_t; /** Numberstack widget context type */
typedef enum {
STOPPED,
SPINUP,
COATING,
SPINDOWN
} coat_state;
lv_obj_t * build_numberstack(lv_obj_t * parent,
const char * label_text,
lv_obj_t ** label_value,
ns_btn_cb_t top_btn_cb,
ns_btn_cb_t bottom_btn_cb,
uint16_t start_value,
uint16_t stepsize);
void update_rpm_readout(uint16_t rpm);
void build_ui(void);