mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-11-04 15:39:42 +08:00
Feature/refactor architecture (#5)
* Refactor everything under one namespace, turn most things into classes, add support for selecting ROI in cameraHandler, add state management * Remove unsued main.py file - other projects are working on that implementation
This commit is contained in:
parent
b6b44dc978
commit
b788823375
13
ESP/include/GlobalVars.h
Normal file
13
ESP/include/GlobalVars.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#ifndef GLOBALVARS_H
|
||||
#define GLOBALVARS_H
|
||||
|
||||
#include "StateManager.h"
|
||||
#include "LEDManager.h"
|
||||
#include "cameraHandler.h"
|
||||
|
||||
extern OpenIris::LEDManager ledManager;
|
||||
extern OpenIris::StateManager stateManager;
|
||||
extern OpenIris::CameraHandler cameraHandler;
|
||||
|
||||
#endif
|
||||
@ -1,20 +1,19 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace LEDManager{
|
||||
namespace OpenIris{
|
||||
|
||||
enum Status {
|
||||
ConnectingToWifi = 1,
|
||||
ConnectingToWifiError = 2,
|
||||
ConnectingToWifiSuccess = 3,
|
||||
ServerError = 3,
|
||||
CameraError = 4
|
||||
class LEDManager{
|
||||
private:
|
||||
uint8_t ledPin;
|
||||
|
||||
public:
|
||||
explicit LEDManager(uint8_t pin) : ledPin(pin) {}
|
||||
|
||||
void setupLED() const;
|
||||
void on() const;
|
||||
void off() const;
|
||||
void blink(unsigned int time);
|
||||
void displayStatus();
|
||||
};
|
||||
|
||||
extern uint8_t ledPin;
|
||||
|
||||
void setupLED();
|
||||
void on();
|
||||
void off();
|
||||
void blink(unsigned int time);
|
||||
void displayPattern(Status status);
|
||||
}
|
||||
@ -1,57 +1,59 @@
|
||||
#pragma once
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
namespace OpenIris{
|
||||
class OTA {
|
||||
private:
|
||||
unsigned long bootTimestamp = 0;
|
||||
bool isOtaEnabled = true;
|
||||
public:
|
||||
void SetupOTA(const char *OTAPassword, uint16_t OTAServerPort) {
|
||||
Serial.println("Setting up OTA updates");
|
||||
|
||||
namespace OTA{
|
||||
|
||||
unsigned long boot_timestamp = 0;
|
||||
bool is_ota_eabled = true;
|
||||
|
||||
void SetupOTA(const char* OTAPassword, uint16_t OTAServerPort){
|
||||
Serial.println("Setting up OTA updates");
|
||||
|
||||
if(OTAPassword == '\0'){
|
||||
Serial.println("THE PASSWORD IS REQUIRED, [[ABORTING]]");
|
||||
return;
|
||||
}
|
||||
ArduinoOTA.setPort(OTAServerPort);
|
||||
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
type = "sketch";
|
||||
else // U_SPIFFS
|
||||
type = "filesystem";
|
||||
})
|
||||
.onEnd([]() {
|
||||
Serial.println("OTA updated finished successfully!");
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
||||
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
||||
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
||||
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
||||
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
||||
});
|
||||
Serial.println("Starting up basic OTA server");
|
||||
Serial.println("OTA will be live for 30s, after which it will be disabled until restart");
|
||||
ArduinoOTA.begin();
|
||||
boot_timestamp = millis();
|
||||
}
|
||||
|
||||
void HandleOTAUpdate(){
|
||||
if(is_ota_eabled){
|
||||
if(boot_timestamp + 30000 < millis()){
|
||||
// we're disabling ota after first 30sec so that nothing bad happens during playtime
|
||||
is_ota_eabled = false;
|
||||
Serial.println("From now on, OTA is disabled");
|
||||
if (OTAPassword == nullptr) {
|
||||
Serial.println("THE PASSWORD IS REQUIRED, [[ABORTING]]");
|
||||
return;
|
||||
}
|
||||
ArduinoOTA.handle();
|
||||
ArduinoOTA.setPort(OTAServerPort);
|
||||
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
type = "sketch";
|
||||
else // U_SPIFFS
|
||||
type = "filesystem";
|
||||
})
|
||||
.onEnd([]() {
|
||||
Serial.println("OTA updated finished successfully!");
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
||||
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
||||
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
||||
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
||||
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
||||
});
|
||||
Serial.println("Starting up basic OTA server");
|
||||
Serial.println("OTA will be live for 30s, after which it will be disabled until restart");
|
||||
ArduinoOTA.begin();
|
||||
bootTimestamp = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleOTAUpdate() {
|
||||
if (isOtaEnabled) {
|
||||
if (bootTimestamp + 30000 < millis()) {
|
||||
// we're disabling ota after first 30sec so that nothing bad happens during playtime
|
||||
isOtaEnabled = false;
|
||||
Serial.println("From now on, OTA is disabled");
|
||||
return;
|
||||
}
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
22
ESP/include/StateManager.h
Normal file
22
ESP/include/StateManager.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace OpenIris{
|
||||
enum State {
|
||||
Starting = 1,
|
||||
ConnectingToWifi = 2,
|
||||
ConnectingToWifiError = 3,
|
||||
ConnectingToWifiSuccess = 4,
|
||||
ServerError = 5,
|
||||
CameraError = 6
|
||||
};
|
||||
|
||||
class StateManager{
|
||||
public:
|
||||
StateManager() : current_state(Starting) {}
|
||||
void setState(State state);
|
||||
State getCurrentState();
|
||||
private:
|
||||
State current_state;
|
||||
};
|
||||
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include <WiFi.h>
|
||||
#include "pinout.h"
|
||||
|
||||
namespace WiFiHandler {
|
||||
void setupWifi(const char* ssid, const char* password);
|
||||
namespace OpenIris {
|
||||
namespace WiFiHandler {
|
||||
void setupWifi(const char *ssid, const char *password);
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,20 @@
|
||||
#pragma once
|
||||
#include "pinout.h"
|
||||
#include "esp_camera.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
namespace CameraHandler{
|
||||
namespace OpenIris{
|
||||
class CameraHandler {
|
||||
private:
|
||||
sensor_t* camera_sensor;
|
||||
camera_config_t config;
|
||||
|
||||
extern sensor_t* camera_sensor;
|
||||
|
||||
int setupCamera();
|
||||
int setCameraResolution(framesize_t framesize);
|
||||
int setVFlip(int direction);
|
||||
int setHFlip(int direction);
|
||||
public:
|
||||
int setupCamera();
|
||||
int setCameraResolution(framesize_t frameSize);
|
||||
int setVFlip(int direction);
|
||||
int setHFlip(int direction);
|
||||
int setVieWindow(int offsetX, int offsetY, int outputX, int outputY);
|
||||
};
|
||||
}
|
||||
@ -1,9 +1,21 @@
|
||||
#pragma once
|
||||
#define PART_BOUNDARY "123456789000000000000987654321"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include "esp_http_server.h"
|
||||
|
||||
namespace HttpdHandler{
|
||||
esp_err_t stream_handler(httpd_req_t *req);
|
||||
esp_err_t parse_get(httpd_req_t *req, char **obuf);
|
||||
esp_err_t command_handler(httpd_req_t *req);
|
||||
int startStreamServer();
|
||||
namespace OpenIris{
|
||||
namespace HTTPHelpers {
|
||||
esp_err_t stream_handler(httpd_req_t *req);
|
||||
esp_err_t parse_get(httpd_req_t *req, char **obuf);
|
||||
esp_err_t command_handler(httpd_req_t *req);
|
||||
}
|
||||
|
||||
class HTTPDHandler{
|
||||
private:
|
||||
httpd_handle_t camera_httpd = nullptr;
|
||||
httpd_handle_t control_httpd = nullptr;
|
||||
public:
|
||||
int startStreamServer();
|
||||
};
|
||||
}
|
||||
@ -20,8 +20,8 @@ build_flags =
|
||||
build_unflags = -Os
|
||||
;upload_port = 192.168.1.43 ;replace this with your own ip
|
||||
;upload_protocol = espota
|
||||
upload_flags =
|
||||
--auth=Password
|
||||
;upload_flags =
|
||||
; --auth=Password
|
||||
board_build.partitions = min_spiffs.csv
|
||||
|
||||
lib_deps =
|
||||
|
||||
@ -1,44 +1,24 @@
|
||||
# include "LEDManager.h"
|
||||
|
||||
namespace LEDManager {
|
||||
uint8_t ledPin = 33;
|
||||
void OpenIris::LEDManager::setupLED() const {
|
||||
pinMode(ledPin, OUTPUT);
|
||||
}
|
||||
|
||||
void setupLED(){
|
||||
pinMode(ledPin, OUTPUT);
|
||||
off();
|
||||
}
|
||||
void OpenIris::LEDManager::on() const {
|
||||
digitalWrite(ledPin, LOW);
|
||||
|
||||
void on(){
|
||||
digitalWrite(ledPin, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void off(){
|
||||
digitalWrite(ledPin, HIGH);
|
||||
}
|
||||
void OpenIris::LEDManager::off() const {
|
||||
digitalWrite(ledPin, HIGH);
|
||||
}
|
||||
|
||||
void blink(unsigned int time){
|
||||
on();
|
||||
delay(time);
|
||||
off();
|
||||
delay(time);
|
||||
}
|
||||
void OpenIris::LEDManager::blink(unsigned int time) {
|
||||
on();
|
||||
delay(time);
|
||||
off();
|
||||
}
|
||||
|
||||
void displayPattern(Status status){
|
||||
if(status == Status::ConnectingToWifi){
|
||||
blink(1600);
|
||||
delay(1600);
|
||||
}
|
||||
if(status == Status::ConnectingToWifiError){
|
||||
for (int i = 0; i < 5; i++){
|
||||
blink(1000);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
if(status == Status::ConnectingToWifiSuccess){
|
||||
for (int i = 0; i < 2; i++){
|
||||
blink(1600);
|
||||
delay(1600);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void OpenIris::LEDManager::displayStatus() {
|
||||
|
||||
}
|
||||
|
||||
11
ESP/src/StateManager.cpp
Normal file
11
ESP/src/StateManager.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "StateManager.h"
|
||||
|
||||
namespace OpenIris{
|
||||
void StateManager::setState(State state){
|
||||
current_state=state;
|
||||
}
|
||||
|
||||
State StateManager::getCurrentState(){
|
||||
return current_state;
|
||||
}
|
||||
}
|
||||
@ -1,75 +1,83 @@
|
||||
#include "cameraHandler.h"
|
||||
|
||||
namespace CameraHandler{
|
||||
sensor_t* camera_sensor = NULL;
|
||||
|
||||
int setupCamera(){
|
||||
Serial.print("Setting up camera \r\n");
|
||||
int OpenIris::CameraHandler::setupCamera(){
|
||||
Serial.print("Setting up camera \r\n");
|
||||
|
||||
camera_config_t config;
|
||||
config.ledc_channel = LEDC_CHANNEL_0;
|
||||
config.ledc_timer = LEDC_TIMER_0;
|
||||
config.pin_d0 = Y2_GPIO_NUM;
|
||||
config.pin_d1 = Y3_GPIO_NUM;
|
||||
config.pin_d2 = Y4_GPIO_NUM;
|
||||
config.pin_d3 = Y5_GPIO_NUM;
|
||||
config.pin_d4 = Y6_GPIO_NUM;
|
||||
config.pin_d5 = Y7_GPIO_NUM;
|
||||
config.pin_d6 = Y8_GPIO_NUM;
|
||||
config.pin_d7 = Y9_GPIO_NUM;
|
||||
config.pin_xclk = XCLK_GPIO_NUM;
|
||||
config.pin_pclk = PCLK_GPIO_NUM;
|
||||
config.pin_vsync = VSYNC_GPIO_NUM;
|
||||
config.pin_href = HREF_GPIO_NUM;
|
||||
config.pin_sscb_sda = SIOD_GPIO_NUM;
|
||||
config.pin_sscb_scl = SIOC_GPIO_NUM;
|
||||
config.pin_pwdn = PWDN_GPIO_NUM;
|
||||
config.pin_reset = RESET_GPIO_NUM;
|
||||
config.xclk_freq_hz = 20000000;
|
||||
config.pixel_format = PIXFORMAT_JPEG;
|
||||
config.ledc_channel = LEDC_CHANNEL_0;
|
||||
config.ledc_timer = LEDC_TIMER_0;
|
||||
config.pin_d0 = Y2_GPIO_NUM;
|
||||
config.pin_d1 = Y3_GPIO_NUM;
|
||||
config.pin_d2 = Y4_GPIO_NUM;
|
||||
config.pin_d3 = Y5_GPIO_NUM;
|
||||
config.pin_d4 = Y6_GPIO_NUM;
|
||||
config.pin_d5 = Y7_GPIO_NUM;
|
||||
config.pin_d6 = Y8_GPIO_NUM;
|
||||
config.pin_d7 = Y9_GPIO_NUM;
|
||||
config.pin_xclk = XCLK_GPIO_NUM;
|
||||
config.pin_pclk = PCLK_GPIO_NUM;
|
||||
config.pin_vsync = VSYNC_GPIO_NUM;
|
||||
config.pin_href = HREF_GPIO_NUM;
|
||||
config.pin_sscb_sda = SIOD_GPIO_NUM;
|
||||
config.pin_sscb_scl = SIOC_GPIO_NUM;
|
||||
config.pin_pwdn = PWDN_GPIO_NUM;
|
||||
config.pin_reset = RESET_GPIO_NUM;
|
||||
config.xclk_freq_hz = 20000000;
|
||||
config.pixel_format = PIXFORMAT_JPEG;
|
||||
|
||||
if (psramFound()){
|
||||
Serial.println("Found psram, setting the UXGA image quality");
|
||||
config.frame_size = FRAMESIZE_240X240;
|
||||
config.jpeg_quality = 10;
|
||||
config.fb_count = 2;
|
||||
}else{
|
||||
Serial.println("Did not find psram, setting svga quality");
|
||||
config.frame_size = FRAMESIZE_SVGA;
|
||||
config.jpeg_quality = 12;
|
||||
config.fb_count = 1;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_camera_init(&config);
|
||||
|
||||
camera_sensor = esp_camera_sensor_get();
|
||||
|
||||
if (err != ESP_OK){
|
||||
Serial.printf("Camera initialization failed with error: 0x%x \r\n", err);
|
||||
return -1;
|
||||
}else{
|
||||
Serial.println("Sucessfully initialized the camera!");
|
||||
return 0;
|
||||
}
|
||||
if (psramFound()){
|
||||
Serial.println("Found psram, setting the 176x144 image quality");
|
||||
config.frame_size = FRAMESIZE_QCIF;
|
||||
config.jpeg_quality = 10;
|
||||
config.fb_count = 2;
|
||||
}else{
|
||||
Serial.println("Did not find psram, setting svga quality");
|
||||
config.frame_size = FRAMESIZE_SVGA;
|
||||
config.jpeg_quality = 12;
|
||||
config.fb_count = 1;
|
||||
}
|
||||
|
||||
int setCameraResolution(framesize_t framesize){
|
||||
if(camera_sensor->pixformat == PIXFORMAT_JPEG){
|
||||
try{
|
||||
return camera_sensor->set_framesize(camera_sensor, framesize);
|
||||
}catch(...){
|
||||
// they sent us a malformed or unsupported framesize - rather than crash - tell them about it
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
esp_err_t err = esp_camera_init(&config);
|
||||
|
||||
camera_sensor = esp_camera_sensor_get();
|
||||
camera_sensor->set_special_effect(camera_sensor, 2);
|
||||
|
||||
if (err != ESP_OK){
|
||||
Serial.printf("Camera initialization failed with error: 0x%x \r\n", err);
|
||||
// TODO add led blinking here
|
||||
return -1;
|
||||
}else{
|
||||
Serial.println("Sucessfully initialized the camera!");
|
||||
// TODO add led blinking here
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int setVFlip(int direction){
|
||||
return camera_sensor->set_vflip(camera_sensor, direction);
|
||||
int OpenIris::CameraHandler::setCameraResolution(framesize_t frameSize){
|
||||
if(camera_sensor->pixformat == PIXFORMAT_JPEG){
|
||||
try{
|
||||
return camera_sensor->set_framesize(camera_sensor, frameSize);
|
||||
}catch(...){
|
||||
// they sent us a malformed or unsupported frameSize - rather than crash - tell them about it
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int setHFlip(int direction){
|
||||
return camera_sensor->set_hmirror(camera_sensor, direction);
|
||||
}
|
||||
int OpenIris::CameraHandler::setVFlip(int direction){
|
||||
return camera_sensor->set_vflip(camera_sensor, direction);
|
||||
}
|
||||
|
||||
int OpenIris::CameraHandler::setHFlip(int direction){
|
||||
return camera_sensor->set_hmirror(camera_sensor, direction);
|
||||
}
|
||||
|
||||
int OpenIris::CameraHandler::setVieWindow(int offsetX, int offsetY, int outputX, int outputY) {
|
||||
// we're only providing these parameters as these are the only ones that actually affect the ov2640
|
||||
// TODO: if we're going to support different sensors - this will have to be moved to per-sensor implementation 'cause
|
||||
// TODO: manufacturers handle it differently each time
|
||||
|
||||
// we're doubling the totalX and totalY parameters to make it easier for end user to adjust the eye ROI
|
||||
// it also seems to produce a cleaner image
|
||||
return camera_sensor->set_res_raw(camera_sensor, 0, 0, 0, 0, offsetX, offsetY, outputX * 2, outputY * 2, outputX, outputY, true, true);
|
||||
}
|
||||
@ -1,177 +1,170 @@
|
||||
#include <Arduino.h>
|
||||
#include "cameraHandler.h"
|
||||
#include "GlobalVars.h"
|
||||
#include "httpdHandler.h"
|
||||
|
||||
#define PART_BOUNDARY "123456789000000000000987654321"
|
||||
constexpr static char* STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
constexpr static char* STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
constexpr static char* STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\nX-Timestamp: %d.%06d\r\n\r\n";
|
||||
|
||||
namespace HttpdHandler {
|
||||
httpd_handle_t camera_httpd = NULL;
|
||||
httpd_handle_t control_httpd = NULL;
|
||||
esp_err_t OpenIris::HTTPHelpers::stream_handler(httpd_req_t *req) {
|
||||
camera_fb_t *fb = NULL;
|
||||
struct timeval _timestamp;
|
||||
|
||||
static const char*_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
|
||||
static const char*_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
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 res = ESP_OK;
|
||||
|
||||
esp_err_t stream_handler(httpd_req_t *req) {
|
||||
camera_fb_t *fb = NULL;
|
||||
struct timeval _timestamp;
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t *_jpg_buf = NULL;
|
||||
|
||||
esp_err_t res = ESP_OK;
|
||||
char *part_buf[128];
|
||||
|
||||
size_t _jpg_buf_len = 0;
|
||||
uint8_t *_jpg_buf = NULL;
|
||||
static int64_t last_frame = 0;
|
||||
if (!last_frame)
|
||||
last_frame = esp_timer_get_time();
|
||||
|
||||
char *part_buf[128];
|
||||
|
||||
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; Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n", "*");
|
||||
httpd_resp_set_hdr(req, "X-Framerate", "60");
|
||||
|
||||
while (true) {
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb){
|
||||
ESP_LOGE(TAG, "Camera capture failed");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
else{
|
||||
_timestamp.tv_sec = fb->timestamp.tv_sec;
|
||||
_timestamp.tv_usec = fb->timestamp.tv_usec;
|
||||
if (fb->format != PIXFORMAT_JPEG){
|
||||
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
if (!jpeg_converted){
|
||||
ESP_LOGE(TAG, "JPEG compression failed");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
}
|
||||
else{
|
||||
_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;
|
||||
}
|
||||
}
|
||||
last_frame = 0;
|
||||
res = httpd_resp_set_type(req, STREAM_CONTENT_TYPE);
|
||||
if (res != ESP_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_err_t parse_get(httpd_req_t *req, char **obuf) {
|
||||
char *buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin; Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n", "*");
|
||||
httpd_resp_set_hdr(req, "X-Framerate", "60");
|
||||
|
||||
buf_len = httpd_req_get_url_query_len(req) + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = (char *)malloc(buf_len);
|
||||
if (!buf) {
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
||||
*obuf = buf;
|
||||
return ESP_OK;
|
||||
}
|
||||
free(buf);
|
||||
while (true) {
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb){
|
||||
ESP_LOGE(TAG, "Camera capture failed");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
else{
|
||||
_timestamp.tv_sec = fb->timestamp.tv_sec;
|
||||
_timestamp.tv_usec = fb->timestamp.tv_usec;
|
||||
if (fb->format != PIXFORMAT_JPEG){
|
||||
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
if (!jpeg_converted){
|
||||
ESP_LOGE(TAG, "JPEG compression failed");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
}
|
||||
else{
|
||||
_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;
|
||||
}
|
||||
}
|
||||
last_frame = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
esp_err_t OpenIris::HTTPHelpers::parse_get(httpd_req_t *req, char **obuf) {
|
||||
char *buf = nullptr;
|
||||
size_t buf_len = 0;
|
||||
|
||||
buf_len = httpd_req_get_url_query_len(req) + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = (char *)malloc(buf_len);
|
||||
if (!buf) {
|
||||
httpd_resp_send_500(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
||||
*obuf = buf;
|
||||
return ESP_OK;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t OpenIris::HTTPHelpers::command_handler(httpd_req_t *req) {
|
||||
char *buf = nullptr;
|
||||
char variable[32];
|
||||
char value[32];
|
||||
|
||||
if (parse_get(req, &buf) != ESP_OK)
|
||||
return ESP_FAIL;
|
||||
if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) != ESP_OK ||
|
||||
httpd_query_key_value(buf, "val", value, sizeof(value)) != ESP_OK) {
|
||||
free(buf);
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(buf);
|
||||
|
||||
esp_err_t command_handler(httpd_req_t *req) {
|
||||
char *buf = NULL;
|
||||
char variable[32];
|
||||
char value[32];
|
||||
int val = atoi(value);
|
||||
int res = 0;
|
||||
if (!strcmp(variable, "framesize"))
|
||||
res = cameraHandler.setCameraResolution((framesize_t)val);
|
||||
else if (!strcmp(variable, "hmirror"))
|
||||
res = cameraHandler.setHFlip(val);
|
||||
else if (!strcmp(variable, "vflip"))
|
||||
res = cameraHandler.setVFlip(val);
|
||||
else
|
||||
res = -1; // invalid command
|
||||
|
||||
if (parse_get(req, &buf) != ESP_OK)
|
||||
return ESP_FAIL;
|
||||
if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) != ESP_OK ||
|
||||
httpd_query_key_value(buf, "val", value, sizeof(value)) != ESP_OK) {
|
||||
free(buf);
|
||||
httpd_resp_send_404(req);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(buf);
|
||||
if (res < 0)
|
||||
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Command not supported");
|
||||
|
||||
int val = atoi(value);
|
||||
int res = 0;
|
||||
if (!strcmp(variable, "framesize"))
|
||||
res = CameraHandler::setCameraResolution((framesize_t)val);
|
||||
else if (!strcmp(variable, "hmirror"))
|
||||
res = CameraHandler::setHFlip(val);
|
||||
else if (!strcmp(variable, "vflip"))
|
||||
res = CameraHandler::setVFlip(val);
|
||||
else
|
||||
res = -1; // invalid command
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
return httpd_resp_send(req, nullptr, 0);
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Command not supported");
|
||||
int OpenIris::HTTPDHandler::startStreamServer(){
|
||||
Serial.println("Setting up the server");
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
return httpd_resp_send(req, NULL, 0);
|
||||
}
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.max_uri_handlers = 3;
|
||||
|
||||
int startStreamServer(){
|
||||
Serial.println("Setting up the server");
|
||||
httpd_uri_t control_page = {
|
||||
.uri = "/control/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &OpenIris::HTTPHelpers::command_handler,
|
||||
.user_ctx = nullptr
|
||||
};
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.max_uri_handlers = 3;
|
||||
httpd_uri_t stream_page = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &OpenIris::HTTPHelpers::stream_handler,
|
||||
.user_ctx = nullptr
|
||||
};
|
||||
|
||||
httpd_uri_t control_page = {
|
||||
.uri = "/control/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &command_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
int streamer_status = httpd_start(&control_httpd, &config);
|
||||
|
||||
httpd_uri_t stream_page = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = &stream_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
config.server_port += 1;
|
||||
config.ctrl_port += 1;
|
||||
|
||||
int streamer_status = httpd_start(&control_httpd, &config);
|
||||
|
||||
config.server_port += 1;
|
||||
config.ctrl_port += 1;
|
||||
|
||||
int cmd_controller_status = httpd_start(&camera_httpd, &config);
|
||||
int cmd_controller_status = httpd_start(&camera_httpd, &config);
|
||||
|
||||
if (streamer_status != ESP_OK || cmd_controller_status != ESP_OK)
|
||||
return -1;
|
||||
else {
|
||||
httpd_register_uri_handler(control_httpd, &control_page);
|
||||
httpd_register_uri_handler(camera_httpd, &stream_page);
|
||||
Serial.println("Server is ready to serve!");
|
||||
return 0;
|
||||
}
|
||||
if (streamer_status != ESP_OK || cmd_controller_status != ESP_OK)
|
||||
return -1;
|
||||
else {
|
||||
httpd_register_uri_handler(control_httpd, &control_page);
|
||||
httpd_register_uri_handler(camera_httpd, &stream_page);
|
||||
Serial.println("Server is ready to serve!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -6,24 +6,29 @@
|
||||
#include "LEDManager.h"
|
||||
#include "httpdHandler.h"
|
||||
#include "OTA.h"
|
||||
#include "StateManager.h"
|
||||
|
||||
auto ota = OpenIris::OTA();
|
||||
auto ledManager = OpenIris::LEDManager(33);
|
||||
auto cameraHandler = OpenIris::CameraHandler();
|
||||
auto stateManager = OpenIris::StateManager();
|
||||
auto httpdHandler = OpenIris::HTTPDHandler();
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true);
|
||||
Serial.println();
|
||||
|
||||
Serial.println("setting up led");
|
||||
LEDManager::setupLED();
|
||||
// todo add blink handling
|
||||
CameraHandler::setupCamera();
|
||||
WiFiHandler::setupWifi(ssid, password);
|
||||
// todo add blink handling
|
||||
HttpdHandler::startStreamServer();
|
||||
LEDManager::on();
|
||||
|
||||
OTA::SetupOTA(OTAPassword, OTAServerPort);
|
||||
ledManager.setupLED();
|
||||
cameraHandler.setupCamera();
|
||||
OpenIris::WiFiHandler::setupWifi(ssid, password);
|
||||
httpdHandler.startStreamServer();
|
||||
ledManager.on();
|
||||
|
||||
ota.SetupOTA(OTAPassword, OTAServerPort);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
OTA::HandleOTAUpdate();
|
||||
ota.HandleOTAUpdate();
|
||||
ledManager.displayStatus();
|
||||
}
|
||||
@ -1,39 +1,37 @@
|
||||
#include "WifiHandler.h"
|
||||
#include "LEDManager.h"
|
||||
#include "GlobalVars.h"
|
||||
|
||||
namespace WiFiHandler {
|
||||
void setupWifi(const char* ssid, const char* password){
|
||||
Serial.println("Initializing connection to wifi");
|
||||
void OpenIris::WiFiHandler::setupWifi(const char* ssid, const char* password){
|
||||
Serial.println("Initializing connection to wifi");
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
Serial.print("connecting");
|
||||
int time_spent_connecting = 0;
|
||||
int connection_timeout = 6400;
|
||||
int wifi_status = WiFi.status();
|
||||
Serial.print("connecting");
|
||||
int time_spent_connecting = 0;
|
||||
int connection_timeout = 6400;
|
||||
int wifi_status = WiFi.status();
|
||||
|
||||
while (time_spent_connecting < connection_timeout || wifi_status != WL_CONNECTED){
|
||||
wifi_status = WiFi.status();
|
||||
Serial.print(".");
|
||||
LEDManager::blink(LEDManager::Status::ConnectingToWifi);
|
||||
time_spent_connecting += 1600;
|
||||
delay(1600);
|
||||
}
|
||||
|
||||
if(wifi_status == WL_CONNECTED){
|
||||
LEDManager::blink(LEDManager::Status::ConnectingToWifiSuccess);
|
||||
delay(1600);
|
||||
Serial.print("\n\rWiFi connected\n\r");
|
||||
Serial.print("ESP will be streaming under 'http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.print(":81/\r\n");
|
||||
Serial.print("ESP will be accepting commands under 'http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.print(":80/control\r\n");
|
||||
}
|
||||
else{
|
||||
LEDManager::blink(LEDManager::Status::ConnectingToWifiError);
|
||||
return;
|
||||
}
|
||||
while (time_spent_connecting < connection_timeout || wifi_status != WL_CONNECTED){
|
||||
wifi_status = WiFi.status();
|
||||
Serial.print(".");
|
||||
stateManager.setState(OpenIris::State::ConnectingToWifi);
|
||||
time_spent_connecting += 1600;
|
||||
delay(1600);
|
||||
}
|
||||
}
|
||||
|
||||
if(wifi_status == WL_CONNECTED){
|
||||
stateManager.setState(OpenIris::State::ConnectingToWifiSuccess);
|
||||
delay(1600);
|
||||
Serial.print("\n\rWiFi connected\n\r");
|
||||
Serial.print("ESP will be streaming under 'http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.print(":81/\r\n");
|
||||
Serial.print("ESP will be accepting commands under 'http://");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.print(":80/control\r\n");
|
||||
}
|
||||
else{
|
||||
stateManager.setState(OpenIris::State::ConnectingToWifiError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
46
main.py
46
main.py
@ -1,46 +0,0 @@
|
||||
import cv2 as cv
|
||||
import threading
|
||||
import numpy as np
|
||||
|
||||
|
||||
class ThreadedCamera:
|
||||
def __init__(self, camera_index=0):
|
||||
self.cam = cv.VideoCapture(camera_index)
|
||||
self.status = False
|
||||
self.frame = None
|
||||
|
||||
if not self.cam.isOpened():
|
||||
raise Exception("Could not connect to a camera")
|
||||
|
||||
self.cam.set(cv.CAP_PROP_BUFFERSIZE, 3)
|
||||
self.camera_thread = threading.Thread(target=self.update, args=(), daemon=True)
|
||||
self.camera_thread.start()
|
||||
|
||||
def update(self):
|
||||
while True:
|
||||
ret, frame = self.cam.read()
|
||||
if not ret:
|
||||
print("something went wrong with reading frame, exiting")
|
||||
break
|
||||
|
||||
self.status, self.frame = ret, frame
|
||||
|
||||
def display_frame(self):
|
||||
if self.frame is not None:
|
||||
image = cv.cvtColor(self.frame, cv.COLOR_BGR2GRAY)
|
||||
image = cv.resize(image, dsize=(int(self.frame.shape[1]/3), int(self.frame.shape[0]/3)))
|
||||
cv.imshow("frame", image)
|
||||
fps = self.cam.get(cv.CAP_PROP_FPS)
|
||||
print("Frames per second using video.get(cv2.CAP_PROP_FPS) : {0}".format(fps))
|
||||
if cv.waitKey(1) == ord("q"):
|
||||
exit()
|
||||
|
||||
|
||||
def main():
|
||||
camera = ThreadedCamera(0)
|
||||
while True:
|
||||
camera.display_frame()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user