Overview

VCON is a framework for remote (OTA) firmware updates for a wide range of microcontrollers: STM32Fx, STM32Lx, SAMD21, SAMD51, Mega328. VCON framework consists of two parts:

  • A hardware OTA shield, wired to your microcontroller (Host MCU),
  • A device management cloud (public at https://dash.vcon.io or private).

VCON architecture

VCON OTA shield is, in turn:

  • Any device or module based on an ESP32 microcontroller,
  • With a special pre-built firmware loaded on ESP32.

VCON shield is a "smart" connectivity module of a new type. Unlike traditional modules, it knows how to reprogram a Host MCU (a microcontroller that is wired to a VCON module). So essentially, a VCON module is a remote programmer with REST API that can re-program your device at any time.

Optionally, in addition to the OTA wiring, VCON module can be wired to the Host MCU's UART. In this case, VCON shield could be configured to act as a network bridge - either in transparent, or non-transparent mode:

  • In trasparent mode, everything that Host MCU prints to the UART, VCON forwards to the server of your choice, MQTT or Websocket. Everything that VCON receives from the server, VCON writes to the MCU's UART. This enables bi-directional communication, where Host MCU "thinks" it talks to the UART where in fact it talks to the remote server of your choice - e.g. AWS IoT, or Azure, or your private MQTT server.
  • In non-transparent mode, Host MCU can exchange JSON-RPC commands with the VCON shield and access a wide range of functionality - call external REST services, manage files, etc. Also, it can export RPC services itself, and VCON shield bridges those services to the REST API. This way, it is easy to create custom management interface to communicate with your MCU via familiar, secure REST API.

Quick Start Guide

Arduino Nano

Arduino Nano wiring

Shield Setup

Once wiring is complete, power the Arduino Nano board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Host MCU setup

Now we have to tell VCON shield, which microcontroller is attached, which pins are used, etcetera. Switch to the https://dash.vcon.io, select "action" → "Manage device":

Manage device

This loads device management console with several different sections. Now we're interested in "Host MCU" section. Change values to make it look like this and hit "save":

Atmega328 MCU setup

After a second or two, a device should momentarily go offline and then online again: that means it has updated configuration and rebooted. Now we are ready to update firmware over the air!

OTA firmware update

Fire Arduino IDE, and create a new sketch. This sketch is a modification of the classic Blink sketch with some changes:

  • LED status is printed on the serial console on each "blink"
  • By entering numbers from "0" to "9" on a serial console, we could change blink interval

This sketch, therefore, implements some trivial remote control over UART. Choose one of the following:

  1. Skip firmware build step and download a pre-compiled firmware for this sketch mega328p.hex

  2. Build the firmware yourself. Copy/paste the code into a new Arduino sketch:

    int led = LED_BUILTIN, sleeptime = 1200, on = 0;
    
    void setup() {
     Serial.begin(115200);
     pinMode(led, OUTPUT);
    }
    
    void loop() {
     on = !on;               // Invert LED status
     digitalWrite(led, on);  // Set LED
     delay(sleeptime);       // Sleep for `sleeptime` milliseconds
     Serial.println(on);     // Print LED status to serial
    
     // Read serial input. If we read a number from "0" to "9",
     // then change blink interval 
     if (Serial.available() > 0) {
       int ch = Serial.read();
       if (ch >= '0' && ch <= '9') sleeptime = (ch - '0' + 1) * 300;
     }
    }

    Next, let us compile (but not upload!) this sketch into a downloadable file. In Arduino IDE, perform the following steps:

    • Select "Tools" → "Board" → "Arduino AVR boards" → Arduino Uno
    • Select "Sketch" → "Export Compiled Binary"
    • Select "Sketch" → "Show Sketch Folder"

    A folder with a compiled .hex file should popup. It should be named SKETCH_NAME.ino.standard.hex, which is a text file in Intel hex format that contains binary code of your compiled firmware.

Switch to the VCON dashboard, device management console. In the "Host MCU" section, click on the "firmware update" button, choose firmware .hex file. A button is going to show an OTA progress indicator, and finish in a second or two:

Congratulations! Arduino Nano firmware has been updated remotely over the secure management interface.

Remote control

When the previous step is done and Arduino Nano firmware is remotely updated, two things should be noticed:

  1. Arduino Nano starts to blink LED
  2. In the "Serial Console" section, a serial output starts to appear

What happens? In the Host MCU configuration, we told VCON to forward UART to a Websocket server. VCON grabs UART output from a specified pin, and sends it to the dashboard, since no Websocket URL is specified. That's why we can see what Host MCU prints to the serial port.

Now, type "7" to the serial input and press "send":

Serial monitor

The dashboard grabs your input and calls a serial.write function on the VCON shield, which sends the input to the specified rx UART pin. Also, serial monitor prints your input on a console in an alternate color, making it easy to see input and output. Host MCU receives character "7", adjusts blinking interval to 800 milliseconds, and Arduino starts blinking slower.

Congratulations! Now your Arduino is remotely controllable.

The cool things is that Arduino is not even "aware" it is connected to the Internet. It "thinks" it communicates via UART - which is very easy to develop, debug and test. This way, a Host MCU can be nicely isolated to perform only required business tasks and not care about networking and management.

See Recipes section for some further insights on how can you enhance remote control and data reporting.

RaspberryPI Pico

RPI wiring

Shield Setup

Once wiring is complete, power the board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Host MCU setup

Now we have to tell VCON shield, which microcontroller is attached, which pins are used, etcetera. Switch to the https://dash.vcon.io, select "action" → "Manage device":

Manage device

This loads device management console with several different sections. Now we're interested in "Host MCU" section. Change values to make it look like this and hit "save":

RP2040 setup

After a second or two, a device should momentarily go offline and then online again: that means it has updated configuration and rebooted. Now we are ready to update firmware over the air!

OTA firmware update

Let's build a firmware that performs the following:

  • The firmware blinks an LED like a classic Blinky firmware
  • LED status is printed on the serial console on each "blink"
  • By entering numbers from "0" to "9" on a serial console, we could change blink interval (a trivial UART control)

Choose one of the following:

  1. Skip firmware build step and download a pre-compiled firmware from rpico.uf2

  2. Build the firmware yourself. Setup RP2040 Arduino support. Copy/paste the code into a new Arduino sketch:

    int led = LED_BUILTIN, sleeptime = 1200, on = 0;
    
    void setup() {
     Serial1.begin(115200);   // Note: we're using Serial1, not Serial
     pinMode(led, OUTPUT);
    }
    
    void loop() {
     on = !on;
     if (Serial1.available() > 0) { int ch = Serial1.read(); if (ch >= '0' && ch <= '9') sleeptime = (ch - '0' + 1) * 300; }
     Serial1.println(on);
     digitalWrite(led, on);
     delay(sleeptime);
    }

    Next, let us compile (but not upload!) this sketch into a downloadable file. In Arduino IDE, perform the following steps:

    • Select "Tools" → "Board" → "Raspberry Pi RP2040 Boards" → "Raspberry Pi Pico"
    • Select "Sketch" → "Export Compiled Binary"
    • Select "Sketch" → "Show Sketch Folder"

    A folder with a compiled .uf2 file should popup.

Switch to the VCON dashboard, device management console. In the "Host MCU" section, click on the "firmware update" button, choose firmware .uf2 file. A button is going to show an OTA progress indicator until OTA is done.

Congratulations! You've updated firmware over the air.

Remote control

When the previous step is done and firmware is remotely updated, two things should be noticed:

  1. The board starts to blink LED
  2. In the "Serial Console" section, a serial output starts to appear

What happens? In the Host MCU configuration, we told VCON to forward UART to a Websocket server. VCON grabs UART output from a specified pin, and sends it to the dashboard, since no Websocket URL is specified. That's why we can see what Host MCU prints to the serial port.

Now, type "7" to the serial input and press "send":

Serial monitor

The dashboard grabs your input and calls a serial.write function on the VCON shield, which sends the input to the specified rx UART pin. Also, serial monitor prints your input on a console in an alternate color, making it easy to see input and output. Host MCU receives character "7", adjusts blinking interval to 2400 milliseconds, and the board starts blinking slower.

Congratulations! Now your microcontroller is remotely controllable.

Seeeduino XIAO

XIAO wiring

NOTE: the SWDIO and SWCLK pads are on the bottom of the board and need to be soldered.

Shield Setup

Once wiring is complete, power the board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Host MCU setup

Now we have to tell VCON shield, which microcontroller is attached, which pins are used, etcetera. Switch to the https://dash.vcon.io, select "action" → "Manage device":

Manage device

This loads device management console with several different sections. Now we're interested in "Host MCU" section. Change values to make it look like this and hit "save":

SAMD21 setup

After a second or two, a device should momentarily go offline and then online again: that means it has updated configuration and rebooted. Now we are ready to update firmware over the air!

OTA firmware update

Let's build a firmware that performs the following:

  • The firmware blinks an LED like a classic Blinky firmware
  • LED status is printed on the serial console on each "blink"
  • By entering numbers from "0" to "9" on a serial console, we could change blink interval (a trivial UART control)

Choose one of the following:

  1. Skip firmware build step and download a pre-compiled firmware from xiao.bin

  2. Build the firmware yourself. Setup Seeeduino Arduino support. Copy/paste the code into a new Arduino sketch:

    int led = LED_BUILTIN, sleeptime = 1200, on = 0;
    
    void setup() {
     Serial1.begin(115200);   // Note: we're using Serial1, not Serial
     pinMode(led, OUTPUT);
    }
    
    void loop() {
     on = !on;
     if (Serial1.available() > 0) { int ch = Serial1.read(); if (ch >= '0' && ch <= '9') sleeptime = (ch - '0' + 1) * 300; }
     Serial1.println(on);
     digitalWrite(led, on);
     delay(sleeptime);
    }

    Next, let us compile (but not upload!) this sketch into a downloadable file. In Arduino IDE, perform the following steps:

    • Select "Tools" → "Board" → "Seeed SAMD" → Seeeduino XIAO "Raspberry Pi Pico"
    • Select "Sketch" → "Export Compiled Binary"
    • Select "Sketch" → "Show Sketch Folder"

    A folder with a compiled .bin file should popup.

Switch to the VCON dashboard, device management console. In the "Host MCU" section, click on the "firmware update" button, choose firmware .bin file. A button is going to show an OTA progress indicator until OTA is done.

Congratulations! You've updated firmware over the air.

Remote control

When the previous step is done and firmware is remotely updated, two things should be noticed:

  1. The board starts to blink LED
  2. In the "Serial Console" section, a serial output starts to appear

What happens? In the Host MCU configuration, we told VCON to forward UART to a Websocket server. VCON grabs UART output from a specified pin, and sends it to the dashboard, since no Websocket URL is specified. That's why we can see what Host MCU prints to the serial port.

Now, type "7" to the serial input and press "send":

Serial monitor

The dashboard grabs your input and calls a serial.write function on the VCON shield, which sends the input to the specified rx UART pin. Also, serial monitor prints your input on a console in an alternate color, making it easy to see input and output. Host MCU receives character "7", adjusts blinking interval to 2400 milliseconds, and the board starts blinking slower.

Congratulations! Now your microcontroller is remotely controllable.

Arduino Pro Mini

Arduino Pro Mini wiring

Shield Setup

Once wiring is complete, power the Arduino Nano board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Shield Setup

Arduino Pro Mini, Arduino Nano, Arduino Uno use the same microcontroller - Atmega328p. The next steps are identical to those described for the Arduino Nano. Therefore, jump to the Arduino Nano tutorial and continue from the Host MCU setup section.

Arduino Uno

Arduino Uno wiring

Shield Setup

Once wiring is complete, power the Arduino Nano board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Shield Setup

Arduino Pro Mini, Arduino Nano, Arduino Uno use the same microcontroller - Atmega328p. The next steps are identical to those described for the Arduino Nano. Therefore, jump to the Arduino Nano tutorial and continue from the Host MCU setup section.

STM32 BluePill

BluePill wiring

Shield Setup

Once wiring is complete, power the Arduino Nano board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Host MCU setup

Now we have to tell VCON shield, which microcontroller is attached, which pins are used, etcetera. Switch to the https://dash.vcon.io, select "action" → "Manage device":

Manage device

This loads device management console with several different sections. Now we're interested in "Host MCU" section. Change values to make it look like this and hit "save":

STM32 MCU setup

After a second or two, a device should momentarily go offline and then online again: that means it has updated configuration and rebooted. Now we are ready to update firmware over the air!

OTA firmware update

First, build an STM32 equivalent of the demo Arduino sketch:

  • The firmware blinks an LED
  • LED status is printed on the serial console on each "blink"
  • Entering numbers from "0" to "9" on a serial console changes blink interval

Choose one of the 3 options:

  1. Skip build step and download a pre-compiled firmware, bluepill-baremetal.bin

  2. Build a bare-metal firmware at https://github.com/cesanta/stm32-bluepill. Clone that repository, and type make to build bluepill.bin.

  3. Use ARM mbed compiler. Choose "Nucleo F103RB" board and compile the following code:

    #include "mbed.h"
    #include "platform/mbed_thread.h"
    
    Serial      pc(PA_9, PA_10, 115200);  // TX, RX
    DigitalOut  myled(PC_13);             // on-board LED
    
    int main() {
     int on = 0, wait_ms = 300;    // Initial blink interval is 300ms
     for (;;) {
       on = !on;                   // Invert LED status
       myled = on;                 // Set LED
       pc.printf(on ? "1" : "0");  // Print LED status to serial
       thread_sleep_for(wait_ms);  // Sleep
    
       // Read serial input. If we read a number from "0" to "9", change blink interval 
       if (pc.readable()) {
         int ch = pc.getc();
         if (ch >= '0' && ch <= '9') wait_ms = (ch - '0' + 1) * 300;
       }
     }
    }

    Click on "Compile", that will compile and download a .bin firmware file.

Now, we have a built .bin firmware file. Switch to the VCON dashboard, device management console. In the "Host MCU" section, click on the "firmware update" button, navigate to, and choose a .bin firmware file. A firmware update button would spin for some time, showing OTA progress. When OTA finishes, the board should start blinking.

Congratulations! A firmware has been updated remotely over the secure management interface.

Remote control

When the previous step is done, notice one more thing that changed: in the "Serial Console" section of the device dashboard, a serial output of "0" and "1" starts to appear.

What happens? In the Host MCU configuration, we told VCON to forward UART to a Websocket server. VCON grabs UART output from a specified pin, and sends it to the dashboard, since no Websocket URL is specified. That's why we can see what Host MCU prints to the serial port.

Now, type "7" to the serial input and press "send":

Serial monitor

The dashboard grabs your input and calls a serial.write function on the VCON shield, which sends the input to the specified rx UART pin. Also, serial monitor prints your input on a console in an alternate color, making it easy to see input and output. Host MCU receives character "7", adjusts blinking interval to 800 milliseconds, and your board starts blinking slower.

Congratulations! Now your microcontroller is remotely controllable.

The cool things is that your microcontroller is not even "aware" it is connected to the Internet. It "thinks" it communicates via UART - which is very easy to develop, debug and test. This way, a microcontoller can be nicely isolated to perform only required business tasks and not care about networking and management.

Adafruit ItsyBitsy M4

ItsyBitsy wiring

Shield Setup

Once wiring is complete, power the board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Host MCU setup

Now we have to tell VCON shield, which microcontroller is attached, which pins are used, etcetera. Switch to the https://dash.vcon.io, select "action" → "Manage device":

Manage device

This loads device management console with several different sections. Now we're interested in "Host MCU" section. Change values to make it look like this and hit "save":

ItsyBitsyM4 MCU setup

After a second or two, a device should momentarily go offline and then online again: that means it has updated configuration and rebooted. Now we are ready to update firmware over the air!

OTA firmware update

Let's build a firmware that performs the following:

  • The firmware blinks an LED like a classic Blinky firmware
  • LED status is printed on the serial console on each "blink"
  • By entering numbers from "0" to "9" on a serial console, we could change blink interval (a trivial UART control)

Choose one of the following:

  1. Skip firmware build step and download a pre-compiled firmware from itsym4.bin

  2. Build the firmware yourself. Copy/paste the code into a new Arduino sketch:

    int led = LED_BUILTIN, sleeptime = 1200, on = 0;
    
    void setup() {
     Serial1.begin(115200);   // Note: we're using Serial1, not Serial
     pinMode(led, OUTPUT);
    }
    
    void loop() {
     on = !on;
     if (Serial1.available() > 0) { int ch = Serial1.read(); if (ch >= '0' && ch <= '9') sleeptime = (ch - '0' + 1) * 300; }
     Serial1.println(on);
     digitalWrite(led, on);
     delay(sleeptime);
    }

    Next, let us compile (but not upload!) this sketch into a downloadable file. In Arduino IDE, perform the following steps:

    • Select "Tools" → "Board" → "Adafruit SAMD" → ItsyBitsyM4
    • Select "Sketch" → "Export Compiled Binary"
    • Select "Sketch" → "Show Sketch Folder"

    A folder with a compiled .bin file should popup. It should be named SKETCH_NAME.ino.itsybitsy_m4.bin.

Switch to the VCON dashboard, device management console. In the "Host MCU" section, click on the "firmware update" button, choose firmware .bin file. A button is going to show an OTA progress indicator, and finish in a second or two:

Congratulations! You've updated firmware over the air.

Remote control

When the previous step is done and firmware is remotely updated, two things should be noticed:

  1. The board starts to blink LED
  2. In the "Serial Console" section, a serial output starts to appear

What happens? In the Host MCU configuration, we told VCON to forward UART to a Websocket server. VCON grabs UART output from a specified pin, and sends it to the dashboard, since no Websocket URL is specified. That's why we can see what Host MCU prints to the serial port.

Now, type "7" to the serial input and press "send":

Serial monitor

The dashboard grabs your input and calls a serial.write function on the VCON shield, which sends the input to the specified rx UART pin. Also, serial monitor prints your input on a console in an alternate color, making it easy to see input and output. Host MCU receives character "7", adjusts blinking interval to 800 milliseconds, and the board starts blinking slower.

Congratulations! Now your microcontroller is remotely controllable.

The cool things is that your microcontroller is not even "aware" it is connected to the Internet. It "thinks" it communicates via UART - which is very easy to develop, debug and test. This way, a microcontroller can be nicely isolated to perform only required business tasks and not care about networking and management.

See Recipes section for some further insights on how can you enhance remote control and data reporting.

Adafruit ItsyBitsy M0

The setup and the start guide for the Adafruit ItsyBitsyM0 is identical to the Adafruit ItsyBitsyM4, with the following differences:

  1. In the Host MCU setup section, "addr" value should be 8192 instead of 16384
  2. The pre-built firmware for ItsyBitsyM0 is itsym0.bin
  3. In the firmware built step, choose "ItsyBitsyM0" board "ItsyBitsyM4"

Condidering the above differences, please follow ItsyBitsyM4 tutorial.

SparkFun Pro Micro RP2040

Pro Micro RP2040 wiring

NOTE: SWDIO and SWCLK pads are on the bottom, and need to be soldered.

Shield Setup

Once wiring is complete, power the board by plugging in USB cable. The VCON shield should start blinking blue LED, which indicates the network is unconfigured. Jump to the Network Setup section to configure network on your VCON shield, and return here when done. The blue LED should be lit solid, and a respective device on a dashboard should become online.

Host MCU setup

Now we have to tell VCON shield, which microcontroller is attached, which pins are used, etcetera. Switch to the https://dash.vcon.io, select "action" → "Manage device":

Manage device

This loads device management console with several different sections. Now we're interested in "Host MCU" section. Change values to make it look like this and hit "save":

RP2040 setup

After a second or two, a device should momentarily go offline and then online again: that means it has updated configuration and rebooted. Now we are ready to update firmware over the air!

OTA firmware update

Let's build a firmware that performs the following:

  • The firmware blinks an LED like a classic Blinky firmware
  • LED status is printed on the serial console on each "blink"
  • By entering numbers from "0" to "9" on a serial console, we could change blink interval (a trivial UART control)

Choose one of the following:

  1. Skip firmware build step and download a pre-compiled firmware from rpmicro.uf2

  2. Build the firmware yourself. Setup RP2040 Arduino support and the newest version of Adafruit NeoPixel library. Copy/paste the code into a new Arduino sketch:

    int sleeptime = 1200, on = 0;
    
    #include <Adafruit_NeoPixel.h>
    Adafruit_NeoPixel led = Adafruit_NeoPixel(1, 25, NEO_GRB + NEO_KHZ800);
    
    void setup() {
     Serial1.begin(115200);   // Note: we're using Serial1, not Serial
     led.begin();
    }
    
    void loop() {
     on = !on;
     if (Serial1.available() > 0) { int ch = Serial1.read(); if (ch >= '0' && ch <= '9') sleeptime = (ch - '0' + 1) * 300; }
     Serial1.println(on);
     led.setPixelColor(0, on ? 0xff00ff : 0);
     led.show();
     delay(sleeptime);
    }

    Next, let us compile (but not upload!) this sketch into a downloadable file. In Arduino IDE, perform the following steps:

    • Select "Tools" → "Board" → "Raspberry Pi RP2040 Boards" → "Raspberry Pi Pico"
    • Select "Sketch" → "Export Compiled Binary"
    • Select "Sketch" → "Show Sketch Folder"

    A folder with a compiled .uf2 file should popup.

Switch to the VCON dashboard, device management console. In the "Host MCU" section, click on the "firmware update" button, choose firmware .uf2 file. A button is going to show an OTA progress indicator until OTA is done.

Congratulations! You've updated firmware over the air.

Remote control

When the previous step is done and firmware is remotely updated, two things should be noticed:

  1. The board starts to blink LED
  2. In the "Serial Console" section, a serial output starts to appear

What happens? In the Host MCU configuration, we told VCON to forward UART to a Websocket server. VCON grabs UART output from a specified pin, and sends it to the dashboard, since no Websocket URL is specified. That's why we can see what Host MCU prints to the serial port.

Now, type "7" to the serial input and press "send":

Serial monitor

The dashboard grabs your input and calls a serial.write function on the VCON shield, which sends the input to the specified rx UART pin. Also, serial monitor prints your input on a console in an alternate color, making it easy to see input and output. Host MCU receives character "7", adjusts blinking interval to 2400 milliseconds, and the board starts blinking slower.

Congratulations! Now your microcontroller is remotely controllable.

OTA Shield Hardware

VCON-W1

W1 top view W1 bottom view W1 dimensions W1 pinout
  • Connectivity: WiFi (bult-in), Cellular (via an external modem)
  • Uses ESP32-MINI-1 certified module with integrated PCB antenna
  • Very compact design: 25.40 x 17.78 mm
  • No components on the bottom side. That allows soldering directly on a board as a traditional SMT component
  • Integrated blue LED status indicator:
    • Double short blinking: WiFi not configured
    • Single fast blinking: WiFi configured, but can't connect
    • Single slow blinking: WiFi connected, but cloud is not connected
    • Solid light: cloud connected
  • Has AP2112 3.3V regulator, therefore can be powered either by 3.3V input, or unregulated DC input up to 6V
  • Reset button
  • IO0 button for reprogramming and restore factory defaults on long press
  • Extra IO pins are broken out for the external Cellular modem
  • Schematic can be downloaded at w1-schematic.pdf

VCON-Nano

NANO top view NANO bottom view NANO dimensions
  • Intended for, and pin-compatible with the classic Arduino Nano
  • Required no wiring. With female pin headers soldered, all you need is to insert Arduino Nano into the shield, and that's it!
  • Connectivity: WiFi (bult-in), Cellular (via an external modem)
  • Uses ESP32-MINI-1 certified module with integrated PCB antenna
  • No components on the bottom side. That allows soldering directly on a board as a traditional SMT component
  • Integrated blue LED status indicator:
    • Double short blinking: WiFi not configured
    • Single fast blinking: WiFi configured, but can't connect
    • Single slow blinking: WiFi connected, but cloud is not connected
    • Solid light: cloud connected
  • Has AP2112 3.3V regulator, therefore can be powered either by 3.3V input, or unregulated DC input up to 6V
  • Reset button
  • IO0 button for reprogramming and restore factory defaults on long press

VCON-D51

D51 top view D51 bottom view D51 dimensions

VCON-D51 is a SAMD51 development board with the integrated OTA shield. This is a good choice for any product that requires a powerful microcontroller with a built-in firmware OTA updates.

  • Host MCU: SAMD51, 120Mhz clock, 192Kb RAM, 512Kb flash
  • Connectivity: WiFi, using ESP32-MINI-1 certified module with integrated PCB antenna
  • No components on the bottom side. That allows soldering directly on a board as a traditional SMT component
  • Reset button
  • User button, tied to PA12
  • 4 megabyte QSPI external flash
  • Yellow user LED, tied to PA22
  • Integrated blue LED status indicator:
    • Double short blinking: WiFi not configured
    • Single fast blinking: WiFi configured, but can't connect
    • Single slow blinking: WiFi connected, but cloud is not connected
    • Solid light: cloud connected
  • It is possible to flash any bootloader on it (remotely!), which makes VCON-D51 to appear as a different board. By default, an Adafruit ItsyBitsy M4's bootloader is flashed, therefore VCON-D51 is recognised by Arduino IDE as Adafruit ItsyBitsy M4
  • Schematic can be downloaded at d51-schematic.pdf

Generic ESP32

It is possible to use any ESP32-based board or module as VCON OTA shield. In order to do so, follow these steps:

  1. Connect your ESP32 hardware to the workstation. It should be accessible via a serial port

  2. Download and unzip VCON flashing tool from https://vcon.io/downloads/cli/cli.zip

  3. Download VCON firmware, https://vcon.io/downloads/fw/4m.hex, into the unzipped directory

  4. Start command prompt (or terminal on Mac/Linux). Run cd PATH/TO/cli to go into the unzipped cli/ directory. After that, run the following command (change COMPORT to the board's serial port):

    OS Command
    Windows
    .\windows\cli -p COMPORT flash 4m.hex
    Linux
    ./linux/cli -p COMPORT flash 4m.hex
    MacOS
    ./macos/cli -p COMPORT flash 4m.hex

Done! Now you can use your ESP32-based hardware as a VCON OTA shield.

Recipes

Send data to MQTT

The commumication chip can be configured to read the serial output of the Host MCU and forward all data to the MQTT server of your choice. That is called a "UART-MQTT bridge mode". In order to enable that,

Step 1. Login to https://dash.vcon.io, make sure your device is online, choose "action" → "Manage device"

Step 2. In the file editor section, choose config.json file, and edit the configuration JSON object by adding the following section:

  ...
  "mqtt": {
    "rx": "vcon/rx",
    "tx": "vcon/tx",
    "url": "mqtt://broker.hivemq.com:1883"
  },
  ...

Step 3. Click on "save" button, then click on "reboot device" button

That's it! Now all data that is printed to the serial, gets forwarded to the topic vcon/tx of the MQTT server broker.hivemq.com:1883. Feel free to choose a different topic or server address.

Note there is an rx topic, too. You've guessed it: everything that is sent to that MQTT topic, a communication chip catches and sends to the Host MCU serial line, so MQTT/Serial communication is bi-directional.

NOTE: on Arduino framework, Serial on VCON-328 and Serial1 on VCON-D51.

Send data to anything

What if you want to send data to something that is not MQTT - for example, to a custom server, or to a database like Influx? That is also possible. When a communication chip reads Host MCU serial, it generates a notification on the management cloud. It is possible to intercept that notification, and have a custom code to forward data to any destination.

Read the Notifications section for exact details.

Remote control - simple

An easy remote control via MQTT could be implemented by the "Send data to MQTT" recipe. Configure your RX and TX topics, MQTT server, then send data to the TX topic.

Use a simple Arduino sketch to read Serial to turn an LED on or off:

// IMPORTANT! On VCON-D51, use Serial1 instead of Serial
void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    int character = Serial.read();
    if (character == '0') digitalWrite(LED_BUILTIN, LOW);  // '0' switches LED off
    if (character == '1') digitalWrite(LED_BUILTIN, HIGH); // '1' switches LED on
  }
}

Of course, the serial message format in this simple example is trivial. It is done for demonstration purposes. You could implement any format you wish, test it on a local serial monitor, then it is going to work the same way when you send commands over MQTT.

Keep it simple, though.

Remote control - advanced

When a communication chip is in Serial/MQTT bridge mode, a Host MCU is not aware it is connected to the Internet. It "thinks" that it is controlled via Serial, but in fact, serial data comes from the internets. A communication chip is, so to say, transparent, invisible to the Host MCU.

It is possible to put communication chip into an RPC mode. In this mode, a communication chip expects data to be in a JSON-RPC format: each chunk of data must be a JSON-RPC frame, separated by a newline character. This way, a communication chip is not transparent to the Host MCU: Host MCU knows it is there, it could send JSON-RPC requests to it and receive responses.

In RPC mode, a Host MCU can register any number of custom handler functions, like GetStatus, SwitchOnMotor, or whatever is required. A communication chip can bridge these to the management cloud, and any function could be called via a familiar REST API.

Read RPC section below for a detailed explanation and an example sketch.

Complete dashboard

This recipe demonstrates how to build a complete custom monitoring dashboard using VCON platform. The implementation is very compact, well commented, and is designed to be a reference implementation of the production system. It consists of the following parts:

  1. A device, which
    • can be controlled remotely by switching LED on or off
    • sends data to the cloud that simulates sensor readings
  2. A custom dashboard, which
    • is authenticated, requires users to log in
    • stores sensor data in a local database (to a JSON file for simplicity)
    • could be modified, and hosted anywhere - on a development laptop or on online hosting platform like AWS. The dashboard looks like this:

The overall architecture of such setup is as follows:

Step 1. Follow Quick Start Guide to register a VCON device on a dash.vcon.io management dashboard

Step 2. Clone or download the https://github.com/cesanta/vcon-app-example repository to your workstation

Step 3. Flash the firmware/firmware.ino sketch to your VCON device using remote OTA.

This sketch implements a simple LED control and simulates periodic sensor data upload:

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  // Handle serial input: switch LED on/off
  if (Serial.available() > 0) {
    int ch = Serial.read();
    if (ch == '0') digitalWrite(LED_BUILTIN, LOW);   // '0' switches LED off
    if (ch == '1') digitalWrite(LED_BUILTIN, HIGH);  // '1' switches LED on
  }

  // Print current status to serial every 5 seconds
  // A status message is a JSON string like this: {"led": 0, "sensor": 27}
  static unsigned long prev;
  unsigned long curr = millis();
  if (curr - prev > 5000) {
    char buf[100];
    snprintf(buf, sizeof(buf), "{\"led\": %d, \"sensor\": %d}",
             (int) digitalRead(LED_BUILTIN), (int) random(20, 30));
    Serial.println(buf);
    prev = curr;
  }
}

Step 4. Install Node.js, or skip this step if you have it installed

Step 5. Install ws node package for Websocket support:

$ npm i -g ws

Step 6. Edit backend/config.json file, change vcon_api_key value and save the file. You can get you API key by logging to https://dash.vcon.io dashboard on the Account page.

Step 4. Run your backend

$ node backend/main.js

That's it! Now point your browser to https://localhost:8000 and login as test/test. Here are the links for the core pieces of functionality:

Here are some possible choices for where you can host your custom node.js backend, once the customisation is complete:

Service Price from Payment type
https://www.heroku.com/ 0 usage based, with freebie quota
https://aws.amazon.com/ 0 usage based, with freebie quota
https://cloud.google.com/ 0 usage based, with freebie quota
https://azure.microsoft.com/ 0 usage based, with freebie quota
https://www.digitalocean.com/ $5 / month fixed price, run on a virtual instance

Calling VCON RPC

A simplest way to use VCON is to set $.host.mode parameter to a MQTT or Websocket bridge (2 or 3 respectively). In this case, everything that Host MCU sends UART, VCON catches and sends over to a remote server. VCON does not interpret UART data in any way, so the bridge is transparent.

However, if $.host.mode is set to 1 (RPC bridge), then VCON interprets UART data that Host MCU sends as JSON-RPC commands. VCON module exports many RPC methods (see RPC reference below), therefore Host MCU can call them and utilise additional functionality VCON offers - like, reading/writing files on VCON module, send HTTP requests to remote servers, etc.

Note that not all available RPC methods are exported via UART by default. Available methods are controlled by the $.rpc.safe configuration setting, see configuration reference below. Set $.rpc.safe to * to allow all methods. Note: for production, tighten that up, because methods allowed by $.rpc.safe are available over UART and BLE.

So, in this recipe, Host MCU periodically calls rpc.list RPC function that lists methods available for Host MCU to call. The Arduino sketch is for VCON-D51, or any other hardware setup where Serial is wired to the serial console, and Serial1 to the VCON.

Step 1. Follow Quick Start Guide to register a VCON device on a dash.vcon.io management dashboard

Step 2. When you device is online on dash.vcon.io, choose action → Manage device. In the FileSystem section, choose config.json file. Add "rpc": {"safe": "*"}, right after the first opening { at the beginning of the file. Change $.host.mode from 2 to 1. Click Save. Click "reboot device" in the "General Information" section.

Step 3. Open Arduino IDE, Tools → Manage Libraries, search for msjon and install mjson library.

Step 4. Create a new sketch. If you use VCON-D51, choose Adafruit ItsyBitsy M4 as a board. Copy-paste the following code: using remote OTA.

// Example firmware that calls VCON RPC function
// Set $.host.mode to 1 in the VCON configuration
#include <mjson.h>

int fn(const char *buf, int len, void *userdata) {
  return Serial1.write(buf, len);
}

int response_callback(const char *buf, int len, void *userdata) {
  Serial.write("Got response: ");
  Serial.write(buf, len);
  Serial.println();
  return len;
}

void setup() {
  Serial1.begin(115200);
  Serial.begin(115200);
  jsonrpc_init(response_callback, NULL);
}

void loop() {
  char buf[800];
  if (Serial1.available() > 0) {
    int len = Serial1.readBytes(buf, sizeof(buf));
    jsonrpc_process(buf, len, fn, NULL, NULL);
  }

  // Call RPC function every 3 seconds
  static unsigned long old;
  unsigned long now = millis();
  if (old > now || old + 3000 < now) {
    old = now;
    mjson_printf(fn, NULL, "{%Q:%d,%Q:%Q,%Q:{}}\n", "id", 1, "method", "rpc.list", "params");
  }
}

Step 5. Update Host MCU remotely. Open Serial console. You should see messages printed periodically by the response_callback() function, that shows a list of available RPC functions.

Congratulations! Now you know how to call any VCON RPC method from a Host MCU. See RPC reference below for a detailed description of each method.

Management Cloud

Overview

VCON cloud provides management API access to your devices, and an easy to use Web UI with device dashboard:

Cloud dashboard

Authentication

Cloud API access can be authenticated via a Basic HTTP authorisation, or by setting a Authorization: Bearer APIKEY header. For a Basic auth, use your user/password. For a Bearer auth, take your APIKEY from the "Account" tab in the cloud UI.

NOTE: you can use APIKEY in a Basic auth. Set user to an empty string, and password to your APIKEY. Curl examples for all three methods are listed below:

Method Description
Basic
curl -su EMAIL:PASSWORD https://dash.vcon.io/api/v3/devices 
APIKEY
curl -X 'Authorization: Bearer APIKEY' https://dash.vcon.io/api/v3/devices
Basic + APIKEY
curl -su :APIKEY https://dash.vcon.io/api/v3/devices

Test account

dash.vcon.io provides a test account with the following credentials:

Email Password APIKEY
test test test

It is open to everyone for demonstration purposes. Anyone can login to dash.vcon.io using test/test as an email/password. And below is an example of an API usage:

Get all devices registered under a test account:

curl -su :test https://dash.vcon.io/api/v3/devices

List all available functions of device 42:

curl -su :test https://dash.vcon.io/api/v3/devices/42/rpc/rpc.list

Call device 42, function sys.reboot:

curl -su :test https://dash.vcon.io/api/v3/devices/42/rpc/sys.reboot

Cloud API Reference

NOTE: URI in a table below must be prefixed with https://dash.vcon.io/api/v3.

Method URI Description
GET /user Get user
POST /user Set user. Parameters: JSON object with new user data: {"fullname":"...","company":"...","email":"...","address":"...","phone":"...","settings":{...}}
GET /devices Get device list
POST /devices Create new device. Parameters: none
GET /devices/:id Get device with ID :id
POST /devices/:id Update device with ID :id. Parameters: JSON object with device data: {"labels":{"board":"nano33"}, "state":{...}}
GET /devices/:id/fs List files on device :id
GET /devices/:id/fs/:name Get file :name on device :id
POST /devices/:id/fs/:name Write file :name on device :id. File data is the HTTP POST body, sent varbatim - e.g. using curl --data-binary
POST /devices/:id/rpc/:name Call RPC function :name on device :id. Parameters: none, or a JSON object specific to that RPC function. If no parameters are given, a GET request can be used too
POST /devices/:id/ota Update firmware over the air. By default, a Host MCU gets updated. If you want to OTA a VCON module, append ?target=vcon to the URL. HTTP body must be a raw .hex or .bin file data with a new firmware. Example: curl -su :test ttps://dash.vcon.io/api/v3/devices/42/ota?hex=1 --data-binary @PATH_TO_FIRMWARE.hex
GET /devices/:id/bye Disconnect device. If, for various reasons, a device gets stuck, force reconnection

OTA Shield

Network Setup

This section describes how to configure network on the VCON shield, and make it connected to the https://dash.vcon.io cloud. It is assumed that the shield is already wired and powered. So please proceed to the next section, specific to your microcontroller, and return here when wiring is done

Step 1. Open https://vcon.io/app/ in any Bluetooth-enabled browser, like Google Chrome or Microsoft Edge. Click on "Choose device" button:

BLE app

Step 2. Select a device and click "Pair":

BLE app chooser

Step 3. Login to https://dash.vcon.io. Each connected OTA shield must have an associated "cloud device" with unique access key (password). Click on "add device" to add a new device. Click on "action" button. Click on "Copy device password to clipboard:

Copy device password

Step 4. Switch back to the configuration app. Fill in WiFi network name, WiFi password, device password and save settings:

BLE app chooser

Your device on a dashboard should become online:

Copy device password

RPC (remote control)

A Host MCU can receive commands remotely using a technique called RPC bridge. RPC stands for Remote Procedure Call.

VCON module and a cloud communicate with each other using a standard JSON-RPC 2.0 protocol over the secure Websocket. Also, by default, a Host MCU and VCON module talk to each other using JSON-RPC. JSON-RPC is a very simple protocol: client and server exchange JSON strings with the following format:

JSON-RPC

The rules are simple:

  • Request frames have a mandatory method attribute, and optional params
  • If request frame has an optional id attribute, then a server must send a response frame. Otherwise, frames without id are considered notification frames. Server does not respond on notification frames
  • On error, server replies with an error frame, e.g.: {"id":1, "error": {"code": 500, "message": doh""}}
  • For serial communication, frames are delimited by newline characters. There should not be newline characters inside frames
  • Communication is bidirectional: Host MCU can call VCON's functions, and VCON can call Host's functions

For example, VCON sends a status notifications to the Host MCU. This frame does not have an id attribute, thus Host does not send a response:

{"method": "status", "params": "cloud_down"}  // VCON -> Host

When Host receives a JSON-RPC frame, it parses the frame and calls a corresponding handler function. A handler function processes the request and produces a reply, which is sent back to the VCON. In the following communication example, VCON calls a custom function sum that adds two numbers:

{"id": 12, "method": "Sum", "params": [2,3]}   // VCON -> Host
{"id": 12, "result": 5}                        // VCON <- Host

And here is the example when VCON calls a non-existent function:

{"id": 13, "method": "This_Function_Does_Not_Exist", "params": true}   // VCON -> Host
{"id": 13, "error": {"code": -32601, "message": "method not found"}}   // Host -> VCON

VCON cloud exposes JSON-RPC interface as a RESTful endpoints, implementing a so-called RPC bridge. Commands are sent to the cloud via HTTP/REST, and the cloud sends commands to devices via JSON-RPC wrapped into a TLS-secured WebSocket connection:

RPC to REST

The REST URL should be https://dash.vcon.io/api/v3/devices/:id/rpc/:name, where :id is a device ID, and :name is an RPC function name that must be implemented by a device. Note that dash.vcon.io does not know which functions are implemented by a device - whatever :name is specified in the URL, it triggers RPC function :name on a device. Devices, however, implement rpc.list function that enumerates all functions that device exports. This gives an ability to easily see what functions are implemented by a device. For example, this curl command lists all functions implemented by device with ID 42 under a test account:

curl -su :test https://dash.vcon.io/api/v3/devices/42/rpc/rpc.list

An RPC function may be without parameters, like rpc.list, fs.list, sys.info or sys.reboot. Other RPC functions take parameters. Parameters are set in a JSON string, passed as HTTP POST body. For example, RPC function fs.read reads a file on a VCON filesystem, and it expects a file and offset parameters. A function returns base64-encoded file data from given offset, and also tells how many bytes are left to read:

curl -su :test https://dash.vcon.io/api/v3/devices/42/rpc/fs.read -d "{\"file\":\"ca.pem\",\"offset\":123}"

Response:

{"file":"ca.pem","offset":123,"left":28127,"data":"MjAyMS...."}

If VCON module has a host microcontroller attached in an RPC (non-transparent) bridge mode, then an additional link is added between VCON and Host MCU. They also talk using JSON-RPC.

NOTE: to stress it again, the following applies only when a bridge setting set to RPC mode. See Configuration Reference section for details.

There is a wildcard RPC function on VCON, host.* that passes all RPC calls that start with host. to the Host microcontroller. So, for example, RPC function rpc.list lists all functions on VCON module, whereas RPC function host.rpc.list lists all functions exported by a Host MCU. For example, the following session lists all function on a Host microcontroller on device 42, and then reads a temperature an Host MCU. Note the host. prefix in those API calls:

curl -su :test https://dash.vcon.io/api/v3/devices/42/rpc/host.rpc.list
["rpc.list","sensor.read"]

curl -su :test https://dash.vcon.io/api/v3/devices/42/rpc/host.sensor.read
{"value":42}

Below is a detailed diagram describing the whole sequence of calls for the host.sensor.read request:

RPC call diagram

Host MCU can use mjson client library to implement JSON-RPC. Note that mjson.h is a completely stand-alone piece of software, independent of VCON. It helps to turn a microcontroller into a JSON-RPC client and server using any available transport, for example an UART connection. Let's demonstrate it.

Take any Arduino board. Install mjson library if it is not already installed.

Note: in order to install an mjson library, start Arduino IDE, choose "Sketch" → "Include Library" → "Manage libraries". In the dialog search field, enter "mjson". You should see a "mjson" library. Click on "Install" button to install that library.

Create a new sketch. Copy/paste the following code:

#include "mjson.h"

// RPC handler for "Sum". Expected RPC frame is like this:
// {id: 1, "method": "Sum", "params": [40, 2]}
static void sum(struct jsonrpc_request *r) {
  double a = 0, b = 0;
  mjson_get_number(r->params, r->params_len, "$[0]", &a);
  mjson_get_number(r->params, r->params_len, "$[1]", &b);
  jsonrpc_return_success(r, "%d", (int) (a + b));
}

void setup() {
  Serial.begin(115200);              // Setup serial port
  jsonrpc_init(NULL, NULL);          // Initialise library
  jsonrpc_export("Sum", sum, NULL);  // Export "Sum" function
}

static int wfn(const char *frame, int frame_len, void *privdata) {
  return Serial.write(frame, frame_len);
}

void loop() {
  if (Serial.available() > 0) jsonrpc_process_byte(Serial.read(), wfn, NULL);
}

The sketch above registers a custom RPC function Sum. In the loop(), we read serial input. An expected input is a series of JSON-RPC strings, delimited by the new line characters.

The jsonrpc_process_byte() function buffers serial input into an internal buffer, and when a new line character is read, jsonrpc_process_byte() calls jsonrpc_process() function which parses the received frame and calls a registered handler function, if any.

Therefore, in order to "talk" to the Arduino using JSON-RPC protocol, we need to open a Serial Monitor, type in a valid JSON-RPC frame in it and press "Enter" to emit a new line character.

Let's call our custom function Sum we've just created. Start Arduino Serial Monitor, choose port speed 115200, and type {"id": 1,"method": "Sum", "params": [2,3]}. Hit enter. You should see an answer frame:

Arduino RPC tutorial

Note that mjson.h library provides one built-in RPC function rpc.list, which returns a list of all registered RPC functions. If you are unsure which functions are provided by the mjson-enabled microcontroller, call rpc.list:

{"id": 1,"method": "rpc.list"}            // Request
{"id": 1,"result": ["rpc.list","Sum"]}    // Response

Now you should get an idea. Any microcontroller that implements mjson.h becomes controlled using a standard JSON-RPC over UART - which you can easily test manually using Serial Monitor, or in your lab by setting up an integration test. VCON module just makes that serial line accessible via cloud.

Notifications

VCON cloud provides a special secure WebSocket endpoint wss://dash.vcon.io/api/v3/notify. This is a read-only notifications endpoint. Each notification is a JSON object with three keys:

  • name - a notification name, e.g. online, offline, and so on
  • did - an ID of a device that generated the event
  • data - an optional notification-specific data.

Notifications diagram

Below is the list of the events:

  • {"name":"online","did":ID} - generated by the cloud when a device becomes online
  • {"name":"offline","did":ID} - generated by the cloud when a device becomes offline
  • {"name":"updated","did":ID,"data":{...}} - generated by the cloud when a device changes its state attribute
  • {"name":"ble.adv","did":ID,"data":{...}} - generaged by the VCON module in the BLE bridge mode, when ble.mode=2. The data contains BLE beacon data.
  • {"name":"CUSTOM","did":ID,"data":CUSTOM} - a custom event that could be generated by a Host microcontroller in the RPC bridge mode. A Host micro must call the cloud.CUSTOM RPC function with any params you want. For example, this is how a Host microcontroller can send some sensor data:
    jsonrpc_printf(fn, NULL, "{%Q:%Q,%Q:{%Q:%g}}",
                  "method", "cloud.temperature",
                  "params", "value", 123.456)
    This call generates {"name":"temperature","did":ID,"data":{"value":123.456}}

NOTE: the cloud UI uses notification endpoint to catch device changes, like online/offline, and dispays that respectively. You can create your custom dashboard using cloud API and notifications.

You can catch all notifications sent by the cloud. Below is a simple NodeJS application that catches all incoming notifications and prints them on a console. Create file catcher.js, copy/paste the folowing content:

const Websocket = require('ws');  // npm install -g ws
const addr = 'wss://dash.vcon.io/api/v3/notify?access_token=APIKEY';
const reconnect = function() {
  const ws = new Websocket(addr, {origin: addr});
  ws.on('error', msg => console.log('Got error:', msg.toString()));
  ws.on('message', msg => console.log('Got message:', msg.toString()));
  ws.on('close', () => setTimeout(reconnect, 1000));
};
reconnect();

Change APIKEY to your API key which could be copied from the "Account" tab in the cloud UI. Run this file and see how arriving notifications are printed:

$ node catcher.js
Got message: {"name":"online","did":1}

NOTE: by bringing up a custom notification catcher, you can integrate with any other 3rd party service - for example, store data into a Google/AWS/Azure, dump data in your custom dashboard, send SMS, run analytics, et cetera.

BLE gateway

In the BLE bridge mode, VCON module receives BLE beacon advertisements from nearby BLE beacons, and forwards them to the cloud as device notifications. You can catch those notifications by using a notification catcher, and do whatever you want with the data - store in a database, do real-time processing, etc.

Also VCON provides a BLE.Advertise RPC function, which you can call from anywhere via the cloud and trigger VCON module to send a BLE advertisement with configurable payload.

To configure BLE bridge, see ble section in the Configuration Reference.

OTA updates

VCON module can update Host MCU remotely. You can securely pass a new firmware over the VCON cloud, then VCON reprograms an attached microcontroller. It works because VCON knows respective flashing protocols for different architectures. Network communication is over TLS, encrypted and authenticated.

OTA diagram

See Configuration Reference for exact details on how to setup OTA for your microcontroller.

VCON can also update itself, using the same cloud API. Just add an extra ?target=vcon query string to the ota endpoint, and send a VCON firmware, not your MCU firmware:

curl -su :API_KEY 'https://dash.vcon.io/api/v3/devices/ID/ota?target=vcon' --data-binary @fw.bin

Useful links:

Provisioning process

A VCON module, in order to operate, requires two crucial settings in its configuration file:

  • WiFi network name + password, wifi.sta.ssid and wifi.sta.pass. For ethernet and cellular, these are not required
  • Cloud password, dash.pass

Thus this section explains different methods of setting these configuration parameters.

Method 1. Flashing VCON configuration file using a flasher CLI tool

Method 2. Using WiFi access point

A VCON module with unconfigured wifi.sta.ssid automatically starts a WiFi network vcon-????. Join that network, and talk to a VCON module on IP address 192.168.4.1. VCON runs an HTTP server which accepts configuration command:

curl http://192.168.4.1/rpc/config.set -d "{\"config\":{\"wifi\":{\"sta\":{\"ssid\":\"X\",\"pass\":\"Y\"}}},\"save\":true}"

Method 3. Using Bluetooth Low Energy (BLE), described at Network Setup section

RPC API Reference

The following table summarises built-in RPC functions that are exposed by a VCON module. These functions can be called remotely via REST, or locally via UART (for example, Host MCU can call any of the VCONs RPC function).

This is an example of the remote function call using curl utility. APIKEY is your API key you can get from the Account tab on dash.vcon.io, ID is a device ID, and NAME is an RPC name:

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/NAME

This is an example calling sys.info function on device 1:

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/1/rpc/sys.info
{"id":1,"result":{"version":"4m-1.3.3-v3.3","built":"2020-05-09 20:03:20","uptime":9,"ram_free":104360,"reboot_reason":"other watchdog"}}

Some API calls require parameters, a JSON string in HTTP POST body:

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/1/rpc/fs.read -d "{\"file\":\"config.json\"}"
{"id":2,"result":{"file":"config.json","offset":0,"left":0,"data":"ewogICJ3aWZp..."}}
Method Parameters Description
rpc.list - Show all RPC functions available
sys.info - Show general information about a device, like build timestamp, firmware version, uptime in seconds
sys.log - Show the current console log buffer. Every console log message is stored in a fixed-size, 1K buffer. New messages are appended to the end, older messages are removed to keep the space fixed. This RPC returns the log buffer and cleans it. Call this function repeatedly to get devices's console log
sys.reboot - Reboot device
config.get - Show devices configuration
config.set
{"config":{...},"save": false}
Update device configuration. config attribute specify configuration changes. save is an optional parameter (default false) that tells to make changes permanent by storing them in a config.json file on a device. Note: config.json file keeps only configuration overrides. If you set configuration option to its default value, it disappears from the "config.json" file
gpio.write
{"pin": 4,"val": 0}
Set a given VCON pin to a given value
fs.list - List files on VCON filesystem
fs.read
{"file": "NAME","offset": 0}
Get file data from given offset. Returned file data is base64-encoded
fs.write
{ "file": "NAME","data": "BASE64","append": true}
Write data to a file. Data must be base64-encoded. Data is appended to the end of the file. A written file gets truncated before write, unless an optional append parameter is set to true (it is false by default)
swd.exec
{"req": "rst rd0"}
Send a list of SWD commands to the Host MCU. Available SWD commands:
rst - send SWD reset sequence
rd0,rd1,rd2,rd3 - read debug register
wd0,wd1,wd2 - write debug register
ra0,ra1,ra2,ra3 - read access register
wa0,wa1,wa2,wa3 - write access register
rm,ADDR - read memory at ADDR (hex)
rm,ADDR,MASK,VAL - read memory at ADDR until MASK equals to VAL
wm,ADDR,VAL - write memory
Note: ADDR, VAL, MASK values must be lowercase hexadecimal numbers without the 0x prefix, e.g.: 2ef. Example - a standard MCU init sequence is rst rd0 wd0,1f wd2,0 wd1,50000f00 wa0,23000012
swd.rmem
{"size": 128,"addr": 0}
Read Host MCU memory over SWD
mcu.reset - Reset Host MCU
spi.txn
{"cmd":"COMMANDS"}
Execute SPI transaction on Host MCU. $host.spi must be configured. COMMANDS is a space-separated list of SPI commands which has format ADDRESS HEX_WRITE.READ_SIZE. For example, reading AVR fuses is the following command: ac530000.04 50000000.04 58080000.04 50080000.04
wifi.scan - Scan WiFi and return a list of available WiFi networks
http.request
{"method": "POST","url": "https://foo.com/bar","headers":"A:B\r\nC:D\r\n","body":"a=1&b=2"}
Send HTTP request to a given URL. Note: $.host.mode must be set to 1 (RPC bridge). The url parameter is mandatory, whereas method, headers and body are optional. Response is an object {"code": 200, "headers": {...}, "body": "base64-encoded-response-body"}. Note that the response gets fully buffered in VCON RAM. You can see the amount of free RAM on VCON by calling sys.info RPC call. Do not send requests which trigger responses larger than several Kb
ws.send
{"data": "hello there"}
Send data to the Websocket server. Note: $.host.mode must be set to 3, and $.ws section configured
host.* any Forward RPC frame to the Host MCU, stripping host. prefix from method name
cloud.* any Forward RPC frame to the cloud, stripping cloud. prefix from method name. Use this RPC to send custom notifications to the cloud. The following method names are treated specially by the cloud:
{"method":"cloud.state.set","data":...} - This notification changes device's state attribute on the cloud. The behavior is similar to AWS/Azure device shadow - setting an attribute touches only that attribute and leaves the rest intact. To delete an attribute, set it to null

Configuration Reference

VCON module configuration is a JSON string. It is stored on the root filesystem in "config.json" file. It could be viewed by calling config.get RPC function:

curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/config.get
{"wifi":{"sta":{"ssid":"MyWiFiNetwork","pass":"mypassword"}},"dash":{...},...}

In order to change one or more configuration option, call config.set RPC function and pass a JSON object with 2 parameters: required config with your changes, and optional boolean save to save your changes to "config.json". For example, this command changes an architecture of Host microcontroller to 200 (STM32 with 1k flash pages), and saves:

curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/config.set -d "{\"config\":{\"host\":{\"arch\":200}},\"save\":true}"

The following table describes all configuration options. Every option name is specified by its JSON path.

Name Type Description
$.wifi.mode int WiFi mode. Default: 1 (enabled). Possible values: 0 - disabled, 1 - enabled
$.wifi.sta.ssid string WiFi network name. Default: "" (unset)
$.wifi.sta.pass string WiFi network password. Default: "" (unset)
$.wifi.ap.ssid string WiFi Access Point name for an unconfigured device . Default: "vcon-????". Note: ? characters in the AP name will be replaced with the MAC address hex values
$.dash.url string Cloud address. Default: "wss://dash.vcon.io/api/v3/rpc". Override it if you have a dedicated/private cloud instance
$.dash.pass string Device cloud auth token. Default: "" (unset)
$.debug.level string Console log level. Default: "2". Possible values:
"0" - disable console log
"1" - log errors only
"2" - log errors and info messages
"3" - log erros, info and debug messages
"4" - log everything
Per-file overrides are supported, e.g. "2,tcp:3"
$.debug.tlslevel int Console log level for TLS. Default: 1. Possible values: 0-5
$.debug.udpaddr string UDP log address "IP:PORT". Default: "". If set, then console log messages are also sent to the specified HOST:PORT
$.mqtt.url string MQTT server URL. Required only when $.host.mode is 2. Default: NULL (not set). The format of the URL is PROTO://USER:PASS@HOST:PORT, where PROTO is mqtt or mqtts, USER and PASS are MQTT credentials (could be omitted). NOTE: VCON sets MQTT client ID equal to the $.device.id
$.mqtt.rx string MQTT topic for VCON to subsribe to. All data read from this topic, VCON will forward to the Host MCU UART RX
$.mqtt.tx string MQTT topic for VCON to publish to. All data VCON reads from the Host MCU UAET, VCON publishes to that topic
$.mqtt.cert string Client TLS certificate file name. Required only for 2-way TLS. Must be prefixed with /spiffs, e.g. /spiffs/my_cert.pem
$.mqtt.key string Client TLS certificate key file name. Required only for 2-way TLS. Must be prefixed with /spiffs, e.g. /spiffs/my_key.pem
$.ws.url string Websocket server URL. Required only when $.host.mode is 3. Default: NULL (not set)
$.ws.hdrs string Websocket extra HTTP headers for WS handshake. Required only when $.host.mode is 3. Default: NULL (not set). Example: Authorization: Bearer foobar\r\n
$.ws.cert string Client TLS certificate file name. Required only for 2-way TLS. Must be prefixed with /spiffs, e.g. /spiffs/my_cert.pem
$.ws.key string Client TLS certificate key file name. Required only for 2-way TLS. Must be prefixed with /spiffs, e.g. /spiffs/my_key.pem
$.device.id string An arbitrary device identification. Used only in the BLE adv forwarding, and passed as a gw BLE adv frame parameter
$.device.dns string Custom DNS server URL. Example: udp://1.1.1.1:53 - which is a CloudFlare DNS server. Default: "" (unset), in which case Google's udp://8.8.8.8:53 is used
$.rpc.safe string List of comma-separated RPC functions available to Host MCU, and over the HTTP server. Default: config.set,sys.*,rpc.*,gpio.*,cloud.*
$.host.arch int A Host MCU arhitecture. Default: 0 (unset). Required for reprogramming. Possible values:
100 - Microchip SAMD
200 - STM32 with 1k flash pages
201 - STM32 with 2k flash pages
202 - STM32 with 16,16,16,16,64,128,128,128k pages
203 - STM32 with 32,32,32,32,128,256,256,256k pages
300 - AVR (e.g. classic Arduinos)
$.host.rst int VCON pin to which Host MCU's RESET pin is connected. Default: -1 (unset). Required for reprogramming / rebooting an attached MCU if that cannot be done via SWD, for example for attached AVR
$.host.mode int Host MCU communication mode. Default: 0 (disabled). Possible values:
0 - disabled
1 - RPC bridge. Serial link is exchanging JSON-RPC frames, delimited by newline characters
2 - transparent UART/MQTT bridge. Serial data is uninterpeted, sent to/from mqtt.rx and mqtt.tx topics. Make sure to configure the mqtt section
3 - transparent UART/Websocket bridge. Serial data is uninterpeted, sent to/from $.ws.url Websocket server. Make sure to configure the $.ws section
$.host.addr int Flash memory address for .bin firmwares. For .hex firmwares, this value is ignored. Default: 0
$.host.swd.io int VCON pin to which Host MCU's SWDIO pin is connected. Default: -1 (unset). Required for reprogramming ARM MCUs
$.host.swd.clk int VCON pin to which Host MCU's SWDCLK pin is connected. Default: -1 (unset). Required for reprogramming ARM MCUs
$.host.swd.dur int SWD clock duration in VCON's nop instructions. Default: 50
$.host.uart.tx int VCON pin to which Host MCU's UART TX pin is connected. Default: -1
$.host.uart.rx int VCON pin to which Host MCU's UART RX pin is connected. Default: -1
$.host.uart.cts int VCON pin to which Host MCU's UART CTS pin is connected. Default: -1 (unset)
$.host.uart.rts int VCON pin to which Host MCU's UART RTS pin is connected. Default: -1 (unset)
$.host.uart.baud int A Host MCU's UART baud rate. Default: 115200
$.host.spi.mosi int VCON pin to which Host MCU's SPI MOSI pin is connected. Default: -1
$.host.spi.miso int VCON pin to which Host MCU's SPI MISO pin is connected. Default: -1
$.host.spi.clk int VCON pin to which Host MCU's SPI CLK pin is connected. Default: -1
$.host.spi.freq int Host MCU's SPI clock frequency. Default: 50000
$.ble.mode int BLE gateway mode. Default: 0 (disabled). Possible values:
0 - BLE disabled
1 - BLE enabled, configurator is starged
2 - BLE enabled. Beacon data is forwarded to the cloud as a ble.adv notification. NOTE: this mode disables BLE device configurator
3 Beacon data is forwarded to the $.mqtt.tx topic of the configured MQTT server. NOTE: this mode disables BLE device configurator
$.ble.filter string A comma separated list of glob patterns. BLE advertisements whose MAC addresses or names do not match all patterns, are ignored. Default: empty, which means all BLE advertisements match. Example: MyName*,aabbcc* - match devices whose names start with MyName or MAC address start with aabbcc
$.ble.lowat int A RAM threshold. If free RAM is lower than this value, then ignore an incoming BLE advertisement, do not forward it to the cloud to preserve RAM. Default: 0
$.eth.mode int Ethernet mode. Default: 0 (disabled). Possible value:
0 - Ethernet disabled
1 - Ethernet enabled
$.eth.addr int Ethernet PHY address. Default: -1
$.eth.clk int Ethernet clock source. Default: 0. Possible values: CONFIG_ETH_RMII_CLK_* constants
$.eth.mdc int Ethernet MDC pin. Default: -1
$.eth.mdio int Ethernet MDIO pin. Default: -1
$.eth.pwr int Ethernet PHY power pin. Default: -1
$.pins.status int Status LED pin. Default: 2 (enabled). To disable, set to -1. If enabled, it is assumed that is an LED, and does the following:
fast 75 ms blink - when network is not connected,
slow 750 ms blink - when network is connected, but cloud is not,
steady light - when cloud is connected
$.pins.factory int Factory reset pin. Default: 4 (enabled). To disable, set to -1. If enabled, it is assumed that this pin is connected to a (pulled up) button. Pressing this button for more than 3 seconds (pulling low) triggers factory reset, which copies factory.json to `config.json

Troubleshooting

If any quick start guide step fails, or the VCON module behaves in an unexpected way, please follow these steps:

  • Attach a serial console to the ESP32 (ERX and ETX pins)
  • Physically power cycle the device
  • Post a message to the VCON user forum with the following:
    • Which hardware you're using
    • Which guide you are following
    • Which exactly step (or command) fails, include the command and an output
    • The full console log, including all boot messages