Skip to content

Commit

Permalink
wifi configuration can be changed via BLE
Browse files Browse the repository at this point in the history
  • Loading branch information
wifwucite committed May 24, 2021
1 parent 4a59c49 commit 4000bfb
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 16 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ The maintenance BLE service is provided implicitly when you include `esp32_ble_c
Allows to send commands to the ESP32 and receives answers back from it. A command is a string which consists of the name of the command and (possibly) arguments, separated by spaces.
You can define your own custom commands in yaml as described below in detail.
There are also some built-in commands, which are always available:
* help [command]:
* help [<command>]:
Without argument, it lists all available commands. When the name of a command is given like in "help log-level" it displays a specific description for this command.
* ble-services [on|off]:
Switches the component related (non-maintenance) BLE services on or off. You may wonder why one should switch off these services. On most ESP32 boards both BLE and WiFi share the same physical 2,4 GHz antenna on the ESP32. So, too much traffic on both of them can cause it to crash and reboot. Short-lived WiFi connections for sending MQTT messages work fine with services enabled. However, when connecting to the [web server](https://esphome.io/components/web_server.html) or for [OTA updates](https://esphome.io/components/ota.html) services should be disabled.
Switches the component related (non-maintenance) BLE services on or off. You may wonder why one should switch off these services. On most ESP32 boards both BLE and WiFi share the same physical 2,4 GHz antenna on the ESP32. So, too much traffic on both of them can cause it to crash and reboot. Short-lived WiFi connections for sending MQTT messages work fine with services enabled. However, when connecting to the [web server](https://esphome.io/components/web_server.html) or for [OTA updates](https://esphome.io/components/ota.html) services should be disabled.
* wifi-config <ssid> <password> [hidden]:
Sets the SSID and the password to use for connecting to WIFI. The optional 'hidden' argument marks the network as hidden network. It is recommended to use this command only when security is enabled.
* log-level [level]:
If no argument is provided, it queries the current log level for logging over BLE. When a level argument is provided like in "log-level 0" the log level is adjusted. Currently the levels have to be specified as integer number between 0 (= no logging) and 7 (= very verbose).
⚠️ **Note**: You cannot get finer logging than the overall log level specified for the [logger component](https://esphome.io/components/logger.html).
Expand Down
45 changes: 35 additions & 10 deletions components/esp32_ble_controller/ble_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@
namespace esphome {
namespace esp32_ble_controller {

static const char *TAG = "ble_command";

// generic ///////////////////////////////////////////////////////////////////////////////////////////////

void BLECommand::set_result(const string& result) const {
global_ble_controller->set_command_result(result);
}

// help ///////////////////////////////////////////////////////////////////////////////////////////////

BLECommandHelp::BLECommandHelp() : BLECommand("help", "show help for commands") {}
BLECommandHelp::BLECommandHelp() : BLECommand("help", "shows help for commands.") {}

void BLECommandHelp::execute(const vector<string>& arguments) const {
if (arguments.empty()) {
Expand All @@ -18,26 +26,26 @@ void BLECommandHelp::execute(const vector<string>& arguments) const {
help += command->get_name();
}
help += ", 'help <cmd>' for more.";
global_ble_controller->set_command_result(help);
set_result(help);
} else {
string command_name = arguments[0];
for (const auto& command : global_ble_controller->get_commands()) {
if (command->get_name() == command_name) {
global_ble_controller->set_command_result(command->get_name() + ": " + command->get_description());
set_result(command->get_name() + ": " + command->get_description());
return;
}
global_ble_controller->set_command_result("Unknown BLE command '" + command_name + "'");
set_result("Unknown BLE command '" + command_name + "'");
}
}
}

// ble-services ///////////////////////////////////////////////////////////////////////////////////////////////

BLECommandSwitchServicesOnOrOff::BLECommandSwitchServicesOnOrOff() : BLECommand("ble-services", "ble-services on|off enables or disables the non-maintenance BLE services") {}
BLECommandSwitchServicesOnOrOff::BLECommandSwitchServicesOnOrOff() : BLECommand("ble-services", "'ble-services on|off' enables or disables the non-maintenance BLE services.") {}

void BLECommandSwitchServicesOnOrOff::execute(const vector<string>& arguments) const {
if (!arguments.empty()) {
string on_or_off = arguments[0];
const string& on_or_off = arguments[0];
if (on_or_off == "off") {
global_ble_controller->set_ble_mode(BLEMaintenanceMode::WIFI_ONLY);
} else {
Expand All @@ -46,13 +54,30 @@ void BLECommandSwitchServicesOnOrOff::execute(const vector<string>& arguments) c
}
BLEMaintenanceMode mode = global_ble_controller->get_ble_mode();
string enabled_or_disabled = mode == BLEMaintenanceMode::WIFI_ONLY ? "disabled" : "enabled";
global_ble_controller->set_command_result("Non-maintenance services are " + enabled_or_disabled +".");
set_result("Non-maintenance services are " + enabled_or_disabled +".");
}

// wifi-config ///////////////////////////////////////////////////////////////////////////////////////////////

BLECommandWifiConfiguration::BLECommandWifiConfiguration() : BLECommand("wifi-config", "'wifi-config <ssid> <pwd> [hidden]' sets WIFI SSID and password and if it is hidden.") {}

void BLECommandWifiConfiguration::execute(const vector<string>& arguments) const {
if (arguments.size() >= 2) {
const string& ssid = arguments[0];
const string& password = arguments[1];
const bool hidden_network = arguments.size() == 3 && arguments[2] == "hidden";
global_ble_controller->set_wifi_configuration(ssid, password, hidden_network);

set_result("WIFI configuration updated.");
} else {
set_result("At least two arguments (SSID and password) required!");
}
}

// log-level ///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef USE_LOGGER
BLECommandLogLevel::BLECommandLogLevel() : BLECommand("log-level", "get or set log level (0=None, 4=Config, 5=Debug)") {}
BLECommandLogLevel::BLECommandLogLevel() : BLECommand("log-level", "gets or sets log level (0=None, 4=Config, 5=Debug).") {}

void BLECommandLogLevel::execute(const vector<string>& arguments) const {
if (!arguments.empty()) {
Expand All @@ -62,7 +87,7 @@ void BLECommandLogLevel::execute(const vector<string>& arguments) const {
global_ble_controller->set_log_level(level.value());
}
}
global_ble_controller->set_command_result("Log level is " + to_string(global_ble_controller->get_log_level())+".");
set_result("Log level is " + to_string(global_ble_controller->get_log_level())+".");
}
#endif

Expand All @@ -76,7 +101,7 @@ void BLECustomCommand::execute(const vector<string>& arguments) const {
optional<string> result = result_holder.get_result();
if (result.has_value()) {
ESP_LOGI(TAG, "Setting result %s", result.value().c_str());
global_ble_controller->set_command_result(result.value());
set_result(result.value());
}
}

Expand Down
13 changes: 13 additions & 0 deletions components/esp32_ble_controller/ble_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class BLECommand {

virtual void execute(const std::vector<string>& arguments) const = 0;

protected:
void set_result(const string& result) const;

private:
string name;
string description;
Expand All @@ -49,6 +52,16 @@ class BLECommandSwitchServicesOnOrOff : public BLECommand {
virtual void execute(const vector<string>& arguments) const override;
};

// wifi-config ///////////////////////////////////////////////////////////////////////////////////////////////

class BLECommandWifiConfiguration : public BLECommand {
public:
BLECommandWifiConfiguration();
virtual ~BLECommandWifiConfiguration() {}

virtual void execute(const vector<string>& arguments) const override;
};

// log-level ///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef USE_LOGGER
Expand Down
6 changes: 3 additions & 3 deletions components/esp32_ble_controller/ble_maintenance_handler.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#include <BLE2902.h>

#include "esphome/core/log.h"

#include "ble_maintenance_handler.h"

#include "esphome/core/log.h"
#include "esphome/core/application.h"
#ifdef USE_LOGGER
#include "esphome/components/logger/logger.h"
Expand All @@ -27,6 +26,7 @@ static const char *TAG = "ble_maintenance_handler";
BLEMaintenanceHandler::BLEMaintenanceHandler() {
commands.push_back(new BLECommandHelp());
commands.push_back(new BLECommandSwitchServicesOnOrOff());
commands.push_back(new BLECommandWifiConfiguration());

#ifdef USE_LOGGER
commands.push_back(new BLECommandLogLevel());
Expand Down Expand Up @@ -60,7 +60,7 @@ void BLEMaintenanceHandler::setup(BLEServer* ble_server) {
this->send_log_message(level, tag, message);
});
}
#endif
#endif
}

void BLEMaintenanceHandler::onWrite(BLECharacteristic *characteristic) {
Expand Down
10 changes: 9 additions & 1 deletion components/esp32_ble_controller/esp32_ble_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ void ESP32BLEController::setup() {

enable_ble_security();

wifi_configuration_handler.setup();

// Start advertising
// BLEAdvertising* advertising = BLEDevice::getAdvertising();
// advertising->setMinInterval(0x800); // suggested default: 1.28s
Expand Down Expand Up @@ -191,7 +193,8 @@ void ESP32BLEController::setup_ble_service_for_component(C* component, BLECompon
}

void ESP32BLEController::initialize_ble_mode() {
ble_mode_preference = global_preferences.make_preference<uint8_t>(fnv1_hash("BLEMaintenanceMode"));
// Note: We include the compilation time to force a reset after flashing new firmware
ble_mode_preference = global_preferences.make_preference<uint8_t>(fnv1_hash("ble-mode#" + App.get_compilation_time()));

uint8_t mode;
if (!ble_mode_preference.load(&mode)) {
Expand Down Expand Up @@ -223,6 +226,11 @@ void ESP32BLEController::set_ble_mode(uint8_t newMode) {
}
}


void ESP32BLEController::ESP32BLEController::set_wifi_configuration(const string& ssid, const string& password, bool hidden_network) {
wifi_configuration_handler.set_credentials(ssid, password, hidden_network);
}

void ESP32BLEController::dump_config() {
ESP_LOGCONFIG(TAG, "Bluetooth Low Energy Controller:");
ESP_LOGCONFIG(TAG, " BLE mode: %d", (uint8_t) ble_mode);
Expand Down
5 changes: 5 additions & 0 deletions components/esp32_ble_controller/esp32_ble_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ble_component_handler_base.h"
#include "ble_maintenance_handler.h"
#include "thread_safe_bounded_queue.h"
#include "wifi_configuration_handler.h"

using std::string;
using std::unordered_map;
Expand Down Expand Up @@ -62,6 +63,8 @@ class ESP32BLEController : public Component, public Controller, private BLESecur
void set_ble_mode(BLEMaintenanceMode mode);
void set_ble_mode(uint8_t mode);

void set_wifi_configuration(const string& ssid, const string& password, bool hidden_network);

void set_command_result(const string& result_message);

#ifdef USE_LOGGER
Expand Down Expand Up @@ -128,6 +131,8 @@ class ESP32BLEController : public Component, public Controller, private BLESecur

BLEMaintenanceHandler* maintenance_handler;

WifiSettingsHandler wifi_configuration_handler;

unordered_map<string, BLECharacteristicInfoForHandler> info_for_component;
unordered_map<string, BLEComponentHandlerBase*> handler_for_component;

Expand Down
52 changes: 52 additions & 0 deletions components/esp32_ble_controller/wifi_configuration_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "wifi_configuration_handler.h"

#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/components/wifi/wifi_component.h"

namespace esphome {
namespace esp32_ble_controller {

static const char *TAG = "wifi_configuration_handler";

void WifiSettingsHandler::setup() {
// Hash with compilation time
// This ensures the AP override is not applied for OTA
uint32_t hash = fnv1_hash("wifi_settings#" + App.get_compilation_time());
wifi_settings_preference = global_preferences.make_preference<WifiSettings>(hash, true);

WifiSettings settings;
if (wifi_settings_preference.load(&settings)) {
ESP_LOGI(TAG, "Overriding WIFI settings with stored preferences");
override_sta(settings);
}
}

void WifiSettingsHandler::set_credentials(const std::string &ssid, const std::string &password, bool hidden_network) {
ESP_LOGI(TAG, "Updating WIFI settings");

WifiSettings settings;

strncpy(settings.ssid, ssid.c_str(), WIFI_SSID_LEN);
strncpy(settings.password, password.c_str(), WIFI_PASSWORD_LEN);
settings.hidden_network = hidden_network;

if (!wifi_settings_preference.save(&settings)) {
ESP_LOGE(TAG, "Could not save new WIFI settings");
}

override_sta(settings);
}

void WifiSettingsHandler::override_sta(const WifiSettings& settings) {
wifi::WiFiAP sta;

sta.set_ssid(settings.ssid);
sta.set_password(settings.password);
sta.set_hidden(settings.hidden_network);

wifi::global_wifi_component->set_sta(sta);
}

} // namespace esp32_ble_controller
} // namespace esphome
34 changes: 34 additions & 0 deletions components/esp32_ble_controller/wifi_configuration_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <string>

#include "esphome/core/defines.h"
#include "esphome/core/preferences.h"

namespace esphome {
namespace esp32_ble_controller {

#define WIFI_SSID_LEN 33
#define WIFI_PASSWORD_LEN 65

struct WifiSettings {
char ssid[WIFI_SSID_LEN];
char password[WIFI_PASSWORD_LEN];
bool hidden_network;
} PACKED; // NOLINT

class WifiSettingsHandler {
public:
void setup();

void set_credentials(const std::string& ssid, const std::string& password, bool hidden_network);

private:
void override_sta(const WifiSettings& settings);

private:
ESPPreferenceObject wifi_settings_preference;
};

} // namespace esp32_ble_controller
} // namespace esphome

0 comments on commit 4000bfb

Please sign in to comment.