mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-09-26 23:29:14 +08:00
Compare commits
13 Commits
0511f149b0
...
5e73704e8c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5e73704e8c | ||
![]() |
ebecc61292 | ||
![]() |
f460867497 | ||
![]() |
25f99ce449 | ||
![]() |
c7aef8457e | ||
![]() |
18cf39f291 | ||
![]() |
a085b6d885 | ||
![]() |
04af0160c4 | ||
![]() |
6241cf9add | ||
![]() |
7bf33d8c45 | ||
![]() |
48e1e8de17 | ||
![]() |
27177e411c | ||
![]() |
4705e41476 |
12
.github/workflows/build_release_bins.yml
vendored
12
.github/workflows/build_release_bins.yml
vendored
@ -40,7 +40,7 @@ jobs:
|
||||
matrix: ${{fromJson(needs.setup.outputs.matrix)}}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build OpenIris Firmware
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
run: |
|
||||
echo "FIRMWARE_NAME=$(cat ESP/build/${{ matrix.target_name }}${{ matrix.target_build_type }}/firmware_name.txt)" >> $GITHUB_ENV
|
||||
- name: Archive Firmware Binaries
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target_name }}${{ matrix.target_build_type }}-firmware
|
||||
#path: "./ESP/.pio/build/${{ matrix.target_name }}${{ matrix.target_build_type }}/${{ env.FIRMWARE_NAME }}.bin"
|
||||
@ -80,17 +80,17 @@ jobs:
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Create Directory
|
||||
run: mkdir -p build
|
||||
- name: Download Firmware Files
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build/
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install -g conventional-changelog-conventionalcommits@6
|
||||
@ -117,6 +117,6 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: "♻️ remove build artifacts"
|
||||
uses: geekyeggo/delete-artifact@v1
|
||||
uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: ${{ matrix.target_name }}${{ matrix.target_build_type }}-firmware
|
||||
|
37
CHANGELOG.md
37
CHANGELOG.md
@ -3,6 +3,43 @@
|
||||
[](https://semver.org)
|
||||
> All notable changes to this project will be documented in this file
|
||||
|
||||
## [2.8.0](https://github.com/EyeTrackVR/OpenIris/compare/v2.7.0...v2.8.0) (2025-01-15)
|
||||
|
||||
|
||||
### 🍕 Features
|
||||
|
||||
* Fix typo in SerialManager ([#84](https://github.com/EyeTrackVR/OpenIris/issues/84)) ([f460867](https://github.com/EyeTrackVR/OpenIris/commit/f4608674978edf5728e495824ed615d9d45f85fd))
|
||||
|
||||
## [2.7.0](https://github.com/EyeTrackVR/OpenIris/compare/v2.6.0...v2.7.0) (2025-01-12)
|
||||
|
||||
|
||||
### 🍕 Features
|
||||
|
||||
* Release 2.7.0 ([#83](https://github.com/EyeTrackVR/OpenIris/issues/83)) ([c7aef84](https://github.com/EyeTrackVR/OpenIris/commit/c7aef8457eb68fef6713daaf81c6eb23491d9ec7))
|
||||
|
||||
## [2.7.0-beta.2](https://github.com/EyeTrackVR/OpenIris/compare/v2.7.0-beta.1...v2.7.0-beta.2) (2025-01-12)
|
||||
|
||||
|
||||
### 🍕 Features
|
||||
|
||||
* Add camera resolution configuration support ([#79](https://github.com/EyeTrackVR/OpenIris/issues/79)) ([5c76e01](https://github.com/EyeTrackVR/OpenIris/commit/5c76e01c5e466c8cc7ef4cd43e78a763af00c06e))
|
||||
|
||||
## [2.7.0-beta.1](https://github.com/EyeTrackVR/OpenIris/compare/v2.6.0...v2.7.0-beta.1) (2025-01-11)
|
||||
|
||||
|
||||
### 🍕 Features
|
||||
|
||||
* Add support for ov5640 cameras ([14d2cb0](https://github.com/EyeTrackVR/OpenIris/commit/14d2cb0858cd65452f2ce1cee9a3a1f68d8f7f51))
|
||||
* Fix ov5640 support implementation after beta cleanup ([4894c22](https://github.com/EyeTrackVR/OpenIris/commit/4894c2205864d5a7d52e37c81b14ff7dc46517d4))
|
||||
* fix usb streaming on wroom boards, add support for babble custom board ([049b160](https://github.com/EyeTrackVR/OpenIris/commit/049b16078ec5c07042652f2de3545ea6b69de6a9))
|
||||
* Move logging in serial streaming so that we at least attempt to get new frame ([53a3d4b](https://github.com/EyeTrackVR/OpenIris/commit/53a3d4bcd603e2fd07546621628e69de88e4139a))
|
||||
* Refactor commands and serial manager to support updated commands payload ([811a9f8](https://github.com/EyeTrackVR/OpenIris/commit/811a9f898a3f1bdb1e20ffd6f0d6e77a4533f52c))
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* serial output for esp32cams and other boards not supporting higher frequency baud rate ([01e5e28](https://github.com/EyeTrackVR/OpenIris/commit/01e5e280ad26d2709231fe6aa2a96d1991cbf360))
|
||||
|
||||
## [2.6.0](https://github.com/EyeTrackVR/OpenIris/compare/v2.5.0...v2.6.0) (2024-07-14)
|
||||
|
||||
|
||||
|
@ -190,7 +190,10 @@ upload_flags = --no-stub
|
||||
|
||||
[env:Babble-wrooms-s3]
|
||||
board = esp32-s3-devkitc-1
|
||||
board_build.flash_mode = qio ; qio_qspi
|
||||
board_upload.flashsize = "4MB"
|
||||
board_upload.flash_size=4MB
|
||||
board_upload.maximum_size = 4194304
|
||||
board_build.flash_mode = qio
|
||||
board_build.arduino.memory_type = qio_qspi
|
||||
build_type = debug
|
||||
build_flags = ${env.build_flags}
|
||||
@ -200,9 +203,27 @@ build_flags = ${env.build_flags}
|
||||
${pinoutSWROOMBABBLES3.build_flags}
|
||||
upload_flags = --no-stub
|
||||
|
||||
[env:Babble-wrooms-s3_release]
|
||||
board = esp32-s3-devkitc-1
|
||||
board_upload.flashsize = "4MB"
|
||||
board_upload.flash_size=4MB
|
||||
board_upload.maximum_size = 4194304
|
||||
board_build.flash_mode = qio
|
||||
board_build.arduino.memory_type = qio_qspi
|
||||
build_type = debug
|
||||
build_flags = ${env.build_flags}
|
||||
-DCORE_DEBUG_LEVEL=1
|
||||
-DDEBUG_MODE=0
|
||||
-DSERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
||||
${pinoutSWROOMBABBLES3.build_flags}
|
||||
upload_flags = --no-stub
|
||||
|
||||
[env:Babble_USB-wrooms-s3]
|
||||
board = esp32-s3-devkitc-1
|
||||
board_build.flash_mode = qio ; qio_qspi
|
||||
board_upload.flashsize = "4MB"
|
||||
board_upload.flash_size=4MB
|
||||
board_upload.maximum_size = 4194304
|
||||
board_build.flash_mode = qio
|
||||
board_build.arduino.memory_type = qio_qspi
|
||||
build_type = debug
|
||||
build_flags = ${env.build_flags}
|
||||
@ -211,6 +232,25 @@ build_flags = ${env.build_flags}
|
||||
-DETVR_EYE_TRACKER_USB_API
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_FLASH
|
||||
-DSERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
||||
${pinoutSWROOMBABBLES3.build_flags}
|
||||
upload_flags = --no-stub
|
||||
|
||||
[env:Babble_USB-wrooms-s3_release]
|
||||
board = esp32-s3-devkitc-1
|
||||
board_upload.flashsize = "4MB"
|
||||
board_upload.flash_size=4MB
|
||||
board_upload.maximum_size = 4194304
|
||||
board_build.flash_mode = qio
|
||||
board_build.arduino.memory_type = qio_qspi
|
||||
build_type = debug
|
||||
build_flags = ${env.build_flags}
|
||||
-DCORE_DEBUG_LEVEL=1
|
||||
-DDEBUG_MODE=0
|
||||
-DETVR_EYE_TRACKER_USB_API
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DSERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
||||
${pinoutSWROOMBABBLES3.build_flags}
|
||||
upload_flags = --no-stub
|
||||
|
@ -5,7 +5,7 @@
|
||||
platform = https://github.com/platformio/platform-espressif32.git
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
custom_firmware_version = 2.6.0
|
||||
custom_firmware_version = 2.8.0
|
||||
monitor_rts = 0
|
||||
monitor_dtr = 0
|
||||
monitor_filters =
|
||||
@ -46,6 +46,7 @@ build_flags =
|
||||
|
||||
'-DOTA_PASSWORD=${ota.otapassword}' ; Set the OTA password
|
||||
'-DOTA_LOGIN=${ota.otalogin}'
|
||||
'-DCAM_RESOLUTION=${cam.resolution}'
|
||||
|
||||
-O2 ; optimize for speed
|
||||
-DASYNCWEBSERVER_REGEX ; enable regex in asyncwebserver
|
||||
|
@ -18,3 +18,6 @@ otapassword = "12345678"
|
||||
|
||||
[development]
|
||||
serial_flush_enabled = 0
|
||||
|
||||
[cam]
|
||||
resolution = FRAMESIZE_240X240
|
@ -20,7 +20,7 @@
|
||||
"include": "/lib/src"
|
||||
},
|
||||
"dependencies": {},
|
||||
"version": "2.6.0",
|
||||
"version": "2.8.0",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "espressif32"
|
||||
}
|
||||
|
19
ESP/lib/src/data/CommandManager/BaseCommand.cpp
Normal file
19
ESP/lib/src/data/CommandManager/BaseCommand.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "BaseCommand.hpp"
|
||||
|
||||
CommandResult PingCommand::execute() {
|
||||
return CommandResult::getSuccessResult("pong");
|
||||
}
|
||||
|
||||
CommandResult ToggleStreamCommand::validate() {
|
||||
if (!data.containsKey("state"))
|
||||
return CommandResult::getErrorResult("Missing state field");
|
||||
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult ToggleStreamCommand::execute() {
|
||||
this->streamServer.toggleTCPStream(data["state"].as<bool>());
|
||||
|
||||
return CommandResult::getSuccessResult("TCP Stream state set to:" +
|
||||
data["state"].as<std::string>());
|
||||
}
|
72
ESP/lib/src/data/CommandManager/BaseCommand.hpp
Normal file
72
ESP/lib/src/data/CommandManager/BaseCommand.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef COMMAND_HPP
|
||||
#define COMMAND_HPP
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "data/config/project_config.hpp"
|
||||
#include "network/stream/streamServer.hpp"
|
||||
|
||||
class CommandResult {
|
||||
private:
|
||||
std::optional<std::string> successMessage;
|
||||
std::optional<std::string> errorMessage;
|
||||
|
||||
public:
|
||||
CommandResult(std::optional<std::string> success_message,
|
||||
std::optional<std::string> error_message) {
|
||||
if (success_message.has_value()) {
|
||||
this->successMessage =
|
||||
"{\"message\":\"" + success_message.value() + "\"}";
|
||||
} else
|
||||
this->successMessage = std::nullopt;
|
||||
|
||||
if (error_message.has_value())
|
||||
this->errorMessage = "{\"error\":\"" + error_message.value() + "\"}";
|
||||
else
|
||||
this->errorMessage = std::nullopt;
|
||||
}
|
||||
|
||||
bool isSuccess() const { return successMessage.has_value(); }
|
||||
|
||||
static CommandResult getSuccessResult(std::string message) {
|
||||
return CommandResult(message, std::nullopt);
|
||||
}
|
||||
|
||||
static CommandResult getErrorResult(std::string message) {
|
||||
return CommandResult(std::nullopt, message);
|
||||
}
|
||||
|
||||
std::string getSuccessMessage() const { return successMessage.value(); };
|
||||
std::string getErrorMessage() const { return errorMessage.value(); }
|
||||
};
|
||||
|
||||
class ICommand {
|
||||
public:
|
||||
virtual CommandResult validate() = 0;
|
||||
virtual CommandResult execute() = 0;
|
||||
virtual ~ICommand() = default;
|
||||
};
|
||||
|
||||
class PingCommand : public ICommand {
|
||||
public:
|
||||
CommandResult validate() override {
|
||||
return CommandResult::getSuccessResult("");
|
||||
};
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class ToggleStreamCommand : public ICommand {
|
||||
StreamServer& streamServer;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
ToggleStreamCommand(StreamServer& streamServer, JsonVariant data)
|
||||
: streamServer(streamServer), data(data) {}
|
||||
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
||||
#endif
|
@ -1,9 +1,26 @@
|
||||
#include "CommandManager.hpp"
|
||||
|
||||
CommandManager::CommandManager(ProjectConfig* deviceConfig)
|
||||
: deviceConfig(deviceConfig) {}
|
||||
std::unique_ptr<ICommand> CommandManager::createCommand(CommandType commandType,
|
||||
JsonVariant& data) {
|
||||
switch (commandType) {
|
||||
case CommandType::PING:
|
||||
return std::make_unique<PingCommand>();
|
||||
case CommandType::SET_WIFI:
|
||||
return std::make_unique<SetWiFiCommand>(this->projectConfig, data);
|
||||
case CommandType::SET_MDNS:
|
||||
return std::make_unique<SetMDNSCommand>(this->projectConfig, data);
|
||||
case CommandType::SAVE_CONFIG:
|
||||
return std::make_unique<SaveConfigCommand>(this->projectConfig);
|
||||
case CommandType::SET_FPS:
|
||||
return std::make_unique<SetFPSCommand>(this->projectConfig, data);
|
||||
case CommandType::TOGGLE_STREAM:
|
||||
return std::make_unique<ToggleStreamCommand>(this->streamServer, data);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const CommandType CommandManager::getCommandType(JsonVariant& command) {
|
||||
CommandType CommandManager::getCommandType(JsonVariant& command) {
|
||||
if (!command.containsKey("command"))
|
||||
return CommandType::None;
|
||||
|
||||
@ -18,64 +35,115 @@ bool CommandManager::hasDataField(JsonVariant& command) {
|
||||
return command.containsKey("data");
|
||||
}
|
||||
|
||||
void CommandManager::handleCommands(CommandsPayload commandsPayload) {
|
||||
CommandResult CommandManager::handleBatchCommands(
|
||||
CommandsPayload commandsPayload) {
|
||||
std::vector<std::unique_ptr<ICommand>> commands;
|
||||
std::vector<std::string> results = {};
|
||||
std::vector<std::string> errors = {};
|
||||
|
||||
if (!commandsPayload.data.containsKey("commands")) {
|
||||
log_e("Json data sent not supported, lacks commands field");
|
||||
return;
|
||||
std::string error = "Json data sent not supported, lacks commands field";
|
||||
log_e("%s", error.c_str());
|
||||
return CommandResult::getErrorResult(error);
|
||||
}
|
||||
|
||||
// we first try to create a command based on the payload
|
||||
// if it's not supported, we register that as an error
|
||||
// then, we try to validate the command, if it's succeful
|
||||
// we add it to the list of commands to execute
|
||||
// otherwise - you guessed it, error
|
||||
// we only execute them if no errors were registered
|
||||
for (JsonVariant commandData :
|
||||
commandsPayload.data["commands"].as<JsonArray>()) {
|
||||
this->handleCommand(commandData);
|
||||
auto command_or_result = this->createCommandFromJsonVariant(commandData);
|
||||
if (auto command_ptr =
|
||||
std::get_if<std::unique_ptr<ICommand>>(&command_or_result)) {
|
||||
auto validation_result = (*command_ptr)->validate();
|
||||
if (validation_result.isSuccess())
|
||||
commands.emplace_back(std::move((*command_ptr)));
|
||||
else
|
||||
errors.push_back(validation_result.getErrorMessage());
|
||||
} else {
|
||||
errors.push_back(
|
||||
std::get<CommandResult>(command_or_result).getErrorMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this->deviceConfig->save();
|
||||
// if we have any errors, consolidate them into a single message and return
|
||||
if (errors.size() > 0)
|
||||
return CommandResult::getErrorResult(
|
||||
Helpers::format_string("\"[%s]\"", this->join_strings(errors, ", ")));
|
||||
|
||||
for (auto& valid_command : commands) {
|
||||
auto result = valid_command->execute();
|
||||
if (result.isSuccess()) {
|
||||
results.push_back(result.getSuccessMessage());
|
||||
} else {
|
||||
// since we're executing them already, and we've encountered an error
|
||||
// we should add it to regular results
|
||||
results.push_back(result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult::getErrorResult(
|
||||
Helpers::format_string("\"[%s]\"", this->join_strings(results, ", ")));
|
||||
;
|
||||
}
|
||||
|
||||
void CommandManager::handleCommand(JsonVariant command) {
|
||||
auto command_type = this->getCommandType(command);
|
||||
CommandResult CommandManager::handleSingleCommand(
|
||||
CommandsPayload commandsPayload) {
|
||||
if (!commandsPayload.data.containsKey("command")) {
|
||||
std::string error = "Json data sent not supported, lacks commands field";
|
||||
log_e("%s", error.c_str());
|
||||
|
||||
switch (command_type) {
|
||||
case CommandType::SET_WIFI: {
|
||||
if (!this->hasDataField(command))
|
||||
// malformed command, lacked data field
|
||||
break;
|
||||
|
||||
if (!command["data"].containsKey("ssid") ||
|
||||
!command["data"].containsKey("password"))
|
||||
break;
|
||||
|
||||
std::string customNetworkName = "main";
|
||||
if (command["data"].containsKey("network_name"))
|
||||
customNetworkName = command["data"]["network_name"].as<std::string>();
|
||||
|
||||
this->deviceConfig->setWifiConfig(customNetworkName,
|
||||
command["data"]["ssid"],
|
||||
command["data"]["password"],
|
||||
0, // channel, should this be zero?
|
||||
0, // power, should this be zero?
|
||||
false, false);
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandType::SET_MDNS: {
|
||||
if (!this->hasDataField(command))
|
||||
break;
|
||||
|
||||
if (!command["data"].containsKey("hostname") ||
|
||||
!strlen(command["data"]["hostname"]))
|
||||
break;
|
||||
|
||||
this->deviceConfig->setMDNSConfig(command["data"]["hostname"],
|
||||
"openiristracker", false);
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandType::PING: {
|
||||
Serial.println("PONG \n\r");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
CommandResult::getErrorResult(error);
|
||||
}
|
||||
|
||||
JsonVariant commandData = commandsPayload.data;
|
||||
auto command_or_result = this->createCommandFromJsonVariant(commandData);
|
||||
|
||||
if (std::holds_alternative<CommandResult>(command_or_result)) {
|
||||
return std::get<CommandResult>(command_or_result);
|
||||
}
|
||||
|
||||
auto command =
|
||||
std::move(std::get<std::unique_ptr<ICommand>>(command_or_result));
|
||||
|
||||
auto validation_result = command->validate();
|
||||
if (!validation_result.isSuccess()) {
|
||||
return validation_result;
|
||||
};
|
||||
|
||||
return command->execute();
|
||||
}
|
||||
|
||||
std::variant<std::unique_ptr<ICommand>, CommandResult>
|
||||
CommandManager::createCommandFromJsonVariant(JsonVariant& command) {
|
||||
auto command_type = this->getCommandType(command);
|
||||
if (command_type == CommandType::None) {
|
||||
std::string error =
|
||||
Helpers::format_string("Command not supported: %s", command["command"]);
|
||||
log_e("%s", error.c_str());
|
||||
return CommandResult::getErrorResult(error);
|
||||
}
|
||||
|
||||
if (!this->hasDataField(command)) {
|
||||
std::string error = Helpers::format_string(
|
||||
"Command is missing data field: %s", command["command"]);
|
||||
log_e("%s", error.c_str());
|
||||
return CommandResult::getErrorResult(error);
|
||||
}
|
||||
|
||||
auto command_data = command["data"].as<JsonVariant>();
|
||||
auto command_ptr = this->createCommand(command_type, command_data);
|
||||
|
||||
if (!command_ptr) {
|
||||
std::string error = Helpers::format_string("Command is not supported: %s",
|
||||
command["command"]);
|
||||
log_e("%s", error.c_str());
|
||||
return CommandResult::getErrorResult(error);
|
||||
}
|
||||
|
||||
return command_ptr;
|
||||
}
|
@ -2,37 +2,74 @@
|
||||
#ifndef TASK_MANAGER_HPP
|
||||
#define TASK_MANAGER_HPP
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "data/CommandManager/BaseCommand.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
#include "network/stream/streamServer.hpp"
|
||||
|
||||
struct CommandsPayload {
|
||||
JsonVariant data;
|
||||
};
|
||||
|
||||
enum CommandType {
|
||||
None,
|
||||
PING,
|
||||
SET_WIFI,
|
||||
SET_MDNS,
|
||||
SET_FPS,
|
||||
TOGGLE_STREAM,
|
||||
SAVE_CONFIG,
|
||||
};
|
||||
|
||||
struct CommandsPayload {
|
||||
JsonDocument data;
|
||||
};
|
||||
const std::unordered_map<std::string, CommandType> commandMap = {
|
||||
{"ping", CommandType::PING},
|
||||
{"set_wifi", CommandType::SET_WIFI},
|
||||
{"set_mdns", CommandType::SET_MDNS},
|
||||
{"set_fps", CommandType::SET_FPS},
|
||||
{"toggle_stream", CommandType::TOGGLE_STREAM},
|
||||
{"save_config", CommandType::SAVE_CONFIG}};
|
||||
|
||||
class CommandManager {
|
||||
private:
|
||||
const std::unordered_map<std::string, CommandType> commandMap = {
|
||||
{"ping", CommandType::PING},
|
||||
{"set_wifi", CommandType::SET_WIFI},
|
||||
{"set_mdns", CommandType::SET_MDNS},
|
||||
};
|
||||
ProjectConfig &projectConfig;
|
||||
StreamServer &streamServer;
|
||||
|
||||
ProjectConfig* deviceConfig;
|
||||
std::string join_strings(std::vector<std::string> const &strings,
|
||||
std::string delim) {
|
||||
std::stringstream ss;
|
||||
std::copy(strings.begin(), strings.end(),
|
||||
std::ostream_iterator<std::string>(ss, delim.c_str()));
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool hasDataField(JsonVariant& command);
|
||||
void handleCommand(JsonVariant command);
|
||||
const CommandType getCommandType(JsonVariant& command);
|
||||
bool hasDataField(JsonVariant &command);
|
||||
std::unique_ptr<ICommand> createCommand(CommandType commandType,
|
||||
JsonVariant &data);
|
||||
|
||||
std::variant<std::unique_ptr<ICommand>, CommandResult>
|
||||
createCommandFromJsonVariant(JsonVariant &command);
|
||||
|
||||
CommandType getCommandType(JsonVariant &command);
|
||||
|
||||
// // TODO rewrite the API
|
||||
// // TODO add FPS/ Freq / cropping to the API
|
||||
// // TODO rewrite camera handler to be simpler and easier to change
|
||||
|
||||
public:
|
||||
CommandManager(ProjectConfig* deviceConfig);
|
||||
void handleCommands(CommandsPayload commandsPayload);
|
||||
};
|
||||
CommandManager(ProjectConfig &projectConfig, StreamServer &streamServer)
|
||||
: projectConfig(projectConfig), streamServer(streamServer) {};
|
||||
|
||||
CommandResult handleSingleCommand(CommandsPayload commandsPayload);
|
||||
CommandResult handleBatchCommands(CommandsPayload commandsPayload);
|
||||
};
|
||||
#endif
|
60
ESP/lib/src/data/CommandManager/ConfigCommands.cpp
Normal file
60
ESP/lib/src/data/CommandManager/ConfigCommands.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "ConfigCommands.hpp"
|
||||
|
||||
CommandResult SetWiFiCommand::validate() {
|
||||
if (!data.containsKey("ssid"))
|
||||
return CommandResult::getErrorResult("Missing ssid");
|
||||
if (!data.containsKey("password"))
|
||||
return CommandResult::getErrorResult("Missing password");
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult SetWiFiCommand::execute() {
|
||||
std::string network_name = "main";
|
||||
if (data.containsKey("network_name"))
|
||||
network_name = data["network_name"].as<std::string>();
|
||||
|
||||
projectConfig.setWifiConfig(network_name, data["ssid"], data["password"], 0,
|
||||
0, false, false);
|
||||
return CommandResult::getSuccessResult("WIFI Set to: " +
|
||||
data["ssid"].as<std::string>());
|
||||
}
|
||||
|
||||
CommandResult SetMDNSCommand::validate() {
|
||||
if (!data.containsKey("hostname") || !strlen(data["hostname"]))
|
||||
return CommandResult::getErrorResult("Missing hostname");
|
||||
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult SetMDNSCommand::execute() {
|
||||
projectConfig.setMDNSConfig(data, false);
|
||||
return CommandResult::getSuccessResult("MDNS set to:" +
|
||||
data["hostname"].as<std::string>());
|
||||
}
|
||||
|
||||
CommandResult SetDeviceConfigCommand::validate() {
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult SetDeviceConfigCommand::execute() {
|
||||
projectConfig.setDeviceConfig(data, false);
|
||||
return CommandResult::getSuccessResult("Device config updated.");
|
||||
}
|
||||
|
||||
CommandResult SaveConfigCommand::execute() {
|
||||
projectConfig.save();
|
||||
return CommandResult::getSuccessResult("CONFIG SAVED");
|
||||
}
|
||||
|
||||
CommandResult SetFPSCommand::validate() {
|
||||
if (!data.containsKey("fps") || !data["hostname"])
|
||||
return CommandResult::getErrorResult("Missing fps or FPS were negative");
|
||||
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult SetFPSCommand::execute() {
|
||||
projectConfig.setCameraConfig("fps", data["fps"]);
|
||||
return CommandResult::getSuccessResult("FPS set to:" +
|
||||
data["fps"].as<std::string>());
|
||||
}
|
61
ESP/lib/src/data/CommandManager/ConfigCommands.hpp
Normal file
61
ESP/lib/src/data/CommandManager/ConfigCommands.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "data/CommandManager/BaseCommand.hpp"
|
||||
|
||||
// todo: make use of the update
|
||||
class SetWiFiCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
SetWiFiCommand(ProjectConfig& projectConfig, JsonVariant data)
|
||||
: projectConfig(projectConfig), data(data) {}
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SetMDNSCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
SetMDNSCommand(ProjectConfig& projectConfig, JsonVariant data)
|
||||
: projectConfig(projectConfig), data(data) {}
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SetDeviceConfigCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
SetDeviceConfigCommand(ProjectConfig& projectConfig, JsonVariant data)
|
||||
: projectConfig(projectConfig), data(data) {}
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SaveConfigCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
|
||||
public:
|
||||
SaveConfigCommand(ProjectConfig& projectConfig)
|
||||
: projectConfig(projectConfig) {}
|
||||
|
||||
CommandResult validate() override {
|
||||
return CommandResult::getSuccessResult("");
|
||||
};
|
||||
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SetFPSCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
SetFPSCommand(ProjectConfig& projectConfig, JsonVariant data)
|
||||
: projectConfig(projectConfig), data(data) {}
|
||||
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
#include "project_config.hpp"
|
||||
#include "sensor.h"
|
||||
|
||||
ProjectConfig::ProjectConfig(const std::string& name,
|
||||
const std::string& mdnsName)
|
||||
@ -53,7 +54,7 @@ void ProjectConfig::initConfig() {
|
||||
this->config.camera = {
|
||||
.vflip = 0,
|
||||
.href = 0,
|
||||
.framesize = 4,
|
||||
.framesize = (uint8_t)CAM_RESOLUTION,
|
||||
.quality = 7,
|
||||
.brightness = 2,
|
||||
};
|
||||
@ -200,7 +201,7 @@ void ProjectConfig::load() {
|
||||
/* Camera Config */
|
||||
this->config.camera.vflip = getInt("vflip", 0);
|
||||
this->config.camera.href = getInt("href", 0);
|
||||
this->config.camera.framesize = getInt("framesize", 4);
|
||||
this->config.camera.framesize = getInt("framesize", (uint8_t)CAM_RESOLUTION);
|
||||
this->config.camera.quality = getInt("quality", 7);
|
||||
this->config.camera.brightness = getInt("brightness", 2);
|
||||
|
||||
@ -213,59 +214,44 @@ void ProjectConfig::load() {
|
||||
//! DeviceConfig
|
||||
//*
|
||||
//**********************************************************************************************************************
|
||||
void ProjectConfig::setDeviceConfig(const std::string& OTALogin,
|
||||
const std::string& OTAPassword,
|
||||
int OTAPort,
|
||||
bool shouldNotify) {
|
||||
void ProjectConfig::setDeviceConfig(JsonVariant& data, bool shouldNotify) {
|
||||
log_d("Updating device config");
|
||||
this->config.device.OTALogin.assign(OTALogin);
|
||||
this->config.device.OTAPassword.assign(OTAPassword);
|
||||
this->config.device.OTAPort = OTAPort;
|
||||
for (JsonPair kv : data.as<JsonObject>()) {
|
||||
config.device.update(kv.key().c_str(), kv.value());
|
||||
}
|
||||
|
||||
if (shouldNotify)
|
||||
this->notifyAll(ConfigState_e::deviceConfigUpdated);
|
||||
}
|
||||
|
||||
void ProjectConfig::setMDNSConfig(const std::string& hostname,
|
||||
const std::string& service,
|
||||
bool shouldNotify) {
|
||||
void ProjectConfig::setMDNSConfig(JsonVariant& data, bool shouldNotify) {
|
||||
log_d("Updating MDNS config");
|
||||
this->config.mdns.hostname.assign(hostname);
|
||||
this->config.mdns.service.assign(service);
|
||||
|
||||
for (JsonPair kv : data.as<JsonObject>()) {
|
||||
this->config.mdns.update(kv.key().c_str(), kv.value());
|
||||
}
|
||||
|
||||
if (shouldNotify)
|
||||
this->notifyAll(ConfigState_e::mdnsConfigUpdated);
|
||||
}
|
||||
|
||||
void ProjectConfig::setCameraConfig(uint8_t vflip,
|
||||
uint8_t framesize,
|
||||
uint8_t href,
|
||||
uint8_t quality,
|
||||
uint8_t brightness,
|
||||
bool shouldNotify) {
|
||||
void ProjectConfig::setCameraConfig(JsonVariant& data, bool shouldNotify) {
|
||||
log_d("Updating camera config");
|
||||
this->config.camera.vflip = vflip;
|
||||
this->config.camera.href = href;
|
||||
this->config.camera.framesize = framesize;
|
||||
this->config.camera.quality = quality;
|
||||
this->config.camera.brightness = brightness;
|
||||
|
||||
log_d("Updating Camera config");
|
||||
for (JsonPair kv : data.as<JsonObject>()) {
|
||||
this->config.camera.update(kv.key().c_str(), kv.value());
|
||||
}
|
||||
|
||||
if (shouldNotify)
|
||||
this->notifyAll(ConfigState_e::cameraConfigUpdated);
|
||||
}
|
||||
|
||||
void ProjectConfig::setWifiConfig(const std::string& networkName,
|
||||
const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel,
|
||||
uint8_t power,
|
||||
bool adhoc,
|
||||
bool shouldNotify) {
|
||||
void ProjectConfig::setWifiConfig(JsonVariant& data, bool shouldNotify) {
|
||||
// we store the ADHOC flag as false because the networks we store in the
|
||||
// config are the ones we want the esp to connect to, rather than host as AP,
|
||||
// and here we're just updating them
|
||||
size_t size = this->config.networks.size();
|
||||
auto networkName = data["name"].as<std::string>();
|
||||
|
||||
for (auto it = this->config.networks.begin();
|
||||
it != this->config.networks.end();) {
|
||||
@ -273,12 +259,10 @@ void ProjectConfig::setWifiConfig(const std::string& networkName,
|
||||
log_i("[Project Config]: Found network %s, updating it ...",
|
||||
it->name.c_str());
|
||||
|
||||
it->name = networkName;
|
||||
it->ssid = ssid;
|
||||
it->password = password;
|
||||
it->channel = channel;
|
||||
it->power = power;
|
||||
it->adhoc = false;
|
||||
// we found the network, so we can just update it with the new values
|
||||
for (JsonPair kv : data.as<JsonObject>()) {
|
||||
it->update(kv.key().c_str(), kv.value());
|
||||
}
|
||||
|
||||
if (shouldNotify) {
|
||||
wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected);
|
||||
@ -293,20 +277,19 @@ void ProjectConfig::setWifiConfig(const std::string& networkName,
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 3 && size > 0) {
|
||||
Serial.println("We're adding a new network");
|
||||
if (size < 3) {
|
||||
if (size == 0)
|
||||
Serial.println("No networks, We're adding a new network");
|
||||
else
|
||||
Serial.println("We're adding a new network");
|
||||
// we don't have that network yet, we can add it as we still have some
|
||||
// space we're using emplace_back as push_back will create a copy of it,
|
||||
// we want to avoid that
|
||||
this->config.networks.emplace_back(networkName, ssid, password, channel,
|
||||
power, false);
|
||||
}
|
||||
|
||||
// we're allowing to store up to three additional networks
|
||||
if (size == 0) {
|
||||
Serial.println("No networks, We're adding a new network");
|
||||
this->config.networks.emplace_back(networkName, ssid, password, channel,
|
||||
power, false);
|
||||
this->config.networks.emplace_back(
|
||||
networkName, data["ssid"].as<std::string>(),
|
||||
// todo add validation for this, maybe deconstruct it before emplacing
|
||||
data["password"].as<std::string>(), data["channel"].as<std::string>(),
|
||||
data["power"].as<std::string>(), false);
|
||||
}
|
||||
|
||||
if (shouldNotify) {
|
||||
@ -324,17 +307,19 @@ void ProjectConfig::deleteWifiConfig(const std::string& networkName,
|
||||
Serial.println("No networks, nothing to delete");
|
||||
}
|
||||
|
||||
for (auto it = this->config.networks.begin();
|
||||
it != this->config.networks.end();) {
|
||||
if (it->name == networkName) {
|
||||
log_i("[Project Config]: Found network %s", it->name.c_str());
|
||||
it = this->config.networks.erase(it);
|
||||
log_i("[Project Config]: Deleted network %s", networkName.c_str());
|
||||
auto networkPredicate = [networkName](WiFiConfig_t network) {
|
||||
return network.name == networkName;
|
||||
};
|
||||
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (auto networkToBeDeleted =
|
||||
std::find_if(this->config.networks.begin(),
|
||||
this->config.networks.end(), networkPredicate);
|
||||
networkToBeDeleted != this->config.networks.end()) {
|
||||
log_i("[Project Config]: Found network %s",
|
||||
networkToBeDeleted->name.c_str());
|
||||
this->config.networks.erase(networkToBeDeleted);
|
||||
log_i("[Project Config]: Deleted network %s", networkName.c_str());
|
||||
};
|
||||
|
||||
if (shouldNotify) {
|
||||
this->wifiConfigSave();
|
||||
@ -343,24 +328,20 @@ void ProjectConfig::deleteWifiConfig(const std::string& networkName,
|
||||
}
|
||||
|
||||
void ProjectConfig::setWiFiTxPower(uint8_t power, bool shouldNotify) {
|
||||
this->config.txpower.power = power;
|
||||
log_d("Updating wifi tx power");
|
||||
|
||||
this->config.txpower.power = power;
|
||||
if (shouldNotify)
|
||||
this->notifyAll(ConfigState_e::wifiTxPowerUpdated);
|
||||
}
|
||||
|
||||
void ProjectConfig::setAPWifiConfig(const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel,
|
||||
bool adhoc,
|
||||
bool shouldNotify) {
|
||||
this->config.ap_network.ssid.assign(ssid);
|
||||
this->config.ap_network.password.assign(password);
|
||||
this->config.ap_network.channel = channel;
|
||||
this->config.ap_network.adhoc = adhoc;
|
||||
|
||||
void ProjectConfig::setAPWifiConfig(JsonVariant& data, bool shouldNotify) {
|
||||
log_d("Updating access point config");
|
||||
|
||||
for (JsonPair kv : data.as<JsonObject>()) {
|
||||
config.device.update(kv.key().c_str(), kv.value());
|
||||
}
|
||||
|
||||
if (shouldNotify) {
|
||||
wifiStateManager.setState(WiFiState_e::WiFiState_None);
|
||||
WiFi.disconnect();
|
||||
|
@ -2,9 +2,12 @@
|
||||
#ifndef PROJECT_CONFIG_HPP
|
||||
#define PROJECT_CONFIG_HPP
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "data/StateManager/StateManager.hpp"
|
||||
@ -33,22 +36,62 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
||||
std::string OTAPassword;
|
||||
int OTAPort;
|
||||
std::string toRepresentation();
|
||||
|
||||
void update(std::string field, JsonVariant value) {
|
||||
// this technically could be done with a hashmap that takes
|
||||
// and std::function<void(JsonVariant)> and some lambda captures
|
||||
// but it seemed too unvieldy for the time being
|
||||
|
||||
if (field == "OTALogin") {
|
||||
this->OTALogin = value.as<std::string>();
|
||||
} else if (field == "OTAPassword") {
|
||||
this->OTAPassword = value.as<std::string>();
|
||||
} else if (field == "OTAPort") {
|
||||
this->OTAPort = value.as<int>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct MDNSConfig_t {
|
||||
std::string hostname;
|
||||
std::string service;
|
||||
std::string toRepresentation();
|
||||
|
||||
void update(std::string field, JsonVariant value) {
|
||||
if (field == "hostname") {
|
||||
this->hostname = std::move(value.as<std::string>());
|
||||
} else if (field == "service") {
|
||||
this->service = std::move(value.as<std::string>());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CameraConfig_t {
|
||||
public:
|
||||
uint8_t vflip;
|
||||
uint8_t href;
|
||||
uint8_t framesize;
|
||||
uint8_t quality;
|
||||
uint8_t brightness;
|
||||
uint8_t fps;
|
||||
|
||||
std::string toRepresentation();
|
||||
|
||||
void update(std::string field, JsonVariant value) {
|
||||
if (field == "vflip") {
|
||||
this->vflip = value.as<uint8_t>();
|
||||
} else if (field == "href") {
|
||||
this->href = value.as<uint8_t>();
|
||||
} else if (field == "framesize") {
|
||||
this->framesize = value.as<uint8_t>();
|
||||
} else if (field == "quality") {
|
||||
this->quality = value.as<uint8_t>();
|
||||
} else if (field == "brightness") {
|
||||
this->brightness = value.as<uint8_t>();
|
||||
} else if (field == "fps") {
|
||||
this->fps = value.as<uint8_t>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WiFiConfig_t {
|
||||
@ -73,6 +116,22 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
||||
bool adhoc;
|
||||
|
||||
std::string toRepresentation();
|
||||
|
||||
void update(std::string field, JsonVariant value) {
|
||||
if (field == "name") {
|
||||
this->name = std::move(value.as<std::string>());
|
||||
} else if (field == "ssid") {
|
||||
this->ssid = std::move(value.as<std::string>());
|
||||
} else if (field == "password") {
|
||||
this->password = std::move(value.as<std::string>());
|
||||
} else if (field == "channel") {
|
||||
this->channel = value.as<uint8_t>();
|
||||
} else if (field == "power") {
|
||||
this->power = value.as<uint8_t>();
|
||||
} else if (field == "adhoc") {
|
||||
this->adhoc = value.as<bool>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AP_WiFiConfig_t {
|
||||
@ -81,11 +140,23 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
||||
uint8_t channel;
|
||||
bool adhoc;
|
||||
std::string toRepresentation();
|
||||
void update(std::string field, JsonVariant value) {
|
||||
if (field == "ssid") {
|
||||
this->ssid = std::move(value.as<std::string>());
|
||||
} else if (field == "password") {
|
||||
this->password = std::move(value.as<std::string>());
|
||||
} else if (field == "channel") {
|
||||
this->channel = value.as<uint8_t>();
|
||||
} else if (field == "adhoc") {
|
||||
this->adhoc = value.as<bool>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WiFiTxPower_t {
|
||||
uint8_t power;
|
||||
std::string toRepresentation();
|
||||
void update(uint8_t power) { this->power = power; }
|
||||
};
|
||||
|
||||
struct TrackerConfig_t {
|
||||
@ -104,31 +175,13 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
|
||||
MDNSConfig_t& getMDNSConfig();
|
||||
WiFiTxPower_t& getWiFiTxPowerConfig();
|
||||
|
||||
void setDeviceConfig(const std::string& OTALogin,
|
||||
const std::string& OTAPassword,
|
||||
int OTAPort,
|
||||
bool shouldNotify);
|
||||
void setMDNSConfig(const std::string& hostname,
|
||||
const std::string& service,
|
||||
bool shouldNotify);
|
||||
void setCameraConfig(uint8_t vflip,
|
||||
uint8_t framesize,
|
||||
uint8_t href,
|
||||
uint8_t quality,
|
||||
uint8_t brightness,
|
||||
bool shouldNotify);
|
||||
void setWifiConfig(const std::string& networkName,
|
||||
const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel,
|
||||
uint8_t power,
|
||||
bool adhoc,
|
||||
bool shouldNotify);
|
||||
void setAPWifiConfig(const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel,
|
||||
bool adhoc,
|
||||
bool shouldNotify);
|
||||
void setDeviceConfig(JsonVariant& data, bool shouldNotify);
|
||||
|
||||
void setMDNSConfig(JsonVariant& data, bool shouldNotify);
|
||||
void setCameraConfig(JsonVariant& data, bool shouldNotify);
|
||||
void setWifiConfig(JsonVariant& data, bool shouldNotify);
|
||||
void setAPWifiConfig(JsonVariant& data, bool shouldNotify);
|
||||
|
||||
void setWiFiTxPower(uint8_t power, bool shouldNotify);
|
||||
|
||||
void deleteWifiConfig(const std::string& networkName, bool shouldNotify);
|
||||
|
@ -1,99 +1,93 @@
|
||||
#include "helpers.hpp"
|
||||
|
||||
char *Helpers::itoa(int value, char *result, int base)
|
||||
{
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 36)
|
||||
{
|
||||
*result = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
char *ptr = result, *ptr1 = result, tmp_char;
|
||||
int tmp_value;
|
||||
|
||||
do
|
||||
{
|
||||
tmp_value = value;
|
||||
value /= base;
|
||||
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
|
||||
} while (value);
|
||||
|
||||
// Apply negative sign
|
||||
if (tmp_value < 0)
|
||||
*ptr++ = '-';
|
||||
*ptr-- = '\0';
|
||||
while (ptr1 < ptr)
|
||||
{
|
||||
tmp_char = *ptr;
|
||||
*ptr-- = *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
char* Helpers::itoa(int value, char* result, int base) {
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 36) {
|
||||
*result = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
char *ptr = result, *ptr1 = result, tmp_char;
|
||||
int tmp_value;
|
||||
|
||||
do {
|
||||
tmp_value = value;
|
||||
value /= base;
|
||||
*ptr++ =
|
||||
"zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxy"
|
||||
"z"[35 + (tmp_value - value * base)];
|
||||
} while (value);
|
||||
|
||||
// Apply negative sign
|
||||
if (tmp_value < 0)
|
||||
*ptr++ = '-';
|
||||
*ptr-- = '\0';
|
||||
while (ptr1 < ptr) {
|
||||
tmp_char = *ptr;
|
||||
*ptr-- = *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void split(const std::string &str, const std::string &splitBy, std::vector<std::string> &tokens)
|
||||
{
|
||||
/* Store the original string in the array, so we can loop the rest
|
||||
* of the algorithm. */
|
||||
tokens.emplace_back(str);
|
||||
void split(const std::string& str,
|
||||
const std::string& splitBy,
|
||||
std::vector<std::string>& tokens) {
|
||||
/* Store the original string in the array, so we can loop the rest
|
||||
* of the algorithm. */
|
||||
tokens.emplace_back(str);
|
||||
|
||||
// Store the split index in a 'size_t' (unsigned integer) type.
|
||||
size_t splitAt;
|
||||
// Store the size of what we're splicing out.
|
||||
size_t splitLen = splitBy.size();
|
||||
// Create a string for temporarily storing the fragment we're processing.
|
||||
std::string frag;
|
||||
// Loop infinitely - break is internal.
|
||||
while (true)
|
||||
{
|
||||
/* Store the last string in the vector, which is the only logical
|
||||
* candidate for processing. */
|
||||
frag = tokens.back();
|
||||
/* The index where the split is. */
|
||||
splitAt = frag.find(splitBy);
|
||||
// If we didn't find a new split point...
|
||||
if (splitAt == std::string::npos)
|
||||
{
|
||||
// Break the loop and (implicitly) return.
|
||||
break;
|
||||
}
|
||||
/* Put everything from the left side of the split where the string
|
||||
* being processed used to be. */
|
||||
tokens.back() = frag.substr(0, splitAt);
|
||||
/* Push everything from the right side of the split to the next empty
|
||||
* index in the vector. */
|
||||
tokens.emplace_back(frag.substr(splitAt + splitLen, frag.size() - (splitAt + splitLen)));
|
||||
// Store the split index in a 'size_t' (unsigned integer) type.
|
||||
size_t splitAt;
|
||||
// Store the size of what we're splicing out.
|
||||
size_t splitLen = splitBy.size();
|
||||
// Create a string for temporarily storing the fragment we're processing.
|
||||
std::string frag;
|
||||
// Loop infinitely - break is internal.
|
||||
while (true) {
|
||||
/* Store the last string in the vector, which is the only logical
|
||||
* candidate for processing. */
|
||||
frag = tokens.back();
|
||||
/* The index where the split is. */
|
||||
splitAt = frag.find(splitBy);
|
||||
// If we didn't find a new split point...
|
||||
if (splitAt == std::string::npos) {
|
||||
// Break the loop and (implicitly) return.
|
||||
break;
|
||||
}
|
||||
/* Put everything from the left side of the split where the string
|
||||
* being processed used to be. */
|
||||
tokens.back() = frag.substr(0, splitAt);
|
||||
/* Push everything from the right side of the split to the next empty
|
||||
* index in the vector. */
|
||||
tokens.emplace_back(
|
||||
frag.substr(splitAt + splitLen, frag.size() - (splitAt + splitLen)));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Helpers::split(const std::string &s, char delimiter)
|
||||
{
|
||||
std::vector<std::string> parts;
|
||||
std::string part;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, part, delimiter))
|
||||
{
|
||||
parts.push_back(part);
|
||||
}
|
||||
return parts;
|
||||
std::vector<std::string> Helpers::split(const std::string& s, char delimiter) {
|
||||
std::vector<std::string> parts;
|
||||
std::string part;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, part, delimiter)) {
|
||||
parts.push_back(part);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
void Helpers::update_progress_bar(int progress, int total)
|
||||
{
|
||||
int barWidth = 70;
|
||||
void Helpers::update_progress_bar(int progress, int total) {
|
||||
int barWidth = 70;
|
||||
|
||||
std::cout << "\r[";
|
||||
int pos = barWidth * progress / total;
|
||||
for (int i = 0; i < barWidth; ++i)
|
||||
{
|
||||
if (i < pos)
|
||||
std::cout << "=";
|
||||
else if (i == pos)
|
||||
std::cout << ">";
|
||||
else
|
||||
std::cout << " ";
|
||||
}
|
||||
std::cout << "] " << int(progress * 100.0 / total) << " %\r";
|
||||
std::cout.flush();
|
||||
std::cout << "\r[";
|
||||
int pos = barWidth * progress / total;
|
||||
for (int i = 0; i < barWidth; ++i) {
|
||||
if (i < pos)
|
||||
std::cout << "=";
|
||||
else if (i == pos)
|
||||
std::cout << ">";
|
||||
else
|
||||
std::cout << " ";
|
||||
}
|
||||
std::cout << "] " << int(progress * 100.0 / total) << " %\r";
|
||||
std::cout.flush();
|
||||
}
|
@ -1,37 +1,41 @@
|
||||
#ifndef HELPERS_HPP
|
||||
#define HELPERS_HPP
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Helpers
|
||||
{
|
||||
char *itoa(int value, char *result, int base);
|
||||
void split(std::string str, std::string splitBy, std::vector<std::string> &tokens);
|
||||
std::vector<std::string> split(const std::string &s, char delimiter);
|
||||
void update_progress_bar(int progress, int total);
|
||||
const unsigned char ETVR_HEADER[] = {0xFF, 0xA0, 0xFF, 0xA1, 0x01};
|
||||
const char* const ETVR_HEADER_BYTES = "\xff\xa0\xff\xa1";
|
||||
|
||||
/// @brief
|
||||
/// @tparam ...Args
|
||||
/// @param format
|
||||
/// @param ...args
|
||||
/// @return
|
||||
template <typename... Args>
|
||||
std::string format_string(const std::string &format, Args... args)
|
||||
{
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
if (size_s <= 0)
|
||||
{
|
||||
std::cout << "Error during formatting.";
|
||||
return "";
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||
namespace Helpers {
|
||||
char* itoa(int value, char* result, int base);
|
||||
void split(std::string str,
|
||||
std::string splitBy,
|
||||
std::vector<std::string>& tokens);
|
||||
std::vector<std::string> split(const std::string& s, char delimiter);
|
||||
void update_progress_bar(int progress, int total);
|
||||
|
||||
/// @brief
|
||||
/// @tparam ...Args
|
||||
/// @param format
|
||||
/// @param ...args
|
||||
/// @return
|
||||
template <typename... Args>
|
||||
std::string format_string(const std::string& format, Args... args) {
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) +
|
||||
1; // Extra space for '\0'
|
||||
if (size_s <= 0) {
|
||||
std::cout << "Error during formatting.";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(),
|
||||
buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
} // namespace Helpers
|
||||
|
||||
#endif // HELPERS_HPP
|
||||
#endif // HELPERS_HPP
|
||||
|
@ -9,6 +9,7 @@
|
||||
*between blinks.
|
||||
*/
|
||||
|
||||
// TODO rethink this
|
||||
LEDManager::ledStateMap_t LEDManager::ledStateMap = {
|
||||
{LEDStates_e::_LedStateNone, {{0, 500}}},
|
||||
{LEDStates_e::_Improv_Error,
|
||||
@ -58,7 +59,7 @@ LEDManager::~LEDManager() {}
|
||||
|
||||
void LEDManager::begin() {
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
// the defualt state is _LedStateNone so we're fine
|
||||
// the default state is _LedStateNone so we're fine
|
||||
this->currentState = ledStateManager.getCurrentState();
|
||||
this->currentPatternIndex = 0;
|
||||
BlinkPatterns_t pattern =
|
||||
@ -66,7 +67,12 @@ void LEDManager::begin() {
|
||||
this->toggleLED(pattern.state);
|
||||
this->nextStateChangeMillis = pattern.delayTime;
|
||||
|
||||
log_d("begin %d", this->currentPatternIndex);
|
||||
log_d("Led manager began with: %d", this->currentPatternIndex);
|
||||
|
||||
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3
|
||||
log_d("Setting up LED for the Babble board");
|
||||
this->setupBabbeLed();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,6 +126,19 @@ void LEDManager::handleLED() {
|
||||
log_d("updated stage %d", this->currentPatternIndex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3
|
||||
void LEDManager::setupBabbeLed() {
|
||||
// Set IR emitter strength to 100%.
|
||||
const int ledPin = 1; // Replace this with a command endpoint eventually.
|
||||
const int freq = 5000;
|
||||
const int ledChannel = 0;
|
||||
const int resolution = 8;
|
||||
const int dutyCycle = 255;
|
||||
ledcSetup(ledChannel, freq, resolution);
|
||||
ledcAttachPin(ledPin, ledChannel);
|
||||
ledcWrite(ledChannel, dutyCycle);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* @brief Turn the LED on or off
|
||||
*
|
||||
|
@ -1,36 +1,38 @@
|
||||
#ifndef LEDMANAGER_HPP
|
||||
#define LEDMANAGER_HPP
|
||||
#include <vector>
|
||||
#include <Arduino.h>
|
||||
#include <data/StateManager/StateManager.hpp>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class LEDManager
|
||||
{
|
||||
public:
|
||||
LEDManager(byte pin);
|
||||
virtual ~LEDManager();
|
||||
class LEDManager {
|
||||
public:
|
||||
LEDManager(byte pin);
|
||||
virtual ~LEDManager();
|
||||
|
||||
void begin();
|
||||
void handleLED();
|
||||
void toggleLED(bool state) const;
|
||||
void begin();
|
||||
void handleLED();
|
||||
void toggleLED(bool state) const;
|
||||
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3
|
||||
void setupBabbeLed();
|
||||
#endif
|
||||
|
||||
private:
|
||||
byte _ledPin;
|
||||
unsigned long nextStateChangeMillis = 0;
|
||||
bool _ledState;
|
||||
private:
|
||||
byte _ledPin;
|
||||
unsigned long nextStateChangeMillis = 0;
|
||||
bool _ledState;
|
||||
|
||||
struct BlinkPatterns_t
|
||||
{
|
||||
int state;
|
||||
int delayTime;
|
||||
};
|
||||
struct BlinkPatterns_t {
|
||||
int state;
|
||||
int delayTime;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<LEDStates_e, std::vector<BlinkPatterns_t>> ledStateMap_t;
|
||||
static ledStateMap_t ledStateMap;
|
||||
static std::vector<LEDStates_e> keepAliveStates;
|
||||
LEDStates_e currentState;
|
||||
unsigned int currentPatternIndex = 0;
|
||||
typedef std::unordered_map<LEDStates_e, std::vector<BlinkPatterns_t>>
|
||||
ledStateMap_t;
|
||||
static ledStateMap_t ledStateMap;
|
||||
static std::vector<LEDStates_e> keepAliveStates;
|
||||
LEDStates_e currentState;
|
||||
unsigned int currentPatternIndex = 0;
|
||||
};
|
||||
|
||||
#endif // LEDMANAGER_HPP
|
||||
#endif // LEDMANAGER_HPP
|
||||
|
@ -52,7 +52,7 @@ void SerialManager::send_frame() {
|
||||
#endif
|
||||
|
||||
void SerialManager::init() {
|
||||
#ifndef SERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
||||
#ifdef SERIAL_MANAGER_USE_HIGHER_FREQUENCY
|
||||
Serial.begin(3000000);
|
||||
#endif
|
||||
if (SERIAL_FLUSH_ENABLED) {
|
||||
@ -67,12 +67,30 @@ void SerialManager::run() {
|
||||
|
||||
if (deserializationError) {
|
||||
log_e("Command deserialization failed: %s", deserializationError.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CommandsPayload commands = {doc};
|
||||
this->commandManager->handleCommands(commands);
|
||||
CommandResult result = CommandResult::getSuccessResult("");
|
||||
|
||||
if (doc.containsKey("command")) {
|
||||
result = this->commandManager->handleSingleCommand(commands);
|
||||
} else {
|
||||
result = this->commandManager->handleBatchCommands(commands);
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
Serial.printf("%s \n\r", result.getSuccessMessage().c_str());
|
||||
|
||||
// we also save the config if the commands were successful
|
||||
JsonDocument saveCommanddDoc;
|
||||
saveCommanddDoc["command"] = "save_config";
|
||||
saveCommanddDoc["data"].to<JsonObject>();
|
||||
CommandsPayload saveCommandPayload = {saveCommanddDoc};
|
||||
this->commandManager->handleSingleCommand(saveCommandPayload);
|
||||
} else {
|
||||
Serial.printf("%s \n\r", result.getErrorMessage().c_str());
|
||||
}
|
||||
}
|
||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||
else {
|
||||
|
@ -8,9 +8,7 @@
|
||||
#include <esp_camera.h>
|
||||
#include "data/CommandManager/CommandManager.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
|
||||
const char* const ETVR_HEADER = "\xff\xa0";
|
||||
const char* const ETVR_HEADER_FRAME = "\xff\xa1";
|
||||
#include "data/utilities/helpers.hpp"
|
||||
|
||||
enum QueryAction {
|
||||
READY_TO_RECEIVE,
|
||||
|
@ -16,7 +16,8 @@ void CameraHandler::setupCameraPinout() {
|
||||
// 16500000 optimal freq on ESP32-CAM (default)
|
||||
// 20000000 max freq on ESP32-CAM
|
||||
// 24000000 optimal freq on ESP32-S3
|
||||
int xclk_freq_hz = DEFAULT_XCLK_FREQ_HZ;
|
||||
// int xclk_freq_hz = DEFAULT_XCLK_FREQ_HZ;
|
||||
int xclk_freq_hz = USB_DEFAULT_XCLK_FREQ_HZ;
|
||||
|
||||
#if CONFIG_CAMERA_MODULE_ESP_EYE
|
||||
/* IO13, IO14 is designed for JTAG by default,
|
||||
@ -63,7 +64,7 @@ void CameraHandler::setupCameraPinout() {
|
||||
|
||||
void CameraHandler::setupBasicResolution() {
|
||||
config.pixel_format = PIXFORMAT_JPEG;
|
||||
config.frame_size = FRAMESIZE_240X240;
|
||||
config.frame_size = CAM_RESOLUTION;
|
||||
|
||||
if (!psramFound()) {
|
||||
log_e("[Camera]: Did not find psram, setting lower image quality");
|
||||
@ -108,6 +109,10 @@ void CameraHandler::setupCameraSensor() {
|
||||
camera_sensor->set_aec2(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
camera_sensor->set_ae_level(camera_sensor, 0); // -2 to 2
|
||||
camera_sensor->set_aec_value(camera_sensor, 300); // 0 to 1200
|
||||
// Use a lower aec value for babble to better isolate the face with illuminators
|
||||
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3
|
||||
camera_sensor->set_aec_value(camera_sensor, 100); // 0 to 1200
|
||||
#endif
|
||||
|
||||
// controls the gain
|
||||
camera_sensor->set_gain_ctrl(camera_sensor, 0); // 0 = disable , 1 = enable
|
||||
@ -195,7 +200,29 @@ void CameraHandler::loadConfigData() {
|
||||
log_d("Loading camera config data done");
|
||||
}
|
||||
|
||||
int CameraHandler::setCameraResolution(framesize_t frameSize) {
|
||||
#ifdef CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3
|
||||
int CameraHandler::setCameraResolution(framesize_t frameSize) { // For Babble, use a firmware crop as shown by Physdude
|
||||
if (camera_sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
try {
|
||||
int outputSize = 240;
|
||||
|
||||
int baseRes = 2; //CIF
|
||||
int ROIsize = 240;
|
||||
int startPointX = 80;
|
||||
int startPointY = 28;
|
||||
|
||||
return camera_sensor->set_res_raw(camera_sensor, baseRes, 0, 0, 0, startPointX, startPointY, ROIsize, ROIsize, outputSize, outputSize, 0, 0);
|
||||
|
||||
} catch (...) {
|
||||
// they sent us a malformed or unsupported frameSize - rather than crash -
|
||||
// tell them about it
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int CameraHandler::setCameraResolution(framesize_t frameSize) { // By default, use the standard method.
|
||||
if (camera_sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
try {
|
||||
return camera_sensor->set_framesize(camera_sensor, frameSize);
|
||||
@ -207,6 +234,7 @@ int CameraHandler::setCameraResolution(framesize_t frameSize) {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int CameraHandler::setVFlip(int direction) {
|
||||
return camera_sensor->set_vflip(camera_sensor, direction);
|
||||
|
@ -1,17 +0,0 @@
|
||||
#ifndef HASH_H_
|
||||
#define HASH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
// #define DEBUG_SHA1
|
||||
|
||||
void sha1(const uint8_t* data, uint32_t size, uint8_t hash[20]);
|
||||
void sha1(const char* data, uint32_t size, uint8_t hash[20]);
|
||||
void sha1(const std::string& data, uint8_t hash[20]);
|
||||
|
||||
std::string sha1(const uint8_t* data, uint32_t size);
|
||||
std::string sha1(const char* data, uint32_t size);
|
||||
std::string sha1(const std::string& data);
|
||||
|
||||
#endif /* HASH_H_ */
|
@ -102,17 +102,6 @@ void BaseAPI::setWiFi(AsyncWebServerRequest* request) {
|
||||
projectConfig.setWifiConfig(networkName, ssid, password, channel, power,
|
||||
adhoc, true);
|
||||
|
||||
/* if (WiFiStateManager->getCurrentState() ==
|
||||
WiFiState_e::WiFiState_ADHOC)
|
||||
{
|
||||
projectConfig.setAPWifiConfig(ssid, password, &channel, adhoc,
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
} */
|
||||
|
||||
request->send(200, MIMETYPE_JSON,
|
||||
"{\"msg\":\"Done. Wifi Creds have been set.\"}");
|
||||
break;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
//! Warning do not format this file with clang-format or it will break the code
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
@ -31,7 +30,6 @@ constexpr int HTTP_ANY = 0b01111111;
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <FS.h>
|
||||
#include "Hash.h"
|
||||
|
||||
#include "data/utilities/network_utilities.hpp"
|
||||
#include "tasks/tasks.hpp"
|
||||
@ -100,33 +98,34 @@ class BaseAPI {
|
||||
typedef std::unordered_map<std::string, WebRequestMethodComposite>
|
||||
networkMethodsMap_t;
|
||||
|
||||
ProjectConfig &projectConfig;
|
||||
/// @brief Local instance of the AsyncWebServer - so that we dont need to use new and delete
|
||||
AsyncWebServer server;
|
||||
ProjectConfig& projectConfig;
|
||||
/// @brief Local instance of the AsyncWebServer - so that we dont need to use
|
||||
/// new and delete
|
||||
AsyncWebServer server;
|
||||
#ifndef SIM_ENABLED
|
||||
CameraHandler &camera;
|
||||
CameraHandler& camera;
|
||||
#endif // SIM_ENABLED
|
||||
|
||||
public :
|
||||
BaseAPI(ProjectConfig& projectConfig,
|
||||
public:
|
||||
BaseAPI(ProjectConfig& projectConfig,
|
||||
#ifndef SIM_ENABLED
|
||||
CameraHandler& camera,
|
||||
CameraHandler& camera,
|
||||
#endif // SIM_ENABLED
|
||||
const std::string& api_url,
|
||||
const std::string& api_url,
|
||||
#ifndef SIM_ENABLED
|
||||
int port = 81
|
||||
int port = 81
|
||||
#else
|
||||
int port = 80
|
||||
int port = 80
|
||||
#endif
|
||||
);
|
||||
|
||||
virtual ~BaseAPI();
|
||||
virtual void begin();
|
||||
void checkAuthentication(AsyncWebServerRequest* request,
|
||||
const char* login,
|
||||
const char* password);
|
||||
void beginOTA();
|
||||
void notFound(AsyncWebServerRequest* request) const;
|
||||
virtual ~BaseAPI();
|
||||
virtual void begin();
|
||||
void checkAuthentication(AsyncWebServerRequest* request,
|
||||
const char* login,
|
||||
const char* password);
|
||||
void beginOTA();
|
||||
void notFound(AsyncWebServerRequest* request) const;
|
||||
};
|
||||
|
||||
#endif // BASEAPI_HPP
|
||||
|
@ -28,11 +28,10 @@ void APIServer::setup() {
|
||||
"^\\%s\\/([a-zA-Z0-9]+)\\/command\\/([a-zA-Z0-9]+)$",
|
||||
this->api_url.c_str());
|
||||
log_d("API URL: %s", buffer);
|
||||
server.on(buffer, 0b01111111, [&](AsyncWebServerRequest* request) {
|
||||
handleRequest(request);
|
||||
});
|
||||
server.on(buffer, 0b01111111,
|
||||
[&](AsyncWebServerRequest* request) { handleRequest(request); });
|
||||
#ifndef SIM_ENABLED
|
||||
//this->_authRequired = true;
|
||||
// this->_authRequired = true;
|
||||
#endif // SIM_ENABLED
|
||||
beginOTA();
|
||||
server.begin();
|
||||
|
@ -6,7 +6,8 @@ MDNSHandler::MDNSHandler(ProjectConfig& configManager)
|
||||
bool MDNSHandler::startMDNS() {
|
||||
const std::string service = "_openiristracker";
|
||||
auto mdnsConfig = configManager.getMDNSConfig();
|
||||
if (!MDNS.begin(mdnsConfig.hostname.c_str())) // lowercase only - as this will be the url
|
||||
if (!MDNS.begin(mdnsConfig.hostname
|
||||
.c_str())) // lowercase only - as this will be the url
|
||||
{
|
||||
mdnsStateManager.setState(MDNSState_e::MDNSState_Error);
|
||||
log_e("Error initializing MDNS");
|
||||
|
@ -1,114 +1,203 @@
|
||||
#include "streamServer.hpp"
|
||||
|
||||
constexpr static const char *STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
constexpr static const char *STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
constexpr static const char *STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %d.%06d\r\n\r\n";
|
||||
constexpr static const char* STREAM_CONTENT_TYPE =
|
||||
"multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
constexpr static const char* STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
constexpr static const char* STREAM_PART =
|
||||
"Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: "
|
||||
"%d.%06d\r\n\r\n";
|
||||
|
||||
esp_err_t StreamHelpers::stream(httpd_req_t *req)
|
||||
{
|
||||
long last_request_time = 0;
|
||||
camera_fb_t *fb = NULL;
|
||||
struct timeval _timestamp;
|
||||
esp_err_t StreamHelpers::stream(httpd_req_t* req) {
|
||||
long last_request_time = 0;
|
||||
camera_fb_t* fb = NULL;
|
||||
struct timeval _timestamp;
|
||||
|
||||
esp_err_t res = ESP_OK;
|
||||
esp_err_t res = ESP_OK;
|
||||
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t *_jpg_buf = NULL;
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t* _jpg_buf = NULL;
|
||||
|
||||
char *part_buf[256];
|
||||
char* part_buf[256];
|
||||
|
||||
static int64_t last_frame = 0;
|
||||
if (!last_frame)
|
||||
last_frame = esp_timer_get_time();
|
||||
static int64_t last_frame = 0;
|
||||
if (!last_frame)
|
||||
last_frame = esp_timer_get_time();
|
||||
|
||||
res = httpd_resp_set_type(req, STREAM_CONTENT_TYPE);
|
||||
if (res != ESP_OK)
|
||||
return res;
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_hdr(req, "X-Framerate", "60");
|
||||
|
||||
while (true)
|
||||
{
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb)
|
||||
{
|
||||
log_e("Camera capture failed with response: %s", esp_err_to_name(res));
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timestamp.tv_sec = fb->timestamp.tv_sec;
|
||||
_timestamp.tv_usec = fb->timestamp.tv_usec;
|
||||
_jpg_buf_len = fb->len;
|
||||
_jpg_buf = fb->buf;
|
||||
}
|
||||
if (res == ESP_OK)
|
||||
res = httpd_resp_send_chunk(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||
if (res == ESP_OK)
|
||||
{
|
||||
size_t hlen = snprintf((char *)part_buf, 128, STREAM_PART, _jpg_buf_len, _timestamp.tv_sec, _timestamp.tv_usec);
|
||||
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
|
||||
}
|
||||
if (res == ESP_OK)
|
||||
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
|
||||
if (fb)
|
||||
{
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
else if (_jpg_buf)
|
||||
{
|
||||
free(_jpg_buf);
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
if (res != ESP_OK)
|
||||
break;
|
||||
long request_end = millis();
|
||||
long latency = (request_end - last_request_time);
|
||||
last_request_time = request_end;
|
||||
log_d("Size: %uKB, Time: %ums (%ifps)\n", _jpg_buf_len / 1024, latency, 1000 / latency);
|
||||
}
|
||||
last_frame = 0;
|
||||
res = httpd_resp_set_type(req, STREAM_CONTENT_TYPE);
|
||||
if (res != ESP_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
StreamServer::StreamServer(const int STREAM_PORT) : STREAM_SERVER_PORT(STREAM_PORT) {}
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_hdr(req, "X-Framerate", "60");
|
||||
|
||||
int StreamServer::startStreamServer()
|
||||
{
|
||||
// WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //! Turn-off the 'brownout detector'
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.stack_size = 20480;
|
||||
config.max_uri_handlers = 1;
|
||||
config.server_port = this->STREAM_SERVER_PORT;
|
||||
config.ctrl_port = this->STREAM_SERVER_PORT;
|
||||
config.stack_size = 20480;
|
||||
|
||||
httpd_uri_t stream_page = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &StreamHelpers::stream,
|
||||
.user_ctx = nullptr};
|
||||
|
||||
int status = httpd_start(&camera_stream, &config);
|
||||
|
||||
if (status != ESP_OK)
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
httpd_register_uri_handler(camera_stream, &stream_page);
|
||||
Serial.println("Stream server initialized");
|
||||
switch (wifiStateManager.getCurrentState())
|
||||
{
|
||||
case WiFiState_e::WiFiState_ADHOC:
|
||||
Serial.printf("\n\rThe stream is under: http://%s:%i\n\r", WiFi.softAPIP().toString().c_str(), this->STREAM_SERVER_PORT);
|
||||
break;
|
||||
default:
|
||||
Serial.printf("\n\rThe stream is under: http://%s:%i\n\r", WiFi.localIP().toString().c_str(), this->STREAM_SERVER_PORT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
while (true) {
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
log_e("Camera capture failed with response: %s", esp_err_to_name(res));
|
||||
res = ESP_FAIL;
|
||||
} else {
|
||||
_timestamp.tv_sec = fb->timestamp.tv_sec;
|
||||
_timestamp.tv_usec = fb->timestamp.tv_usec;
|
||||
_jpg_buf_len = fb->len;
|
||||
_jpg_buf = fb->buf;
|
||||
}
|
||||
if (res == ESP_OK)
|
||||
res =
|
||||
httpd_resp_send_chunk(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||
if (res == ESP_OK) {
|
||||
size_t hlen = snprintf((char*)part_buf, 128, STREAM_PART, _jpg_buf_len,
|
||||
_timestamp.tv_sec, _timestamp.tv_usec);
|
||||
res = httpd_resp_send_chunk(req, (const char*)part_buf, hlen);
|
||||
}
|
||||
if (res == ESP_OK)
|
||||
res = httpd_resp_send_chunk(req, (const char*)_jpg_buf, _jpg_buf_len);
|
||||
if (fb) {
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
_jpg_buf = NULL;
|
||||
} else if (_jpg_buf) {
|
||||
free(_jpg_buf);
|
||||
_jpg_buf = NULL;
|
||||
}
|
||||
if (res != ESP_OK)
|
||||
break;
|
||||
long request_end = millis();
|
||||
long latency = (request_end - last_request_time);
|
||||
last_request_time = request_end;
|
||||
log_d("Size: %uKB, Time: %ums (%ifps)\n", _jpg_buf_len / 1024, latency,
|
||||
1000 / latency);
|
||||
}
|
||||
last_frame = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
int StreamServer::startStreamServer() {
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.stack_size = 20480;
|
||||
config.max_uri_handlers = 1;
|
||||
config.server_port = this->STREAM_SERVER_PORT;
|
||||
config.ctrl_port = this->STREAM_SERVER_PORT;
|
||||
|
||||
httpd_uri_t stream_page = {.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &StreamHelpers::stream,
|
||||
.user_ctx = nullptr};
|
||||
|
||||
int status = httpd_start(&camera_stream, &config);
|
||||
|
||||
if (status != ESP_OK)
|
||||
return -1;
|
||||
else {
|
||||
httpd_register_uri_handler(camera_stream, &stream_page);
|
||||
Serial.println("Stream server initialized");
|
||||
String serverStatusMessage = "The stream is under: http://";
|
||||
|
||||
switch (wifiStateManager.getCurrentState()) {
|
||||
case WiFiState_e::WiFiState_ADHOC:
|
||||
// this should be Serial.printf but for some odd reason
|
||||
// Serial.printf shows up only on debug, not in release
|
||||
Serial.println((serverStatusMessage + WiFi.softAPIP().toString() + ":" +
|
||||
this->STREAM_SERVER_PORT + "\n\r")
|
||||
.c_str());
|
||||
break;
|
||||
default:
|
||||
Serial.println((serverStatusMessage + WiFi.localIP().toString() + ":" +
|
||||
this->STREAM_SERVER_PORT + "\n\r")
|
||||
.c_str());
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool StreamServer::startTCPStreamServer() {
|
||||
tcp_server = new AsyncServer(this->TCP_STREAM_SERVER_PORT);
|
||||
tcp_server->onClient(
|
||||
[this](void* arg, AsyncClient* client) {
|
||||
this->handleNewTCPClient(arg, client);
|
||||
},
|
||||
tcp_server);
|
||||
|
||||
tcp_server->begin();
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamServer::handleNewTCPClient(void* arg, AsyncClient* client) {
|
||||
Serial.printf("Client connecting with ip: %s \n\r",
|
||||
client->remoteIP().toString().c_str());
|
||||
|
||||
if (this->tcp_connected_client == nullptr) {
|
||||
this->tcp_connected_client = client;
|
||||
|
||||
this->tcp_connected_client->onError(
|
||||
[this](void* arg, AsyncClient* client, int8_t error) {
|
||||
Serial.printf("\n connection error %s from client %s \n",
|
||||
client->errorToString(error),
|
||||
client->remoteIP().toString().c_str());
|
||||
|
||||
this->tcp_connected_client = nullptr;
|
||||
});
|
||||
|
||||
this->tcp_connected_client->onDisconnect(
|
||||
[this](void* arg, AsyncClient* client) {
|
||||
this->tcp_connected_client = nullptr;
|
||||
});
|
||||
|
||||
Serial.println("Client connected!");
|
||||
} else {
|
||||
client->close();
|
||||
Serial.println("Rejected client, only one connection allowed!");
|
||||
}
|
||||
}
|
||||
|
||||
void StreamServer::sendTCPFrame() {
|
||||
if (this->tcp_connected_client == nullptr ||
|
||||
!this->tcp_connected_client->connected() || this->pauseTCPStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
// todo test this
|
||||
if (last_time_frame_sent &&
|
||||
last_time_frame_sent - millis() < target_fps_time) {
|
||||
return;
|
||||
}
|
||||
last_time_frame_sent = millis();
|
||||
|
||||
auto fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
log_e("Camera capture failed");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = fb->len;
|
||||
this->tcp_connected_client->write(ETVR_HEADER_BYTES, 4);
|
||||
this->tcp_connected_client->write((const char*)fb->buf, fb->len);
|
||||
|
||||
if (fb) {
|
||||
esp_camera_fb_return(fb);
|
||||
}
|
||||
|
||||
long request_end = millis();
|
||||
long latency = request_end - last_request_time;
|
||||
last_request_time = request_end;
|
||||
log_d("Size: %uKB, Time: %ums (%ifps)\n", len / 1024, latency,
|
||||
1000 / latency);
|
||||
}
|
||||
|
||||
std::string StreamServer::getName() {
|
||||
return "StreamServer";
|
||||
}
|
||||
|
||||
void StreamServer::toggleTCPStream(bool state) {
|
||||
pauseTCPStream = state;
|
||||
}
|
||||
|
||||
void StreamServer::update(ConfigState_e event) {
|
||||
switch (event) {
|
||||
case ConfigState_e::cameraConfigUpdated:
|
||||
// add FPS update here
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
@ -3,8 +3,17 @@
|
||||
#define STREAM_SERVER_HPP
|
||||
#define PART_BOUNDARY "123456789000000000000987654321"
|
||||
#include <Arduino.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <AsyncUDP.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "data/StateManager/StateManager.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
#include "data/utilities/Observer.hpp"
|
||||
#include "data/utilities/helpers.hpp"
|
||||
|
||||
// Camera includes
|
||||
#include "esp_camera.h"
|
||||
@ -13,20 +22,47 @@
|
||||
#include "fb_gfx.h"
|
||||
#include "img_converters.h"
|
||||
|
||||
namespace StreamHelpers
|
||||
{
|
||||
esp_err_t stream(httpd_req_t *req);
|
||||
constexpr int CHUNK_SIZE = 1323; // old: 1024;
|
||||
|
||||
namespace StreamHelpers {
|
||||
esp_err_t stream(httpd_req_t* req);
|
||||
}
|
||||
class StreamServer
|
||||
{
|
||||
// namespace StreamHelpers
|
||||
class StreamServer : public IObserver<ConfigState_e> {
|
||||
private:
|
||||
ProjectConfig& configManager;
|
||||
int64_t last_frame = 0;
|
||||
|
||||
private:
|
||||
httpd_handle_t camera_stream = nullptr;
|
||||
int STREAM_SERVER_PORT;
|
||||
AsyncServer* tcp_server;
|
||||
AsyncClient* tcp_connected_client;
|
||||
httpd_handle_t camera_stream = nullptr;
|
||||
|
||||
public:
|
||||
StreamServer(const int STREAM_PORT = 80);
|
||||
int startStreamServer();
|
||||
long last_request_time = 0;
|
||||
int STREAM_SERVER_PORT = 80;
|
||||
int TCP_STREAM_SERVER_PORT = 82;
|
||||
|
||||
int last_time_frame_sent = 0;
|
||||
float target_fps_time = 1000 / 30;
|
||||
|
||||
bool pauseTCPStream = true;
|
||||
|
||||
public:
|
||||
StreamServer(ProjectConfig& configManager,
|
||||
const int STREAM_PORT,
|
||||
const int TPC_SERVER_PORT)
|
||||
: configManager(configManager),
|
||||
STREAM_SERVER_PORT(STREAM_PORT),
|
||||
TCP_STREAM_SERVER_PORT(TPC_SERVER_PORT) {};
|
||||
|
||||
int startStreamServer();
|
||||
bool startTCPStreamServer();
|
||||
|
||||
void toggleTCPStream(bool state);
|
||||
void sendTCPFrame();
|
||||
void handleNewTCPClient(void* arg, AsyncClient* client);
|
||||
|
||||
void update(ConfigState_e event) override;
|
||||
std::string getName() override;
|
||||
};
|
||||
|
||||
#endif // STREAM_SERVER_HPP
|
||||
#endif // STREAM_SERVER_HPP
|
||||
|
@ -1,7 +1,4 @@
|
||||
#include "wifihandler.hpp"
|
||||
#include <WiFi.h>
|
||||
#include "data/StateManager/StateManager.hpp"
|
||||
#include "data/utilities/helpers.hpp"
|
||||
|
||||
WiFiHandler::WiFiHandler(ProjectConfig& configManager,
|
||||
const std::string& ssid,
|
||||
@ -18,10 +15,11 @@ WiFiHandler::WiFiHandler(ProjectConfig& configManager,
|
||||
WiFiHandler::~WiFiHandler() {}
|
||||
|
||||
void WiFiHandler::begin() {
|
||||
|
||||
// just to be sure, we reeset everything before we do anything, some boards were having problems otherwise
|
||||
// just to be sure, we reeset everything before we do anything, some boards
|
||||
// were having problems otherwise
|
||||
WiFi.disconnect();
|
||||
// we purposefully set the lowest min required security level, some boards have problems connecting otherwise
|
||||
// we purposefully set the lowest min required security level, some boards
|
||||
// have problems connecting otherwise
|
||||
// https://github.com/espressif/arduino-esp32/issues/8770
|
||||
WiFi.setMinSecurity(WIFI_AUTH_WEP);
|
||||
|
||||
@ -33,7 +31,9 @@ void WiFiHandler::begin() {
|
||||
return;
|
||||
}
|
||||
|
||||
log_d("ADHOC is disabled, setting up STA network and checking transmission power \n\r");
|
||||
log_d(
|
||||
"ADHOC is disabled, setting up STA network and checking transmission "
|
||||
"power \n\r");
|
||||
auto txpower = configManager.getWiFiTxPowerConfig();
|
||||
log_d("Setting Wifi Power to: %d", txpower.power);
|
||||
log_d("Setting WiFi sleep mode to NONE \n\r");
|
||||
@ -46,14 +46,9 @@ void WiFiHandler::begin() {
|
||||
|
||||
if (networks.empty()) {
|
||||
log_i("No networks found in config, trying the default one \n\r");
|
||||
|
||||
if (this->iniSTA(
|
||||
this->ssid,
|
||||
this->password,
|
||||
this->channel,
|
||||
(wifi_power_t)txpower.power
|
||||
)
|
||||
) {
|
||||
|
||||
if (this->iniSTA(this->ssid, this->password, this->channel,
|
||||
(wifi_power_t)txpower.power)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -139,12 +134,11 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel,
|
||||
wifi_power_t power) {
|
||||
|
||||
// since networks may not have a password, we only need to check if we have an ssid
|
||||
// bail if we don't
|
||||
if (ssid == ""){
|
||||
// since networks may not have a password, we only need to check if we have an
|
||||
// ssid bail if we don't
|
||||
if (ssid == "") {
|
||||
log_d("ssid missing, bailing");
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long currentMillis = millis();
|
||||
@ -159,8 +153,9 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
|
||||
INADDR_NONE); // need to call before setting hostname
|
||||
log_d("Setting hostname %s \n\r");
|
||||
WiFi.setHostname(mdnsConfig.hostname.c_str());
|
||||
log_i("Setting TX power to: %d \n\r", (uint8_t)power);
|
||||
WiFi.setTxPower(power); // https://github.com/espressif/arduino-esp32/issues/5698
|
||||
log_i("Setting TX power to: %d \n\r", (uint8_t)power);
|
||||
WiFi.setTxPower(
|
||||
power); // https://github.com/espressif/arduino-esp32/issues/5698
|
||||
WiFi.begin(ssid.c_str(), password.c_str(), channel);
|
||||
|
||||
log_d("Waiting for WiFi to connect... \n\r");
|
||||
|
@ -1,8 +1,12 @@
|
||||
#pragma once
|
||||
#ifndef WIFIHANDLER_HPP
|
||||
#define WIFIHANDLER_HPP
|
||||
#include <WiFi.h>
|
||||
#include <string>
|
||||
#include "data/StateManager/StateManager.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
#include "data/utilities/helpers.hpp"
|
||||
|
||||
#include "data/utilities/Observer.hpp"
|
||||
|
||||
class WiFiHandler : public IObserver<ConfigState_e> {
|
||||
@ -29,7 +33,6 @@ class WiFiHandler : public IObserver<ConfigState_e> {
|
||||
|
||||
ProjectConfig& configManager;
|
||||
|
||||
|
||||
bool _enable_adhoc;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
|
@ -6,18 +6,14 @@
|
||||
* @param mdnsName The mDNS hostname to use
|
||||
*/
|
||||
ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME);
|
||||
CommandManager commandManager(&deviceConfig);
|
||||
SerialManager serialManager(&commandManager);
|
||||
|
||||
#ifdef CONFIG_CAMERA_MODULE_ESP32S3_XIAO_SENSE
|
||||
LEDManager ledManager(LED_BUILTIN);
|
||||
|
||||
#elif CONFIG_CAMERA_MODULE_SWROOM_BABBLE_S3
|
||||
LEDManager ledManager(38);
|
||||
|
||||
#else
|
||||
LEDManager ledManager(33);
|
||||
#endif // ESP32S3_XIAO_SENSE
|
||||
#endif
|
||||
|
||||
#ifndef SIM_ENABLED
|
||||
CameraHandler cameraHandler(deviceConfig);
|
||||
@ -30,13 +26,16 @@ WiFiHandler wifiHandler(deviceConfig,
|
||||
WIFI_CHANNEL,
|
||||
ENABLE_ADHOC);
|
||||
MDNSHandler mdnsHandler(deviceConfig);
|
||||
#ifdef SIM_ENABLED
|
||||
APIServer apiServer(deviceConfig, wifiStateManager, "/control");
|
||||
#else
|
||||
|
||||
APIServer apiServer(deviceConfig, cameraHandler, "/control");
|
||||
StreamServer streamServer;
|
||||
|
||||
#ifndef SIM_ENABLED
|
||||
StreamServer streamServer(deviceConfig, 80, 82);
|
||||
#endif // SIM_ENABLED
|
||||
|
||||
CommandManager commandManager(deviceConfig, streamServer);
|
||||
SerialManager serialManager(&commandManager);
|
||||
|
||||
void etvr_eye_tracker_web_init() {
|
||||
log_d("[SETUP]: Starting Network Handler");
|
||||
deviceConfig.attach(mdnsHandler);
|
||||
@ -45,37 +44,27 @@ void etvr_eye_tracker_web_init() {
|
||||
log_d("[SETUP]: Starting MDNS Handler");
|
||||
mdnsHandler.startMDNS();
|
||||
|
||||
switch (wifiStateManager.getCurrentState()) {
|
||||
case WiFiState_e::WiFiState_Disconnected: {
|
||||
//! TODO: Implement
|
||||
break;
|
||||
}
|
||||
case WiFiState_e::WiFiState_ADHOC: {
|
||||
auto wifiState = wifiStateManager.getCurrentState();
|
||||
#ifndef SIM_ENABLED
|
||||
log_d("[SETUP]: Starting Stream Server");
|
||||
streamServer.startStreamServer();
|
||||
#endif // SIM_ENABLED
|
||||
if (wifiState == WiFiState_e::WiFiState_Connected ||
|
||||
wifiState == WiFiState_e::WiFiState_ADHOC) {
|
||||
{
|
||||
log_d("[SETUP]: Starting HTTP Stream Server");
|
||||
auto httpd_result = streamServer.startStreamServer();
|
||||
|
||||
log_d("[SETUP]: Starting TPC Stream Server");
|
||||
auto tpc_result = streamServer.startTCPStreamServer();
|
||||
|
||||
log_d("[SETUP]: Stream Server states: HTTP: %s, TCP: %s",
|
||||
httpd_result
|
||||
? "Failed to connect"
|
||||
: "Connected", // we return 0 in case of successful connection
|
||||
tpc_result ? "Connected" : "Failed to connect");
|
||||
|
||||
log_d("[SETUP]: Starting API Server");
|
||||
apiServer.setup();
|
||||
break;
|
||||
}
|
||||
case WiFiState_e::WiFiState_Connected: {
|
||||
#ifndef SIM_ENABLED
|
||||
log_d("[SETUP]: Starting Stream Server");
|
||||
streamServer.startStreamServer();
|
||||
#endif // SIM_ENABLED
|
||||
log_d("[SETUP]: Starting API Server");
|
||||
apiServer.setup();
|
||||
break;
|
||||
}
|
||||
case WiFiState_e::WiFiState_Connecting: {
|
||||
//! TODO: Implement
|
||||
break;
|
||||
}
|
||||
case WiFiState_e::WiFiState_Error: {
|
||||
//! TODO: Implement
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ETVR_EYE_TRACKER_WEB_API
|
||||
@ -86,22 +75,10 @@ void setup() {
|
||||
Logo::printASCII();
|
||||
ledManager.begin();
|
||||
|
||||
#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 freq = 5000;
|
||||
const int ledChannel = 0;
|
||||
const int resolution = 8;
|
||||
const int dutyCycle = 255;
|
||||
ledcSetup(ledChannel, freq, resolution);
|
||||
ledcAttachPin(1, ledChannel);
|
||||
ledcWrite(ledChannel, dutyCycle);
|
||||
#endif
|
||||
|
||||
#ifndef SIM_ENABLED
|
||||
deviceConfig.attach(cameraHandler);
|
||||
#endif // SIM_ENABLED
|
||||
deviceConfig.load();
|
||||
|
||||
serialManager.init();
|
||||
|
||||
#ifndef ETVR_EYE_TRACKER_USB_API
|
||||
@ -112,6 +89,9 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#ifndef ETVR_EYE_TRACKER_USB_API
|
||||
streamServer.sendTCPFrame();
|
||||
#endif
|
||||
ledManager.handleLED();
|
||||
serialManager.run();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user