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