Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2139673
feat: attempt to add hub support with backend abstraction.
Stuckya Mar 6, 2025
99b2042
feat: adds commandType which indicates key press action like short of…
Stuckya Mar 7, 2025
00fde23
chore: fix espnow serial prints to be more consistent.
Stuckya Mar 7, 2025
82289f5
chore: update include_hal_esp32 import to match.
Stuckya Mar 7, 2025
ad9bed5
chore: update include_hal_windows_linux import to match.
Stuckya Mar 7, 2025
246a4bc
chore: wrap espnowBackend include so it isn't included if disabled.
Stuckya Mar 7, 2025
2be69f1
feat: add different key command modes to appleTV scene.
Stuckya Mar 7, 2025
5c9cb71
chore: adds conditional to device_appleTV, so we don't try to registe…
Stuckya Mar 10, 2025
9a4e96a
chore: restores settings.json.
Stuckya Mar 10, 2025
baa3f33
chore: moves naming consistency changes to another branch.
Stuckya Mar 10, 2025
162ddc1
chore: reverts old mqtt restore.
Stuckya Mar 10, 2025
714c200
feat: adds hub support to some other devices for testing.
Stuckya Mar 10, 2025
8cc787b
chore: fixes formatting and adds helper.
Stuckya Mar 11, 2025
e85984f
chore: replaces mac address in secrets.
Stuckya Mar 11, 2025
4334a05
chore: renames backend to transport to be consistent with hub domain.
Stuckya Mar 11, 2025
a1840c1
chore: fixes compiler bug when hub functionality is disabled.
Stuckya Mar 11, 2025
a879e08
chore: adds empty string back to executeCommandWithData.
Stuckya Mar 11, 2025
84f15a8
feat: simplify hub build flag.
Stuckya Mar 14, 2025
38fa0b8
chore: remove duplicate library include.
Stuckya Mar 14, 2025
5e914be
chore: fix linux / macos builds.
Stuckya Mar 20, 2025
5e3450f
chore: add stdlib to arduinoLayer.h in MacOS builds.
Stuckya Mar 20, 2025
160ac45
Revert "chore: fix linux / macos builds."
Stuckya Aug 19, 2025
f37a860
chore: fixes compiler issues.
Stuckya Aug 19, 2025
99a4e83
chore: merges main.
Stuckya Aug 19, 2025
c17f225
chore: remove unused code from merge.
Stuckya Aug 19, 2025
a0f8bbb
chore: remove non-critical changes for separate PRs
Stuckya Sep 20, 2025
3c05a2b
fix: fixes build error.
Stuckya Sep 20, 2025
6464579
chore: restores src/scenes/scene_appleTV.cpp to limit hub changes on …
Stuckya Sep 20, 2025
1d0b19e
chore: restores src/guis/gui_irReceiver.h to limit hub changes on main.
Stuckya Sep 20, 2025
2987b56
chore: restores src/devices/mediaPlayer/device_appleTV/gui_appleTV.cp…
Stuckya Sep 20, 2025
a56be74
chore: updates formatting for appleTV device.
Stuckya Sep 20, 2025
5075b6c
chore: improves formatting of hub enablement in devices.
Stuckya Sep 20, 2025
af3ce37
chore: merges main and resolves conflicts.
Stuckya Sep 29, 2025
ca2d26d
fix: resolves break in hub functionality from merging main.
Stuckya Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Platformio/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
.vscode/launch.json
.vscode/ipch
src/secrets_override.h
**/.DS_Store
109 changes: 109 additions & 0 deletions Platformio/hardware/ESP32/espnow_hal_esp32.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <esp_now.h>
#include <WiFi.h>
#include <string>
#include <sstream>
#include <nlohmann/json.hpp>
#include "espnow_hal_esp32.h"
#include <esp_wifi.h>
#include "secrets.h"

using json = nlohmann::json;

// Define the MAC address of the Raspberry Pi hub
// This should be defined in secrets.h as ESPNOW_HUB_MAC
// Example: #define ESPNOW_HUB_MAC {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
uint8_t hub_mac[6] = ESPNOW_HUB_MAC;
esp_now_peer_info_t hub_peer;

// Callback for ESP-NOW received data
tAnnounceEspNowMessage_cb thisAnnounceEspNowMessage_cb = NULL;
void onDataReceived(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
if (thisAnnounceEspNowMessage_cb == NULL) return;

// Convert MAC to string for logging
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

// Serial.printf("ESP-NOW message received from %s\n", macStr);

try {
// Try to parse the received data as MessagePack
auto unpacked_json = json::from_msgpack(data, data + data_len);

// Forward to callback
thisAnnounceEspNowMessage_cb(unpacked_json);
} catch (const std::exception& e) {
Serial.printf("Error parsing ESP-NOW message: %s\n", e.what());
}
}

void set_announceEspNowMessage_cb_HAL(tAnnounceEspNowMessage_cb pAnnounceEspNowMessage_cb) {
thisAnnounceEspNowMessage_cb = pAnnounceEspNowMessage_cb;
}

void init_espnow_HAL(void) {
Serial.println("Starting ESP-NOW");
// Set WiFi mode to station (required for ESP-NOW)
WiFi.mode(WIFI_STA);
esp_wifi_set_channel(8, WIFI_SECOND_CHAN_NONE);

// Initialize ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}

// Register callbacks
esp_now_register_recv_cb(onDataReceived);

// Register hub as peer
memcpy(hub_peer.peer_addr, hub_mac, 6);
// Setting channel0 defaults to existing channel setting
hub_peer.channel = 0;
hub_peer.encrypt = false;

// Add peer
if (esp_now_add_peer(&hub_peer) != ESP_OK) {
Serial.println("Failed to add hub peer");
return;
}

Serial.println("ESP-NOW initialized successfully");
}

void espnow_loop_HAL() {
// Nothing to do in the loop for ESP-NOW
// ESP-NOW callbacks are handled by the ESP32 in the background
}

bool publishEspNowMessage_HAL(json payload) {
// Pack the JSON into MessagePack format
std::vector<std::uint8_t> packed_json = json::to_msgpack(payload);

if (packed_json.size() > 250) {
Serial.println("Error: Message exceeds ESP-NOW maximum size");
return false;
}

// Send the message
esp_err_t result = esp_now_send(hub_peer.peer_addr, packed_json.data(), packed_json.size());

if (result == ESP_OK) {
// Serial.println("ESP-NOW sent message with success");
return true;
}

Serial.println("ESP-NOW failed to send message");
return false;
}

void espnow_shutdown_HAL() {
// Unregister peer
esp_now_del_peer(hub_peer.peer_addr);

// Deinitialize ESP-NOW
esp_now_deinit();

Serial.println("ESP-NOW shutdown complete");
}
14 changes: 14 additions & 0 deletions Platformio/hardware/ESP32/espnow_hal_esp32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

void init_espnow_HAL(void);
void espnow_loop_HAL();
bool publishEspNowMessage_HAL(json payload);
void espnow_shutdown_HAL();

typedef void (*tAnnounceEspNowMessage_cb)(json payload);
void set_announceEspNowMessage_cb_HAL(tAnnounceEspNowMessage_cb pAnnounceEspNowMessage_cb);
1 change: 1 addition & 0 deletions Platformio/hardware/ESP32/include_hal_esp32.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
#include "ESP32/sleep_hal_esp32.h"
#include "ESP32/tft_hal_esp32.h"
#include "ESP32/user_led_hal_esp32.h"
#include "ESP32/espnow_hal_esp32.h"
8 changes: 7 additions & 1 deletion Platformio/hardware/ESP32/sleep_hal_esp32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "mqtt_hal_esp32.h"
// disconnect BLE keyboard
#include "keyboard_ble_hal_esp32.h"
// disconnect ESP-NOW
#include "espnow_hal_esp32.h"
// prepare keypad keys to wakeup
#include "keypad_keys_hal_esp32.h"

Expand Down Expand Up @@ -153,6 +155,10 @@ void enterSleep(){
keyboardBLE_end_HAL();
#endif

#if (ENABLE_ESPNOW == 1)
espnow_shutdown_HAL();
#endif

// Prepare IO states
digitalWrite(LCD_DC_GPIO, LOW); // LCD control signals off
digitalWrite(LCD_CS_GPIO, LOW);
Expand Down Expand Up @@ -290,4 +296,4 @@ void set_wakeupByIMUEnabled_HAL(bool aWakeupByIMUEnabled) {
}
uint32_t get_lastActivityTimestamp() {
return lastActivityTimestamp;
}
}
26 changes: 26 additions & 0 deletions Platformio/hardware/windows_linux/espnow_hal_windows_linux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "espnow_hal_windows_linux.h"

// Callback function pointer
static EspNowMessageCallback espNowMessageCallback = nullptr;

void set_announceEspNowMessage_cb_HAL(EspNowMessageCallback callback) {
espNowMessageCallback = callback;
printf("ESP-NOW callback registered (stub implementation)");
}

void init_espnow_HAL() {
printf("ESP-NOW initialized (stub implementation)");
}

void espnow_loop_HAL() {
// Nothing to do in the stub implementation
}

bool publishEspNowMessage_HAL(json payload) {
printf("ESP-NOW message published (stub implementation): %s", payload.dump().c_str());
return true; // Always return success in the stub implementation
}

void espnow_shutdown_HAL() {
printf("ESP-NOW shutdown (stub implementation)");
}
14 changes: 14 additions & 0 deletions Platformio/hardware/windows_linux/espnow_hal_windows_linux.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <nlohmann/json.hpp>
using json = nlohmann::json;

// Callback type definition
typedef void (*EspNowMessageCallback)(json);

// Function declarations
void set_announceEspNowMessage_cb_HAL(EspNowMessageCallback callback);
void init_espnow_HAL();
void espnow_loop_HAL();
bool publishEspNowMessage_HAL(json payload);
void espnow_shutdown_HAL();
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
#include "windows_linux/sleep_hal_windows_linux.h"
#include "windows_linux/tft_hal_windows_linux.h"
#include "windows_linux/user_led_hal_windows_linux.h"
#include "windows_linux/espnow_hal_windows_linux.h"
4 changes: 4 additions & 0 deletions Platformio/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ build_flags =
-D ENABLE_KEYBOARD_MQTT=0
-D ENABLE_BLUETOOTH=1
-D ENABLE_KEYBOARD_BLE=1
-D ENABLE_HUB_COMMUNICATION=0
-D ENABLE_ESPNOW=0
-D PREFERRED_HUB_TRANSPORT=0 ; 0 = ESP-NOW, 1 = MQTT
-D USE_SCENE_SPECIFIC_GUI_LIST=1
-D SCR_WIDTH=${env.custom_screen_width}
-D SCR_HEIGHT=${env.custom_screen_height}
Expand Down Expand Up @@ -96,6 +99,7 @@ lib_deps =
;https://github.com/h2zero/NimBLE-Arduino#master
sparkfun/SparkFun MAX1704x Fuel Gauge Arduino Library@^1.0.4
;t-vk/ESP32 BLE Keyboard@^0.3.2
johboh/nlohmann-json@^3.11.3
build_flags =
${env.build_flags}
;-- Arduino log -----------------------------------------------------------
Expand Down
101 changes: 100 additions & 1 deletion Platformio/src/applicationInternal/commandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "guis/gui_irReceiver.h"
// show received BLE connection messages
#include "guis/gui_BLEpairing.h"
#include "hub/hubManager.h"

uint16_t COMMAND_UNKNOWN;

Expand Down Expand Up @@ -239,13 +240,76 @@ void executeCommandWithData(uint16_t command, commandData commandData, std::stri
}
break;
}

#if (ENABLE_HUB_COMMUNICATION == 1)
case HUB: {
omote_log_w("HUB commands should be handled using the CommandExecutionParams struct\r\n");
break;
}
#endif
}
}

void executeCommandWithData(const CommandExecutionParams& params, commandData commandData) {
#if (ENABLE_HUB_COMMUNICATION == 1)
if (commandData.commandHandler != HUB) {
// For non-HUB commands, pass through to the original function
omote_log_d("command: will execute command '%u'%s%s\r\n",
params.commandId,
params.additionalPayload.empty() ? "" : " with additionalPayload '",
params.additionalPayload.empty() ? "" : (params.additionalPayload + "'").c_str());

executeCommandWithData(params.commandId, commandData, params.additionalPayload);
return;
}

auto current = commandData.commandPayloads.begin();

// Extract device and command
std::string deviceName = *current;
current = std::next(current, 1);

std::string commandName = *current;
current = std::next(current, 1);

// Create JSON payload
json payload;
payload["device"] = deviceName;
payload["command"] = commandName;
payload["type"] = params.commandType;

// Create a data array if we have any additional data
json dataArray = json::array();

// Add all remaining items from commandPayloads to the data array
while (current != commandData.commandPayloads.end()) {
dataArray.push_back(*current);
current = std::next(current, 1);
}

// If additionalPayload is provided, add it to the data array
if (!params.additionalPayload.empty()) {
dataArray.push_back(params.additionalPayload);
}

// Only add the data array if it has any content
if (!dataArray.empty()) {
payload["data"] = dataArray;
}

omote_log_d("execute: will send hub message for device '%s', command '%s'\r\n",
deviceName.c_str(), commandName.c_str());

// Send using the hub manager
HubManager::getInstance().sendMessage(payload);
#endif
}

void executeCommand(uint16_t command, std::string additionalPayload) {
try {
if (commands.count(command) > 0) {
omote_log_d("command: will execute command '%u' with additionalPayload '%s'\r\n", command, additionalPayload.c_str());
omote_log_d("command: will execute command '%u' with additionalPayload '%s'\r\n",
command, additionalPayload.c_str());
executeCommandWithData(command, commands.at(command), additionalPayload);
} else {
omote_log_w("command: command '%u' not found\r\n", command);
Expand All @@ -256,6 +320,22 @@ void executeCommand(uint16_t command, std::string additionalPayload) {
}
}

void executeCommand(const CommandExecutionParams& params) {
try {
// Early return if command not found
if (commands.count(params.commandId) == 0) {
omote_log_w("command: command '%u' not found\r\n", params.commandId);
return;
}

commandData cmdData = commands.at(params.commandId);
executeCommandWithData(params, cmdData);
}
catch (const std::out_of_range& oor) {
omote_log_e("executeCommand: internal error, command not registered\r\n");
}
}

void receiveNewIRmessage_cb(std::string message) {
showNewIRmessage(message);
}
Expand Down Expand Up @@ -287,5 +367,24 @@ void receiveWiFiConnected_cb(bool connected) {
void receiveMQTTmessage_cb(std::string topic, std::string payload) {
showMQTTmessage(topic, payload);
}
#endif

#if (ENABLE_ESPNOW == 1)
void receiveEspNowMessage_cb(json payload) {
// Extract device and command from the payload
std::string device, command, jsonStr;

if (payload.contains("device") && payload.contains("command")) {
device = payload["device"];
command = payload["command"];

// Serialize the payload to a string
std::string jsonStr = payload.dump();

// Show the message in the UI
showEspNowMessage(jsonStr);

// TODO: Process the command based on device and command
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more of a stub for 2-way communication with the hub

}
}
#endif
Loading