Compare commits

...

8 Commits

Author SHA1 Message Date
Lorow
7117acbc6b
Merge 02ad8887c1 into ebecc61292 2025-01-18 16:23:11 +01:00
semantic-release-bot
ebecc61292 chore(minor): 2.8.0 [skip ci]
## [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](f460867497))
2025-01-15 08:45:51 +00:00
Lorow
f460867497
feat: Fix typo in SerialManager (#84) 2025-01-15 09:41:26 +01:00
semantic-release-bot
25f99ce449 chore(minor): 2.7.0 [skip ci]
## [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](c7aef8457e))
2025-01-12 19:33:48 +00:00
Lorow
c7aef8457e
feat: Release 2.7.0 (#83)
Add better support for Babble, make resolution easier to select via ini files configuration

---------

Co-authored-by: Rames The Generic <ramesthegeneric@gmail.com>
Co-authored-by: m-RNA <41933749+m-RNA@users.noreply.github.com>
2025-01-12 20:29:40 +01:00
Lorow
02ad8887c1 Try to fix the linking error 2024-09-05 23:09:51 +02:00
Lorow
88e3b0633b add https://github.com/espressif/esp32-camera.git component as a submodule 2024-09-05 22:56:47 +02:00
Lorow
118343e1a6 Initial changes making OpenIris compatible with espidf framework 2024-09-05 22:53:05 +02:00
28 changed files with 3502 additions and 249 deletions

View File

@ -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

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "ESP/components/esp32-camera"]
path = ESP/components/esp32-camera
url = https://github.com/espressif/esp32-camera.git

View File

@ -3,6 +3,43 @@
[![semantic versioning](https://img.shields.io/badge/semantic%20versioning-2.0.0-green.svg)](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)

3
ESP/CMakeLists.txt Normal file
View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(TestingESPIDF)

@ -0,0 +1 @@
Subproject commit 7aa37d4f22503fdac9ccd449e4678c4894c40055

9
ESP/dependencies.lock Normal file
View File

@ -0,0 +1,9 @@
dependencies:
idf:
component_hash: null
source:
type: idf
version: 4.4.7
manifest_hash: 3e6980bc5fca475de2a39d94c3ea3944c0528040d862609e810d1d8f5ea7f661
target: esp32s3
version: 1.0.0

View File

@ -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

View File

@ -3,9 +3,9 @@
[env]
platform = https://github.com/platformio/platform-espressif32.git
framework = arduino
framework = arduino, espidf
monitor_speed = 115200
custom_firmware_version = 2.6.0
custom_firmware_version = 2.8.0
monitor_rts = 0
monitor_dtr = 0
monitor_filters =
@ -22,8 +22,8 @@ lib_ldf_mode = deep+
;115200 is used for compatability - if you are on windows and want the code to flash faster use 921600
upload_speed = 921600
lib_deps =
# https://github.com/espressif/esp32-camera
esp32-camera
; # https://github.com/espressif/esp32-camera
espressif/esp32-camera
https://github.com/me-no-dev/ESPAsyncWebServer.git
https://github.com/me-no-dev/AsyncTCP.git
https://github.com/bblanchon/ArduinoJson.git
@ -31,6 +31,11 @@ extra_scripts =
pre:tools/customname.py
post:tools/createzip.py
build_flags =
-I ../components/esp32-camera
; esp32-camera esp-idf settings
-D CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO
-D CONFIG_SCCB_CLK_FREQ=100000
-DENABLE_ADHOC=${wifi.enableadhoc}
-DADHOC_CHANNEL=${wifi.adhocchannel}
-DWIFI_CHANNEL=${wifi.channel}
@ -46,6 +51,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

View File

@ -18,3 +18,6 @@ otapassword = "12345678"
[development]
serial_flush_enabled = 0
[cam]
resolution = FRAMESIZE_240X240

View File

@ -20,7 +20,7 @@
"include": "/lib/src"
},
"dependencies": {},
"version": "2.6.0",
"version": "2.8.0",
"frameworks": "arduino",
"platforms": "espressif32"
}

View File

@ -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);

View File

@ -63,8 +63,8 @@ class ProjectConfig : public Preferences, public ISubject<ConfigState_e> {
ssid(std::move(ssid)),
password(std::move(password)),
channel(channel),
adhoc(adhoc),
power(power) {}
power(power),
adhoc(adhoc) {}
std::string name;
std::string ssid;
std::string password;

View File

@ -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) {

View File

@ -63,7 +63,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 +108,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,10 +199,19 @@ 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 {
return camera_sensor->set_framesize(camera_sensor, frameSize);
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
@ -207,6 +220,14 @@ int CameraHandler::setCameraResolution(framesize_t frameSize) {
}
return -1;
}
#else
int CameraHandler::setCameraResolution(framesize_t frameSize) { // By default, use the standard method.
if (camera_sensor->pixformat == PIXFORMAT_JPEG) {
return camera_sensor->set_framesize(camera_sensor, frameSize);
}
return -1;
}
#endif
int CameraHandler::setVFlip(int direction) {
return camera_sensor->set_vflip(camera_sensor, direction);

View File

@ -164,10 +164,6 @@ void BaseAPI::getJsonConfig(AsyncWebServerRequest* request) {
void BaseAPI::setDeviceConfig(AsyncWebServerRequest* request) {
switch (_networkMethodsMap_enum[request->method()]) {
case GET: {
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
break;
}
case POST: {
int params = request->params();
@ -175,7 +171,7 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest* request) {
std::string service;
std::string ota_password;
std::string ota_login;
int ota_port;
int ota_port = 3232;
for (int i = 0; i < params; i++) {
AsyncWebParameter* param = request->getParam(i);
@ -203,6 +199,11 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest* request) {
projectConfig.setMDNSConfig(hostname, service, true);
request->send(200, MIMETYPE_JSON,
"{\"msg\":\"Done. Device Config has been set.\"}");
break;
}
default: {
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
}
}
}
@ -225,6 +226,10 @@ void BaseAPI::setWiFiTXPower(AsyncWebServerRequest* request) {
projectConfig.wifiTxPowerConfigSave();
request->send(200, MIMETYPE_JSON,
"{\"msg\":\"Done. TX Power has been set.\"}");
break;
}
default: {
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
}
}
}
@ -234,6 +239,7 @@ void BaseAPI::rebootDevice(AsyncWebServerRequest* request) {
case GET: {
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Rebooting Device\"}");
OpenIrisTasks::ScheduleRestart(2000);
break;
}
default: {
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
@ -248,6 +254,7 @@ void BaseAPI::factoryReset(AsyncWebServerRequest* request) {
log_d("Factory Reset");
projectConfig.reset();
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Factory Reset\"}");
break;
}
default: {
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");

View File

@ -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>
@ -43,7 +42,6 @@ constexpr int HTTP_ANY = 0b01111111;
class BaseAPI {
protected:
std::string api_url;
bool _authRequired;
static const char* MIMETYPE_HTML;
@ -54,7 +52,6 @@ class BaseAPI {
/* static const char *MIMETYPE_ICO; */
static const char* MIMETYPE_JSON;
protected:
/* Commands */
void setWiFi(AsyncWebServerRequest* request);
void setWiFiTXPower(AsyncWebServerRequest* request);
@ -100,33 +97,36 @@ 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;
#ifndef SIM_ENABLED
CameraHandler &camera;
#endif // SIM_ENABLED
/// @brief Local instance of the AsyncWebServer - so that we dont need to use
/// new and delete
AsyncWebServer server;
ProjectConfig& projectConfig;
public :
BaseAPI(ProjectConfig& projectConfig,
#ifndef SIM_ENABLED
CameraHandler& camera,
CameraHandler& camera;
#endif // SIM_ENABLED
const std::string& api_url,
std::string api_url;
public:
BaseAPI(ProjectConfig& projectConfig,
#ifndef SIM_ENABLED
int port = 81
CameraHandler& camera,
#endif // SIM_ENABLED
const std::string& api_url,
#ifndef SIM_ENABLED
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

View File

@ -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();
@ -85,30 +84,26 @@ void APIServer::addRouteMap(const std::string& index,
}
void APIServer::handleRequest(AsyncWebServerRequest* request) {
try {
// Get the route
log_i("Request URL: %s", request->url().c_str());
log_i("Request: %s", request->pathArg(0).c_str());
log_i("Request: %s", request->pathArg(1).c_str());
// Get the route
log_i("Request URL: %s", request->url().c_str());
log_i("Request: %s", request->pathArg(0).c_str());
log_i("Request: %s", request->pathArg(1).c_str());
auto it_map = route_map.find(request->pathArg(0).c_str());
auto it_method = it_map->second.find(request->pathArg(1).c_str());
auto it_map = route_map.find(request->pathArg(0).c_str());
auto it_method = it_map->second.find(request->pathArg(1).c_str());
if (it_map != route_map.end()) {
if (it_method != it_map->second.end()) {
log_d("We are trying to execute the function");
(*this.*(it_method->second))(request);
} else {
log_e("Invalid Command");
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Command\"}");
return;
}
if (it_map != route_map.end()) {
if (it_method != it_map->second.end()) {
log_d("We are trying to execute the function");
(*this.*(it_method->second))(request);
} else {
log_e("Invalid Map Index");
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Map Index\"}");
log_e("Invalid Command");
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Command\"}");
return;
}
} catch (...) {
log_e("Error handling request");
} else {
log_e("Invalid Map Index");
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Map Index\"}");
return;
}
}

View File

@ -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");
@ -15,7 +16,6 @@ bool MDNSHandler::startMDNS() {
mdnsStateManager.setState(MDNSState_e::MDNSState_Starting);
MDNS.addService(service.c_str(), "_tcp", 80);
char port[20];
//! Add service needs leading _ on ESP32 implementation for some reason
//! (according to the docs)
MDNS.addServiceTxt(service.c_str(), "_tcp", "stream_port", "80");

View File

@ -1,114 +1,113 @@
#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: "
"%ld.%06ld\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;
}
StreamServer::StreamServer(const int STREAM_PORT)
: STREAM_SERVER_PORT(STREAM_PORT) {}
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;
}
}

View File

@ -10,23 +10,19 @@
#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"
#include "fb_gfx.h"
#include "img_converters.h"
namespace StreamHelpers
{
esp_err_t stream(httpd_req_t *req);
namespace StreamHelpers {
esp_err_t stream(httpd_req_t* req);
}
class StreamServer
{
class StreamServer {
private:
httpd_handle_t camera_stream = nullptr;
int STREAM_SERVER_PORT;
private:
httpd_handle_t camera_stream = nullptr;
int STREAM_SERVER_PORT;
public:
StreamServer(const int STREAM_PORT = 80);
int startStreamServer();
public:
StreamServer(const int STREAM_PORT = 80);
int startStreamServer();
};
#endif // STREAM_SERVER_HPP
#endif // STREAM_SERVER_HPP

View File

@ -18,10 +18,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 +34,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 +49,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 +137,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 +156,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");

View File

@ -29,11 +29,11 @@ class WiFiHandler : public IObserver<ConfigState_e> {
ProjectConfig& configManager;
bool _enable_adhoc;
std::string ssid;
std::string password;
uint8_t channel;
uint8_t power;
bool _enable_adhoc;
};
#endif // WIFIHANDLER_HPP

7
ESP/min_spiffs.csv Normal file
View File

@ -0,0 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1E0000,
app1, app, ota_1, 0x1F0000,0x1E0000,
spiffs, data, spiffs, 0x3D0000,0x20000,
coredump, data, coredump,0x3F0000,0x10000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x1E0000
5 app1 app ota_1 0x1F0000 0x1E0000
6 spiffs data spiffs 0x3D0000 0x20000
7 coredump data coredump 0x3F0000 0x10000

7
ESP/sdkconfig.defaults Normal file
View File

@ -0,0 +1,7 @@
CONFIG_AUTOSTART_ARDUINO=y
# CONFIG_WS2812_LED_ENABLE is not set
CONFIG_FREERTOS_HZ=1000
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="8MB"

1567
ESP/sdkconfig.xiaosenses3 Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

7
ESP/src/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
# FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
# idf_component_register(SRCS ${app_sources})
idf_component_register(SRCS "main.cpp")

View File

@ -45,37 +45,15 @@ 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();
if (wifiState == WiFiState_e::WiFiState_ADHOC ||
wifiState == WiFiState_e::WiFiState_Connected) {
#ifndef SIM_ENABLED
log_d("[SETUP]: Starting Stream Server");
streamServer.startStreamServer();
log_d("[SETUP]: Starting Stream Server");
streamServer.startStreamServer();
#endif // SIM_ENABLED
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;
}
log_d("[SETUP]: Starting API Server");
apiServer.setup();
}
}
#endif // ETVR_EYE_TRACKER_WEB_API
@ -86,16 +64,17 @@ 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
#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);