mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-11-04 15:39:42 +08:00
Merge fe09dd0c62 into 5da262c8da
This commit is contained in:
commit
176a85a403
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
PythonExamples/__pycache__/mode_switch_test.cpython-311.pyc
|
||||||
@ -1,10 +1,12 @@
|
|||||||
#include "CommandManager.hpp"
|
#include "CommandManager.hpp"
|
||||||
|
#include "tasks/tasks.hpp"
|
||||||
|
|
||||||
|
|
||||||
CommandManager::CommandManager(ProjectConfig* deviceConfig)
|
CommandManager::CommandManager(ProjectConfig* deviceConfig)
|
||||||
: deviceConfig(deviceConfig) {}
|
: deviceConfig(deviceConfig) {}
|
||||||
|
|
||||||
const CommandType CommandManager::getCommandType(JsonVariant& command) {
|
const CommandType CommandManager::getCommandType(JsonVariant& command) {
|
||||||
if (!command.containsKey("command"))
|
if (!command["command"].is<const char*>())
|
||||||
return CommandType::None;
|
return CommandType::None;
|
||||||
|
|
||||||
if (auto search = commandMap.find(command["command"]);
|
if (auto search = commandMap.find(command["command"]);
|
||||||
@ -15,11 +17,11 @@ const CommandType CommandManager::getCommandType(JsonVariant& command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CommandManager::hasDataField(JsonVariant& command) {
|
bool CommandManager::hasDataField(JsonVariant& command) {
|
||||||
return command.containsKey("data");
|
return command["data"].is<JsonObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandManager::handleCommands(CommandsPayload commandsPayload) {
|
void CommandManager::handleCommands(CommandsPayload commandsPayload) {
|
||||||
if (!commandsPayload.data.containsKey("commands")) {
|
if (!commandsPayload.data["commands"].is<JsonArray>()) {
|
||||||
log_e("Json data sent not supported, lacks commands field");
|
log_e("Json data sent not supported, lacks commands field");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -41,12 +43,12 @@ void CommandManager::handleCommand(JsonVariant command) {
|
|||||||
// malformed command, lacked data field
|
// malformed command, lacked data field
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!command["data"].containsKey("ssid") ||
|
if (!command["data"]["ssid"].is<const char*>() ||
|
||||||
!command["data"].containsKey("password"))
|
!command["data"]["password"].is<const char*>())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
std::string customNetworkName = "main";
|
std::string customNetworkName = "main";
|
||||||
if (command["data"].containsKey("network_name"))
|
if (command["data"]["network_name"].is<const char*>())
|
||||||
customNetworkName = command["data"]["network_name"].as<std::string>();
|
customNetworkName = command["data"]["network_name"].as<std::string>();
|
||||||
|
|
||||||
this->deviceConfig->setWifiConfig(customNetworkName,
|
this->deviceConfig->setWifiConfig(customNetworkName,
|
||||||
@ -56,13 +58,17 @@ void CommandManager::handleCommand(JsonVariant command) {
|
|||||||
0, // power, should this be zero?
|
0, // power, should this be zero?
|
||||||
false, false);
|
false, false);
|
||||||
|
|
||||||
|
this->deviceConfig->setHasWiFiCredentials(true, false);
|
||||||
|
this->deviceConfig->setDeviceMode(DeviceMode::WIFI_MODE, true);
|
||||||
|
log_i("[CommandManager] Switching to WiFi mode after receiving credentials");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CommandType::SET_MDNS: {
|
case CommandType::SET_MDNS: {
|
||||||
if (!this->hasDataField(command))
|
if (!this->hasDataField(command))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!command["data"].containsKey("hostname") ||
|
if (!command["data"]["hostname"].is<const char*>() ||
|
||||||
!strlen(command["data"]["hostname"]))
|
!strlen(command["data"]["hostname"]))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -75,6 +81,55 @@ void CommandManager::handleCommand(JsonVariant command) {
|
|||||||
Serial.println("PONG \n\r");
|
Serial.println("PONG \n\r");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CommandType::SWITCH_MODE: {
|
||||||
|
if (!this->hasDataField(command))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!command["data"]["mode"].is<int>())
|
||||||
|
break;
|
||||||
|
|
||||||
|
int modeValue = command["data"]["mode"];
|
||||||
|
DeviceMode newMode = static_cast<DeviceMode>(modeValue);
|
||||||
|
DeviceMode currentMode = this->deviceConfig->getDeviceModeConfig().mode;
|
||||||
|
|
||||||
|
// If switching to USB mode from WiFi or AP mode, disconnect WiFi immediately
|
||||||
|
if (newMode == DeviceMode::USB_MODE &&
|
||||||
|
(currentMode == DeviceMode::WIFI_MODE || currentMode == DeviceMode::AP_MODE)) {
|
||||||
|
log_i("[CommandManager] Immediately switching to USB mode");
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->deviceConfig->setDeviceMode(newMode, true);
|
||||||
|
log_i("[CommandManager] Switching to mode: %d", modeValue);
|
||||||
|
|
||||||
|
// Removed automatic restart to allow explicit control via RESTART_DEVICE command
|
||||||
|
// if (!(newMode == DeviceMode::USB_MODE &&
|
||||||
|
// (currentMode == DeviceMode::WIFI_MODE || currentMode == DeviceMode::AP_MODE) &&
|
||||||
|
// wifiStateManager.getCurrentState() == WiFiState_e::WiFiState_Connecting)) {
|
||||||
|
// OpenIrisTasks::ScheduleRestart(2000);
|
||||||
|
// }
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CommandType::WIPE_WIFI_CREDS: {
|
||||||
|
|
||||||
|
auto networks = this->deviceConfig->getWifiConfigs();
|
||||||
|
for (auto& network : networks) {
|
||||||
|
this->deviceConfig->deleteWifiConfig(network.name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->deviceConfig->setHasWiFiCredentials(false, false);
|
||||||
|
this->deviceConfig->setDeviceMode(DeviceMode::USB_MODE, true);
|
||||||
|
log_i("[CommandManager] Switching to USB mode after wiping credentials");
|
||||||
|
// Removed automatic restart to allow processing of all commands in payload
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CommandType::RESTART_DEVICE: {
|
||||||
|
log_i("[CommandManager] Explicit restart requested");
|
||||||
|
OpenIrisTasks::ScheduleRestart(2000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,9 @@ enum CommandType {
|
|||||||
PING,
|
PING,
|
||||||
SET_WIFI,
|
SET_WIFI,
|
||||||
SET_MDNS,
|
SET_MDNS,
|
||||||
|
SWITCH_MODE,
|
||||||
|
WIPE_WIFI_CREDS,
|
||||||
|
RESTART_DEVICE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CommandsPayload {
|
struct CommandsPayload {
|
||||||
@ -22,6 +25,9 @@ class CommandManager {
|
|||||||
{"ping", CommandType::PING},
|
{"ping", CommandType::PING},
|
||||||
{"set_wifi", CommandType::SET_WIFI},
|
{"set_wifi", CommandType::SET_WIFI},
|
||||||
{"set_mdns", CommandType::SET_MDNS},
|
{"set_mdns", CommandType::SET_MDNS},
|
||||||
|
{"switch_mode", CommandType::SWITCH_MODE},
|
||||||
|
{"wipe_wifi_creds", CommandType::WIPE_WIFI_CREDS},
|
||||||
|
{"restart_device", CommandType::RESTART_DEVICE},
|
||||||
};
|
};
|
||||||
|
|
||||||
ProjectConfig* deviceConfig;
|
ProjectConfig* deviceConfig;
|
||||||
@ -33,6 +39,7 @@ class CommandManager {
|
|||||||
public:
|
public:
|
||||||
CommandManager(ProjectConfig* deviceConfig);
|
CommandManager(ProjectConfig* deviceConfig);
|
||||||
void handleCommands(CommandsPayload commandsPayload);
|
void handleCommands(CommandsPayload commandsPayload);
|
||||||
|
ProjectConfig* getDeviceConfig() { return deviceConfig; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -28,7 +28,8 @@ struct DeviceStates {
|
|||||||
networksConfigUpdated,
|
networksConfigUpdated,
|
||||||
apConfigUpdated,
|
apConfigUpdated,
|
||||||
wifiTxPowerUpdated,
|
wifiTxPowerUpdated,
|
||||||
cameraConfigUpdated
|
cameraConfigUpdated,
|
||||||
|
deviceModeUpdated
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WiFiState_e {
|
enum WiFiState_e {
|
||||||
|
|||||||
@ -58,6 +58,12 @@ void ProjectConfig::initConfig() {
|
|||||||
.quality = 7,
|
.quality = 7,
|
||||||
.brightness = 2,
|
.brightness = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Initialize device mode with default values
|
||||||
|
this->config.deviceMode = {
|
||||||
|
.mode = DeviceMode::AUTO_MODE,
|
||||||
|
.hasWiFiCredentials = false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectConfig::save() {
|
void ProjectConfig::save() {
|
||||||
@ -67,11 +73,13 @@ void ProjectConfig::save() {
|
|||||||
cameraConfigSave();
|
cameraConfigSave();
|
||||||
wifiConfigSave();
|
wifiConfigSave();
|
||||||
wifiTxPowerConfigSave();
|
wifiTxPowerConfigSave();
|
||||||
end(); // we call end() here to close the connection to the NVS partition, we
|
deviceModeConfigSave();
|
||||||
// only do this because we call ESP.restart() next.
|
end(); // we call end() here to close the connection to the NVS partition
|
||||||
OpenIrisTasks::ScheduleRestart(2000);
|
// Removed automatic restart to allow explicit control via RESTART_DEVICE command
|
||||||
|
// OpenIrisTasks::ScheduleRestart(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ProjectConfig::wifiConfigSave() {
|
void ProjectConfig::wifiConfigSave() {
|
||||||
log_d("Saving wifi config");
|
log_d("Saving wifi config");
|
||||||
|
|
||||||
@ -126,6 +134,15 @@ void ProjectConfig::wifiTxPowerConfigSave() {
|
|||||||
putInt("txpower", this->config.txpower.power);
|
putInt("txpower", this->config.txpower.power);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectConfig::deviceModeConfigSave() {
|
||||||
|
/* Device Mode Config */
|
||||||
|
putInt(MODE_KEY, static_cast<int>(this->config.deviceMode.mode));
|
||||||
|
putBool(HAS_WIFI_CREDS_KEY, this->config.deviceMode.hasWiFiCredentials);
|
||||||
|
log_i("[ProjectConfig] Device mode config saved: mode=%d, hasWiFiCredentials=%d",
|
||||||
|
static_cast<int>(this->config.deviceMode.mode),
|
||||||
|
this->config.deviceMode.hasWiFiCredentials);
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectConfig::cameraConfigSave() {
|
void ProjectConfig::cameraConfigSave() {
|
||||||
/* Camera Config */
|
/* Camera Config */
|
||||||
putInt("vflip", this->config.camera.vflip);
|
putInt("vflip", this->config.camera.vflip);
|
||||||
@ -205,6 +222,18 @@ void ProjectConfig::load() {
|
|||||||
this->config.camera.quality = getInt("quality", 7);
|
this->config.camera.quality = getInt("quality", 7);
|
||||||
this->config.camera.brightness = getInt("brightness", 2);
|
this->config.camera.brightness = getInt("brightness", 2);
|
||||||
|
|
||||||
|
int savedMode = getInt(MODE_KEY, static_cast<int>(DeviceMode::AUTO_MODE));
|
||||||
|
this->config.deviceMode.mode = static_cast<DeviceMode>(savedMode);
|
||||||
|
this->config.deviceMode.hasWiFiCredentials = getBool(HAS_WIFI_CREDS_KEY, false);
|
||||||
|
|
||||||
|
if (this->config.deviceMode.mode == DeviceMode::AUTO_MODE) {
|
||||||
|
this->config.deviceMode.mode = determineMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
log_i("[ProjectConfig] Loaded device mode: %d, hasWiFiCredentials: %d",
|
||||||
|
static_cast<int>(this->config.deviceMode.mode),
|
||||||
|
this->config.deviceMode.hasWiFiCredentials);
|
||||||
|
|
||||||
this->_already_loaded = true;
|
this->_already_loaded = true;
|
||||||
this->notifyAll(ConfigState_e::configLoaded);
|
this->notifyAll(ConfigState_e::configLoaded);
|
||||||
}
|
}
|
||||||
@ -419,6 +448,14 @@ std::string ProjectConfig::WiFiTxPower_t::toRepresentation() {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ProjectConfig::DeviceModeConfig_t::toRepresentation() {
|
||||||
|
std::string json = Helpers::format_string(
|
||||||
|
"\"device_mode\": {\"mode\": %d, \"hasWiFiCredentials\": %s}",
|
||||||
|
static_cast<int>(this->mode),
|
||||||
|
this->hasWiFiCredentials ? "true" : "false");
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
//**********************************************************************************************************************
|
//**********************************************************************************************************************
|
||||||
//*
|
//*
|
||||||
//! Get Methods
|
//! Get Methods
|
||||||
@ -443,3 +480,32 @@ ProjectConfig::MDNSConfig_t& ProjectConfig::getMDNSConfig() {
|
|||||||
ProjectConfig::WiFiTxPower_t& ProjectConfig::getWiFiTxPowerConfig() {
|
ProjectConfig::WiFiTxPower_t& ProjectConfig::getWiFiTxPowerConfig() {
|
||||||
return this->config.txpower;
|
return this->config.txpower;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProjectConfig::DeviceModeConfig_t& ProjectConfig::getDeviceModeConfig() {
|
||||||
|
return this->config.deviceMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectConfig::setDeviceMode(DeviceMode mode, bool shouldNotify) {
|
||||||
|
this->config.deviceMode.mode = mode;
|
||||||
|
putInt(MODE_KEY, static_cast<int>(mode));
|
||||||
|
log_i("[ProjectConfig] Mode set to: %d", static_cast<int>(mode));
|
||||||
|
|
||||||
|
if (shouldNotify) {
|
||||||
|
this->notifyAll(ConfigState_e::deviceModeUpdated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectConfig::setHasWiFiCredentials(bool hasCredentials, bool shouldNotify) {
|
||||||
|
this->config.deviceMode.hasWiFiCredentials = hasCredentials;
|
||||||
|
putBool(HAS_WIFI_CREDS_KEY, hasCredentials);
|
||||||
|
log_i("[ProjectConfig] WiFi credentials status set to: %d", hasCredentials);
|
||||||
|
|
||||||
|
if (shouldNotify) {
|
||||||
|
this->notifyAll(ConfigState_e::deviceModeUpdated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceMode ProjectConfig::determineMode() {
|
||||||
|
// If WiFi credentials are saved, use WiFi mode, otherwise use AP mode
|
||||||
|
return this->config.deviceMode.hasWiFiCredentials ? DeviceMode::WIFI_MODE : DeviceMode::AP_MODE;
|
||||||
|
}
|
||||||
|
|||||||
@ -13,6 +13,14 @@
|
|||||||
#include "data/utilities/network_utilities.hpp"
|
#include "data/utilities/network_utilities.hpp"
|
||||||
#include "tasks/tasks.hpp"
|
#include "tasks/tasks.hpp"
|
||||||
|
|
||||||
|
// Enum to represent the device operating mode
|
||||||
|
enum class DeviceMode {
|
||||||
|
USB_MODE, // Device operates in USB mode only
|
||||||
|
WIFI_MODE, // Device operates in WiFi mode only
|
||||||
|
AP_MODE, // Device operates in AP mode with serial commands enabled
|
||||||
|
AUTO_MODE // Device automatically selects mode based on saved credentials
|
||||||
|
};
|
||||||
|
|
||||||
class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
||||||
public:
|
public:
|
||||||
ProjectConfig(const std::string& name = std::string(),
|
ProjectConfig(const std::string& name = std::string(),
|
||||||
@ -88,6 +96,12 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
|||||||
std::string toRepresentation();
|
std::string toRepresentation();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DeviceModeConfig_t {
|
||||||
|
DeviceMode mode;
|
||||||
|
bool hasWiFiCredentials;
|
||||||
|
std::string toRepresentation();
|
||||||
|
};
|
||||||
|
|
||||||
struct TrackerConfig_t {
|
struct TrackerConfig_t {
|
||||||
DeviceConfig_t device;
|
DeviceConfig_t device;
|
||||||
CameraConfig_t camera;
|
CameraConfig_t camera;
|
||||||
@ -95,6 +109,7 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
|||||||
AP_WiFiConfig_t ap_network;
|
AP_WiFiConfig_t ap_network;
|
||||||
MDNSConfig_t mdns;
|
MDNSConfig_t mdns;
|
||||||
WiFiTxPower_t txpower;
|
WiFiTxPower_t txpower;
|
||||||
|
DeviceModeConfig_t deviceMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceConfig_t& getDeviceConfig();
|
DeviceConfig_t& getDeviceConfig();
|
||||||
@ -103,6 +118,7 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
|||||||
AP_WiFiConfig_t& getAPWifiConfig();
|
AP_WiFiConfig_t& getAPWifiConfig();
|
||||||
MDNSConfig_t& getMDNSConfig();
|
MDNSConfig_t& getMDNSConfig();
|
||||||
WiFiTxPower_t& getWiFiTxPowerConfig();
|
WiFiTxPower_t& getWiFiTxPowerConfig();
|
||||||
|
DeviceModeConfig_t& getDeviceModeConfig();
|
||||||
|
|
||||||
void setDeviceConfig(const std::string& OTALogin,
|
void setDeviceConfig(const std::string& OTALogin,
|
||||||
const std::string& OTAPassword,
|
const std::string& OTAPassword,
|
||||||
@ -133,11 +149,23 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
|||||||
|
|
||||||
void deleteWifiConfig(const std::string& networkName, bool shouldNotify);
|
void deleteWifiConfig(const std::string& networkName, bool shouldNotify);
|
||||||
|
|
||||||
|
void setDeviceMode(DeviceMode mode, bool shouldNotify);
|
||||||
|
|
||||||
|
void setHasWiFiCredentials(bool hasCredentials, bool shouldNotify);
|
||||||
|
|
||||||
|
DeviceMode determineMode();
|
||||||
|
|
||||||
|
void deviceModeConfigSave();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TrackerConfig_t config;
|
TrackerConfig_t config;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
std::string _mdnsName;
|
std::string _mdnsName;
|
||||||
bool _already_loaded;
|
bool _already_loaded;
|
||||||
|
|
||||||
|
// Device mode related constants
|
||||||
|
const char* MODE_KEY = "mode";
|
||||||
|
const char* HAS_WIFI_CREDS_KEY = "has_wifi_creds";
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PROJECT_CONFIG_HPP
|
#endif // PROJECT_CONFIG_HPP
|
||||||
|
|||||||
@ -3,7 +3,30 @@
|
|||||||
SerialManager::SerialManager(CommandManager* commandManager)
|
SerialManager::SerialManager(CommandManager* commandManager)
|
||||||
: commandManager(commandManager) {}
|
: commandManager(commandManager) {}
|
||||||
|
|
||||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
void SerialManager::sendQuery(QueryAction action,
|
||||||
|
QueryStatus status,
|
||||||
|
std::string additional_info) {
|
||||||
|
JsonDocument doc;
|
||||||
|
doc["action"] = queryActionMap.at(action);
|
||||||
|
doc["status"] = static_cast<int>(status);
|
||||||
|
if (!additional_info.empty()) {
|
||||||
|
doc["info"] = additional_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
String output;
|
||||||
|
serializeJson(doc, output);
|
||||||
|
Serial.println(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialManager::checkUSBMode() {
|
||||||
|
// Get device mode from ProjectConfig via CommandManager
|
||||||
|
DeviceMode currentMode = this->commandManager->getDeviceConfig()->getDeviceModeConfig().mode;
|
||||||
|
if (currentMode == DeviceMode::USB_MODE) {
|
||||||
|
log_i("[SerialManager] USB mode active - auto-streaming enabled");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SerialManager::send_frame() {
|
void SerialManager::send_frame() {
|
||||||
if (!last_frame)
|
if (!last_frame)
|
||||||
last_frame = esp_timer_get_time();
|
last_frame = esp_timer_get_time();
|
||||||
@ -49,7 +72,6 @@ void SerialManager::send_frame() {
|
|||||||
log_d("Size: %uKB, Time: %ums (%ifps)\n", len / 1024, latency,
|
log_d("Size: %uKB, Time: %ums (%ifps)\n", len / 1024, latency,
|
||||||
1000 / latency);
|
1000 / latency);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void SerialManager::init() {
|
void SerialManager::init() {
|
||||||
#ifdef SERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
#ifdef SERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
||||||
@ -58,25 +80,27 @@ void SerialManager::init() {
|
|||||||
if (SERIAL_FLUSH_ENABLED) {
|
if (SERIAL_FLUSH_ENABLED) {
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we're in USB mode and set up accordingly
|
||||||
|
checkUSBMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialManager::run() {
|
void SerialManager::run() {
|
||||||
|
// Process any available commands first to ensure mode changes are detected immediately
|
||||||
if (Serial.available()) {
|
if (Serial.available()) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError deserializationError = deserializeJson(doc, Serial);
|
DeserializationError deserializationError = deserializeJson(doc, Serial);
|
||||||
|
|
||||||
if (deserializationError) {
|
if (deserializationError) {
|
||||||
log_e("Command deserialization failed: %s", deserializationError.c_str());
|
log_e("Command deserialization failed: %s", deserializationError.c_str());
|
||||||
|
} else {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandsPayload commands = {doc};
|
CommandsPayload commands = {doc};
|
||||||
this->commandManager->handleCommands(commands);
|
this->commandManager->handleCommands(commands);
|
||||||
}
|
}
|
||||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
}
|
||||||
else {
|
|
||||||
|
DeviceMode currentMode = this->commandManager->getDeviceConfig()->getDeviceModeConfig().mode;
|
||||||
|
if (currentMode == DeviceMode::USB_MODE) {
|
||||||
this->send_frame();
|
this->send_frame();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,12 +35,10 @@ class SerialManager {
|
|||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
CommandManager* commandManager;
|
CommandManager* commandManager;
|
||||||
|
|
||||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
|
||||||
int64_t last_frame = 0;
|
int64_t last_frame = 0;
|
||||||
long last_request_time = 0;
|
long last_request_time = 0;
|
||||||
|
|
||||||
void send_frame();
|
void send_frame();
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SerialManager(CommandManager* commandManager);
|
SerialManager(CommandManager* commandManager);
|
||||||
@ -49,6 +47,7 @@ class SerialManager {
|
|||||||
std::string additional_info);
|
std::string additional_info);
|
||||||
void init();
|
void init();
|
||||||
void run();
|
void run();
|
||||||
|
void checkUSBMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -36,6 +36,8 @@ void BaseAPI::begin() {
|
|||||||
|
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
// The restart_device endpoint has been removed in favor of using rebootDevice through POST
|
||||||
|
|
||||||
// std::bind(&BaseAPI::notFound, &std::placeholders::_1);
|
// std::bind(&BaseAPI::notFound, &std::placeholders::_1);
|
||||||
server.onNotFound([&](AsyncWebServerRequest* request) { notFound(request); });
|
server.onNotFound([&](AsyncWebServerRequest* request) { notFound(request); });
|
||||||
}
|
}
|
||||||
@ -214,7 +216,15 @@ void BaseAPI::rebootDevice(AsyncWebServerRequest* request) {
|
|||||||
switch (_networkMethodsMap_enum[request->method()]) {
|
switch (_networkMethodsMap_enum[request->method()]) {
|
||||||
case GET: {
|
case GET: {
|
||||||
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Rebooting Device\"}");
|
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Rebooting Device\"}");
|
||||||
|
|
||||||
OpenIrisTasks::ScheduleRestart(2000);
|
OpenIrisTasks::ScheduleRestart(2000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case POST: {
|
||||||
|
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Rebooting Device\"}");
|
||||||
|
|
||||||
|
OpenIrisTasks::ScheduleRestart(2000);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
|
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
|
||||||
@ -381,7 +391,7 @@ void BaseAPI::beginOTA() {
|
|||||||
esp_camera_deinit(); // deinitialize the camera driver
|
esp_camera_deinit(); // deinitialize the camera driver
|
||||||
digitalWrite(PWDN_GPIO_NUM, HIGH); // turn power off to camera module
|
digitalWrite(PWDN_GPIO_NUM, HIGH); // turn power off to camera module
|
||||||
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse_P(
|
AsyncWebServerResponse* response = request->beginResponse(
|
||||||
200, "text/html", ELEGANT_HTML, ELEGANT_HTML_SIZE);
|
200, "text/html", ELEGANT_HTML, ELEGANT_HTML_SIZE);
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
|
|||||||
@ -169,6 +169,19 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
|
|||||||
currentMillis = millis();
|
currentMillis = millis();
|
||||||
log_i(".");
|
log_i(".");
|
||||||
log_d("Progress: %d \n\r", progress);
|
log_d("Progress: %d \n\r", progress);
|
||||||
|
|
||||||
|
// Check if mode has been changed to USB mode during connection attempt
|
||||||
|
if (configManager.getDeviceModeConfig().mode == DeviceMode::USB_MODE) {
|
||||||
|
log_i("[WiFiHandler] Mode changed to USB during connection, aborting WiFi setup");
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Serial.available()) {
|
||||||
|
yield(); // Allow other processes to run
|
||||||
|
}
|
||||||
|
|
||||||
if ((currentMillis - startingMillis) >= connectionTimeout) {
|
if ((currentMillis - startingMillis) >= connectionTimeout) {
|
||||||
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
|
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
|
||||||
log_e("Connection to: %s TIMEOUT \n\r", ssid.c_str());
|
log_e("Connection to: %s TIMEOUT \n\r", ssid.c_str());
|
||||||
|
|||||||
@ -38,10 +38,33 @@ StreamServer streamServer;
|
|||||||
#endif // SIM_ENABLED
|
#endif // SIM_ENABLED
|
||||||
|
|
||||||
void etvr_eye_tracker_web_init() {
|
void etvr_eye_tracker_web_init() {
|
||||||
|
// Check if mode has been changed to USB mode before starting network initialization
|
||||||
|
if (deviceConfig.getDeviceModeConfig().mode == DeviceMode::USB_MODE) {
|
||||||
|
log_i("[SETUP]: Mode changed to USB before network initialization, aborting");
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log_d("[SETUP]: Starting Network Handler");
|
log_d("[SETUP]: Starting Network Handler");
|
||||||
deviceConfig.attach(mdnsHandler);
|
deviceConfig.attach(mdnsHandler);
|
||||||
|
|
||||||
|
// Check mode again before starting WiFi
|
||||||
|
if (deviceConfig.getDeviceModeConfig().mode == DeviceMode::USB_MODE) {
|
||||||
|
log_i("[SETUP]: Mode changed to USB before WiFi initialization, aborting");
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log_d("[SETUP]: Starting WiFi Handler");
|
log_d("[SETUP]: Starting WiFi Handler");
|
||||||
wifiHandler.begin();
|
wifiHandler.begin();
|
||||||
|
|
||||||
|
// Check mode again before starting MDNS
|
||||||
|
if (deviceConfig.getDeviceModeConfig().mode == DeviceMode::USB_MODE) {
|
||||||
|
log_i("[SETUP]: Mode changed to USB before MDNS initialization, aborting");
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log_d("[SETUP]: Starting MDNS Handler");
|
log_d("[SETUP]: Starting MDNS Handler");
|
||||||
mdnsHandler.startMDNS();
|
mdnsHandler.startMDNS();
|
||||||
|
|
||||||
@ -86,6 +109,8 @@ void setup() {
|
|||||||
Logo::printASCII();
|
Logo::printASCII();
|
||||||
ledManager.begin();
|
ledManager.begin();
|
||||||
|
|
||||||
|
// Device mode is now managed by ProjectConfig
|
||||||
|
|
||||||
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3 // Set IR emitter strength to 100%.
|
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3 // Set IR emitter strength to 100%.
|
||||||
const int ledPin = 1; // Replace this with a command endpoint eventually.
|
const int ledPin = 1; // Replace this with a command endpoint eventually.
|
||||||
const int freq = 5000;
|
const int freq = 5000;
|
||||||
@ -104,11 +129,20 @@ void setup() {
|
|||||||
|
|
||||||
serialManager.init();
|
serialManager.init();
|
||||||
|
|
||||||
#ifndef ETVR_EYE_TRACKER_USB_API
|
DeviceMode currentMode = deviceConfig.getDeviceModeConfig().mode;
|
||||||
|
|
||||||
|
if (currentMode == DeviceMode::WIFI_MODE) {
|
||||||
|
// Initialize WiFi mode
|
||||||
etvr_eye_tracker_web_init();
|
etvr_eye_tracker_web_init();
|
||||||
#else // ETVR_EYE_TRACKER_WEB_API
|
log_i("[SETUP]: Initialized in WiFi mode");
|
||||||
|
} else if (currentMode == DeviceMode::AP_MODE) {
|
||||||
|
// Initialize AP mode with serial commands enabled
|
||||||
|
etvr_eye_tracker_web_init();
|
||||||
|
log_i("[SETUP]: Initialized in AP mode with serial commands enabled");
|
||||||
|
} else {
|
||||||
WiFi.disconnect(true);
|
WiFi.disconnect(true);
|
||||||
#endif // ETVR_EYE_TRACKER_WEB_API
|
log_i("[SETUP]: Initialized in USB mode");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|||||||
368
PythonExamples/mode_switch_test.py
Normal file
368
PythonExamples/mode_switch_test.py
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import serial
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import argparse
|
||||||
|
import socket
|
||||||
|
|
||||||
|
class OpenIrisModeTester:
|
||||||
|
def __init__(self, port, baudrate=115200, timeout=5):
|
||||||
|
self.port = port
|
||||||
|
self.baudrate = baudrate
|
||||||
|
self.timeout = timeout
|
||||||
|
self.serial_conn = None
|
||||||
|
self.device_ip = None
|
||||||
|
self.device_port = None
|
||||||
|
self.last_boot_time = None
|
||||||
|
|
||||||
|
def connect_serial(self):
|
||||||
|
try:
|
||||||
|
self.serial_conn = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
|
||||||
|
self.flush_serial_logs()
|
||||||
|
print(f"Connected to {self.port} at {self.baudrate} baud")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error connecting to serial port: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def flush_serial_logs(self):
|
||||||
|
if self.serial_conn and self.serial_conn.is_open:
|
||||||
|
self.serial_conn.reset_input_buffer()
|
||||||
|
self.serial_conn.reset_output_buffer()
|
||||||
|
print("Serial logs flushed.")
|
||||||
|
|
||||||
|
def send_command(self, command_obj):
|
||||||
|
if not self.serial_conn:
|
||||||
|
print("Serial connection not established")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
command_json = json.dumps(command_obj)
|
||||||
|
self.serial_conn.write(command_json.encode() + b'\n')
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
response = ""
|
||||||
|
start_time = time.time()
|
||||||
|
while (time.time() - start_time) < self.timeout:
|
||||||
|
if self.serial_conn.in_waiting:
|
||||||
|
line = self.serial_conn.readline().decode('utf-8', errors='replace').strip()
|
||||||
|
if line:
|
||||||
|
response += line
|
||||||
|
try:
|
||||||
|
json.loads(response)
|
||||||
|
return response
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
return response if response else None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending command: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_wifi_credentials(self, ssid, password):
|
||||||
|
command = {"commands": [{"command": "set_wifi", "data": {"ssid": ssid, "password": password, "network_name": "main"}}]}
|
||||||
|
print("Sending WiFi credentials...")
|
||||||
|
response = self.send_command(command)
|
||||||
|
print(f"Response: {response}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
def wipe_wifi_credentials(self):
|
||||||
|
command = {"commands": [{"command": "wipe_wifi_creds"}]}
|
||||||
|
print("Wiping WiFi credentials...")
|
||||||
|
response = self.send_command(command)
|
||||||
|
print(f"Response: {response}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
def discover_device(self, hostname="openiristracker.local"):
|
||||||
|
try:
|
||||||
|
self.device_ip = socket.gethostbyname(hostname)
|
||||||
|
self.device_port = 80
|
||||||
|
print(f"Device found at {self.device_ip}:{self.device_port}")
|
||||||
|
return True
|
||||||
|
except socket.error as e:
|
||||||
|
print(f"Hostname resolution failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_wifi_api(self):
|
||||||
|
if not self.device_ip:
|
||||||
|
print("Device IP not available. Discover device first.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = f"http://{self.device_ip}:81/control/builtin/command/ping"
|
||||||
|
response = requests.get(url, timeout=5)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("WiFi API test successful!")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"WiFi API test failed with status code: {response.status_code}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error testing WiFi API: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def restart_device_serial(self):
|
||||||
|
"""Send reboot command via serial connection"""
|
||||||
|
command = {"commands": [{"command": "restart_device"}]}
|
||||||
|
print("Sending reboot command via serial...")
|
||||||
|
response = self.send_command(command)
|
||||||
|
print(f"Response: {response}")
|
||||||
|
self.last_boot_time = time.time()
|
||||||
|
|
||||||
|
# Close the serial connection after sending reboot command
|
||||||
|
# This helps prevent permission issues when reconnecting
|
||||||
|
if self.serial_conn and self.serial_conn.is_open:
|
||||||
|
try:
|
||||||
|
self.serial_conn.close()
|
||||||
|
print("Serial connection closed after reboot command")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error closing serial connection: {e}")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def restart_device_wifi(self):
|
||||||
|
"""Send reboot command via WiFi API"""
|
||||||
|
if not self.device_ip:
|
||||||
|
print("Device IP not available. Cannot restart via WiFi.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# First verify the device is responsive via WiFi
|
||||||
|
ping_url = f"http://{self.device_ip}:81/control/builtin/command/ping"
|
||||||
|
print(f"Testing device responsiveness with: {ping_url}")
|
||||||
|
ping_response = requests.get(ping_url, timeout=5)
|
||||||
|
if ping_response.status_code != 200:
|
||||||
|
print(f"Device not responsive via WiFi before restart attempt: {ping_response.status_code}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Use rebootDevice endpoint with POST method
|
||||||
|
url = f"http://{self.device_ip}:81/update/rebootDevice"
|
||||||
|
print(f"Sending reboot command to: {url}")
|
||||||
|
|
||||||
|
# Use POST method as recommended
|
||||||
|
try:
|
||||||
|
response = requests.post(url, timeout=5)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("Reboot command via WiFi (POST) sent successfully!")
|
||||||
|
self.last_boot_time = time.time()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Reboot command via WiFi (POST) failed with status code: {response.status_code}")
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print("Connection error during POST: Device may have already started restarting")
|
||||||
|
self.last_boot_time = time.time()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending POST reboot command: {e}")
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print("Connection error: Device may have already started restarting")
|
||||||
|
self.last_boot_time = time.time()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending reboot command via WiFi: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def verify_restart(self, wait_time=60, retry_count=3):
|
||||||
|
"""Verify that the device has rebooted by checking uptime"""
|
||||||
|
if not self.last_boot_time:
|
||||||
|
print("No reboot command was previously sent")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Calculate elapsed time since reboot command
|
||||||
|
elapsed = time.time() - self.last_boot_time
|
||||||
|
if elapsed < wait_time:
|
||||||
|
# Wait remaining time if needed
|
||||||
|
remaining = wait_time - elapsed
|
||||||
|
print(f"Waiting {remaining:.1f} more seconds for device to reboot...")
|
||||||
|
time.sleep(remaining)
|
||||||
|
else:
|
||||||
|
print(f"Already waited {elapsed:.1f} seconds since reboot command")
|
||||||
|
|
||||||
|
# Close the serial connection before attempting to reconnect
|
||||||
|
if self.serial_conn and self.serial_conn.is_open:
|
||||||
|
try:
|
||||||
|
self.serial_conn.close()
|
||||||
|
time.sleep(1) # Give the OS time to release the port
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error closing serial connection: {e}")
|
||||||
|
|
||||||
|
# Try to reconnect serial with retries
|
||||||
|
for attempt in range(retry_count):
|
||||||
|
try:
|
||||||
|
if self.connect_serial():
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during reconnection attempt {attempt+1}: {e}")
|
||||||
|
|
||||||
|
if attempt < retry_count - 1:
|
||||||
|
print(f"Reconnection attempt {attempt+1} failed, retrying in 2 seconds...")
|
||||||
|
time.sleep(2)
|
||||||
|
else:
|
||||||
|
print(f"Failed to reconnect to device after {retry_count} attempts")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Send ping command to check if device is responsive
|
||||||
|
for attempt in range(retry_count):
|
||||||
|
ping_command = {"commands": [{"command": "ping"}]}
|
||||||
|
response = self.send_command(ping_command)
|
||||||
|
|
||||||
|
if response and "pong" in response.lower():
|
||||||
|
print(f"Device responded after restart (attempt {attempt+1})")
|
||||||
|
return True
|
||||||
|
elif attempt < retry_count - 1:
|
||||||
|
print(f"Ping attempt {attempt+1} failed, retrying in 2 seconds...")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
print(f"Device did not respond properly after {retry_count} ping attempts")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.serial_conn and self.serial_conn.is_open:
|
||||||
|
self.serial_conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def run_test(port, ssid, password, wait_time=15, retry_count=3, skip_wifi_test=False):
|
||||||
|
tester = OpenIrisModeTester(port)
|
||||||
|
test_results = {
|
||||||
|
"serial_connection": False,
|
||||||
|
"wifi_credentials": False,
|
||||||
|
"device_discovery": False,
|
||||||
|
"wifi_api": False,
|
||||||
|
"serial_restart": False,
|
||||||
|
"wifi_restart": False,
|
||||||
|
"usb_mode": False
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("\n=== Step 1: Testing Serial Connection ===")
|
||||||
|
if not tester.connect_serial():
|
||||||
|
print("❌ Serial connection failed. Aborting test.")
|
||||||
|
return test_results
|
||||||
|
test_results["serial_connection"] = True
|
||||||
|
print("✅ Serial connection successful")
|
||||||
|
|
||||||
|
print("\n=== Step 2: Setting WiFi credentials ===")
|
||||||
|
response = tester.set_wifi_credentials(ssid, password)
|
||||||
|
if response and "error" not in response.lower():
|
||||||
|
test_results["wifi_credentials"] = True
|
||||||
|
print("✅ WiFi credentials set successfully")
|
||||||
|
|
||||||
|
# Explicitly send reboot command after setting WiFi credentials
|
||||||
|
print("Sending reboot command after setting WiFi credentials...")
|
||||||
|
tester.restart_device_serial()
|
||||||
|
|
||||||
|
# Wait specifically 30 seconds for the device to restart
|
||||||
|
print("Waiting 30 seconds for device to restart...")
|
||||||
|
time.sleep(30)
|
||||||
|
else:
|
||||||
|
print("❌ Failed to set WiFi credentials")
|
||||||
|
|
||||||
|
# Verify device reboots after setting WiFi credentials
|
||||||
|
if not tester.verify_restart(wait_time=wait_time, retry_count=retry_count):
|
||||||
|
print("❌ Device did not reboot properly after setting WiFi credentials")
|
||||||
|
else:
|
||||||
|
print("✅ Device rebooted successfully after setting WiFi credentials")
|
||||||
|
|
||||||
|
print("\n=== Step 3: Discovering device on network ===")
|
||||||
|
if tester.discover_device():
|
||||||
|
test_results["device_discovery"] = True
|
||||||
|
print("✅ Device discovered on network")
|
||||||
|
|
||||||
|
print("\n=== Step 4: Testing WiFi API ===")
|
||||||
|
if tester.test_wifi_api():
|
||||||
|
test_results["wifi_api"] = True
|
||||||
|
print("✅ WiFi API test successful")
|
||||||
|
|
||||||
|
print("\n=== Step 5: Testing reboot via WiFi ===")
|
||||||
|
if tester.restart_device_wifi():
|
||||||
|
# Wait for device to reboot
|
||||||
|
if tester.verify_restart(wait_time=wait_time, retry_count=retry_count):
|
||||||
|
test_results["wifi_restart"] = True
|
||||||
|
print("✅ Device rebooted successfully via WiFi")
|
||||||
|
else:
|
||||||
|
print("❌ Device did not reboot properly via WiFi")
|
||||||
|
else:
|
||||||
|
print("❌ Failed to send reboot command via WiFi")
|
||||||
|
else:
|
||||||
|
print("❌ WiFi API test failed")
|
||||||
|
else:
|
||||||
|
print("❌ Failed to discover device on network")
|
||||||
|
|
||||||
|
# Reconnect serial if needed
|
||||||
|
if not tester.serial_conn or not tester.serial_conn.is_open:
|
||||||
|
tester.connect_serial()
|
||||||
|
tester.flush_serial_logs()
|
||||||
|
|
||||||
|
print("\n=== Step 6: Testing reboot via Serial ===")
|
||||||
|
tester.restart_device_serial()
|
||||||
|
if tester.verify_restart(wait_time=wait_time, retry_count=retry_count):
|
||||||
|
test_results["serial_restart"] = True
|
||||||
|
print("✅ Device rebooted successfully via Serial")
|
||||||
|
else:
|
||||||
|
print("❌ Device did not reboot properly via Serial")
|
||||||
|
|
||||||
|
print("\n=== Step 7: Wiping WiFi credentials ===")
|
||||||
|
tester.wipe_wifi_credentials()
|
||||||
|
if not tester.verify_restart(wait_time=wait_time, retry_count=retry_count):
|
||||||
|
print("❌ Device did not reboot properly after wiping WiFi credentials")
|
||||||
|
else:
|
||||||
|
print("✅ Device rebooted successfully after wiping WiFi credentials")
|
||||||
|
|
||||||
|
print("\n=== Step 8: Verifying USB mode ===")
|
||||||
|
ping_command = {"commands": [{"command": "ping"}]}
|
||||||
|
response = tester.send_command(ping_command)
|
||||||
|
if response and "pong" in response.lower():
|
||||||
|
test_results["usb_mode"] = True
|
||||||
|
print("✅ Device is in USB mode")
|
||||||
|
else:
|
||||||
|
print("❌ Failed to verify USB mode")
|
||||||
|
|
||||||
|
tester.flush_serial_logs()
|
||||||
|
|
||||||
|
# Print summary
|
||||||
|
print("\n=== Test Summary ===")
|
||||||
|
for test, result in test_results.items():
|
||||||
|
status = "✅ PASS" if result else "❌ FAIL"
|
||||||
|
print(f"{test.replace('_', ' ').title()}: {status}")
|
||||||
|
|
||||||
|
return test_results
|
||||||
|
|
||||||
|
finally:
|
||||||
|
tester.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Test OpenIris mode switching and reboot functionality")
|
||||||
|
parser.add_argument("--port", required=True, help="Serial port (e.g., COM3 or /dev/ttyUSB0)")
|
||||||
|
parser.add_argument("--ssid", required=True, help="WiFi SSID")
|
||||||
|
parser.add_argument("--password", required=True, help="WiFi password")
|
||||||
|
parser.add_argument("--wait-time", type=int, default=60, help="Wait time in seconds for device reboot (default: 15)")
|
||||||
|
parser.add_argument("--retry-count", type=int, default=3, help="Number of retry attempts for reconnection (default: 3)")
|
||||||
|
parser.add_argument("--skip-wifi-test", action="store_true", help="Skip WiFi-related tests")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Configure test parameters
|
||||||
|
test_config = {
|
||||||
|
"port": args.port,
|
||||||
|
"ssid": args.ssid,
|
||||||
|
"password": args.password,
|
||||||
|
"wait_time": args.wait_time,
|
||||||
|
"retry_count": args.retry_count,
|
||||||
|
"skip_wifi_test": args.skip_wifi_test
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run the test and get results
|
||||||
|
results = run_test(**test_config)
|
||||||
|
|
||||||
|
# Exit with appropriate status code
|
||||||
|
if all(results.values()):
|
||||||
|
print("\n✅ All tests passed successfully!")
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
print("\n❌ Some tests failed. See details above.")
|
||||||
|
exit(1)
|
||||||
Loading…
Reference in New Issue
Block a user