dashboard: add cargo relay controls
This commit is contained in:
@@ -10,16 +10,22 @@
|
|||||||
using namespace esp_panel::drivers;
|
using namespace esp_panel::drivers;
|
||||||
using namespace esp_panel::board;
|
using namespace esp_panel::board;
|
||||||
|
|
||||||
static const char *DASHBOARD_VERSION = "0.0.4-clean-overview";
|
static const char *DASHBOARD_VERSION = "0.0.5-relay-control";
|
||||||
|
|
||||||
// Update these if your Cargo ESP AP credentials are different.
|
// Update these if your Cargo ESP AP credentials are different.
|
||||||
static const char *CARGO_WIFI_SSID = "OverlandController";
|
static const char *CARGO_WIFI_SSID = "OverlandController";
|
||||||
static const char *CARGO_WIFI_PASSWORD = "overland1234";
|
static const char *CARGO_WIFI_PASSWORD = "overland1234";
|
||||||
static const char *CARGO_API_STATUS_URL = "http://192.168.4.1/api/v1/status";
|
static const char *CARGO_API_STATUS_URL = "http://192.168.4.1/api/v1/status";
|
||||||
|
static const char *CARGO_API_RELAY_SET_URL = "http://192.168.4.1/api/v1/relay/set";
|
||||||
|
|
||||||
static lv_obj_t *battery_label = nullptr;
|
static lv_obj_t *battery_label = nullptr;
|
||||||
static lv_obj_t *temps_label = nullptr;
|
static lv_obj_t *temps_label = nullptr;
|
||||||
static lv_obj_t *outputs_label = nullptr;
|
static lv_obj_t *outputs_label = nullptr;
|
||||||
|
static lv_obj_t *relay_buttons[6] = {nullptr};
|
||||||
|
static lv_obj_t *relay_button_labels[6] = {nullptr};
|
||||||
|
static String relay_ids[6];
|
||||||
|
static bool relay_states[6] = {false};
|
||||||
|
static int relay_count = 0;
|
||||||
static lv_obj_t *system_label = nullptr;
|
static lv_obj_t *system_label = nullptr;
|
||||||
static lv_obj_t *wifi_label = nullptr;
|
static lv_obj_t *wifi_label = nullptr;
|
||||||
static lv_obj_t *touch_count_label = nullptr;
|
static lv_obj_t *touch_count_label = nullptr;
|
||||||
@@ -66,6 +72,101 @@ static String on_off(bool value)
|
|||||||
return value ? "ON" : "OFF";
|
return value ? "ON" : "OFF";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void force_status_poll_soon()
|
||||||
|
{
|
||||||
|
last_status_poll_ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool post_relay_state(const String &relay_id, bool state)
|
||||||
|
{
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.println("Relay command skipped: WiFi not connected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument request(256);
|
||||||
|
request["id"] = relay_id;
|
||||||
|
request["state"] = state;
|
||||||
|
|
||||||
|
String payload;
|
||||||
|
serializeJson(request, payload);
|
||||||
|
|
||||||
|
Serial.print("POST ");
|
||||||
|
Serial.println(CARGO_API_RELAY_SET_URL);
|
||||||
|
Serial.println(payload);
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
http.setTimeout(5000);
|
||||||
|
http.begin(CARGO_API_RELAY_SET_URL);
|
||||||
|
http.addHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
int code = http.POST(payload);
|
||||||
|
String body = http.getString();
|
||||||
|
|
||||||
|
Serial.print("Relay HTTP code: ");
|
||||||
|
Serial.println(code);
|
||||||
|
Serial.println(body);
|
||||||
|
|
||||||
|
http.end();
|
||||||
|
|
||||||
|
return code >= 200 && code < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void relay_button_event_cb(lv_event_t *event)
|
||||||
|
{
|
||||||
|
if (lv_event_get_code(event) != LV_EVENT_CLICKED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t index_value = reinterpret_cast<intptr_t>(lv_event_get_user_data(event));
|
||||||
|
int index = static_cast<int>(index_value);
|
||||||
|
|
||||||
|
if (index < 0 || index >= relay_count) {
|
||||||
|
Serial.println("Relay button index out of range");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String relay_id = relay_ids[index];
|
||||||
|
bool next_state = !relay_states[index];
|
||||||
|
|
||||||
|
String pending_text = "Sending relay command...\n";
|
||||||
|
pending_text += relay_id;
|
||||||
|
pending_text += " -> ";
|
||||||
|
pending_text += on_off(next_state);
|
||||||
|
set_label_text_if_changed(system_label, last_system_text, pending_text);
|
||||||
|
|
||||||
|
bool ok = post_relay_state(relay_id, next_state);
|
||||||
|
|
||||||
|
String result_text = ok ? "Relay command sent\nRefreshing status..." : "Relay command failed";
|
||||||
|
set_label_text_if_changed(system_label, last_system_text, result_text);
|
||||||
|
|
||||||
|
force_status_poll_soon();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_relay_buttons()
|
||||||
|
{
|
||||||
|
lvgl_port_lock(-1);
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (relay_buttons[i] == nullptr || relay_button_labels[i] == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < relay_count) {
|
||||||
|
lv_obj_clear_flag(relay_buttons[i], LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
|
String label = relay_ids[i];
|
||||||
|
label += relay_states[i] ? " OFF" : " ON";
|
||||||
|
lv_label_set_text(relay_button_labels[i], label.c_str());
|
||||||
|
} else {
|
||||||
|
lv_obj_add_flag(relay_buttons[i], LV_OBJ_FLAG_HIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void set_label_text_if_changed(lv_obj_t *label, String &last_text, const String &text)
|
static void set_label_text_if_changed(lv_obj_t *label, String &last_text, const String &text)
|
||||||
{
|
{
|
||||||
if (label == nullptr || last_text == text) {
|
if (label == nullptr || last_text == text) {
|
||||||
@@ -219,13 +320,22 @@ static void parse_status_json(const String &body)
|
|||||||
String outputs = "";
|
String outputs = "";
|
||||||
JsonArray relay_array = doc["relays"];
|
JsonArray relay_array = doc["relays"];
|
||||||
|
|
||||||
|
relay_count = 0;
|
||||||
|
|
||||||
for (JsonObject relay : relay_array) {
|
for (JsonObject relay : relay_array) {
|
||||||
|
const char *id = relay["id"] | "";
|
||||||
const char *name = relay["name"] | relay["id"] | "Output";
|
const char *name = relay["name"] | relay["id"] | "Output";
|
||||||
bool enabled = relay["enabled"] | false;
|
bool enabled = relay["enabled"] | false;
|
||||||
bool available = relay["available"] | true;
|
bool available = relay["available"] | true;
|
||||||
bool state = relay["state"] | false;
|
bool state = relay["state"] | false;
|
||||||
int channel = relay["hardware_channel"] | 0;
|
int channel = relay["hardware_channel"] | 0;
|
||||||
|
|
||||||
|
if (relay_count < 6 && enabled && available) {
|
||||||
|
relay_ids[relay_count] = String(id);
|
||||||
|
relay_states[relay_count] = state;
|
||||||
|
relay_count++;
|
||||||
|
}
|
||||||
|
|
||||||
outputs += String(channel);
|
outputs += String(channel);
|
||||||
outputs += ". ";
|
outputs += ". ";
|
||||||
outputs += name;
|
outputs += name;
|
||||||
@@ -249,6 +359,8 @@ static void parse_status_json(const String &body)
|
|||||||
status_model.temps_text = temps;
|
status_model.temps_text = temps;
|
||||||
status_model.outputs_text = outputs;
|
status_model.outputs_text = outputs;
|
||||||
|
|
||||||
|
update_relay_buttons();
|
||||||
|
|
||||||
Serial.println("Parsed status summary:");
|
Serial.println("Parsed status summary:");
|
||||||
Serial.print(" SOC: ");
|
Serial.print(" SOC: ");
|
||||||
Serial.println(status_model.soc);
|
Serial.println(status_model.soc);
|
||||||
@@ -442,7 +554,7 @@ static void create_overland_status_screen()
|
|||||||
lv_obj_t *temps_card = create_card(screen, "Temperatures", 300, 230, LV_ALIGN_TOP_MID, 0, 96);
|
lv_obj_t *temps_card = create_card(screen, "Temperatures", 300, 230, LV_ALIGN_TOP_MID, 0, 96);
|
||||||
lv_obj_t *outputs_card = create_card(screen, "Outputs", 300, 230, LV_ALIGN_TOP_RIGHT, -32, 96);
|
lv_obj_t *outputs_card = create_card(screen, "Outputs", 300, 230, LV_ALIGN_TOP_RIGHT, -32, 96);
|
||||||
lv_obj_t *system_card = create_card(screen, "System / Network", 620, 190, LV_ALIGN_BOTTOM_LEFT, 32, -50);
|
lv_obj_t *system_card = create_card(screen, "System / Network", 620, 190, LV_ALIGN_BOTTOM_LEFT, 32, -50);
|
||||||
lv_obj_t *touch_card = create_card(screen, "Touch", 300, 190, LV_ALIGN_BOTTOM_RIGHT, -32, -50);
|
lv_obj_t *touch_card = create_card(screen, "Relay Controls", 300, 190, LV_ALIGN_BOTTOM_RIGHT, -32, -50);
|
||||||
|
|
||||||
battery_label = create_body_label(battery_card, 260);
|
battery_label = create_body_label(battery_card, 260);
|
||||||
temps_label = create_body_label(temps_card, 260);
|
temps_label = create_body_label(temps_card, 260);
|
||||||
@@ -461,20 +573,44 @@ static void create_overland_status_screen()
|
|||||||
lv_label_set_long_mode(system_label, LV_LABEL_LONG_WRAP);
|
lv_label_set_long_mode(system_label, LV_LABEL_LONG_WRAP);
|
||||||
lv_obj_align(system_label, LV_ALIGN_TOP_LEFT, 305, 32);
|
lv_obj_align(system_label, LV_ALIGN_TOP_LEFT, 305, 32);
|
||||||
|
|
||||||
lv_obj_t *button = lv_btn_create(touch_card);
|
for (int i = 0; i < 2; i++) {
|
||||||
lv_obj_set_size(button, 170, 54);
|
relay_buttons[i] = lv_btn_create(touch_card);
|
||||||
lv_obj_align(button, LV_ALIGN_TOP_LEFT, 0, 36);
|
lv_obj_set_size(relay_buttons[i], 120, 48);
|
||||||
lv_obj_add_event_cb(button, touch_test_event_cb, LV_EVENT_CLICKED, nullptr);
|
lv_obj_align(relay_buttons[i], LV_ALIGN_TOP_LEFT, i * 132, 34);
|
||||||
|
lv_obj_add_event_cb(
|
||||||
|
relay_buttons[i],
|
||||||
|
relay_button_event_cb,
|
||||||
|
LV_EVENT_CLICKED,
|
||||||
|
reinterpret_cast<void *>(static_cast<intptr_t>(i))
|
||||||
|
);
|
||||||
|
|
||||||
lv_obj_t *button_label = lv_label_create(button);
|
relay_button_labels[i] = lv_label_create(relay_buttons[i]);
|
||||||
lv_label_set_text(button_label, "Touch Test");
|
lv_label_set_text(relay_button_labels[i], "Relay");
|
||||||
lv_obj_center(button_label);
|
lv_obj_center(relay_button_labels[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 2; i < 6; i++) {
|
||||||
|
relay_buttons[i] = lv_btn_create(touch_card);
|
||||||
|
lv_obj_set_size(relay_buttons[i], 120, 42);
|
||||||
|
lv_obj_align(relay_buttons[i], LV_ALIGN_TOP_LEFT, ((i - 2) % 2) * 132, 92 + (((i - 2) / 2) * 48));
|
||||||
|
lv_obj_add_event_cb(
|
||||||
|
relay_buttons[i],
|
||||||
|
relay_button_event_cb,
|
||||||
|
LV_EVENT_CLICKED,
|
||||||
|
reinterpret_cast<void *>(static_cast<intptr_t>(i))
|
||||||
|
);
|
||||||
|
|
||||||
|
relay_button_labels[i] = lv_label_create(relay_buttons[i]);
|
||||||
|
lv_label_set_text(relay_button_labels[i], "Relay");
|
||||||
|
lv_obj_center(relay_button_labels[i]);
|
||||||
|
lv_obj_add_flag(relay_buttons[i], LV_OBJ_FLAG_HIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
touch_count_label = lv_label_create(touch_card);
|
touch_count_label = lv_label_create(touch_card);
|
||||||
lv_label_set_text(touch_count_label, "Touch count: 0");
|
lv_label_set_text(touch_count_label, "Relay controls");
|
||||||
lv_obj_set_style_text_color(touch_count_label, lv_color_hex(0xB8C0C8), 0);
|
lv_obj_set_style_text_color(touch_count_label, lv_color_hex(0xB8C0C8), 0);
|
||||||
lv_obj_set_width(touch_count_label, 250);
|
lv_obj_set_width(touch_count_label, 250);
|
||||||
lv_obj_align(touch_count_label, LV_ALIGN_TOP_LEFT, 0, 105);
|
lv_obj_align(touch_count_label, LV_ALIGN_TOP_LEFT, 0, 142);
|
||||||
|
|
||||||
lv_obj_t *footer = lv_label_create(screen);
|
lv_obj_t *footer = lv_label_create(screen);
|
||||||
lv_label_set_text(footer, "Polling Cargo ESP /api/v1/status every 5 seconds");
|
lv_label_set_text(footer, "Polling Cargo ESP /api/v1/status every 5 seconds");
|
||||||
|
|||||||
Reference in New Issue
Block a user