88#include < esp_heap_caps.h>
99#include < mdns.h>
1010
11+ #include " cli.hpp"
1112#include " esp32-timer-cam.hpp"
1213#include " nvs.hpp"
1314#include " task.hpp"
1415#include " tcp_socket.hpp"
1516#include " udp_socket.hpp"
1617#include " wifi_sta.hpp"
18+ #include " wifi_sta_menu.hpp"
1719
1820#include " esp_camera.h"
1921
2022#include " rtsp_server.hpp"
2123
2224using namespace std ::chrono_literals;
2325
26+ static espp::Logger logger ({.tag = " Camera Streamer" , .level = espp::Logger::Verbosity::INFO});
27+
28+ std::recursive_mutex server_mutex;
29+ std::unique_ptr<espp::Task> camera_task;
30+ std::shared_ptr<espp::RtspServer> rtsp_server;
31+
32+ esp_err_t initialize_camera (void );
33+ void start_rtsp_server (std::string_view server_address, int server_port);
34+ bool camera_task_fn (const std::mutex &m, const std::condition_variable &cv);
35+
2436extern " C" void app_main (void ) {
2537 esp_err_t err;
26- espp::Logger logger ({.tag = " Camera Streamer" , .level = espp::Logger::Verbosity::INFO});
2738 logger.info (" Bootup" );
2839
2940#if CONFIG_ESP32_WIFI_NVS_ENABLED
@@ -35,38 +46,92 @@ extern "C" void app_main(void) {
3546
3647 auto &timer_cam = espp::EspTimerCam::get ();
3748
49+ // initialize RTC
50+ if (!timer_cam.initialize_rtc ()) {
51+ logger.error (" Could not initialize RTC" );
52+ return ;
53+ }
54+
3855 // initialize LED
39- static constexpr float led_breathing_period = 3 .5f ;
40- if (!timer_cam.initialize_led (led_breathing_period)) {
56+ static constexpr float disconnected_led_breathing_period = 1 .0f ;
57+ static constexpr float connected_led_breathing_period = 3 .5f ;
58+ if (!timer_cam.initialize_led (disconnected_led_breathing_period)) {
4159 logger.error (" Could not initialize LED" );
4260 return ;
4361 }
62+ timer_cam.start_led_breathing ();
63+
64+ // initialize camera
65+ logger.info (" Initializing camera" );
66+ err = initialize_camera ();
67+ if (err != ESP_OK) {
68+ logger.error (" Could not initialize camera: {} '{}'" , err, esp_err_to_name (err));
69+ }
4470
4571 // initialize WiFi
4672 logger.info (" Initializing WiFi" );
47- std::string server_address;
48- espp::WifiSta wifi_sta ({.ssid = CONFIG_ESP_WIFI_SSID,
49- .password = CONFIG_ESP_WIFI_PASSWORD,
50- .num_connect_retries = CONFIG_ESP_MAXIMUM_RETRY,
51- .on_connected = nullptr ,
52- .on_disconnected = nullptr ,
53- .on_got_ip = [&logger, &server_address](ip_event_got_ip_t *eventdata) {
54- server_address =
55- fmt::format (" {}.{}.{}.{}" , IP2STR (&eventdata->ip_info .ip ));
56- logger.info (" got IP: {}" , server_address);
57- }});
58- // wait for network
59- float duty = 0 .0f ;
60- while (!wifi_sta.is_connected ()) {
61- logger.info (" waiting for wifi connection..." );
62- logger.move_up ();
63- logger.clear_line ();
64- timer_cam.set_led_brightness (duty);
65- duty = duty == 0 .0f ? 0 .5f : 0 .0f ;
66- std::this_thread::sleep_for (1s);
67- }
73+ espp::WifiSta wifi_sta (
74+ {.ssid = CONFIG_ESP_WIFI_SSID,
75+ .password = CONFIG_ESP_WIFI_PASSWORD,
76+ .num_connect_retries = CONFIG_ESP_MAXIMUM_RETRY,
77+ .on_connected =
78+ []() {
79+ static auto &timer_cam = espp::EspTimerCam::get ();
80+ timer_cam.set_led_breathing_period (connected_led_breathing_period);
81+ },
82+ .on_disconnected =
83+ []() {
84+ static auto &timer_cam = espp::EspTimerCam::get ();
85+ timer_cam.set_led_breathing_period (disconnected_led_breathing_period);
86+ std::lock_guard<std::recursive_mutex> lock (server_mutex);
87+ logger.info (" Stopping camera task" );
88+ camera_task.reset ();
89+ logger.info (" Stopping RTSP server" );
90+ rtsp_server.reset ();
91+ },
92+ .on_got_ip =
93+ [](ip_event_got_ip_t *eventdata) {
94+ auto server_address = fmt::format (" {}.{}.{}.{}" , IP2STR (&eventdata->ip_info .ip ));
95+ logger.info (" got IP: {}" , server_address);
96+ // create the camera and rtsp server, and the cv/m
97+ // they'll use to communicate
98+ std::lock_guard<std::recursive_mutex> lock (server_mutex);
99+ start_rtsp_server (server_address, CONFIG_RTSP_SERVER_PORT);
100+ // initialize the camera
101+ logger.info (" Creating camera task" );
102+ camera_task = espp::Task::make_unique (
103+ espp::Task::Config{.callback = camera_task_fn,
104+ .task_config = {.name = " Camera Task" , .priority = 10 }});
105+ camera_task->start ();
106+ }});
68107
69- // initialize camera
108+ espp::WifiStaMenu sta_menu (wifi_sta);
109+ auto root_menu = sta_menu.get ();
110+ root_menu->Insert (
111+ " memory" ,
112+ [](std::ostream &out) {
113+ out << " Minimum free memory: " << heap_caps_get_minimum_free_size (MALLOC_CAP_DEFAULT)
114+ << std::endl;
115+ },
116+ " Display minimum free memory." );
117+ root_menu->Insert (
118+ " battery" ,
119+ [](std::ostream &out) {
120+ static auto &timer_cam = espp::EspTimerCam::get ();
121+ out << fmt::format (" Battery voltage: {:.2f}\n " , timer_cam.get_battery_voltage ());
122+ },
123+ " Display the current battery voltage." );
124+
125+ cli::Cli cli (std::move (root_menu));
126+ cli::SetColor ();
127+ cli.ExitAction ([](auto &out) { out << " Goodbye and thanks for all the fish.\n " ; });
128+
129+ espp::Cli input (cli);
130+ input.SetInputHistorySize (10 );
131+ input.Start ();
132+ }
133+
134+ esp_err_t initialize_camera (void ) {
70135 /* *
71136 * @note display sizes supported:
72137 * * QVGA: 320x240
@@ -85,7 +150,7 @@ extern "C" void app_main(void) {
85150 * * UXGA: 1600x1200
86151 */
87152
88- logger. info ( " Initializing camera " );
153+ auto &timer_cam = espp::EspTimerCam::get ( );
89154 static camera_config_t camera_config = {
90155 .pin_pwdn = -1 ,
91156 .pin_reset = timer_cam.get_camera_reset_pin (),
@@ -121,33 +186,23 @@ extern "C" void app_main(void) {
121186 .grab_mode =
122187 CAMERA_GRAB_LATEST // CAMERA_GRAB_WHEN_EMPTY // . Sets when buffers should be filled
123188 };
124- err = esp_camera_init (&camera_config);
125- if (err != ESP_OK) {
126- logger.error (" Could not initialize camera: {} '{}'" , err, esp_err_to_name (err));
127- }
128-
129- timer_cam.start_led_breathing ();
130-
131- // initialize RTC
132- if (!timer_cam.initialize_rtc ()) {
133- logger.error (" Could not initialize RTC" );
134- return ;
135- }
189+ return esp_camera_init (&camera_config);
190+ }
136191
137- // create the camera and rtsp server, and the cv/m they'll use to
138- // communicate
139- int server_port = CONFIG_RTSP_SERVER_PORT;
192+ void start_rtsp_server (std::string_view server_address, int server_port) {
140193 logger.info (" Creating RTSP server at {}:{}" , server_address, server_port);
141- espp::RtspServer rtsp_server ({.server_address = server_address,
142- .port = server_port,
143- .path = " mjpeg/1" ,
144- .log_level = espp::Logger::Verbosity::WARN});
145- rtsp_server.set_session_log_level (espp::Logger::Verbosity::WARN);
146- rtsp_server.start ();
194+ std::lock_guard<std::recursive_mutex> lock (server_mutex);
195+ rtsp_server = std::make_shared<espp::RtspServer>(
196+ espp::RtspServer::Config{.server_address = std::string (server_address),
197+ .port = server_port,
198+ .path = " mjpeg/1" ,
199+ .log_level = espp::Logger::Verbosity::WARN});
200+ rtsp_server->set_session_log_level (espp::Logger::Verbosity::WARN);
201+ rtsp_server->start ();
147202
148203 // initialize mDNS
149204 logger.info (" Initializing mDNS" );
150- err = mdns_init ();
205+ esp_err_t err = mdns_init ();
151206 if (err != ESP_OK) {
152207 logger.error (" Could not initialize mDNS: {}" , err);
153208 return ;
@@ -173,47 +228,32 @@ extern "C" void app_main(void) {
173228 return ;
174229 }
175230 logger.info (" mDNS initialized" );
231+ }
176232
177- // initialize the camera
178- logger.info (" Creating camera task" );
179- auto camera_task_fn = [&rtsp_server, &logger](const auto &m, const auto &cv) -> bool {
180- // take image
181- static camera_fb_t *fb = NULL ;
182- static size_t _jpg_buf_len;
183- static uint8_t *_jpg_buf;
184-
185- fb = esp_camera_fb_get ();
186- if (!fb) {
187- logger.error (" Camera capture failed" );
188- return false ;
189- }
190-
191- _jpg_buf_len = fb->len ;
192- _jpg_buf = fb->buf ;
233+ bool camera_task_fn (const std::mutex &m, const std::condition_variable &cv) {
234+ // take image
235+ static camera_fb_t *fb = NULL ;
236+ static size_t _jpg_buf_len;
237+ static uint8_t *_jpg_buf;
193238
194- if (!(_jpg_buf[_jpg_buf_len - 1 ] != 0xd9 || _jpg_buf[_jpg_buf_len - 2 ] != 0xd9 )) {
195- esp_camera_fb_return (fb);
196- return false ;
197- }
239+ fb = esp_camera_fb_get ();
240+ if (!fb) {
241+ logger.error (" Camera capture failed" );
242+ return false ;
243+ }
198244
199- espp::JpegFrame image ( reinterpret_cast < const char *>(_jpg_buf), _jpg_buf_len) ;
200- rtsp_server. send_frame (image) ;
245+ _jpg_buf_len = fb-> len ;
246+ _jpg_buf = fb-> buf ;
201247
248+ if (!(_jpg_buf[_jpg_buf_len - 1 ] != 0xd9 || _jpg_buf[_jpg_buf_len - 2 ] != 0xd9 )) {
202249 esp_camera_fb_return (fb);
203250 return false ;
204- };
205-
206- auto camera_task = espp::Task::make_unique (
207- {.callback = camera_task_fn, .task_config = {.name = " Camera Task" , .priority = 10 }});
208- camera_task->start ();
209-
210- while (true ) {
211- std::this_thread::sleep_for (100ms);
212- // print out some stats (battery, framerate)
213- logger.info (" Minimum free memory: {}, Battery voltage: {:.2f}" ,
214- heap_caps_get_minimum_free_size (MALLOC_CAP_DEFAULT),
215- timer_cam.get_battery_voltage ());
216- logger.move_up ();
217- logger.clear_line ();
218251 }
219- }
252+
253+ espp::JpegFrame image (reinterpret_cast <const char *>(_jpg_buf), _jpg_buf_len);
254+ std::lock_guard<std::recursive_mutex> lock (server_mutex);
255+ rtsp_server->send_frame (image);
256+
257+ esp_camera_fb_return (fb);
258+ return false ;
259+ };
0 commit comments