ESP-NOW service (#259)
Implemented Chat app with ESP-NOW service. The Chat app was originally created by @maxbrito500 and adapted by me.
This commit is contained in:
committed by
GitHub
parent
358954f8be
commit
49a46f6236
@@ -19,6 +19,9 @@ namespace tt {
|
||||
namespace service::gps { extern const ServiceManifest manifest; }
|
||||
namespace service::wifi { extern const ServiceManifest manifest; }
|
||||
namespace service::sdcard { extern const ServiceManifest manifest; }
|
||||
#ifdef ESP_PLATFORM
|
||||
namespace service::espnow { extern const ServiceManifest manifest; }
|
||||
#endif
|
||||
|
||||
static Dispatcher mainDispatcher;
|
||||
|
||||
@@ -29,6 +32,9 @@ static void registerAndStartSystemServices() {
|
||||
addService(service::gps::manifest);
|
||||
addService(service::sdcard::manifest);
|
||||
addService(service::wifi::manifest);
|
||||
#ifdef ESP_PLATFORM
|
||||
addService(service::espnow::manifest);
|
||||
#endif
|
||||
}
|
||||
|
||||
void initHeadless(const hal::Configuration& config) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "Tactility/service/espnow/EspNow.h"
|
||||
#include "Tactility/service/espnow/EspNowService.h"
|
||||
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
namespace tt::service::espnow {
|
||||
|
||||
constexpr const char* TAG = "EspNow";
|
||||
|
||||
void enable(const EspNowConfig& config) {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
service->enable(config);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
}
|
||||
}
|
||||
|
||||
void disable() {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
service->disable();
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
}
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
return service->isEnabled();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool addPeer(const esp_now_peer_info_t& peer) {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
return service->addPeer(peer);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool send(const uint8_t* address, const uint8_t* buffer, size_t bufferLength) {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
return service->send(address, buffer, bufferLength);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ReceiverSubscription subscribeReceiver(std::function<void(const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length)> onReceive) {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
return service->subscribeReceiver(onReceive);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void unsubscribeReceiver(ReceiverSubscription subscription) {
|
||||
auto service = findService();
|
||||
if (service != nullptr) {
|
||||
service->unsubscribeReceiver(subscription);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Service not found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
@@ -0,0 +1,222 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "Tactility/service/espnow/EspNowService.h"
|
||||
#include "Tactility/TactilityHeadless.h"
|
||||
#include "Tactility/service/ServiceManifest.h"
|
||||
#include "Tactility/service/ServiceRegistry.h"
|
||||
#include "Tactility/service/espnow/EspNowWifi.h"
|
||||
#include <cstring>
|
||||
#include <esp_now.h>
|
||||
#include <esp_random.h>
|
||||
|
||||
namespace tt::service::espnow {
|
||||
|
||||
extern const ServiceManifest manifest;
|
||||
|
||||
constexpr const char* TAG = "EspNowService";
|
||||
constexpr TickType_t MAX_DELAY = 1000U / portTICK_PERIOD_MS;
|
||||
static uint8_t BROADCAST_MAC[ESP_NOW_ETH_ALEN];
|
||||
|
||||
constexpr bool isBroadcastAddress(uint8_t address[ESP_NOW_ETH_ALEN]) { return memcmp(address, BROADCAST_MAC, ESP_NOW_ETH_ALEN) == 0; }
|
||||
|
||||
void EspNowService::onStart(ServiceContext& service) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
memset(BROADCAST_MAC, 0xFF, sizeof(BROADCAST_MAC));
|
||||
}
|
||||
|
||||
void EspNowService::onStop(ServiceContext& service) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
if (isEnabled()) {
|
||||
disable();
|
||||
}
|
||||
}
|
||||
|
||||
// region Enable
|
||||
|
||||
void EspNowService::enable(const EspNowConfig& config) {
|
||||
auto enable_context = std::make_shared<EspNowConfig>(config);
|
||||
getMainDispatcher().dispatch(enableFromDispatcher, enable_context);
|
||||
}
|
||||
|
||||
void EspNowService::enableFromDispatcher(std::shared_ptr<void> context) {
|
||||
auto service = findService();
|
||||
if (service == nullptr) {
|
||||
TT_LOG_E(TAG, "Service not running");
|
||||
return;
|
||||
}
|
||||
|
||||
auto config = std::static_pointer_cast<EspNowConfig>(context);
|
||||
|
||||
service->enableFromDispatcher(*config);
|
||||
}
|
||||
|
||||
void EspNowService::enableFromDispatcher(const EspNowConfig& config) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
if (enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initWifi(config)) {
|
||||
TT_LOG_E(TAG, "initWifi() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (esp_now_init() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_now_init() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (esp_now_register_recv_cb(receiveCallback) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_now_register_recv_cb() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
//#if CONFIG_ESPNOW_ENABLE_POWER_SAVE
|
||||
// ESP_ERROR_CHECK( esp_now_set_wake_window(CONFIG_ESPNOW_WAKE_WINDOW) );
|
||||
// ESP_ERROR_CHECK( esp_wifi_connectionless_module_set_wake_interval(CONFIG_ESPNOW_WAKE_INTERVAL) );
|
||||
//#endif
|
||||
|
||||
if (esp_now_set_pmk(config.masterKey) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_now_set_pmk() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add default unencrypted broadcast peer
|
||||
esp_now_peer_info_t broadcast_peer;
|
||||
memset(&broadcast_peer, 0, sizeof(esp_now_peer_info_t));
|
||||
memcpy(broadcast_peer.peer_addr, BROADCAST_MAC, sizeof(BROADCAST_MAC));
|
||||
service::espnow::addPeer(broadcast_peer);
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
// endregion Enable
|
||||
|
||||
// region Disable
|
||||
|
||||
void EspNowService::disable() {
|
||||
getMainDispatcher().dispatch(disableFromDispatcher, nullptr);
|
||||
}
|
||||
|
||||
void EspNowService::disableFromDispatcher(TT_UNUSED std::shared_ptr<void> context) {
|
||||
auto service = findService();
|
||||
if (service == nullptr) {
|
||||
TT_LOG_E(TAG, "Service not running");
|
||||
return;
|
||||
}
|
||||
|
||||
service->disableFromDispatcher();
|
||||
}
|
||||
|
||||
void EspNowService::disableFromDispatcher() {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (esp_now_deinit() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_now_deinit() failed");
|
||||
}
|
||||
|
||||
if (!deinitWifi()) {
|
||||
TT_LOG_E(TAG, "deinitWifi() failed");
|
||||
}
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
// region Disable
|
||||
|
||||
// region Callbacks
|
||||
|
||||
void EspNowService::receiveCallback(const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) {
|
||||
auto service = findService();
|
||||
if (service == nullptr) {
|
||||
TT_LOG_E(TAG, "Service not running");
|
||||
return;
|
||||
}
|
||||
service->onReceive(receiveInfo, data, length);
|
||||
}
|
||||
|
||||
void EspNowService::onReceive(const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
TT_LOG_D(TAG, "Received %d bytes", length);
|
||||
|
||||
for (const auto& item: subscriptions) {
|
||||
item.onReceive(receiveInfo, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
// endregion Callbacks
|
||||
|
||||
bool EspNowService::isEnabled() const {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
bool EspNowService::addPeer(const esp_now_peer_info_t& peer) {
|
||||
if (esp_now_add_peer(&peer) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to add peer");
|
||||
return false;
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Peer added");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool EspNowService::send(const uint8_t* address, const uint8_t* buffer, size_t bufferLength) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
if (!isEnabled()) {
|
||||
return false;
|
||||
} else {
|
||||
return esp_now_send(address, buffer, bufferLength) == ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
ReceiverSubscription EspNowService::subscribeReceiver(std::function<void(const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length)> onReceive) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
auto id = lastSubscriptionId++;
|
||||
|
||||
subscriptions.push_back(ReceiverSubscriptionData {
|
||||
.id = id,
|
||||
.onReceive = onReceive
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void EspNowService::unsubscribeReceiver(tt::service::espnow::ReceiverSubscription subscriptionId) {
|
||||
auto lock = mutex.asScopedLock();
|
||||
lock.lock();
|
||||
std::erase_if(subscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
|
||||
}
|
||||
|
||||
std::shared_ptr<EspNowService> findService() {
|
||||
return std::static_pointer_cast<EspNowService>(
|
||||
service::findServiceById(manifest.id)
|
||||
);
|
||||
}
|
||||
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "EspNow",
|
||||
.createService = create<EspNowService>
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
@@ -0,0 +1,106 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "Tactility/service/espnow/EspNow.h"
|
||||
#include "Tactility/service/wifi/Wifi.h"
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#include <esp_now.h>
|
||||
#include <esp_wifi.h>
|
||||
|
||||
namespace tt::service::espnow {
|
||||
|
||||
constexpr const char* TAG = "EspNowService";
|
||||
|
||||
static bool disableWifiService() {
|
||||
auto wifi_state = wifi::getRadioState();
|
||||
if (wifi_state != wifi::RadioState::Off && wifi_state != wifi::RadioState::OffPending) {
|
||||
wifi::setEnabled(false);
|
||||
}
|
||||
|
||||
if (wifi::getRadioState() == wifi::RadioState::Off) {
|
||||
return true;
|
||||
} else {
|
||||
TickType_t timeout_time = kernel::getTicks() + kernel::millisToTicks(2000);
|
||||
while (kernel::getTicks() < timeout_time && wifi::getRadioState() != wifi::RadioState::Off) {
|
||||
kernel::delayTicks(50);
|
||||
}
|
||||
|
||||
return wifi::getRadioState() == wifi::RadioState::Off;
|
||||
}
|
||||
}
|
||||
|
||||
bool initWifi(const EspNowConfig& config) {
|
||||
if (!disableWifiService()) {
|
||||
TT_LOG_E(TAG, "Failed to disable wifi");
|
||||
return false;
|
||||
}
|
||||
|
||||
wifi_mode_t mode;
|
||||
if (config.mode == Mode::Station) {
|
||||
mode = wifi_mode_t::WIFI_MODE_STA;
|
||||
} else {
|
||||
mode = wifi_mode_t::WIFI_MODE_AP;
|
||||
}
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
if (esp_wifi_init(&cfg) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_init() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_set_storage() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_mode(mode) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_set_mode() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_start() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_start() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_channel(config.channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_set_channel() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.longRange) {
|
||||
wifi_interface_t wifi_interface;
|
||||
if (config.mode == Mode::Station) {
|
||||
wifi_interface = WIFI_IF_STA;
|
||||
} else {
|
||||
wifi_interface = WIFI_IF_AP;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_protocol(wifi_interface, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR) != ESP_OK) {
|
||||
TT_LOG_W(TAG, "esp_wifi_set_protocol() for long range failed");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deinitWifi() {
|
||||
if (esp_wifi_stop() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to stop radio");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_mode(WIFI_MODE_NULL) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to unset mode");
|
||||
}
|
||||
|
||||
if (esp_wifi_deinit() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to deinit");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace tt::service::espnow
|
||||
|
||||
#endif // ESP_PLATFORM
|
||||
Reference in New Issue
Block a user