Files
tactility/Tactility/Source/app/chat/ChatApp.cpp
T
Shadowtrance 0d16eb606f ESP-NOW chat & P4 fixes (#454)
* Espnow chat fix

Fixes chat app not appearing and coexistence with wifi STA mode.
Wifi OFF and obviously not connected, espnow starts.
Wifi ON and connected, espnow starts.
Wifi ON and NOT connected, espnow starts.

* Fix P4 build
2026-01-24 12:42:49 +01:00

153 lines
5.2 KiB
C++

#ifdef ESP_PLATFORM
#include <sdkconfig.h>
#endif
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED)
#include <Tactility/app/AppManifest.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/Assets.h>
#include <Tactility/Logger.h>
#include <Tactility/service/espnow/EspNow.h>
#include "Tactility/lvgl/LvglSync.h"
#include <cstdio>
#include <cstring>
#include <esp_wifi.h>
#include <lvgl.h>
namespace tt::app::chat {
static const auto LOGGER = Logger("ChatApp");
constexpr uint8_t BROADCAST_ADDRESS[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
class ChatApp : public App {
lv_obj_t* msg_list = nullptr;
lv_obj_t* input_field = nullptr;
service::espnow::ReceiverSubscription receiveSubscription;
void addMessage(const char* message) {
lv_obj_t* msg_label = lv_label_create(msg_list);
lv_label_set_text(msg_label, message);
lv_obj_set_width(msg_label, lv_pct(100));
lv_label_set_long_mode(msg_label, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_align(msg_label, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_pad_all(msg_label, 2, 0);
lv_obj_scroll_to_y(msg_list, lv_obj_get_scroll_y(msg_list) + 20, LV_ANIM_ON);
}
static void onSendClicked(lv_event_t* e) {
auto* self = static_cast<ChatApp*>(lv_event_get_user_data(e));
auto* msg = lv_textarea_get_text(self->input_field);
const auto msg_len = strlen(msg);
if (self->msg_list && msg && msg_len) {
self->addMessage(msg);
if (!service::espnow::send(BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(msg), msg_len)) {
LOGGER.error("Failed to send message");
}
lv_textarea_set_text(self->input_field, "");
}
}
void onReceive(const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) {
// Append \0 to make it a string
auto buffer = static_cast<char*>(malloc(length + 1));
memcpy(buffer, data, length);
buffer[length] = 0x00;
const std::string message_prefixed = std::string("Received: ") + buffer;
lvgl::getSyncLock()->lock();
addMessage(message_prefixed.c_str());
lvgl::getSyncLock()->unlock();
free(buffer);
}
public:
void onCreate(AppContext& appContext) override {
// TODO: Move this to a configuration screen/app
static const uint8_t key[ESP_NOW_KEY_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
auto config = service::espnow::EspNowConfig(
const_cast<uint8_t*>(key),
service::espnow::Mode::Station,
1,
false,
false
);
service::espnow::enable(config);
receiveSubscription = service::espnow::subscribeReceiver([this](const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) {
onReceive(receiveInfo, data, length);
});
}
void onDestroy(AppContext& appContext) override {
service::espnow::unsubscribeReceiver(receiveSubscription);
if (service::espnow::isEnabled()) {
service::espnow::disable();
}
}
void onShow(AppContext& context, lv_obj_t* parent) override {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
lvgl::toolbar_create(parent, context);
// Message list
msg_list = lv_list_create(parent);
lv_obj_set_flex_grow(msg_list, 1);
lv_obj_set_width(msg_list, LV_PCT(100));
lv_obj_set_flex_grow(msg_list, 1);
lv_obj_set_style_bg_color(msg_list, lv_color_hex(0x262626), 0);
lv_obj_set_style_border_width(msg_list, 1, 0);
lv_obj_set_style_pad_ver(msg_list, 0, 0);
lv_obj_set_style_pad_hor(msg_list, 4, 0);
// Input panel
auto* bottom_wrapper = lv_obj_create(parent);
lv_obj_set_flex_flow(bottom_wrapper, LV_FLEX_FLOW_ROW);
lv_obj_set_size(bottom_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(bottom_wrapper, 0, 0);
lv_obj_set_style_pad_column(bottom_wrapper, 4, 0);
lv_obj_set_style_border_opa(bottom_wrapper, 0, LV_STATE_DEFAULT);
// Input field
input_field = lv_textarea_create(bottom_wrapper);
lv_obj_set_flex_grow(input_field, 1);
lv_textarea_set_placeholder_text(input_field, "Type a message...");
lv_textarea_set_one_line(input_field, true);
// Send button
auto* send_btn = lv_button_create(bottom_wrapper);
lv_obj_set_style_margin_all(send_btn, 0, LV_STATE_DEFAULT);
lv_obj_set_style_margin_top(send_btn, 2, LV_STATE_DEFAULT); // Hack to fix alignment
lv_obj_add_event_cb(send_btn, onSendClicked, LV_EVENT_CLICKED, this);
auto* btn_label = lv_label_create(send_btn);
lv_label_set_text(btn_label, "Send");
lv_obj_center(btn_label);
}
~ChatApp() override = default;
};
extern const AppManifest manifest = {
.appId = "Chat",
.appName = "Chat",
.appIcon = TT_ASSETS_APP_ICON_CHAT,
.createApp = create<ChatApp>
};
}
#endif // CONFIG_SOC_WIFI_SUPPORTED && !CONFIG_SLAVE_SOC_WIFI_SUPPORTED