From b788823375d546d1b13b4844eabdc942bef9f0f3 Mon Sep 17 00:00:00 2001 From: Zdzislaw Goik Date: Sun, 20 Mar 2022 15:24:28 +0100 Subject: [PATCH] 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 --- ESP/include/GlobalVars.h | 13 ++ ESP/include/LEDManager.h | 29 ++-- ESP/include/OTA.h | 102 ++++++------- ESP/include/StateManager.h | 22 +++ ESP/include/WifiHandler.h | 7 +- ESP/include/cameraHandler.h | 20 ++- ESP/include/httpdHandler.h | 22 ++- ESP/platformio.ini | 4 +- ESP/src/LEDManager.cpp | 54 +++---- ESP/src/StateManager.cpp | 11 ++ ESP/src/cameraHandler.cpp | 134 +++++++++-------- ESP/src/httpdHandler.cpp | 289 ++++++++++++++++++------------------ ESP/src/main.cpp | 27 ++-- ESP/src/wifiHandler.cpp | 64 ++++---- main.py | 46 ------ 15 files changed, 425 insertions(+), 419 deletions(-) create mode 100644 ESP/include/GlobalVars.h create mode 100644 ESP/include/StateManager.h create mode 100644 ESP/src/StateManager.cpp delete mode 100644 main.py diff --git a/ESP/include/GlobalVars.h b/ESP/include/GlobalVars.h new file mode 100644 index 0000000..b9ecdf4 --- /dev/null +++ b/ESP/include/GlobalVars.h @@ -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 \ No newline at end of file diff --git a/ESP/include/LEDManager.h b/ESP/include/LEDManager.h index 17fdf8c..3dcdf07 100644 --- a/ESP/include/LEDManager.h +++ b/ESP/include/LEDManager.h @@ -1,20 +1,19 @@ +#pragma once #include -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); } \ No newline at end of file diff --git a/ESP/include/OTA.h b/ESP/include/OTA.h index 92ca614..c952a27 100644 --- a/ESP/include/OTA.h +++ b/ESP/include/OTA.h @@ -1,57 +1,59 @@ +#pragma once #include +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(); + } + } + }; } \ No newline at end of file diff --git a/ESP/include/StateManager.h b/ESP/include/StateManager.h new file mode 100644 index 0000000..6e2512b --- /dev/null +++ b/ESP/include/StateManager.h @@ -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; + }; + +} \ No newline at end of file diff --git a/ESP/include/WifiHandler.h b/ESP/include/WifiHandler.h index a7c28ea..ebf3875 100644 --- a/ESP/include/WifiHandler.h +++ b/ESP/include/WifiHandler.h @@ -1,6 +1,9 @@ +#pragma once #include #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); + } } \ No newline at end of file diff --git a/ESP/include/cameraHandler.h b/ESP/include/cameraHandler.h index dae3b59..e762044 100644 --- a/ESP/include/cameraHandler.h +++ b/ESP/include/cameraHandler.h @@ -1,14 +1,20 @@ +#pragma once #include "pinout.h" #include "esp_camera.h" #include -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); + }; } \ No newline at end of file diff --git a/ESP/include/httpdHandler.h b/ESP/include/httpdHandler.h index a6ae0df..3e42a14 100644 --- a/ESP/include/httpdHandler.h +++ b/ESP/include/httpdHandler.h @@ -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(); + }; } \ No newline at end of file diff --git a/ESP/platformio.ini b/ESP/platformio.ini index 9de0e3b..b2503dd 100644 --- a/ESP/platformio.ini +++ b/ESP/platformio.ini @@ -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 = diff --git a/ESP/src/LEDManager.cpp b/ESP/src/LEDManager.cpp index 343c25e..0860cd1 100644 --- a/ESP/src/LEDManager.cpp +++ b/ESP/src/LEDManager.cpp @@ -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); - } - } - } -} \ No newline at end of file +void OpenIris::LEDManager::displayStatus() { + +} diff --git a/ESP/src/StateManager.cpp b/ESP/src/StateManager.cpp new file mode 100644 index 0000000..ba844b9 --- /dev/null +++ b/ESP/src/StateManager.cpp @@ -0,0 +1,11 @@ +#include "StateManager.h" + +namespace OpenIris{ + void StateManager::setState(State state){ + current_state=state; + } + + State StateManager::getCurrentState(){ + return current_state; + } +} \ No newline at end of file diff --git a/ESP/src/cameraHandler.cpp b/ESP/src/cameraHandler.cpp index 662e9b3..a40822e 100644 --- a/ESP/src/cameraHandler.cpp +++ b/ESP/src/cameraHandler.cpp @@ -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); } \ No newline at end of file diff --git a/ESP/src/httpdHandler.cpp b/ESP/src/httpdHandler.cpp index ca1cc63..a227b36 100644 --- a/ESP/src/httpdHandler.cpp +++ b/ESP/src/httpdHandler.cpp @@ -1,177 +1,170 @@ #include -#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; } } \ No newline at end of file diff --git a/ESP/src/main.cpp b/ESP/src/main.cpp index 3d5c184..abf5fcc 100644 --- a/ESP/src/main.cpp +++ b/ESP/src/main.cpp @@ -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(); } \ No newline at end of file diff --git a/ESP/src/wifiHandler.cpp b/ESP/src/wifiHandler.cpp index 328132a..0f4410c 100644 --- a/ESP/src/wifiHandler.cpp +++ b/ESP/src/wifiHandler.cpp @@ -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); } -} \ No newline at end of file + + 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; + } +} diff --git a/main.py b/main.py deleted file mode 100644 index 45e6a38..0000000 --- a/main.py +++ /dev/null @@ -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()