Wednesday, August 1, 2018

HOW TO: Mongoose OS (mos) local build on Windows.

For those of you who are just getting started with mgos, you might have noticed that you would typically compile your app, or an example app, with a command like this:

mos build --arch <arch> 

For example to build the c_hello firmware example for the esp8266 arch, you would navigate to the c_hello app directory, and build it like so:

cd ~/mongoose-os/fw/examples/c_hello/
mos build --arch esp8266

The output will look like this:
$ mos build --arch esp8266
Connecting to https://mongoose.cloud, user test
Uploading sources (2331 bytes)
Firmware saved to build\fw.zip

fw.zip is the file ready to be flashed onto your device, but where did it come from?  How and where did it get built?  What's actually happening here is that it's connecting to the mongoose cloud, uploading your app's source files building it on the cloud (using their build environment, libraries and OS sources) and spitting it back to you when it's done.

But what if, say, you want to modify a driver or library to suit your needs, or insert some debug printouts to figure out why your device won't work?  Or what happens if you want to build your app without relying on someone else's cloud service?

Then you'll need local build.  Mongoose OS does facilitate this, and it's quite easy, but there are some little hiccups along the way for some users.

Step 1:  Docker CE for Windows
The first thing you're going to need to do is download and install Docker CE (Community Edition) for Windows.  It's free, but you're going to have to sign up (I know.... a pain...); and it's pretty hefty too at about 500MB or so.

Docker will probably ask you to enable the above features in Windows in order for Docker to work.  Keep in mind that if you enable these, VirtualBox will no longer work.


Open the Performance tab of Task Manager and check that Virtualization is enabled.  If it's not, enable it from your BIOS settings.  You may need to do a quick Google search to confirm that your CPU supports virtualization and how to enable it.

In my case I had to go to Windows Features, disable Hyper-V, restart, re-enable Hyper-V, and restart before it would show up as 'Enabled' in the Performance tab.  Some other users report having to do this too.

Step 2:  Install Go for Windows
This should be pretty easy.  Just go to https://golang.org/dl/ and select an x86 or x86_64 self-extracting installer for Windows.

To verify the installation:
> go version
go version go1.10.3 windows/amd64

If this doesn't work.  Add the go path to your environment.

Step 3: Run the local build command

mos build --local --arch --esp8266 --libs-dir ../../../libs/ --repo ../../../../mongoose-os/ --verbose

Verbose is optional.  I like to have it enabled so I can see what's going on.  --libs-dir and --repo are both compulsory.  --libs-dir needs to point to the "libs" directory where you have cloned all your Mongoose OS libs (click for a link to the github), and --repo just simply points to the source of your actual Mongoose OS source which you would clone from here.

You run this command from the root directory of the app that you want to compile.  To check that local build is truly working, you will want to disconnect yourself from the World Wide Web before running the command again.  Enjoy.

Saturday, June 30, 2018

Cronjobs on the ESP8266 with Mongoose OS

This is a copy of a post I made on StackOverflow helping another user getting a Cronjob up and going on mgos, but I thought it might be useful to add it here also, with a bit more info.  All it does is turn on an LED at a particular time of the day and turn it off at another time of the day.
There are 6 steps.  They're all pretty easy.  But if you're less experienced with Mongoose OS or experience a bug, please let me know the comments below.
Step 1: Add wifi setup to your mos.yml so it can connect to your wireless AP.  If you don't do this you won't be able to successfully pull the time from the NTP server using SNTP.
config_schema:
  - ["wifi.sta.enable", true]
  - ["wifi.sta.ssid", "MyAP"]
  - ["wifi.sta.pass", "Passwd"]
Step 2: Add these to your mos.yml. Leave off rpc-uart if you have no intention of making rpc calls over UART.  You'll notice there's a lib sntp in here.  There's nothing that needs to be done after adding it to your mos.yml. You don't need to add anything into your app code.  As soon as an internet connection is established it grabs the latest time.  It uses time.google.com by default.
libs:
  - origin: https://github.com/mongoose-os-libs/sntp
  - origin: https://github.com/mongoose-os-libs/crontab
  - origin: https://github.com/mongoose-os-libs/rpc-service-cron
  - origin: https://github.com/mongoose-os-libs/wifi
  - origin: https://github.com/mongoose-os-libs/rpc-uart
Step 3: In your application code, in the entry point for your app (mgos_app_init()), add crontab handlers.  In this case we're just going to add two cronjobs, one for LED on, and LED off. mgos_crontab_register_handler() takes three arguments, the name of the action (we will get to this in step 6), and the exact name of the callback function, which we will define in the next step.  The third argument is void *userdata. For now it's safe to leave this as NULL, because we don't intend of passing any extra data into our callback functions.
enum mgos_app_init_result mgos_app_init(void) {
  /* Set LED GPIOs as outputs */
  mgos_gpio_set_mode(YOUR_LED_GPIO, MGOS_GPIO_MODE_OUTPUT);

  /* Register crontab handler - LED OFF */
  mgos_crontab_register_handler(mg_mk_str("ledoff"), ledoff, NULL);

  /* Register crontab handler - LED ON */
  mgos_crontab_register_handler(mg_mk_str("ledon"), ledon, NULL);

  return MGOS_APP_INIT_SUCCESS;
}
Step 4: Add callbacks.  These callback functions need to match the name of the second argument of mgos_crontab_register_handler() in the previous step.  It's worth noting that the mos-tool build system is extremely pedantic and with error out if you pass a parameter into a function and don't utilize it.  This is why we use (void) parameter; to trick the compiler into letting us pass.
void ledoff(struct mg_str action, struct mg_str payload, void *userdata) {
  mgos_gpio_write(YOUR_LED_GPIO, 0);
  (void) payload;
  (void) userdata;
  (void) action;
}

void ledon(struct mg_str action, struct mg_str payload, void *userdata) {
  mgos_gpio_write(YOUR_LED_GPIO, 1);
  (void) payload;
  (void) userdata;
  (void) action;
}
Step 5: From your app's main directory, build the app as you normally would (eg. mos build --arch esp8266 --verbose).
Step 6: Add your cronjobs from the web UI or UART:
call Cron.Add '{"at":"0 0 10 00 * *, "action":"ledon"}'
call Cron.Add '{"at":"0 0 16 00 * *, "action":"ledoff"}'
The format of cronjob times on Unix-like systems typically looks like this:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │                                  7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * *  command to execute
But Mongoose OS's cron lib specifies a slightly different format:
# ┌───────────── seconds (0 - 59)
# │ ┌───────────── minutes (0 - 59)
# │ │ ┌───────────── hours (0 - 23)
# │ │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ │ ┌───────────── month 1-12 or JAN-DEC
# │ │ │ │ │ ┌───────────── day of week 0-6 or SUN-SAT
# │ │ │ │ │ │
# │ │ │ │ │ │ 
# * * * * * *