SensESP 3.5.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 }
166 const std::vector<ClientSSIDConfig>& wifi_client_configs) {
167 this->wifi_client_configs_ = wifi_client_configs;
168 return this;
169 }
170 const SensESPApp* set_ap_ssid(const String& ssid) {
171 this->ap_ssid_ = ssid;
172 return this;
173 }
174 const SensESPApp* set_ap_password(const String& password) {
175 this->ap_password_ = password;
176 return this;
177 }
178 const SensESPApp* set_sk_server_address(String sk_server_address) {
179 this->sk_server_address_ = sk_server_address;
180 return this;
181 }
182 const SensESPApp* set_sk_server_port(uint16_t sk_server_port) {
183 this->sk_server_port_ = sk_server_port;
184 return this;
185 }
187 std::shared_ptr<SystemStatusLed>& system_status_led) {
188 this->system_status_led_ = system_status_led;
189 return this;
190 }
191 const SensESPApp* set_admin_user(const char* username, const char* password) {
192 pending_admin_user_ = username;
193 pending_admin_pass_ = password;
194 return this;
195 }
196 const SensESPApp* enable_ota(const char* password) {
197 ota_password_ = password;
198 return this;
199 }
200 const SensESPApp* set_button_pin(int pin) {
201 button_gpio_pin_ = pin;
202 return this;
203 }
204
212 void setup() {
213 // call the parent setup()
215
216 // Install the log capture hook as early as practical so the web Log page
217 // can show startup logging. UART output is preserved via the chained
218 // handler. (Pre-WiFi early-boot and ISR-path output bypass the hook.)
220
222
223 // Create the unified NetworkStateProducer FIRST so it is subscribed
224 // to Network.onEvent before any provisioner brings its interface up
225 // — that way we cannot miss the GOT_IP that fires when DHCP
226 // completes a few hundred ms later.
227 network_state_producer_ = std::make_shared<NetworkStateProducer>();
228
229 // Create the chosen provisioner. If the builder installed a custom
230 // factory (via set_network_provisioner_factory()), use it; otherwise
231 // default to WiFi using the legacy constructor arguments — this
232 // preserves backward compatibility for every existing user app that
233 // calls set_wifi_client() or just relies on the defaults.
237 ESP_LOGE(
238 __FILENAME__,
239 "provisioner_factory_ returned nullptr; falling back to "
240 "WiFiProvisioner so the rest of setup() has a valid object.");
241 }
242 }
244 std::shared_ptr<WiFiProvisioner> wifi;
245 if (!wifi_client_configs_.empty()) {
246 // Multi-network client list provided in code (set_wifi_clients()).
247 wifi = std::make_shared<WiFiProvisioner>("/System/WiFi Settings",
250 } else {
251 // Legacy single-SSID path (set_wifi_client() or defaults).
252 wifi = std::make_shared<WiFiProvisioner>("/System/WiFi Settings", ssid_,
255 }
257 wifi_provisioner_ = wifi;
258 }
259
260 // Register the WiFi-specific provisioner as a ConfigItem so the
261 // existing /System/WiFi Settings web UI continues to work. Other
262 // provisioners are responsible for registering their own ConfigItems
263 // from inside their own constructors.
264 if (wifi_provisioner_) {
266 }
267
268 // Backward compat: the old Networking class was a
269 // ValueProducer<WiFiState>. Forward state from the unified
270 // NetworkStateProducer so code that did networking->connect_to(...)
271 // continues to work.
273 auto nsp = network_state_producer_;
274 auto wp = wifi_provisioner_;
275 nsp->attach([nsp, wp]() { wp->emit(nsp->get()); });
276 }
277
278 if (ota_password_ != nullptr) {
279 // create the OTA object
280 ota_ = std::make_shared<OTA>(ota_password_);
281 }
282
283 // Captive portal is meaningful only on WiFi (where there's a soft-AP).
284 bool captive_portal_enabled = false;
285 IPAddress captive_portal_ap_ip;
286 if (wifi_provisioner_) {
287 captive_portal_enabled = wifi_provisioner_->is_captive_portal_enabled();
288 captive_portal_ap_ip = wifi_provisioner_->soft_ap_ip();
289 }
290
291 // create the HTTP server
292 this->http_server_ = std::make_shared<HTTPServer>();
293 this->http_server_->set_captive_portal(captive_portal_enabled,
294 captive_portal_ap_ip);
295
296 if (pending_admin_user_.length() > 0) {
297 http_server_->set_auth_credentials(
298 pending_admin_user_.c_str(), pending_admin_pass_.c_str(), true);
299 }
300
301 // Add the default HTTP server response handlers
304 // WiFi-specific REST handlers (/api/wifi/scan etc.) only register
305 // when the active provisioner is WiFi.
306 if (wifi_provisioner_) {
308 } else {
309 // Always register the transport-agnostic subset (TOFU reset).
311 }
313
315
316 // create the SK delta queue
317 sk_delta_queue_ = std::make_shared<SKDeltaQueue>();
318
319 // create the websocket client
320 bool const use_mdns = sk_server_address_ == "";
321 this->ws_client_ = std::make_shared<SKWSClient>(
322 "/System/Signal K Settings", sk_delta_queue_, sk_server_address_,
323 sk_server_port_, use_mdns);
324
325 ConfigItem(this->ws_client_);
326
327 // Connect the system status controller. The unified
328 // NetworkStateProducer feeds it state from any active interface.
329 // (The consumer is still named get_wifi_state_consumer() for now —
330 // it accepts NetworkState because LinkState=WiFiState=NetworkState
331 // are aliases. Renaming the consumer is a separate cleanup.)
332 this->network_state_producer_->connect_to(
333 &system_status_controller_->get_wifi_state_consumer());
334 this->ws_client_->connect_to(
335 &system_status_controller_->get_ws_connection_state_consumer());
336
337 // create the MDNS discovery object
338 mdns_discovery_ = std::make_shared<MDNSDiscovery>();
339
340 // create a system status led and connect it
341
342 if (system_status_led_ == nullptr) {
343#ifdef PIN_RGB_LED
344 system_status_led_ = std::make_shared<RGBSystemStatusLed>(PIN_RGB_LED);
345#elif defined(LED_BUILTIN)
346 system_status_led_ = std::make_shared<SystemStatusLed>(LED_BUILTIN);
347#endif
348 }
349 if (system_status_led_ != nullptr) {
350 this->system_status_controller_->connect_to(
351 system_status_led_->system_status_consumer_);
352 this->ws_client_->get_delta_tx_count_producer().connect_to(
353 system_status_led_->get_delta_tx_count_consumer());
354 }
355
356 // create the button handler
357 if (button_gpio_pin_ != -1) {
358 button_handler_ = std::make_shared<ButtonHandler>(button_gpio_pin_);
359 }
360
361 // connect status page items
363 }
364
365 // Collect metrics for the status page
367 // Reset cause and the previous boot's heap watermark are fixed for this
368 // session; capture them once here.
372 static_cast<int>(min_free_heap_before_reset()));
373
374 this->hostname_->connect_to(&this->hostname_ui_output_);
375 this->event_loop_->onRepeat(4999, [this]() {
376 // WiFi-specific status: only populated when WiFi is the active
377 // provisioner. On Ethernet (or other transports), the SSID slot
378 // is left empty and RSSI reads as 0.
379 if (wifi_provisioner_) {
382 } else {
383 wifi_ssid_ui_output_.set(String(""));
385 }
387 free_memory_ui_output_.set(ESP.getFreeHeap());
388 min_free_memory_ui_output_.set(ESP.getMinFreeHeap());
389 // Carry this boot's heap low-water mark into RTC memory so it survives a
390 // crash and reports as "min free before last reset" on the next boot.
392
393 // Uptime
394 uptime_ui_output_.set(millis() / 1000);
395
396 // Event loop queue sizes
397 int event_loop_queue_size = event_loop_->getEventQueueSize();
398 int event_loop_timed_queue_size = event_loop_->getTimedEventQueueSize();
399 int event_loop_untimed_queue_size =
400 event_loop_->getUntimedEventQueueSize();
401
402 // Total tick count
403 uint64_t current_tick_count = event_loop_->getTickCount();
404 total_tick_count_ui_output_.set(current_tick_count);
405
406 // Event counts
407 uint64_t current_event_count = event_loop_->getEventCount();
408 uint64_t current_timed_event_count = event_loop_->getTimedEventCount();
409 uint64_t current_untimed_event_count =
410 event_loop_->getUntimedEventCount();
411 event_count_ui_output_.set(current_event_count);
412 timed_event_count_ui_output_.set(current_timed_event_count);
413 untimed_event_count_ui_output_.set(current_untimed_event_count);
414
415 // Ticks and events per second during last interval
416 static uint64_t last_tick_count = 0;
417 static uint64_t last_event_count = 0;
418 static uint64_t last_timed_event_count = 0;
419 static uint64_t last_untimed_event_count = 0;
420 static uint64_t last_millis = 0;
421 uint64_t current_millis = millis();
422 float interval_seconds = (current_millis - last_millis) / 1000.0;
423
424 uint64_t ticks_diff = current_tick_count - last_tick_count;
425 uint64_t events_diff = current_event_count - last_event_count;
426 uint64_t timed_events_diff =
427 current_timed_event_count - last_timed_event_count;
428 uint64_t untimed_events_diff =
429 current_untimed_event_count - last_untimed_event_count;
430
431 // Set outputs
432 event_loop_queue_size_ui_output_.set(event_loop_queue_size);
433 event_loop_timed_queue_ui_output_.set(event_loop_timed_queue_size);
434 event_loop_untimed_queue_ui_output_.set(event_loop_untimed_queue_size);
436 event_loop_->getISREventQueueSize());
437
438 ticks_per_second_ui_output_.set(int(ticks_diff / interval_seconds));
439 events_per_second_ui_output_.set(int(events_diff / interval_seconds));
441 int(timed_events_diff / interval_seconds));
443 int(untimed_events_diff / interval_seconds));
444
445 // Update last values
446 last_tick_count = current_tick_count;
447 last_event_count = current_event_count;
448 last_timed_event_count = current_timed_event_count;
449 last_untimed_event_count = current_untimed_event_count;
450 last_millis = current_millis;
451
452 sk_server_address_ui_output_.set(ws_client_->get_server_address());
453 sk_server_port_ui_output_.set(ws_client_->get_server_port());
454 sk_server_connection_ui_output_.set(ws_client_->get_connection_status());
455 });
456 ws_client_->get_delta_tx_count_producer().connect_to(
458 ws_client_->get_delta_rx_count_producer().connect_to(
460 }
461
462 String ssid_ = "";
464 // Optional multi-network client list. When non-empty, setup() constructs
465 // the default WiFiProvisioner from this list (failing over between the
466 // entries) instead of the single ssid_/wifi_client_password_ pair.
467 std::vector<ClientSSIDConfig> wifi_client_configs_;
469 uint16_t sk_server_port_ = 0;
470 String ap_ssid_ = "";
471 String ap_password_ = "thisisfine";
472 const char* ota_password_ = nullptr;
475
476 std::shared_ptr<MDNSDiscovery> mdns_discovery_;
477 std::shared_ptr<HTTPServer> http_server_;
478
480
481 std::shared_ptr<BaseSystemStatusLed> system_status_led_;
482 std::shared_ptr<SystemStatusController> system_status_controller_ =
483 std::make_shared<SystemStatusController>();
485 std::shared_ptr<ButtonHandler> button_handler_;
486
487 std::shared_ptr<NetworkStateProducer> network_state_producer_;
488 std::shared_ptr<NetworkProvisioner> network_provisioner_;
489 std::shared_ptr<WiFiProvisioner> wifi_provisioner_;
490 std::function<std::shared_ptr<NetworkProvisioner>()> provisioner_factory_;
491
492 std::shared_ptr<OTA> ota_;
493 std::shared_ptr<SKDeltaQueue> sk_delta_queue_;
494 std::shared_ptr<SKWSClient> ws_client_;
495
496 StatusPageItem<int> free_memory_ui_output_{"Free memory (bytes)", 0, "System",
497 1000};
498 StatusPageItem<int> uptime_ui_output_{"Uptime (s)", 0, "System", 1100};
499
501 "System", 1010};
503 "Min free memory before last reset (bytes)", 0, "System", 1020};
505 "System", 1030};
506
507 StatusPageItem<String> hostname_ui_output_{"Hostname", "", "Network", 1200};
508 StatusPageItem<String> mac_address_ui_output_{"MAC Address", "", "Network",
509 1300};
510 StatusPageItem<String> wifi_ssid_ui_output_{"SSID", "", "Network", 1400};
511
512 StatusPageItem<int8_t> wifi_rssi_ui_output_{"WiFi signal strength (dB)", -128,
513 "Network", 1500};
514
516 "", "Signal K", 1600};
518 "Signal K", 1700};
520 "", "Signal K", 1800};
522 "Signal K", 1900};
524 "Signal K", 2000};
525
527 "Event Loop queue size", 0, "Event Loop Queues", 2100};
529 "Event Loop timed queue size", 0, "Event Loop Queues", 2200};
531 "Event Loop untimed queue size", 0, "Event Loop Queues", 2300};
533 "Event Loop interrupt queue size", 0, "Event Loop Queues", 2400};
534
536 "Total ticks processed", 0, "Event Loop Lifetime", 2500};
538 "Event Loop Lifetime", 2600};
540 "Timed events processed", 0, "Event Loop Lifetime", 2700};
542 "Untimed events processed", 0, "Event Loop Lifetime", 2800};
544 "Ticks per second", 0, "Event Loop Performance", 2900};
546 "Events per second", 0, "Event Loop Performance", 3000};
548 "Timed events per second", 0, "Event Loop Performance", 3100};
550 "Untimed events per second", 0, "Event Loop Performance", 3200};
551
553 "SenseESP version", kSensESPVersion, "Software", 3300};
555 "Build date", __DATE__ " " __TIME__, "Software", 3400};
556
557 // Placeholders for system status sensors in case they are created
558 std::shared_ptr<ValueProducer<float>> system_hz_sensor_;
559 std::shared_ptr<ValueProducer<uint32_t>> free_mem_sensor_;
560 std::shared_ptr<ValueProducer<float>> uptime_sensor_;
561 std::shared_ptr<ValueProducer<String>> ip_address_sensor_;
562 std::shared_ptr<ValueProducer<int>> wifi_signal_sensor_;
563
564 friend class WebServer;
565 friend class SensESPAppBuilder;
566};
567
568} // namespace sensesp
569
570#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.
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: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::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: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