SensESP 3.6.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"
29#include "sensesp_base_app.h"
30
31namespace sensesp {
32
33class SensESPApp;
34// I'd rather not have this global variable here but many legacy examples
35// access it. Use SensESPApp::get() instead.
36extern std::shared_ptr<SensESPApp> sensesp_app;
37
43class SensESPApp : public SensESPBaseApp {
44 public:
48 SensESPApp(SensESPApp& other) = delete;
49
53 void operator=(const SensESPApp&) = delete;
54
58 static std::shared_ptr<SensESPApp> get() {
59 if (instance_ == nullptr) {
60 instance_ = std::shared_ptr<SensESPApp>(new SensESPApp());
61 }
62 return std::static_pointer_cast<SensESPApp>(instance_);
63 }
64
65 virtual bool destroy() override {
66 // Drop the global pointer's reference first so the base sees the true
67 // outside-user count, then delegate the shared teardown (registry
68 // clearing, instance reset) to SensESPBaseApp::destroy().
69 sensesp_app = nullptr;
71 }
72
73 // getters for internal members
74 std::shared_ptr<SKDeltaQueue> get_sk_delta() { return this->sk_delta_queue_; }
75 std::shared_ptr<SystemStatusController> get_system_status_controller() {
76 return this->system_status_controller_;
77 }
78
86 std::shared_ptr<NetworkProvisioner>& get_network_provisioner() {
87 return this->network_provisioner_;
88 }
89
98 std::shared_ptr<WiFiProvisioner>& get_wifi_provisioner() {
99 return this->wifi_provisioner_;
100 }
101
110 std::shared_ptr<NetworkStateProducer> get_network_state_producer() {
111 return this->network_state_producer_;
112 }
113
118 std::shared_ptr<WiFiProvisioner>& get_networking() {
119 return this->wifi_provisioner_;
120 }
121
122 std::shared_ptr<SKWSClient> get_ws_client() { return this->ws_client_; }
123
139 std::function<std::shared_ptr<NetworkProvisioner>()> factory) {
140 provisioner_factory_ = std::move(factory);
141 }
142
143 protected:
152
153 // setters for all constructor arguments
154
155 const SensESPApp* set_hostname(String hostname) {
156 this->SensESPBaseApp::set_hostname(hostname);
157 return this;
158 }
159 const SensESPApp* set_ssid(String ssid) {
160 this->ssid_ = ssid;
161 return this;
162 }
163 const SensESPApp* set_wifi_password(String wifi_password) {
164 this->wifi_client_password_ = wifi_password;
165 return this;
166 }
168 const std::vector<ClientSSIDConfig>& wifi_client_configs) {
169 this->wifi_client_configs_ = wifi_client_configs;
170 return this;
171 }
172 const SensESPApp* set_ap_ssid(const String& ssid) {
173 this->ap_ssid_ = ssid;
174 return this;
175 }
176 const SensESPApp* set_ap_password(const String& password) {
177 this->ap_password_ = password;
178 return this;
179 }
180 const SensESPApp* set_sk_server_address(String sk_server_address) {
181 this->sk_server_address_ = sk_server_address;
182 return this;
183 }
184 const SensESPApp* set_sk_server_port(uint16_t sk_server_port) {
185 this->sk_server_port_ = sk_server_port;
186 return this;
187 }
189 std::shared_ptr<SystemStatusLed>& system_status_led) {
190 this->system_status_led_ = system_status_led;
191 return this;
192 }
193 const SensESPApp* set_admin_user(const char* username, const char* password) {
194 pending_admin_user_ = username;
195 pending_admin_pass_ = password;
196 return this;
197 }
198 const SensESPApp* enable_ota(const char* password) {
199 ota_password_ = password;
200 return this;
201 }
202 const SensESPApp* set_button_pin(int pin) {
203 button_gpio_pin_ = pin;
204 return this;
205 }
206
214 void setup() {
215 // call the parent setup()
217
218 // Install the log capture hook as early as practical so the web Log page
219 // can show startup logging. UART output is preserved via the chained
220 // handler. (Pre-WiFi early-boot and ISR-path output bypass the hook.)
222
224
225 // Create the unified NetworkStateProducer FIRST so it is subscribed
226 // to Network.onEvent before any provisioner brings its interface up
227 // — that way we cannot miss the GOT_IP that fires when DHCP
228 // completes a few hundred ms later.
229 network_state_producer_ = std::make_shared<NetworkStateProducer>();
230
231 if (wifi_watchdog_timeout_s_ != 0) {
232 wifi_watchdog_ = std::make_shared<WiFiWatchdog>(
235 ->set_title("WiFi Watchdog")
236 ->set_description(
237 "Restart the device if the network stays down longer than the "
238 "timeout. Armed after the first successful connection.")
239 ->set_sort_order(1500);
240 }
241
242 // Create the chosen provisioner. If the builder installed a custom
243 // factory (via set_network_provisioner_factory()), use it; otherwise
244 // default to WiFi using the legacy constructor arguments — this
245 // preserves backward compatibility for every existing user app that
246 // calls set_wifi_client() or just relies on the defaults.
250 ESP_LOGE(
251 __FILENAME__,
252 "provisioner_factory_ returned nullptr; falling back to "
253 "WiFiProvisioner so the rest of setup() has a valid object.");
254 }
255 }
257 std::shared_ptr<WiFiProvisioner> wifi;
258 if (!wifi_client_configs_.empty()) {
259 // Multi-network client list provided in code (set_wifi_clients()).
260 wifi = std::make_shared<WiFiProvisioner>("/System/WiFi Settings",
263 } else {
264 // Legacy single-SSID path (set_wifi_client() or defaults).
265 wifi = std::make_shared<WiFiProvisioner>("/System/WiFi Settings", ssid_,
268 }
270 wifi_provisioner_ = wifi;
271 }
272
273 // Register the WiFi-specific provisioner as a ConfigItem so the
274 // existing /System/WiFi Settings web UI continues to work. Other
275 // provisioners are responsible for registering their own ConfigItems
276 // from inside their own constructors.
277 if (wifi_provisioner_) {
279 }
280
281 // Backward compat: the old Networking class was a
282 // ValueProducer<WiFiState>. Forward state from the unified
283 // NetworkStateProducer so code that did networking->connect_to(...)
284 // continues to work.
286 auto nsp = network_state_producer_;
287 auto wp = wifi_provisioner_;
288 nsp->attach([nsp, wp]() { wp->emit(nsp->get()); });
289 }
290
291 if (ota_password_ != nullptr) {
292 // create the OTA object
293 ota_ = std::make_shared<OTA>(ota_password_);
294 }
295
296 // Captive portal is meaningful only on WiFi (where there's a soft-AP).
297 bool captive_portal_enabled = false;
298 IPAddress captive_portal_ap_ip;
299 if (wifi_provisioner_) {
300 captive_portal_enabled = wifi_provisioner_->is_captive_portal_enabled();
301 captive_portal_ap_ip = wifi_provisioner_->soft_ap_ip();
302 }
303
304 // create the HTTP server
305 this->http_server_ = std::make_shared<HTTPServer>();
306 this->http_server_->set_captive_portal(captive_portal_enabled,
307 captive_portal_ap_ip);
308
309 if (pending_admin_user_.length() > 0) {
310 http_server_->set_auth_credentials(
311 pending_admin_user_.c_str(), pending_admin_pass_.c_str(), true);
312 }
313
314 // Add the default HTTP server response handlers
317 // WiFi-specific REST handlers (/api/wifi/scan etc.) only register
318 // when the active provisioner is WiFi.
319 if (wifi_provisioner_) {
321 } else {
322 // Always register the transport-agnostic subset (TOFU reset).
324 }
327
329
330 // create the SK delta queue
331 sk_delta_queue_ = std::make_shared<SKDeltaQueue>();
332
333 // create the websocket client
334 bool const use_mdns = sk_server_address_ == "";
335 this->ws_client_ = std::make_shared<SKWSClient>(
336 "/System/Signal K Settings", sk_delta_queue_, sk_server_address_,
337 sk_server_port_, use_mdns);
338
339 ConfigItem(this->ws_client_);
340
341 // Connect the system status controller. The unified
342 // NetworkStateProducer feeds it state from any active interface.
343 // (The consumer is still named get_wifi_state_consumer() for now —
344 // it accepts NetworkState because LinkState=WiFiState=NetworkState
345 // are aliases. Renaming the consumer is a separate cleanup.)
346 this->network_state_producer_->connect_to(
347 &system_status_controller_->get_wifi_state_consumer());
348 this->ws_client_->connect_to(
349 &system_status_controller_->get_ws_connection_state_consumer());
350
351 // create the MDNS discovery object
352 mdns_discovery_ = std::make_shared<MDNSDiscovery>();
353
354 // create a system status led and connect it
355
356 if (system_status_led_ == nullptr) {
357#ifdef PIN_RGB_LED
358 system_status_led_ = std::make_shared<RGBSystemStatusLed>(PIN_RGB_LED);
359#elif defined(LED_BUILTIN)
360 system_status_led_ = std::make_shared<SystemStatusLed>(LED_BUILTIN);
361#endif
362 }
363 if (system_status_led_ != nullptr) {
364 this->system_status_controller_->connect_to(
365 system_status_led_->system_status_consumer_);
366 this->ws_client_->get_delta_tx_count_producer().connect_to(
367 system_status_led_->get_delta_tx_count_consumer());
368 }
369
370 // create the button handler
371 if (button_gpio_pin_ != -1) {
372 button_handler_ = std::make_shared<ButtonHandler>(button_gpio_pin_);
373 }
374
375 // connect status page items
377 }
378
379 // Collect metrics for the status page
381 // Reset cause and the previous boot's heap watermark are fixed for this
382 // session; capture them once here.
386 static_cast<int>(min_free_heap_before_reset()));
387
388 this->hostname_->connect_to(&this->hostname_ui_output_);
389 this->event_loop_->onRepeat(4999, [this]() {
390 // WiFi-specific status: only populated when WiFi is the active
391 // provisioner. On Ethernet (or other transports), the SSID slot
392 // is left empty and RSSI reads as 0.
393 if (wifi_provisioner_) {
396 } else {
397 wifi_ssid_ui_output_.set(String(""));
399 }
401 free_memory_ui_output_.set(ESP.getFreeHeap());
402 min_free_memory_ui_output_.set(ESP.getMinFreeHeap());
403 // Carry this boot's heap low-water mark into RTC memory so it survives a
404 // crash and reports as "min free before last reset" on the next boot.
406
407 // Uptime
408 uptime_ui_output_.set(millis() / 1000);
409
410 // Event loop queue sizes
411 int event_loop_queue_size = event_loop_->getEventQueueSize();
412 int event_loop_timed_queue_size = event_loop_->getTimedEventQueueSize();
413 int event_loop_untimed_queue_size =
414 event_loop_->getUntimedEventQueueSize();
415
416 // Total tick count
417 uint64_t current_tick_count = event_loop_->getTickCount();
418 total_tick_count_ui_output_.set(current_tick_count);
419
420 // Event counts
421 uint64_t current_event_count = event_loop_->getEventCount();
422 uint64_t current_timed_event_count = event_loop_->getTimedEventCount();
423 uint64_t current_untimed_event_count =
424 event_loop_->getUntimedEventCount();
425 event_count_ui_output_.set(current_event_count);
426 timed_event_count_ui_output_.set(current_timed_event_count);
427 untimed_event_count_ui_output_.set(current_untimed_event_count);
428
429 // Ticks and events per second during last interval
430 static uint64_t last_tick_count = 0;
431 static uint64_t last_event_count = 0;
432 static uint64_t last_timed_event_count = 0;
433 static uint64_t last_untimed_event_count = 0;
434 static uint64_t last_millis = 0;
435 uint64_t current_millis = millis();
436 float interval_seconds = (current_millis - last_millis) / 1000.0;
437
438 uint64_t ticks_diff = current_tick_count - last_tick_count;
439 uint64_t events_diff = current_event_count - last_event_count;
440 uint64_t timed_events_diff =
441 current_timed_event_count - last_timed_event_count;
442 uint64_t untimed_events_diff =
443 current_untimed_event_count - last_untimed_event_count;
444
445 // Set outputs
446 event_loop_queue_size_ui_output_.set(event_loop_queue_size);
447 event_loop_timed_queue_ui_output_.set(event_loop_timed_queue_size);
448 event_loop_untimed_queue_ui_output_.set(event_loop_untimed_queue_size);
450 event_loop_->getISREventQueueSize());
451
452 ticks_per_second_ui_output_.set(int(ticks_diff / interval_seconds));
453 events_per_second_ui_output_.set(int(events_diff / interval_seconds));
455 int(timed_events_diff / interval_seconds));
457 int(untimed_events_diff / interval_seconds));
458
459 // Update last values
460 last_tick_count = current_tick_count;
461 last_event_count = current_event_count;
462 last_timed_event_count = current_timed_event_count;
463 last_untimed_event_count = current_untimed_event_count;
464 last_millis = current_millis;
465
466 sk_server_address_ui_output_.set(ws_client_->get_server_address());
467 sk_server_port_ui_output_.set(ws_client_->get_server_port());
468 sk_server_connection_ui_output_.set(ws_client_->get_connection_status());
469 });
470 ws_client_->get_delta_tx_count_producer().connect_to(
472 ws_client_->get_delta_rx_count_producer().connect_to(
474 }
475
476 String ssid_ = "";
478 // Optional multi-network client list. When non-empty, setup() constructs
479 // the default WiFiProvisioner from this list (failing over between the
480 // entries) instead of the single ssid_/wifi_client_password_ pair.
481 std::vector<ClientSSIDConfig> wifi_client_configs_;
483 uint16_t sk_server_port_ = 0;
484 String ap_ssid_ = "";
485 String ap_password_ = "thisisfine";
486 const char* ota_password_ = nullptr;
489
490 std::shared_ptr<MDNSDiscovery> mdns_discovery_;
491 std::shared_ptr<HTTPServer> http_server_;
492
494
495 std::shared_ptr<BaseSystemStatusLed> system_status_led_;
496 std::shared_ptr<SystemStatusController> system_status_controller_ =
497 std::make_shared<SystemStatusController>();
499 std::shared_ptr<ButtonHandler> button_handler_;
500
501 std::shared_ptr<NetworkStateProducer> network_state_producer_;
502 std::shared_ptr<NetworkProvisioner> network_provisioner_;
503 std::shared_ptr<WiFiProvisioner> wifi_provisioner_;
504 std::function<std::shared_ptr<NetworkProvisioner>()> provisioner_factory_;
505
506 std::shared_ptr<OTA> ota_;
507 int wifi_watchdog_timeout_s_ = 0; // 0 = disabled
508 std::shared_ptr<WiFiWatchdog> wifi_watchdog_;
509 std::shared_ptr<SKDeltaQueue> sk_delta_queue_;
510 std::shared_ptr<SKWSClient> ws_client_;
511
512 StatusPageItem<int> free_memory_ui_output_{"Free memory (bytes)", 0, "System",
513 1000};
514 StatusPageItem<int> uptime_ui_output_{"Uptime (s)", 0, "System", 1100};
515
517 "System", 1010};
519 "Min free memory before last reset (bytes)", 0, "System", 1020};
521 "System", 1030};
522
523 StatusPageItem<String> hostname_ui_output_{"Hostname", "", "Network", 1200};
524 StatusPageItem<String> mac_address_ui_output_{"MAC Address", "", "Network",
525 1300};
526 StatusPageItem<String> wifi_ssid_ui_output_{"SSID", "", "Network", 1400};
527
528 StatusPageItem<int8_t> wifi_rssi_ui_output_{"WiFi signal strength (dB)", -128,
529 "Network", 1500};
530
532 "", "Signal K", 1600};
534 "Signal K", 1700};
536 "", "Signal K", 1800};
538 "Signal K", 1900};
540 "Signal K", 2000};
541
543 "Event Loop queue size", 0, "Event Loop Queues", 2100};
545 "Event Loop timed queue size", 0, "Event Loop Queues", 2200};
547 "Event Loop untimed queue size", 0, "Event Loop Queues", 2300};
549 "Event Loop interrupt queue size", 0, "Event Loop Queues", 2400};
550
552 "Total ticks processed", 0, "Event Loop Lifetime", 2500};
554 "Event Loop Lifetime", 2600};
556 "Timed events processed", 0, "Event Loop Lifetime", 2700};
558 "Untimed events processed", 0, "Event Loop Lifetime", 2800};
560 "Ticks per second", 0, "Event Loop Performance", 2900};
562 "Events per second", 0, "Event Loop Performance", 3000};
564 "Timed events per second", 0, "Event Loop Performance", 3100};
566 "Untimed events per second", 0, "Event Loop Performance", 3200};
567
569 "SenseESP version", kSensESPVersion, "Software", 3300};
571 "Build date", __DATE__ " " __TIME__, "Software", 3400};
572
573 // Placeholders for system status sensors in case they are created
574 std::shared_ptr<ValueProducer<float>> system_hz_sensor_;
575 std::shared_ptr<ValueProducer<uint32_t>> free_mem_sensor_;
576 std::shared_ptr<ValueProducer<float>> uptime_sensor_;
577 std::shared_ptr<ValueProducer<String>> ip_address_sensor_;
578 std::shared_ptr<ValueProducer<int>> wifi_signal_sensor_;
579
580 friend class WebServer;
581 friend class SensESPAppBuilder;
582};
583
584} // namespace sensesp
585
586#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:58
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:86
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:65
std::shared_ptr< WiFiProvisioner > wifi_provisioner_
void setup()
Perform initialization of SensESPApp once builder configuration is done.
const SensESPApp * set_wifi_clients(const std::vector< ClientSSIDConfig > &wifi_client_configs)
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:98
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< WiFiWatchdog > wifi_watchdog_
std::shared_ptr< SystemStatusController > get_system_status_controller()
Definition sensesp_app.h:75
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::vector< ClientSSIDConfig > wifi_client_configs_
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:74
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.
void add_button_handlers(std::shared_ptr< HTTPServer > &server)
Handle HTTP requests to /api/buttons.
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