SensESP 3.3.0
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"
25#include "sensesp_base_app.h"
26
27namespace sensesp {
28
29class SensESPApp;
30// I'd rather not have this global variable here but many legacy examples
31// access it. Use SensESPApp::get() instead.
32extern std::shared_ptr<SensESPApp> sensesp_app;
33
39class SensESPApp : public SensESPBaseApp {
40 public:
44 SensESPApp(SensESPApp& other) = delete;
45
49 void operator=(const SensESPApp&) = delete;
50
54 static std::shared_ptr<SensESPApp> get() {
55 if (instance_ == nullptr) {
56 instance_ = std::shared_ptr<SensESPApp>(new SensESPApp());
57 }
58 return std::static_pointer_cast<SensESPApp>(instance_);
59 }
60
61 virtual bool destroy() override {
62 bool outside_users = instance_.use_count() > 2;
63 if (outside_users) {
64 ESP_LOGW(
65 __FILENAME__,
66 "SensESPApp instance has active references and won't be properly "
67 "destroyed.");
68 }
69 instance_ = nullptr;
70 // Also destroy the global pointer
71 sensesp_app = nullptr;
72 return !outside_users;
73 }
74
75 // getters for internal members
76 std::shared_ptr<SKDeltaQueue> get_sk_delta() { return this->sk_delta_queue_; }
77 std::shared_ptr<SystemStatusController> get_system_status_controller() {
78 return this->system_status_controller_;
79 }
80
88 std::shared_ptr<NetworkProvisioner>& get_network_provisioner() {
89 return this->network_provisioner_;
90 }
91
100 std::shared_ptr<WiFiProvisioner>& get_wifi_provisioner() {
101 return this->wifi_provisioner_;
102 }
103
112 std::shared_ptr<NetworkStateProducer> get_network_state_producer() {
113 return this->network_state_producer_;
114 }
115
120 std::shared_ptr<WiFiProvisioner>& get_networking() {
121 return this->wifi_provisioner_;
122 }
123
124 std::shared_ptr<SKWSClient> get_ws_client() { return this->ws_client_; }
125
141 std::function<std::shared_ptr<NetworkProvisioner>()> factory) {
142 provisioner_factory_ = std::move(factory);
143 }
144
145 protected:
154
155 // setters for all constructor arguments
156
157 const SensESPApp* set_hostname(String hostname) {
158 this->SensESPBaseApp::set_hostname(hostname);
159 return this;
160 }
161 const SensESPApp* set_ssid(String ssid) {
162 this->ssid_ = ssid;
163 return this;
164 }
165 const SensESPApp* set_wifi_password(String wifi_password) {
166 this->wifi_client_password_ = wifi_password;
167 return this;
168 }
169 const SensESPApp* set_ap_ssid(const String& ssid) {
170 this->ap_ssid_ = ssid;
171 return this;
172 }
173 const SensESPApp* set_ap_password(const String& password) {
174 this->ap_password_ = password;
175 return this;
176 }
177 const SensESPApp* set_sk_server_address(String sk_server_address) {
178 this->sk_server_address_ = sk_server_address;
179 return this;
180 }
181 const SensESPApp* set_sk_server_port(uint16_t sk_server_port) {
182 this->sk_server_port_ = sk_server_port;
183 return this;
184 }
186 std::shared_ptr<SystemStatusLed>& system_status_led) {
187 this->system_status_led_ = system_status_led;
188 return this;
189 }
190 const SensESPApp* set_admin_user(const char* username, const char* password) {
191 pending_admin_user_ = username;
192 pending_admin_pass_ = password;
193 return this;
194 }
195 const SensESPApp* enable_ota(const char* password) {
196 ota_password_ = password;
197 return this;
198 }
199 const SensESPApp* set_button_pin(int pin) {
200 button_gpio_pin_ = pin;
201 return this;
202 }
203
211 void setup() {
212 // call the parent setup()
214
216
217 // Create the unified NetworkStateProducer FIRST so it is subscribed
218 // to Network.onEvent before any provisioner brings its interface up
219 // — that way we cannot miss the GOT_IP that fires when DHCP
220 // completes a few hundred ms later.
221 network_state_producer_ = std::make_shared<NetworkStateProducer>();
222
223 // Create the chosen provisioner. If the builder installed a custom
224 // factory (via set_network_provisioner_factory()), use it; otherwise
225 // default to WiFi using the legacy constructor arguments — this
226 // preserves backward compatibility for every existing user app that
227 // calls set_wifi_client() or just relies on the defaults.
231 ESP_LOGE(
232 __FILENAME__,
233 "provisioner_factory_ returned nullptr; falling back to "
234 "WiFiProvisioner so the rest of setup() has a valid object.");
235 }
236 }
238 auto wifi = std::make_shared<WiFiProvisioner>(
239 "/System/WiFi Settings", ssid_, wifi_client_password_, ap_ssid_,
242 wifi_provisioner_ = wifi;
243 }
244
245 // Register the WiFi-specific provisioner as a ConfigItem so the
246 // existing /System/WiFi Settings web UI continues to work. Other
247 // provisioners are responsible for registering their own ConfigItems
248 // from inside their own constructors.
249 if (wifi_provisioner_) {
251 }
252
253 // Backward compat: the old Networking class was a
254 // ValueProducer<WiFiState>. Forward state from the unified
255 // NetworkStateProducer so code that did networking->connect_to(...)
256 // continues to work.
258 auto nsp = network_state_producer_;
259 auto wp = wifi_provisioner_;
260 nsp->attach([nsp, wp]() { wp->emit(nsp->get()); });
261 }
262
263 if (ota_password_ != nullptr) {
264 // create the OTA object
265 ota_ = std::make_shared<OTA>(ota_password_);
266 }
267
268 // Captive portal is meaningful only on WiFi (where there's a soft-AP).
269 bool captive_portal_enabled = false;
270 IPAddress captive_portal_ap_ip;
271 if (wifi_provisioner_) {
272 captive_portal_enabled = wifi_provisioner_->is_captive_portal_enabled();
273 captive_portal_ap_ip = wifi_provisioner_->soft_ap_ip();
274 }
275
276 // create the HTTP server
277 this->http_server_ = std::make_shared<HTTPServer>();
278 this->http_server_->set_captive_portal(captive_portal_enabled,
279 captive_portal_ap_ip);
280
281 if (pending_admin_user_.length() > 0) {
282 http_server_->set_auth_credentials(
283 pending_admin_user_.c_str(), pending_admin_pass_.c_str(), true);
284 }
285
286 // Add the default HTTP server response handlers
289 // WiFi-specific REST handlers (/api/wifi/scan etc.) only register
290 // when the active provisioner is WiFi.
291 if (wifi_provisioner_) {
293 } else {
294 // Always register the transport-agnostic subset (TOFU reset).
296 }
298
300
301 // create the SK delta queue
302 sk_delta_queue_ = std::make_shared<SKDeltaQueue>();
303
304 // create the websocket client
305 bool const use_mdns = sk_server_address_ == "";
306 this->ws_client_ = std::make_shared<SKWSClient>(
307 "/System/Signal K Settings", sk_delta_queue_, sk_server_address_,
308 sk_server_port_, use_mdns);
309
310 ConfigItem(this->ws_client_);
311
312 // Connect the system status controller. The unified
313 // NetworkStateProducer feeds it state from any active interface.
314 // (The consumer is still named get_wifi_state_consumer() for now —
315 // it accepts NetworkState because LinkState=WiFiState=NetworkState
316 // are aliases. Renaming the consumer is a separate cleanup.)
317 this->network_state_producer_->connect_to(
318 &system_status_controller_->get_wifi_state_consumer());
319 this->ws_client_->connect_to(
320 &system_status_controller_->get_ws_connection_state_consumer());
321
322 // create the MDNS discovery object
323 mdns_discovery_ = std::make_shared<MDNSDiscovery>();
324
325 // create a system status led and connect it
326
327 if (system_status_led_ == nullptr) {
328#ifdef PIN_RGB_LED
329 system_status_led_ = std::make_shared<RGBSystemStatusLed>(PIN_RGB_LED);
330#elif defined(LED_BUILTIN)
331 system_status_led_ = std::make_shared<SystemStatusLed>(LED_BUILTIN);
332#endif
333 }
334 if (system_status_led_ != nullptr) {
335 this->system_status_controller_->connect_to(
336 system_status_led_->system_status_consumer_);
337 this->ws_client_->get_delta_tx_count_producer().connect_to(
338 system_status_led_->get_delta_tx_count_consumer());
339 }
340
341 // create the button handler
342 if (button_gpio_pin_ != -1) {
343 button_handler_ = std::make_shared<ButtonHandler>(button_gpio_pin_);
344 }
345
346 // connect status page items
348 }
349
350 // Collect metrics for the status page
352 this->hostname_->connect_to(&this->hostname_ui_output_);
353 this->event_loop_->onRepeat(4999, [this]() {
354 // WiFi-specific status: only populated when WiFi is the active
355 // provisioner. On Ethernet (or other transports), the SSID slot
356 // is left empty and RSSI reads as 0.
357 if (wifi_provisioner_) {
360 } else {
361 wifi_ssid_ui_output_.set(String(""));
363 }
365 free_memory_ui_output_.set(ESP.getFreeHeap());
366
367 // Uptime
368 uptime_ui_output_.set(millis() / 1000);
369
370 // Event loop queue sizes
371 int event_loop_queue_size = event_loop_->getEventQueueSize();
372 int event_loop_timed_queue_size = event_loop_->getTimedEventQueueSize();
373 int event_loop_untimed_queue_size =
374 event_loop_->getUntimedEventQueueSize();
375
376 // Total tick count
377 uint64_t current_tick_count = event_loop_->getTickCount();
378 total_tick_count_ui_output_.set(current_tick_count);
379
380 // Event counts
381 uint64_t current_event_count = event_loop_->getEventCount();
382 uint64_t current_timed_event_count = event_loop_->getTimedEventCount();
383 uint64_t current_untimed_event_count =
384 event_loop_->getUntimedEventCount();
385 event_count_ui_output_.set(current_event_count);
386 timed_event_count_ui_output_.set(current_timed_event_count);
387 untimed_event_count_ui_output_.set(current_untimed_event_count);
388
389 // Ticks and events per second during last interval
390 static uint64_t last_tick_count = 0;
391 static uint64_t last_event_count = 0;
392 static uint64_t last_timed_event_count = 0;
393 static uint64_t last_untimed_event_count = 0;
394 static uint64_t last_millis = 0;
395 uint64_t current_millis = millis();
396 float interval_seconds = (current_millis - last_millis) / 1000.0;
397
398 uint64_t ticks_diff = current_tick_count - last_tick_count;
399 uint64_t events_diff = current_event_count - last_event_count;
400 uint64_t timed_events_diff =
401 current_timed_event_count - last_timed_event_count;
402 uint64_t untimed_events_diff =
403 current_untimed_event_count - last_untimed_event_count;
404
405 // Set outputs
406 event_loop_queue_size_ui_output_.set(event_loop_queue_size);
407 event_loop_timed_queue_ui_output_.set(event_loop_timed_queue_size);
408 event_loop_untimed_queue_ui_output_.set(event_loop_untimed_queue_size);
410 event_loop_->getISREventQueueSize());
411
412 ticks_per_second_ui_output_.set(int(ticks_diff / interval_seconds));
413 events_per_second_ui_output_.set(int(events_diff / interval_seconds));
415 int(timed_events_diff / interval_seconds));
417 int(untimed_events_diff / interval_seconds));
418
419 // Update last values
420 last_tick_count = current_tick_count;
421 last_event_count = current_event_count;
422 last_timed_event_count = current_timed_event_count;
423 last_untimed_event_count = current_untimed_event_count;
424 last_millis = current_millis;
425
426 sk_server_address_ui_output_.set(ws_client_->get_server_address());
427 sk_server_port_ui_output_.set(ws_client_->get_server_port());
428 sk_server_connection_ui_output_.set(ws_client_->get_connection_status());
429 });
430 ws_client_->get_delta_tx_count_producer().connect_to(
432 ws_client_->get_delta_rx_count_producer().connect_to(
434 }
435
436 String ssid_ = "";
439 uint16_t sk_server_port_ = 0;
440 String ap_ssid_ = "";
441 String ap_password_ = "thisisfine";
442 const char* ota_password_ = nullptr;
445
446 std::shared_ptr<MDNSDiscovery> mdns_discovery_;
447 std::shared_ptr<HTTPServer> http_server_;
448
449 std::shared_ptr<BaseSystemStatusLed> system_status_led_;
450 std::shared_ptr<SystemStatusController> system_status_controller_ =
451 std::make_shared<SystemStatusController>();
453 std::shared_ptr<ButtonHandler> button_handler_;
454
455 std::shared_ptr<NetworkStateProducer> network_state_producer_;
456 std::shared_ptr<NetworkProvisioner> network_provisioner_;
457 std::shared_ptr<WiFiProvisioner> wifi_provisioner_;
458 std::function<std::shared_ptr<NetworkProvisioner>()> provisioner_factory_;
459
460 std::shared_ptr<OTA> ota_;
461 std::shared_ptr<SKDeltaQueue> sk_delta_queue_;
462 std::shared_ptr<SKWSClient> ws_client_;
463
464 StatusPageItem<int> free_memory_ui_output_{"Free memory (bytes)", 0, "System",
465 1000};
466 StatusPageItem<int> uptime_ui_output_{"Uptime (s)", 0, "System", 1100};
467
468 StatusPageItem<String> hostname_ui_output_{"Hostname", "", "Network", 1200};
469 StatusPageItem<String> mac_address_ui_output_{"MAC Address", "", "Network",
470 1300};
471 StatusPageItem<String> wifi_ssid_ui_output_{"SSID", "", "Network", 1400};
472
473 StatusPageItem<int8_t> wifi_rssi_ui_output_{"WiFi signal strength (dB)", -128,
474 "Network", 1500};
475
477 "", "Signal K", 1600};
479 "Signal K", 1700};
481 "", "Signal K", 1800};
483 "Signal K", 1900};
485 "Signal K", 2000};
486
488 "Event Loop queue size", 0, "Event Loop Queues", 2100};
490 "Event Loop timed queue size", 0, "Event Loop Queues", 2200};
492 "Event Loop untimed queue size", 0, "Event Loop Queues", 2300};
494 "Event Loop interrupt queue size", 0, "Event Loop Queues", 2400};
495
497 "Total ticks processed", 0, "Event Loop Lifetime", 2500};
499 "Event Loop Lifetime", 2600};
501 "Timed events processed", 0, "Event Loop Lifetime", 2700};
503 "Untimed events processed", 0, "Event Loop Lifetime", 2800};
505 "Ticks per second", 0, "Event Loop Performance", 2900};
507 "Events per second", 0, "Event Loop Performance", 3000};
509 "Timed events per second", 0, "Event Loop Performance", 3100};
511 "Untimed events per second", 0, "Event Loop Performance", 3200};
512
514 "SenseESP version", kSensESPVersion, "Software", 3300};
516 "Build date", __DATE__ " " __TIME__, "Software", 3400};
517
518 // Placeholders for system status sensors in case they are created
519 std::shared_ptr<ValueProducer<float>> system_hz_sensor_;
520 std::shared_ptr<ValueProducer<uint32_t>> free_mem_sensor_;
521 std::shared_ptr<ValueProducer<float>> uptime_sensor_;
522 std::shared_ptr<ValueProducer<String>> ip_address_sensor_;
523 std::shared_ptr<ValueProducer<int>> wifi_signal_sensor_;
524
525 friend class WebServer;
526 friend class SensESPAppBuilder;
527};
528
529} // namespace sensesp
530
531#endif
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:54
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_
friend class SensESPAppBuilder
std::shared_ptr< HTTPServer > http_server_
std::shared_ptr< NetworkProvisioner > & get_network_provisioner()
Active network provisioner (transport-agnostic).
Definition sensesp_app.h:88
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:61
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< uint64_t > untimed_event_count_ui_output_
std::shared_ptr< ValueProducer< float > > uptime_sensor_
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.
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:77
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_
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:76
StatusPageItem< int > delta_tx_count_ui_output_
std::shared_ptr< ValueProducer< float > > system_hz_sensor_
const SensESPApp * set_button_pin(int pin)
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.
SensESPBaseApp()
Construct a new SensESP Base App object.
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
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)
std::shared_ptr< ConfigItemT< T > > ConfigItem(std::shared_ptr< T >)
Register a ConfigItemT with the ConfigItemBase.
#define BUTTON_BUILTIN