SensESP 3.4.1-alpha
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
sensesp_app.h
Go to the documentation of this file.
1#ifndef SENSESP_APP_H_
2#define SENSESP_APP_H_
3
4#include <functional>
5
12#include "sensesp/net/ota.h"
27#include "sensesp_base_app.h"
28
29namespace sensesp {
30
31class SensESPApp;
32// I'd rather not have this global variable here but many legacy examples
33// access it. Use SensESPApp::get() instead.
34extern std::shared_ptr<SensESPApp> sensesp_app;
35
41class SensESPApp : public SensESPBaseApp {
42 public:
46 SensESPApp(SensESPApp& other) = delete;
47
51 void operator=(const SensESPApp&) = delete;
52
56 static std::shared_ptr<SensESPApp> get() {
57 if (instance_ == nullptr) {
58 instance_ = std::shared_ptr<SensESPApp>(new SensESPApp());
59 }
60 return std::static_pointer_cast<SensESPApp>(instance_);
61 }
62
63 virtual bool destroy() override {
64 // Drop the global pointer's reference first so the base sees the true
65 // outside-user count, then delegate the shared teardown (registry
66 // clearing, instance reset) to SensESPBaseApp::destroy().
67 sensesp_app = nullptr;
69 }
70
71 // getters for internal members
72 std::shared_ptr<SKDeltaQueue> get_sk_delta() { return this->sk_delta_queue_; }
73 std::shared_ptr<SystemStatusController> get_system_status_controller() {
74 return this->system_status_controller_;
75 }
76
84 std::shared_ptr<NetworkProvisioner>& get_network_provisioner() {
85 return this->network_provisioner_;
86 }
87
96 std::shared_ptr<WiFiProvisioner>& get_wifi_provisioner() {
97 return this->wifi_provisioner_;
98 }
99
108 std::shared_ptr<NetworkStateProducer> get_network_state_producer() {
109 return this->network_state_producer_;
110 }
111
116 std::shared_ptr<WiFiProvisioner>& get_networking() {
117 return this->wifi_provisioner_;
118 }
119
120 std::shared_ptr<SKWSClient> get_ws_client() { return this->ws_client_; }
121
137 std::function<std::shared_ptr<NetworkProvisioner>()> factory) {
138 provisioner_factory_ = std::move(factory);
139 }
140
141 protected:
150
151 // setters for all constructor arguments
152
153 const SensESPApp* set_hostname(String hostname) {
154 this->SensESPBaseApp::set_hostname(hostname);
155 return this;
156 }
157 const SensESPApp* set_ssid(String ssid) {
158 this->ssid_ = ssid;
159 return this;
160 }
161 const SensESPApp* set_wifi_password(String wifi_password) {
162 this->wifi_client_password_ = wifi_password;
163 return this;
164 }
165 const SensESPApp* set_ap_ssid(const String& ssid) {
166 this->ap_ssid_ = ssid;
167 return this;
168 }
169 const SensESPApp* set_ap_password(const String& password) {
170 this->ap_password_ = password;
171 return this;
172 }
173 const SensESPApp* set_sk_server_address(String sk_server_address) {
174 this->sk_server_address_ = sk_server_address;
175 return this;
176 }
177 const SensESPApp* set_sk_server_port(uint16_t sk_server_port) {
178 this->sk_server_port_ = sk_server_port;
179 return this;
180 }
182 std::shared_ptr<SystemStatusLed>& system_status_led) {
183 this->system_status_led_ = system_status_led;
184 return this;
185 }
186 const SensESPApp* set_admin_user(const char* username, const char* password) {
187 pending_admin_user_ = username;
188 pending_admin_pass_ = password;
189 return this;
190 }
191 const SensESPApp* enable_ota(const char* password) {
192 ota_password_ = password;
193 return this;
194 }
195 const SensESPApp* set_button_pin(int pin) {
196 button_gpio_pin_ = pin;
197 return this;
198 }
199
207 void setup() {
208 // call the parent setup()
210
211 // Install the log capture hook as early as practical so the web Log page
212 // can show startup logging. UART output is preserved via the chained
213 // handler. (Pre-WiFi early-boot and ISR-path output bypass the hook.)
215
217
218 // Create the unified NetworkStateProducer FIRST so it is subscribed
219 // to Network.onEvent before any provisioner brings its interface up
220 // — that way we cannot miss the GOT_IP that fires when DHCP
221 // completes a few hundred ms later.
222 network_state_producer_ = std::make_shared<NetworkStateProducer>();
223
224 // Create the chosen provisioner. If the builder installed a custom
225 // factory (via set_network_provisioner_factory()), use it; otherwise
226 // default to WiFi using the legacy constructor arguments — this
227 // preserves backward compatibility for every existing user app that
228 // calls set_wifi_client() or just relies on the defaults.
232 ESP_LOGE(
233 __FILENAME__,
234 "provisioner_factory_ returned nullptr; falling back to "
235 "WiFiProvisioner so the rest of setup() has a valid object.");
236 }
237 }
239 auto wifi = std::make_shared<WiFiProvisioner>(
240 "/System/WiFi Settings", ssid_, wifi_client_password_, ap_ssid_,
243 wifi_provisioner_ = wifi;
244 }
245
246 // Register the WiFi-specific provisioner as a ConfigItem so the
247 // existing /System/WiFi Settings web UI continues to work. Other
248 // provisioners are responsible for registering their own ConfigItems
249 // from inside their own constructors.
250 if (wifi_provisioner_) {
252 }
253
254 // Backward compat: the old Networking class was a
255 // ValueProducer<WiFiState>. Forward state from the unified
256 // NetworkStateProducer so code that did networking->connect_to(...)
257 // continues to work.
259 auto nsp = network_state_producer_;
260 auto wp = wifi_provisioner_;
261 nsp->attach([nsp, wp]() { wp->emit(nsp->get()); });
262 }
263
264 if (ota_password_ != nullptr) {
265 // create the OTA object
266 ota_ = std::make_shared<OTA>(ota_password_);
267 }
268
269 // Captive portal is meaningful only on WiFi (where there's a soft-AP).
270 bool captive_portal_enabled = false;
271 IPAddress captive_portal_ap_ip;
272 if (wifi_provisioner_) {
273 captive_portal_enabled = wifi_provisioner_->is_captive_portal_enabled();
274 captive_portal_ap_ip = wifi_provisioner_->soft_ap_ip();
275 }
276
277 // create the HTTP server
278 this->http_server_ = std::make_shared<HTTPServer>();
279 this->http_server_->set_captive_portal(captive_portal_enabled,
280 captive_portal_ap_ip);
281
282 if (pending_admin_user_.length() > 0) {
283 http_server_->set_auth_credentials(
284 pending_admin_user_.c_str(), pending_admin_pass_.c_str(), true);
285 }
286
287 // Add the default HTTP server response handlers
290 // WiFi-specific REST handlers (/api/wifi/scan etc.) only register
291 // when the active provisioner is WiFi.
292 if (wifi_provisioner_) {
294 } else {
295 // Always register the transport-agnostic subset (TOFU reset).
297 }
299
301
302 // create the SK delta queue
303 sk_delta_queue_ = std::make_shared<SKDeltaQueue>();
304
305 // create the websocket client
306 bool const use_mdns = sk_server_address_ == "";
307 this->ws_client_ = std::make_shared<SKWSClient>(
308 "/System/Signal K Settings", sk_delta_queue_, sk_server_address_,
309 sk_server_port_, use_mdns);
310
311 ConfigItem(this->ws_client_);
312
313 // Connect the system status controller. The unified
314 // NetworkStateProducer feeds it state from any active interface.
315 // (The consumer is still named get_wifi_state_consumer() for now —
316 // it accepts NetworkState because LinkState=WiFiState=NetworkState
317 // are aliases. Renaming the consumer is a separate cleanup.)
318 this->network_state_producer_->connect_to(
319 &system_status_controller_->get_wifi_state_consumer());
320 this->ws_client_->connect_to(
321 &system_status_controller_->get_ws_connection_state_consumer());
322
323 // create the MDNS discovery object
324 mdns_discovery_ = std::make_shared<MDNSDiscovery>();
325
326 // create a system status led and connect it
327
328 if (system_status_led_ == nullptr) {
329#ifdef PIN_RGB_LED
330 system_status_led_ = std::make_shared<RGBSystemStatusLed>(PIN_RGB_LED);
331#elif defined(LED_BUILTIN)
332 system_status_led_ = std::make_shared<SystemStatusLed>(LED_BUILTIN);
333#endif
334 }
335 if (system_status_led_ != nullptr) {
336 this->system_status_controller_->connect_to(
337 system_status_led_->system_status_consumer_);
338 this->ws_client_->get_delta_tx_count_producer().connect_to(
339 system_status_led_->get_delta_tx_count_consumer());
340 }
341
342 // create the button handler
343 if (button_gpio_pin_ != -1) {
344 button_handler_ = std::make_shared<ButtonHandler>(button_gpio_pin_);
345 }
346
347 // connect status page items
349 }
350
351 // Collect metrics for the status page
353 // Reset cause and the previous boot's heap watermark are fixed for this
354 // session; capture them once here.
358 static_cast<int>(min_free_heap_before_reset()));
359
360 this->hostname_->connect_to(&this->hostname_ui_output_);
361 this->event_loop_->onRepeat(4999, [this]() {
362 // WiFi-specific status: only populated when WiFi is the active
363 // provisioner. On Ethernet (or other transports), the SSID slot
364 // is left empty and RSSI reads as 0.
365 if (wifi_provisioner_) {
368 } else {
369 wifi_ssid_ui_output_.set(String(""));
371 }
373 free_memory_ui_output_.set(ESP.getFreeHeap());
374 min_free_memory_ui_output_.set(ESP.getMinFreeHeap());
375 // Carry this boot's heap low-water mark into RTC memory so it survives a
376 // crash and reports as "min free before last reset" on the next boot.
378
379 // Uptime
380 uptime_ui_output_.set(millis() / 1000);
381
382 // Event loop queue sizes
383 int event_loop_queue_size = event_loop_->getEventQueueSize();
384 int event_loop_timed_queue_size = event_loop_->getTimedEventQueueSize();
385 int event_loop_untimed_queue_size =
386 event_loop_->getUntimedEventQueueSize();
387
388 // Total tick count
389 uint64_t current_tick_count = event_loop_->getTickCount();
390 total_tick_count_ui_output_.set(current_tick_count);
391
392 // Event counts
393 uint64_t current_event_count = event_loop_->getEventCount();
394 uint64_t current_timed_event_count = event_loop_->getTimedEventCount();
395 uint64_t current_untimed_event_count =
396 event_loop_->getUntimedEventCount();
397 event_count_ui_output_.set(current_event_count);
398 timed_event_count_ui_output_.set(current_timed_event_count);
399 untimed_event_count_ui_output_.set(current_untimed_event_count);
400
401 // Ticks and events per second during last interval
402 static uint64_t last_tick_count = 0;
403 static uint64_t last_event_count = 0;
404 static uint64_t last_timed_event_count = 0;
405 static uint64_t last_untimed_event_count = 0;
406 static uint64_t last_millis = 0;
407 uint64_t current_millis = millis();
408 float interval_seconds = (current_millis - last_millis) / 1000.0;
409
410 uint64_t ticks_diff = current_tick_count - last_tick_count;
411 uint64_t events_diff = current_event_count - last_event_count;
412 uint64_t timed_events_diff =
413 current_timed_event_count - last_timed_event_count;
414 uint64_t untimed_events_diff =
415 current_untimed_event_count - last_untimed_event_count;
416
417 // Set outputs
418 event_loop_queue_size_ui_output_.set(event_loop_queue_size);
419 event_loop_timed_queue_ui_output_.set(event_loop_timed_queue_size);
420 event_loop_untimed_queue_ui_output_.set(event_loop_untimed_queue_size);
422 event_loop_->getISREventQueueSize());
423
424 ticks_per_second_ui_output_.set(int(ticks_diff / interval_seconds));
425 events_per_second_ui_output_.set(int(events_diff / interval_seconds));
427 int(timed_events_diff / interval_seconds));
429 int(untimed_events_diff / interval_seconds));
430
431 // Update last values
432 last_tick_count = current_tick_count;
433 last_event_count = current_event_count;
434 last_timed_event_count = current_timed_event_count;
435 last_untimed_event_count = current_untimed_event_count;
436 last_millis = current_millis;
437
438 sk_server_address_ui_output_.set(ws_client_->get_server_address());
439 sk_server_port_ui_output_.set(ws_client_->get_server_port());
440 sk_server_connection_ui_output_.set(ws_client_->get_connection_status());
441 });
442 ws_client_->get_delta_tx_count_producer().connect_to(
444 ws_client_->get_delta_rx_count_producer().connect_to(
446 }
447
448 String ssid_ = "";
451 uint16_t sk_server_port_ = 0;
452 String ap_ssid_ = "";
453 String ap_password_ = "thisisfine";
454 const char* ota_password_ = nullptr;
457
458 std::shared_ptr<MDNSDiscovery> mdns_discovery_;
459 std::shared_ptr<HTTPServer> http_server_;
460
462
463 std::shared_ptr<BaseSystemStatusLed> system_status_led_;
464 std::shared_ptr<SystemStatusController> system_status_controller_ =
465 std::make_shared<SystemStatusController>();
467 std::shared_ptr<ButtonHandler> button_handler_;
468
469 std::shared_ptr<NetworkStateProducer> network_state_producer_;
470 std::shared_ptr<NetworkProvisioner> network_provisioner_;
471 std::shared_ptr<WiFiProvisioner> wifi_provisioner_;
472 std::function<std::shared_ptr<NetworkProvisioner>()> provisioner_factory_;
473
474 std::shared_ptr<OTA> ota_;
475 std::shared_ptr<SKDeltaQueue> sk_delta_queue_;
476 std::shared_ptr<SKWSClient> ws_client_;
477
478 StatusPageItem<int> free_memory_ui_output_{"Free memory (bytes)", 0, "System",
479 1000};
480 StatusPageItem<int> uptime_ui_output_{"Uptime (s)", 0, "System", 1100};
481
483 "System", 1010};
485 "Min free memory before last reset (bytes)", 0, "System", 1020};
487 "System", 1030};
488
489 StatusPageItem<String> hostname_ui_output_{"Hostname", "", "Network", 1200};
490 StatusPageItem<String> mac_address_ui_output_{"MAC Address", "", "Network",
491 1300};
492 StatusPageItem<String> wifi_ssid_ui_output_{"SSID", "", "Network", 1400};
493
494 StatusPageItem<int8_t> wifi_rssi_ui_output_{"WiFi signal strength (dB)", -128,
495 "Network", 1500};
496
498 "", "Signal K", 1600};
500 "Signal K", 1700};
502 "", "Signal K", 1800};
504 "Signal K", 1900};
506 "Signal K", 2000};
507
509 "Event Loop queue size", 0, "Event Loop Queues", 2100};
511 "Event Loop timed queue size", 0, "Event Loop Queues", 2200};
513 "Event Loop untimed queue size", 0, "Event Loop Queues", 2300};
515 "Event Loop interrupt queue size", 0, "Event Loop Queues", 2400};
516
518 "Total ticks processed", 0, "Event Loop Lifetime", 2500};
520 "Event Loop Lifetime", 2600};
522 "Timed events processed", 0, "Event Loop Lifetime", 2700};
524 "Untimed events processed", 0, "Event Loop Lifetime", 2800};
526 "Ticks per second", 0, "Event Loop Performance", 2900};
528 "Events per second", 0, "Event Loop Performance", 3000};
530 "Timed events per second", 0, "Event Loop Performance", 3100};
532 "Untimed events per second", 0, "Event Loop Performance", 3200};
533
535 "SenseESP version", kSensESPVersion, "Software", 3300};
537 "Build date", __DATE__ " " __TIME__, "Software", 3400};
538
539 // Placeholders for system status sensors in case they are created
540 std::shared_ptr<ValueProducer<float>> system_hz_sensor_;
541 std::shared_ptr<ValueProducer<uint32_t>> free_mem_sensor_;
542 std::shared_ptr<ValueProducer<float>> uptime_sensor_;
543 std::shared_ptr<ValueProducer<String>> ip_address_sensor_;
544 std::shared_ptr<ValueProducer<int>> wifi_signal_sensor_;
545
546 friend class WebServer;
547 friend class SensESPAppBuilder;
548};
549
550} // namespace sensesp
551
552#endif
Captures ESP_LOGx output into a bounded RAM buffer for the web UI.
Definition log_buffer.h:97
void install()
Install the chained vprintf hook. Call once, early in startup.
void set(const T &value) override
A class for quickly configuring a SensESP application object before wiring up your sensors.
StatusPageItem< int > event_loop_interrupt_queue_ui_output_
const SensESPApp * set_sk_server_port(uint16_t sk_server_port)
StatusPageItem< String > sensesp_version_ui_output_
static std::shared_ptr< SensESPApp > get()
Get the singleton instance of the SensESPApp.
Definition sensesp_app.h:56
SensESPApp()
SensESPApp constructor.
void connect_status_page_items()
void operator=(const SensESPApp &)=delete
std::shared_ptr< SKDeltaQueue > sk_delta_queue_
const SensESPApp * set_hostname(String hostname)
StatusPageItem< uint64_t > timed_event_count_ui_output_
std::shared_ptr< HTTPServer > http_server_
std::shared_ptr< NetworkProvisioner > & get_network_provisioner()
Active network provisioner (transport-agnostic).
Definition sensesp_app.h:84
StatusPageItem< String > wifi_ssid_ui_output_
std::shared_ptr< NetworkStateProducer > network_state_producer_
const SensESPApp * set_sk_server_address(String sk_server_address)
StatusPageItem< float > ticks_per_second_ui_output_
StatusPageItem< int8_t > wifi_rssi_ui_output_
std::shared_ptr< NetworkProvisioner > network_provisioner_
virtual bool destroy() override
Destroy the SensESPBaseApp instance.
Definition sensesp_app.h:63
std::shared_ptr< WiFiProvisioner > wifi_provisioner_
void setup()
Perform initialization of SensESPApp once builder configuration is done.
std::shared_ptr< ValueProducer< String > > ip_address_sensor_
StatusPageItem< int > min_free_before_reset_ui_output_
StatusPageItem< uint64_t > untimed_event_count_ui_output_
std::shared_ptr< ValueProducer< float > > uptime_sensor_
StatusPageItem< int > min_free_memory_ui_output_
StatusPageItem< String > hostname_ui_output_
const SensESPApp * set_wifi_password(String wifi_password)
StatusPageItem< uint16_t > sk_server_port_ui_output_
StatusPageItem< String > sk_server_address_ui_output_
StatusPageItem< int > event_loop_timed_queue_ui_output_
std::shared_ptr< ValueProducer< uint32_t > > free_mem_sensor_
StatusPageItem< String > mac_address_ui_output_
std::shared_ptr< SKWSClient > get_ws_client()
const char * ota_password_
StatusPageItem< uint64_t > total_tick_count_ui_output_
std::shared_ptr< BaseSystemStatusLed > system_status_led_
std::shared_ptr< WiFiProvisioner > & get_wifi_provisioner()
Active WiFi provisioner, or nullptr if a non-WiFi transport is in use.
Definition sensesp_app.h:96
StatusPageItem< int > uptime_ui_output_
std::shared_ptr< ValueProducer< int > > wifi_signal_sensor_
std::function< std::shared_ptr< NetworkProvisioner >()> provisioner_factory_
StatusPageItem< int > delta_rx_count_ui_output_
StatusPageItem< int > event_loop_untimed_queue_ui_output_
StatusPageItem< int > free_memory_ui_output_
StatusPageItem< float > timed_events_per_second_ui_output_
void set_network_provisioner_factory(std::function< std::shared_ptr< NetworkProvisioner >()> factory)
Install a custom factory that setup() will use to construct the NetworkProvisioner instead of the def...
StatusPageItem< int > event_loop_queue_size_ui_output_
std::shared_ptr< WiFiProvisioner > & get_networking()
const SensESPApp * enable_ota(const char *password)
const SensESPApp * set_ap_password(const String &password)
const SensESPApp * set_admin_user(const char *username, const char *password)
friend class WebServer
StatusPageItem< uint64_t > event_count_ui_output_
std::shared_ptr< SystemStatusController > get_system_status_controller()
Definition sensesp_app.h:73
std::shared_ptr< NetworkStateProducer > get_network_state_producer()
Unified producer that emits NetworkState transitions for any active interface (WiFi STA,...
std::shared_ptr< SKWSClient > ws_client_
StatusPageItem< String > build_info_ui_output_
StatusPageItem< float > untimed_events_per_second_ui_output_
const SensESPApp * set_system_status_led(std::shared_ptr< SystemStatusLed > &system_status_led)
StatusPageItem< String > sk_server_connection_ui_output_
StatusPageItem< String > reset_reason_ui_output_
std::shared_ptr< SystemStatusController > system_status_controller_
std::shared_ptr< ButtonHandler > button_handler_
std::shared_ptr< MDNSDiscovery > mdns_discovery_
const SensESPApp * set_ap_ssid(const String &ssid)
const SensESPApp * set_ssid(String ssid)
StatusPageItem< float > events_per_second_ui_output_
std::shared_ptr< OTA > ota_
SensESPApp(SensESPApp &other)=delete
std::shared_ptr< SKDeltaQueue > get_sk_delta()
Definition sensesp_app.h:72
StatusPageItem< int > delta_tx_count_ui_output_
std::shared_ptr< ValueProducer< float > > system_hz_sensor_
const SensESPApp * set_button_pin(int pin)
The base class for SensESP applications.
static String get_hostname()
Get the current hostname.
static std::shared_ptr< SensESPBaseApp > instance_
std::shared_ptr< reactesp::EventLoop > event_loop_
const SensESPBaseApp * set_hostname(String hostname)
std::shared_ptr< PersistingObservableValue< String > > hostname_
virtual void setup()
Perform initialization of SensESPBaseApp once builder configuration is done.
virtual bool destroy()
Destroy the SensESPBaseApp instance.
Item that renders its own value on the web UI status page.
void add_static_file_handlers(std::shared_ptr< HTTPServer > server)
Provide handlers for static web content.
void add_config_handlers(std::shared_ptr< HTTPServer > &server)
Handle HTTP requests to /config.
const char *const kSensESPVersion
void begin_reset_info()
Reset-cause and pre-reset heap diagnostics for the status page.
const char * reset_reason_str()
std::shared_ptr< SensESPApp > sensesp_app
void add_tofu_reset_handler(std::shared_ptr< HTTPServer > &server)
void add_app_http_command_handlers(std::shared_ptr< HTTPServer > &server, std::shared_ptr< WiFiProvisioner > wifi_provisioner)
void add_base_app_http_command_handlers(std::shared_ptr< HTTPServer > &server)
uint32_t min_free_heap_before_reset()
std::shared_ptr< ConfigItemT< T > > ConfigItem(std::shared_ptr< T >)
Register a ConfigItemT with the ConfigItemBase.
void update_min_free_heap_watermark()
#define BUTTON_BUILTIN