Lots of improvements

This commit is contained in:
gorbit99
2025-10-09 06:02:36 +02:00
parent a80e7bdb43
commit fee2979289
15 changed files with 314 additions and 61 deletions

View File

@@ -21,6 +21,8 @@
THE SOFTWARE.
*/
#include <cstdint>
#include <cstring>
#include <string_view>
#include "GlobalVars.h"
@@ -406,6 +408,38 @@ void Connection::sendFlexData(uint8_t sensorId, float flexLevel) {
));
}
// PACKET_PROVISIONING_NEW_TRACER 29
void Connection::sendProvisioningNewTracker(uint8_t mac[6]) {
MUST(m_Connected);
ProvisioningNewTrackerPacket packet{};
memcpy(packet.mac, mac, sizeof(packet.mac));
MUST(sendPacket(SendPacketType::ProvisioningNewTracker, packet));
}
// PACKET_PROVISIONING_STATUS 30
void Connection::sendProvisioningStatus(
uint8_t mac[6],
ProvisioningPackets::ConnectionStatus status
) {
MUST(m_Connected);
ProvisioningStatusPacket packet{
.status = status,
};
memcpy(packet.mac, mac, sizeof(packet.mac));
MUST(sendPacket(SendPacketType::ProvisioningStatus, packet));
}
// PACKET_PROVISIONING_FAILED 31
void Connection::sendProvisioningFailed(
uint8_t mac[6],
ProvisioningPackets::ConnectionError error
) {
MUST(m_Connected);
ProvisioningFailedPacket packet{.error = error};
memcpy(packet.mac, mac, sizeof(packet.mac));
MUST(sendPacket(SendPacketType::ProvisioningFailed, packet));
}
#if ENABLE_INSPECTION
void Connection::sendInspectionRawIMUData(
uint8_t sensorId,

View File

@@ -25,12 +25,16 @@
#include <Arduino.h>
#include <WiFiUdp.h>
#include <sys/types.h>
#include <cstdint>
#include <optional>
#include "../configuration/SensorConfig.h"
#include "c_types.h"
#include "featureflags.h"
#include "globals.h"
#include "network/wifiprovisioning/provisioning-packets.h"
#include "packets.h"
#include "quat.h"
#include "sensors/sensor.h"
@@ -98,6 +102,19 @@ public:
// PACKET_FLEX_DATA 26
void sendFlexData(uint8_t sensorId, float flexLevel);
// PACKET_PROVISIONING_NEW_TRACER 29
void sendProvisioningNewTracker(uint8_t mac[6]);
// PACKET_PROVISIONING_STATUS 30
void sendProvisioningStatus(
uint8_t mac[6],
ProvisioningPackets::ConnectionStatus status
);
// PACKET_PROVISIONING_FAILED 31
void
sendProvisioningFailed(uint8_t mac[6], ProvisioningPackets::ConnectionError error);
#if ENABLE_INSPECTION
void sendInspectionRawIMUData(
uint8_t sensorId,

View File

@@ -56,9 +56,9 @@ enum class SendPacketType : uint8_t {
AcknowledgeConfigChange = 24,
FlexData = 26,
// PositionData = 27,
ProvisioningNewTracker = 28,
ProvisioningStatus = 29,
ProvisioningFailed = 30,
ProvisioningNewTracker = 29,
ProvisioningStatus = 30,
ProvisioningFailed = 31,
Bundle = 100,
Inspection = 105,
};
@@ -73,7 +73,7 @@ enum class ReceivePacketType : uint8_t {
SensorInfo = 15,
FeatureFlags = 22,
SetConfigFlag = 25,
StartWiFiProvisioning = 27,
StartWiFiProvisioning = 28,
};
enum class InspectionPacketType : uint8_t {
@@ -240,16 +240,16 @@ struct StartWiFiProvisioningPacket {
bool start;
};
struct ProvisioningNewTracker {
struct ProvisioningNewTrackerPacket {
uint8_t mac[6];
};
struct ProvisioningStatus {
struct ProvisioningStatusPacket {
uint8_t mac[6];
SlimeVR::Network::ProvisioningPackets::ConnectionStatus status;
};
struct ProvisioningFailed {
struct ProvisioningFailedPacket {
uint8_t mac[6];
SlimeVR::Network::ProvisioningPackets::ConnectionError error;
};

View File

@@ -53,19 +53,21 @@ bool WiFiNetwork::isConnected() const {
void WiFiNetwork::setWiFiCredentials(const char* SSID, const char* pass) {
wifiProvisioning.stopSearchForProvider();
wifiProvisioning.stopProvisioning();
WiFi.persistent(true);
tryConnecting(false, SSID, pass);
retriedOnG = false;
// Reset state, will get back into provisioning if can't connect
hadWifi = false;
wifiState = WiFiReconnectionStatus::ServerCredAttempt;
}
void WiFiNetwork::setProvisionedWiFiCredentials(const char* SSID, const char* pass) {
tryConnecting(false, SSID, pass);
retriedOnG = false;
wifiState = WiFiReconnectionStatus::ProvisionedAttempt;
}
IPAddress WiFiNetwork::getAddress() { return WiFi.localIP(); }
void WiFiNetwork::setUp() {
// Don't need to save the already saved credentials or the hardcoded ones
WiFi.persistent(false);
WiFi.persistent(true);
wifiHandlerLogger.info("Setting up WiFi");
WiFi.mode(WIFI_AP_STA);
WiFi.hostname("SlimeVR FBT Tracker");
@@ -109,7 +111,6 @@ void WiFiNetwork::setUp() {
void WiFiNetwork::onConnected() {
wifiState = WiFiReconnectionStatus::Success;
wifiProvisioning.stopProvisioning();
statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, false);
hadWifi = true;
wifiHandlerLogger.info(
@@ -201,6 +202,12 @@ void WiFiNetwork::upkeep() {
wifiState = WiFiReconnectionStatus::Failed;
}
return;
case WiFiReconnectionStatus::ProvisionedAttempt: // Couldn't connect with
// credentials received from
// provisioning
if (!tryProvisionedCredentials()) {
wifiState = WiFiReconnectionStatus::Failed;
}
case WiFiReconnectionStatus::Failed: // Couldn't connect with second set of
// credentials or server credentials
if (startedProvisioning) {
@@ -347,6 +354,20 @@ bool WiFiNetwork::tryServerCredentials() {
return tryConnecting(true);
}
bool WiFiNetwork::tryProvisionedCredentials() {
if (WiFi.status() != WL_DISCONNECTED) {
return false;
}
if (retriedOnG) {
return false;
}
retriedOnG = true;
return tryConnecting(true);
}
bool WiFiNetwork::tryConnecting(bool phyModeG, const char* SSID, const char* pass) {
#if ESP8266
if (phyModeG) {

View File

@@ -38,6 +38,7 @@ public:
SavedAttempt,
HardcodeAttempt,
ServerCredAttempt,
ProvisionedAttempt,
Failed,
Success
};
@@ -53,10 +54,14 @@ public:
void setUp();
void upkeep();
void setWiFiCredentials(const char* SSID, const char* pass);
void setProvisionedWiFiCredentials(const char* SSID, const char* pass);
static IPAddress getAddress();
WiFiReconnectionStatus getWiFiState();
bool startedProvisioning = false;
static String getSSID();
static String getPassword();
private:
static constexpr float WiFiTimeoutSeconds = 11;
@@ -64,12 +69,10 @@ private:
void setStaticIPIfDefined();
void onConnected();
static String getSSID();
static String getPassword();
bool trySavedCredentials();
bool tryHardcodedCredentials();
bool tryServerCredentials();
bool tryProvisionedCredentials();
bool tryConnecting(
bool phyModeG = false,
const char* SSID = nullptr,

View File

@@ -0,0 +1,27 @@
#include "provisioning-packets.h"
namespace SlimeVR::Network::ProvisioningPackets {
const char* statusToCstr(ConnectionStatus status) {
switch (status) {
case ConnectionStatus::Connecting:
return "Connecting";
case ConnectionStatus::Connected:
return "Connected";
case ConnectionStatus::ServerFound:
return "Server Found";
}
return "Unknown";
}
const char* errorToCstr(ConnectionError error) {
switch (error) {
case ConnectionError::ConnectionFailed:
return "Connection Failed";
case ConnectionError::ServerNotFound:
return "Server Not Found";
}
return "Unknown";
}
} // namespace SlimeVR::Network::ProvisioningPackets

View File

@@ -5,12 +5,13 @@
namespace SlimeVR::Network::ProvisioningPackets {
enum class ProvisioningPacketId : uint8_t {
ProvisioningAvailable = 0,
ProvisioningRequest = 1,
ProvisioningStart = 2,
ProvisioningStarted = 3,
ProvisioningStatus = 4,
ProvisioningFailed = 5,
ProvisioningAvailable,
ProvisioningRequest,
ProvisioningStart,
ProvisioningStatus,
ProvisioningStatusAck,
ProvisioningFailed,
ProvisioningFailedAck,
};
#pragma pack(push, 1)
@@ -26,11 +27,15 @@ enum class ConnectionStatus : uint8_t {
ServerFound,
};
const char* statusToCstr(ConnectionStatus status);
enum class ConnectionError : uint8_t {
ConnectionFailed,
ServerNotFound,
};
const char* errorToCstr(ConnectionError error);
struct ProvisioningAvailable
: ProvisioningPacket<ProvisioningPacketId::ProvisioningAvailable> {};
@@ -46,19 +51,22 @@ struct ProvisioningStart : ProvisioningPacket<ProvisioningPacketId::Provisioning
char wifiPassword[64] = "";
};
struct ProvisioningStarted
: ProvisioningPacket<ProvisioningPacketId::ProvisioningStarted> {};
struct ProvisioningStatus
: ProvisioningPacket<ProvisioningPacketId::ProvisioningStatus> {
ConnectionStatus status = ConnectionStatus::Connecting;
};
struct ProvisioningStatusAck
: ProvisioningPacket<ProvisioningPacketId::ProvisioningStatusAck> {};
struct ProvisioningFailed
: ProvisioningPacket<ProvisioningPacketId::ProvisioningFailed> {
ConnectionError error = ConnectionError::ConnectionFailed;
};
struct ProvisioningFailedAck
: ProvisioningPacket<ProvisioningPacketId::ProvisioningFailedAck> {};
#pragma pack(pop)
} // namespace SlimeVR::Network::ProvisioningPackets

View File

@@ -1,4 +1,5 @@
#include <cstdint>
#include <optional>
#include "logging/Logger.h"
#include "provisioning-party.h"
@@ -8,6 +9,16 @@ namespace SlimeVR::Network {
ProvisioningParty::ProvisioningParty(SlimeVR::Logging::Logger& logger) noexcept
: logger{logger} {}
void ProvisioningParty::handleSendResult(bool success) { lastPacketSuccess = success; }
void ProvisioningParty::resetLastSendResult() { lastPacketSuccess.reset(); }
std::optional<bool> ProvisioningParty::getLastSendResult() {
auto result = lastPacketSuccess;
lastPacketSuccess.reset();
return result;
}
void ProvisioningParty::addPeer(uint8_t macAddress[6]) const {
#if ESP8266
esp_now_add_peer(macAddress, ESP_NOW_ROLE_COMBO, 0, nullptr, 0);
@@ -25,12 +36,4 @@ void ProvisioningParty::removePeer(uint8_t macAddress[6]) const {
esp_now_del_peer(macAddress);
}
void ProvisioningParty::sendMessage(
uint8_t receiverMac[6],
const uint8_t* data,
uint8_t length
) const {
// TODO:
}
} // namespace SlimeVR::Network

View File

@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <optional>
#if ESP8266
#include <espnow.h>
@@ -28,16 +29,31 @@ public:
virtual void
handleMessage(uint8_t macAddress[6], const uint8_t* data, uint8_t length)
= 0;
virtual void stop() = 0;
void handleSendResult(bool success);
protected:
uint8_t BroadcastMacAddress[6]{0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
void addPeer(uint8_t macAddress[6]) const;
void removePeer(uint8_t macAddress[6]) const;
void sendMessage(uint8_t receiverMac[6], const uint8_t* data, uint8_t length) const;
void resetLastSendResult();
std::optional<bool> getLastSendResult();
template <typename Packet>
bool sendMessage(uint8_t receiverMac[6], Packet data) const {
return esp_now_send(
receiverMac,
reinterpret_cast<uint8_t*>(&data),
sizeof(Packet)
)
== 0;
}
SlimeVR::Logging::Logger& logger;
private:
std::optional<bool> lastPacketSuccess;
};
} // namespace SlimeVR::Network

View File

@@ -1,6 +1,7 @@
#include <cstdint>
#include <cstring>
#include "GlobalVars.h"
#include "credentials.h"
#include "logging/Logger.h"
#include "network/wifiprovisioning/provisioning-packets.h"
@@ -31,12 +32,7 @@ bool ProvisioningProvider::tick() {
}
if (messageTimeout.elapsed()) {
ProvisioningPackets::ProvisioningAvailable packet{};
sendMessage(
BroadcastMacAddress,
reinterpret_cast<uint8_t*>(&packet),
sizeof(packet)
);
sendMessage(BroadcastMacAddress, ProvisioningPackets::ProvisioningAvailable{});
messageTimeout.reset();
}
@@ -60,20 +56,43 @@ void ProvisioningProvider::handleMessage(
handleProvisioningRequest(macAddress, packet.provisioningPassword);
break;
}
case ProvisioningPackets::ProvisioningPacketId::ProvisioningStarted: {
break;
}
case ProvisioningPackets::ProvisioningPacketId::ProvisioningStatus: {
auto packet
= *reinterpret_cast<const ProvisioningPackets::ProvisioningStatus*>(data
);
addPeer(macAddress);
sendMessage(macAddress, ProvisioningPackets::ProvisioningStatusAck{});
removePeer(macAddress);
networkConnection.sendProvisioningStatus(macAddress, packet.status);
logger.info(
"Tracker with mac address " MACSTR " reported status %d, %s",
MAC2STR(macAddress),
static_cast<uint8_t>(packet.status),
ProvisioningPackets::statusToCstr(packet.status)
);
break;
}
case ProvisioningPackets::ProvisioningPacketId::ProvisioningFailed: {
auto packet
= *reinterpret_cast<const ProvisioningPackets::ProvisioningFailed*>(data
);
addPeer(macAddress);
sendMessage(macAddress, ProvisioningPackets::ProvisioningFailedAck{});
removePeer(macAddress);
networkConnection.sendProvisioningFailed(macAddress, packet.error);
logger.error(
"Tracker with mac address " MACSTR " reported error %d, %s",
MAC2STR(macAddress),
static_cast<uint8_t>(packet.error),
ProvisioningPackets::errorToCstr(packet.error)
);
break;
}
default:
break;
}
}
void ProvisioningProvider::stop() {}
void ProvisioningProvider::handleProvisioningRequest(
uint8_t macAddress[6],
const char* password
@@ -83,11 +102,16 @@ void ProvisioningProvider::handleProvisioningRequest(
}
ProvisioningPackets::ProvisioningStart packet{};
// TODO: swap with the getSSID/getPassword function once that gets merged in
strcpy(packet.wifiName, WiFi.SSID().c_str());
strcpy(packet.wifiPassword, WiFi.psk().c_str());
strcpy(packet.wifiName, wifiNetwork.getSSID().c_str());
strcpy(packet.wifiPassword, wifiNetwork.getPassword().c_str());
addPeer(macAddress);
sendMessage(macAddress, reinterpret_cast<uint8_t*>(&packet), sizeof(packet));
sendMessage(macAddress, packet);
removePeer(macAddress);
networkConnection.sendProvisioningNewTracker(macAddress);
logger.info(
"Provisioning tracker with mac address " MACSTR "...",
MAC2STR(macAddress)
);
}
} // namespace SlimeVR::Network

View File

@@ -14,7 +14,6 @@ public:
bool tick() final;
void handleMessage(uint8_t macAddress[6], const uint8_t* data, uint8_t length)
final;
void stop() final;
private:
static constexpr float ProvisioningTimeoutSeconds = 60.0f;

View File

@@ -3,9 +3,13 @@
#include <cstdint>
#include <cstring>
#include <type_traits>
#include "GlobalVars.h"
#include "credentials.h"
#include "espnow.h"
#include "logging/Logger.h"
#include "network/wifihandler.h"
#include "network/wifiprovisioning/provisioning-packets.h"
#include "network/wifiprovisioning/provisioning-party.h"
#include "provisioning-target.h"
@@ -20,6 +24,7 @@ void ProvisioningTarget::init() {
searchTimeout.reset();
channelSwitchTimeout.reset();
switchChannel(1);
addPeer(BroadcastMacAddress);
}
bool ProvisioningTarget::tick() {
@@ -36,19 +41,58 @@ bool ProvisioningTarget::tick() {
} else {
switchChannel(currentChannel + 1);
}
channelSwitchTimeout.reset();
}
channelSwitchTimeout.reset();
break;
}
case Status::Authenticating: {
case Status::ConnectionStarted: {
sendAndWaitForAck(ProvisioningPackets::ProvisioningStatus{
.status = ProvisioningPackets::ConnectionStatus::Connecting,
});
break;
}
case Status::Connecting: {
if (WiFi.isConnected()) {
sendAndWaitForAck(ProvisioningPackets::ProvisioningStatus{
.status = ProvisioningPackets::ConnectionStatus::Connected
});
break;
}
if (wifiNetwork.getWiFiState()
== WiFiNetwork::WiFiReconnectionStatus::Failed) {
sendAndWaitForAck(ProvisioningPackets::ProvisioningFailed{
.error = ProvisioningPackets::ConnectionError::ConnectionFailed,
});
break;
}
break;
}
case Status::SearchingForServer: {
if (!searchingForServer) {
serverSearchTimeout.reset();
searchingForServer = true;
}
if (networkConnection.isConnected()) {
sendAndWaitForAck(ProvisioningPackets::ProvisioningStatus{
.status = ProvisioningPackets::ConnectionStatus::ServerFound,
});
break;
}
if (serverSearchTimeout.elapsed()) {
sendAndWaitForAck(ProvisioningPackets::ProvisioningFailed{
.error = ProvisioningPackets::ConnectionError::ServerNotFound,
});
break;
}
break;
}
case Status::Done:
printf("Finished\n");
return false;
}
return true;
@@ -78,33 +122,58 @@ void ProvisioningTarget::handleMessage(
startConnection(packet.wifiName, packet.wifiPassword);
break;
}
case ProvisioningPackets::ProvisioningPacketId::ProvisioningStatusAck: {
status = static_cast<Status>(static_cast<int>(status) + 1);
waitingForAck = false;
retryTimeout.reset();
break;
}
case ProvisioningPackets::ProvisioningPacketId::ProvisioningFailedAck: {
status = Status::Done;
break;
}
default:
break;
}
}
void ProvisioningTarget::stop() {}
void ProvisioningTarget::switchChannel(uint8_t channel) {
logger.info("Switching to channel %d...", channel);
wifi_set_channel(channel);
currentChannel = channel;
esp_now_set_peer_channel(BroadcastMacAddress, channel);
}
void ProvisioningTarget::handleProvisioningOffer(uint8_t macAddress[6]) {
if (status != Status::WaitingForProvider) {
return;
}
logger.info("Received provisioning offer from " MACSTR, MAC2STR(macAddress));
status = Status::Authenticating;
addPeer(macAddress);
memcpy(providerMac, macAddress, sizeof(providerMac));
ProvisioningPackets::ProvisioningRequest packet;
strcpy(packet.provisioningPassword, provisioningPassword);
sendMessage(providerMac, reinterpret_cast<uint8_t*>(&packet), sizeof(packet));
sendMessage(providerMac, packet);
}
void ProvisioningTarget::startConnection(const char* ssid, const char* password) {
// TODO:
logger.info("Received wifi credentials SSID: %s, password: %s!", ssid, password);
logger.info(
"Received WiFi credentials SSID %s and password length %zu! Connecting...",
ssid,
strlen(password)
);
wifiNetwork.setProvisionedWiFiCredentials(ssid, password);
status = Status::ConnectionStarted;
resetLastSendResult();
sendMessage(
providerMac,
ProvisioningPackets::ProvisioningStatus{
.status = ProvisioningPackets::ConnectionStatus::Connecting,
}
);
}
} // namespace SlimeVR::Network

View File

@@ -14,28 +14,45 @@ public:
bool tick() final;
void handleMessage(uint8_t macAddress[6], const uint8_t* data, uint8_t length)
final;
void stop() final;
private:
static constexpr float ChannelSwitchSeconds = 0.5f;
static constexpr float SearchTimeoutSeconds = 120.0f;
static constexpr float RetryIntervalSeconds = 0.25f;
static constexpr float ServerSearchTimeoutSeconds = 10.0f;
enum class Status {
WaitingForProvider,
Authenticating,
ConnectionStarted,
Connecting,
SearchingForServer,
Done,
};
void switchChannel(uint8_t channel);
void handleProvisioningOffer(uint8_t macAddress[6]);
void startConnection(const char* ssid, const char* password);
template <typename Packet>
void sendAndWaitForAck(Packet packet) {
if (waitingForAck && !retryTimeout.elapsed()) {
return;
}
sendMessage(providerMac, packet);
retryTimeout.reset();
waitingForAck = true;
}
TimeOut searchTimeout{SearchTimeoutSeconds};
TimeOut channelSwitchTimeout{ChannelSwitchSeconds};
TimeOut retryTimeout{RetryIntervalSeconds};
TimeOut serverSearchTimeout{ServerSearchTimeoutSeconds};
uint8_t currentChannel = 0;
Status status = Status::WaitingForProvider;
uint8_t providerMac[6] = {};
bool waitingForAck = false;
bool searchingForServer = false;
};
} // namespace SlimeVR::Network

View File

@@ -21,6 +21,7 @@
THE SOFTWARE.
*/
#include <cstdint>
#include <memory>
#include "network/wifiprovisioning/provisioning-provider.h"
@@ -44,6 +45,10 @@ namespace SlimeVR::Network {
void espnowReceiveCallback(uint8_t* macAddress, uint8_t* data, uint8_t length) {
wifiProvisioning.handleMessage(macAddress, data, length);
}
void espnowSendCallback(uint8_t* macAddress, uint8_t status) {
wifiProvisioning.handleSendResult(status == 0);
}
#elif ESP32
void espnowReceiveCallback(
const esp_now_recv_info_t* senderInfo,
@@ -139,6 +144,8 @@ void WiFiProvisioning::tick() {
if (!role->tick()) {
role.reset();
esp_now_deinit();
esp_now_unregister_recv_cb();
}
}
@@ -162,11 +169,13 @@ bool WiFiProvisioning::initEspnow() {
result = esp_now_register_recv_cb(espnowReceiveCallback);
if (result != 0) {
logger.error("Couldn't register ESP-Now receive callback! Error: %d", result);
logger.error("Couldn't register ESP-Now receiver callback! Error: %d", result);
esp_now_deinit();
return false;
}
esp_now_register_send_cb(espnowSendCallback);
return true;
}
@@ -178,4 +187,8 @@ void WiFiProvisioning::handleMessage(
role->handleMessage(macAddress, data, length);
}
void WiFiProvisioning::handleSendResult(bool success) {
role->handleSendResult(success);
}
} // namespace SlimeVR::Network

View File

@@ -49,12 +49,14 @@ public:
private:
bool initEspnow();
void handleMessage(uint8_t* macAddress, const uint8_t* data, uint8_t length);
void handleSendResult(bool success);
std::unique_ptr<ProvisioningParty> role;
SlimeVR::Logging::Logger logger{"WiFiProvisioning"};
#if ESP8266
friend void espnowReceiveCallback(uint8_t*, uint8_t*, uint8_t);
friend void espnowSendCallback(uint8_t*, uint8_t);
#elif ESP32
friend void espnowReceiveCallback(
const esp_now_recv_info_t* senderInfo,