Need Help: Recommended way to perform device boot

#1

Hi,

I’m looking for some advice on the best way to set up my device first boot.
I’ve been following the Mongoose OS tutorials till now with good success, but I’ve now run in to a wall with implementing my first boot sequence.

This is the ideal sequence of events I would like on first boot:

  1. Device boots to some welcome message (my device has a screen)
  2. Device sits in this welcome screen till WiFi is connected
  3. With WiFi connected I orchestrate a bunch of functions that would normally be run on a schedule using mgos_set_timer
  4. Once I have the state I need to show my normal operation screen I kick back over to the scheduled operation of the device.

This is what I have been using till now:

enum mgos_app_init_result mgos_app_init(void)
{
  struct mgos_i2c *i2c;

  // ili9341 Screen Configuration
  mgos_ili9341_set_orientation((ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV), 321, 241);
  mgos_ili9341_set_fgcolor(0, 0, 0);
  mgos_ili9341_fillRect(0, 0, 321, 241); // clear screen

  // Set device boot screen
  bootScreen();

  // Setup i2c bus and temp sensor
  i2c = mgos_i2c_get_global();
  s_si7021 = mgos_si7021_create(i2c, 0x40); // Default I2C address

  // Network connectivity events
  mgos_event_add_group_handler(MGOS_EVENT_GRP_NET, net_cb, NULL);
  mgos_event_add_group_handler(MGOS_WIFI_EV_BASE, wifi_cb, NULL);
  mgos_event_add_handler(MGOS_EVENT_CLOUD_CONNECTED, cloud_cb, NULL);
  mgos_event_add_handler(MGOS_EVENT_CLOUD_DISCONNECTED, cloud_cb, NULL);

  // Remote Procedure Call setup
  mg_rpc_add_handler(mgos_rpc_get_global(), "Device.Foo", NULL, get_system_foo_handler, NULL);
  mg_rpc_add_handler(mgos_rpc_get_global(), "Device.Bar", "{someValue: %d, otherValue: %d}", update_bar_handler, NULL);

  // Set the time based callback functions
  mgos_set_timer(1000, MGOS_TIMER_REPEAT, clock_cb, NULL);                    // every second
  mgos_set_timer(10000, MGOS_TIMER_REPEAT, read_sensor_cb, NULL);               // every ten seconds

  // If the device passes the self test then the OTA update is OK to apply
  if (deviceSelfTest())
  {
    struct mg_rpc_call_opts opts = {.dst = mg_mk_str(MGOS_RPC_LOOPBACK_ADDR) };
    mg_rpc_callf(mgos_rpc_get_global(), mg_mk_str("OTA.Commit"), NULL, NULL, &opts, NULL);
    LOG(LL_INFO, ("Commiting OTA changes"));
  }
  else
  {
    LOG(LL_INFO, ("Reverting back to old FW... rebooting"));
    mgos_system_restart();
  }
  
  // Finished
  return MGOS_APP_INIT_SUCCESS;
}

The issue I’m having is that if I try to hold up the mgos_app_init_result function then my WiFI connection and other services do not start.

I attempted to move the mgos_set_timer initialisation to a first boot function that was scheduled via a callback, then remove the schedule for the first boot script but that cause some unexpected results. Basically I was getting a hundreds of callbacks that I was not expecting. Almost like the timer division was off by a factor of 100 or 1000. This is what the code looked like:

static void first_boot_cb(void *user_data)
{
  // Disable first boot callback
  mgos_clear_timer(first_boot_timer_id);

  // Wait for WiFi
  // Orchestrate the sequence of events I need to get the info for first boot

  // Now that first boot is complete, set all the recurring functions
  mgos_set_timer(1000, MGOS_TIMER_REPEAT, clock_cb, NULL);                    // every second
  mgos_set_timer(10000, MGOS_TIMER_REPEAT, read_sensor_cb, NULL);               // every ten seconds
}

enum mgos_app_init_result mgos_app_init(void)

{
  bla bla...
  first_boot_timer_id = mgos_set_timer(0, MGOS_TIMER_REPEAT, first_boot_cb, NULL);
  bla bla ...
}

I’m out of ideas on the best way to solve this problem.
I’d appreciate any suggestions.

Thanks.

#2

Are you looking at something like this:

static void cloud_cb(int ev, void *ev_data, void *userdata) {
  do_the_rest_of_init();
}

enum mgos_app_init_result mgos_app_init(void) {
  init_screen();
  mgos_event_add_handler(MGOS_EVENT_CLOUD_CONNECTED, cloud_cb, NULL)
  return MGOS_APP_INIT_SUCCESS;
}

#3

Thanks for the suggestion @cpq, not quite what I wanted but it did send me down the right path!

Couple of things I found out:

  1. mgos_set_timer() takes an argument that in all examples is MGOS_TIMER_REPEAT. Setting this to 0 will cause the timer event to only be run once.
  2. mgos_wifi_get_status() can be used to get the WiFi status. I’m testing for MGOS_WIFI_CONNECTED or MGOS_WIFI_IP_ACQUIRED

More of an FYI than anything, the way I’ve gone about my setup is like this:

static void first_boot_cb(void *user_data)
{  
  // Wait for WiFi to connect before we kick off
  if ((mgos_wifi_get_status() == MGOS_WIFI_CONNECTED) || (mgos_wifi_get_status() == MGOS_WIFI_IP_ACQUIRED))
  {
    LOG(LL_INFO, ("WiFi is connected"));
    read_sensor_cb(NULL);

    // Now that first boot is complete, set all the recurring functions
    mgos_set_timer(1000, MGOS_TIMER_REPEAT, clock_cb, NULL);                    // every second
    mgos_set_timer(10000, MGOS_TIMER_REPEAT, read_sensor_cb, NULL);             // every ten seconds
  }
  else
  {
    LOG(LL_INFO, ("WiFi is NOT connected... rescheduling in 3 seconds"));

    // reschedule this function in 3 seconds
    mgos_set_timer(3000, 0, first_boot_cb, NULL);
  }
}

enum mgos_app_init_result mgos_app_init(void)
{
  // Set device boot screen
  boot_screen();

  // Set the first boot function to run once, and run immediately
  mgos_set_timer(0, 0, first_boot_cb, NULL);
  
  // Network connectivity events
  mgos_event_add_group_handler(MGOS_EVENT_GRP_NET, net_cb, NULL);
  mgos_event_add_handler(MGOS_EVENT_CLOUD_DISCONNECTED, cloud_cb, NULL);

  // Remote Procedure Call setup
  mg_rpc_add_handler(mgos_rpc_get_global(), "Device.Foo", NULL, foo_handler, NULL);
  mg_rpc_add_handler(mgos_rpc_get_global(), "Device.Bar", "{a: %d, b: %d, d: %d, d: %d}", bar_handler, NULL);

  // If the device passes the self test then the OTA update is OK to apply
  if (device_self_test())
  {
    struct mg_rpc_call_opts opts = {.dst = mg_mk_str(MGOS_RPC_LOOPBACK_ADDR) };
    mg_rpc_callf(mgos_rpc_get_global(), mg_mk_str("OTA.Commit"), NULL, NULL, &opts, NULL);
    LOG(LL_INFO, ("Commiting OTA changes"));
  }
  else
  {
    LOG(LL_INFO, ("Reverting back to old FW... rebooting"));
    mgos_system_restart();
  }
  
  // Finished
  return MGOS_APP_INIT_SUCCESS;
}
1 Like