Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Espnow Support #663

Merged
merged 3 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 7 additions & 2 deletions include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,9 @@ extern RemoteDebug Debug; // Let everyone in the project know about it
#define PROJECT_NAME "Plate Cover"
#endif

#define ENABLE_WIFI 1 // Connect to WiFi
#define INCOMING_WIFI_ENABLED 1 // Accepting incoming color data and commands
#define ENABLE_ESPNOW 1 // Connect to ESPNOW and listen for packets
#define ENABLE_WIFI 0 // Connect to WiFi
#define INCOMING_WIFI_ENABLED 0 // Accepting incoming color data and commands
#define WAIT_FOR_WIFI 0 // Hold in setup until we have WiFi - for strips without effects
#define TIME_BEFORE_LOCAL 3 // How many seconds before the lamp times out and shows local content
#define MAX_BUFFERS 60 // Times 4 channels, but they're only NUM_LEDS big
Expand Down Expand Up @@ -1292,6 +1293,10 @@ extern RemoteDebug Debug; // Let everyone in the project know about it
#define ENABLE_OTA 1 // Listen for over the air update to the flash
#endif

#ifndef ENABLE_ESPNOW
#define ENABLE_ESPNOW 0 // Listen for ESPNOW packets
davepl marked this conversation as resolved.
Show resolved Hide resolved
#endif

#ifndef ENABLE_NTP
#define ENABLE_NTP 1 // Update the clock from NTP
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/effectmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ void EffectManager::ClearRemoteColor(bool retainRemoteEffect)

void EffectManager::ApplyGlobalColor(CRGB color)
{
debugI("Setting Global Color: %08X\n", (uint) color);
debugI("Setting Global Color: %08X\n", (uint32_t) color);

auto& deviceConfig = g_ptrSystem->DeviceConfig();
deviceConfig.SetColorSettings(color, deviceConfig.GlobalColor());
Expand Down
16 changes: 15 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,14 @@
#include "values.h"
#include "improvserial.h" // ImprovSerial impl for setting WiFi credentials over the serial port
#include <TJpg_Decoder.h>
#include <esp_now.h>

#if defined(TOGGLE_BUTTON_1) || defined(TOGGLE_BUTTON_2)
#include "Bounce2.h" // For Bounce button class
#endif

void IRAM_ATTR ScreenUpdateLoopEntry(void *);
void onReceiveESPNOW(const uint8_t *macAddr, const uint8_t *data, int dataLen);

//
// Global Variables
Expand Down Expand Up @@ -323,8 +325,19 @@ void setup()
err = nvs_flash_init();
}


ESP_ERROR_CHECK(err);

#if ENABLE_ESPNOW
WiFi.mode(WIFI_STA); // or WIFI_AP if applicable

if (esp_now_init() != ESP_OK)
throw std::runtime_error("Error initializing ESP-NOW");
// Register receive callback function
esp_now_register_recv_cb(onReceiveESPNOW);
debugI("ESP-NOW initialized with MAC address: %s", WiFi.macAddress().c_str());
#endif

#if ENABLE_WIFI
String WiFi_ssid;
String WiFi_password;
Expand Down Expand Up @@ -534,6 +547,7 @@ void setup()
taskManager.StartSocketThread();

SaveEffectManagerConfig();
// Start the main loop
}

// loop - main execution loop
Expand Down Expand Up @@ -569,7 +583,7 @@ void loop()
String strOutput;

#if ENABLE_WIFI
strOutput += str_sprintf("WiFi: %s, IP: %s, ", WLtoString(WiFi.status()), WiFi.localIP().toString().c_str());
strOutput += str_sprintf("WiFi: %s, MAC: %s, IP: %s ", WLtoString(WiFi.status()), WiFi.macAddress().c_str(), WiFi.localIP().toString().c_str());
Copy link
Contributor

Choose a reason for hiding this comment

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

Cat this be in the stgartup prose instead of in the .5hz loop?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, we show WiFi status every 5 seconds, if WiFi is enabled. This is based on the fact that connectivity might drop, addresses can change, etc. This decision was made after multiple sessions of hair-pulling over networking stuff being not working stuff.

#endif

strOutput += str_sprintf("Mem: %u, LargestBlk: %u, PSRAM Free: %u/%u, ", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), ESP.getFreePsram(), ESP.getPsramSize());
Expand Down
86 changes: 86 additions & 0 deletions src/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,92 @@ static DRAM_ATTR WiFiUDP l_Udp; // UDP object used for NNTP, etc
DRAM_ATTR bool NTPTimeClient::_bClockSet = false; // Has our clock been set by SNTP?
DRAM_ATTR std::mutex NTPTimeClient::_clockMutex; // Clock guard mutex for SNTP client

#if ENABLE_ESPNOW

// ESPNOW Support
//
// We accept ESPNOW commands to change effects and so on. This is a simple structure that we'll receive over ESPNOW.
enum class ESPNowCommand : uint8_t
{
ESPNOW_NEXTEFFECT = 1,
ESPNOW_PREVEFFECT,
ESPNOW_SETEFFECT,
ESPNOW_INVALID = 255 // Followed by a uint32_t argument
};

// Message class
//
// Encapsulates an ESPNOW message, which is a command and an optional argument
class Message
{
public:
constexpr Message(ESPNowCommand cmd, uint32_t argument)
: cbSize(sizeof(Message)), command(cmd), arg1(argument)
{
}

constexpr Message()
: cbSize(sizeof(Message)), command(ESPNowCommand::ESPNOW_INVALID), arg1(0)
{
}

const uint8_t* data() const
{
return reinterpret_cast<const uint8_t*>(this);
}

constexpr size_t byte_size() const
{
return sizeof(Message);
}

uint8_t cbSize;
ESPNowCommand command;
uint32_t arg1;
} __attribute__((packed));

// onReceiveESPNOW
//
// Callback function for ESPNOW that is called when a data packet is received

void onReceiveESPNOW(const uint8_t *macAddr, const uint8_t *data, int dataLen)
{
Message message;

memcpy(&message, data, sizeof(message));
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this copy necessary at all? Why is message a global at all?
If so, should it be after the packet is validated, no?

If all you're doign is peeking at the command member of data, just cast it and use it in place after verifying the cbSize is appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Depends how async the code is. Am I guaranteed that a second message won't come in in the meantime and corrupt the first? I assume so, but copying seemed safer, and it's nominally 6 bytes, so...

debugI("ESPNOW Message received.");

if (message.cbSize != sizeof(message))
{
debugE("ESPNOW Message received with wrong structure size: %d but should be %d", message.cbSize, sizeof(message));
return;
}

switch(message.command)
{
case ESPNowCommand::ESPNOW_NEXTEFFECT:
debugI("ESPNOW Next effect");
g_ptrSystem->EffectManager().NextEffect();
break;

case ESPNowCommand::ESPNOW_PREVEFFECT:
debugI("ESPNOW Previous effect");
g_ptrSystem->EffectManager().PreviousEffect();
break;

case ESPNowCommand::ESPNOW_SETEFFECT:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not doing any argument validation here because I assume (and have confirmed) that the APIs themselves do the sanity checks, so don't want a separate layer of logic if I can avoid it.

debugI("ESPNOW Setting effect index to %d", message.arg1);
g_ptrSystem->EffectManager().SetCurrentEffectIndex(message.arg1);
break;

default:
debugE("ESPNOW Message received with unknown command: %d", (byte) message.command);
break;
}
}

#endif

// processRemoteDebugCmd
//
// Callback function that the debug library (which exposes a little console over telnet and serial) calls
Expand Down
2 changes: 1 addition & 1 deletion src/remotecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ void RemoteControl::handle()
// crate a ColorFillEffect, and apply it as a temporary effect. This will override the current
// effect until the next effect change or remote command.

debugI("Changing Color via remote: %08X\n", (uint) RemoteColorCodes[i].color);
debugI("Changing Color via remote: %08X\n", (uint32_t) RemoteColorCodes[i].color);
effectManager.ApplyGlobalColor(RemoteColorCodes[i].color);
#if FULL_COLOR_REMOTE_FILL
auto effect = make_shared_psram<ColorFillEffect>("Remote Color", RemoteColorCodes[i].color, 1, true);
Expand Down