Overview

VCON is a framework to connect microcontrollers online. It provides secure IoT connectivity, remote management, and OTA update. VCON consists of two parts:

  • VCON hardware connectivity module (or an all-in-one board like Nano 33 IoT)
  • VCON device management cloud (shared at https://dash.vcon.io or dedicated)

VCON architecture

VCON module is, in turn:

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

VCON module is a "smart" connectivity module of a new type. Unlike traditional modules, it knows how to reprogram a Host MCU (a microcontroller that is attached to a VCON module). It implements ARM SWD protocol - so essentially, it is a remote programmer, which allows to connect to any of your live remote devices with a debugger. So VCON can debug and re-program your device at any time, giving you an unprecedented control over your fleet.

Also, VCON module acts as a network bridge - either transparent, or non-transparent. In transparent mode, VCON forwards UART data to the server of your choice. In this case, a Host MCU (a microcontroller used in your product) is not even aware it is connected to the Internet - it thinks it is controlled over the UART locally. This mode allows to retrofit existing non-connected devices to the Internet with minimum efforts.

In non-transparent mode, a Host microcontroller can use a tiny client library and turn itself into the UART JSON-RPC server. VCON bridges it to the REST. So essentially a Host microcontroller appears as a REST service, controllable in a familiar way from any Web client like mobile phone, management dashboard UI, or an automation script.

In any case, a Host microcontroller does not implement a single line of networking code. That allows to connect and control even the tiniest 8-bit systems, like a classic Arduino Uno.

VCON functionality includes, but is not limited to:

  • A Host MCU can create custom functions (like GetSensorData or TurnOnMotor), callable via REST from anywhere (RPC bridge mode)
  • A Host MCU can send custom notifications (with data) to the cloud
  • A Host MCU can make outbound HTTP requests
  • A Host MCU can read, write, delete files on the VCON module
  • You can OTA a Host MCU via Internet (supported architectures are STM32, SAMD, AVR)
  • You can OTA a VCON module itself via Internet
  • You can set up automatic OTA updates
  • You can catch BLE beacon advertisements and forward them to the cloud
  • You can send a custom BLE advertisement at anytime from anywhere

A table below contains an incomplete list of hardware that could be used as a VCON module. Since a VCON firmware could be loaded on any ESP32 based device, therefore a complete hardware list includes all modules / devices that have ESP32 in them.

Name Image Type Notes
Arduino Nano 33 IoT nano33 WiFi All-in-one system - has both Host MCU (SAMD21) and ESP32 (ublox NINA)
PICO-D4 Kit PICO WiFi, BLE Instead of PICO-D4 Kit, any other ESP32 dev board can be used
Olimex ESP32-POE ESP32 POE WiFi, BLE, Ethernet Can be powered from Ethernet! Ideal for BLE bridge applications
Pycom GPy Pycom WiFi, BLE, Cellular Provides Cellular connectivity
WROOM32 WROOM32 WiFi, BLE Instead of WROOM32, any other ESP32 module can be used

Quick Start Guide

Arduino Nano 33 IoT

In this section, we learn:

  • How to remotely update firmware on Arduino Nano 33 IoT board,
  • How to remotely control it by switching an LED on/off,
  • How to report custom data to the cloud

If any step of this guide fails, please follow the Troubleshooting section.

Step 1. Get an Arduino Nano 33 IoT board and connect it to your workstation over the USB cable. A serial port must appear on your workstation, e.g. COM3 on windows or /dev/cu.usbmodem14101 on Mac.

Step 2. Flash a Nina passthrough sketch to the board. Start Arduino IDE, choose File → Examples → WiFiNINA → Tools → SerialNINAPassthrough. Choose Tools → Board → Arduino Nano 33 IoT. Choose Tools → Port. Upload the sketch.

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

Step 4. In the unzipped folder, edit fs/config.json file - set WiFi network name, WiFi password, and cloud password:

{
  "wifi": {"sta": {"ssid": "WIFI_NETWORK", "pass": "WIFI_PASSWORD"}},
  "dash": {"pass": "CLOUD_DEVICE_PASSWORD"},
  "host": {
    "arch": 100,
    "uart": {"rx": 23, "tx": 12,"cts": 19, "rts":22},
    "swd": {"io": 32, "clk": 14}
  }
}

To obtain device's cloud password, login to https://dash.vcon.io, add a new device if needed, then choose "action" → "Copy device password to clipboard". Save edited file.

Step 5. Download https://vcon.io/downloads/fw/2m.full.hex into the unzipped cli/ directory

Step 6. 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 -fs fs flash 2m.full.hex
Linux
./linux/cli -p COMPORT -fs fs flash 2m.full.hex
MacOS
./macos/cli -p COMPORT -fs fs flash 2m.full.hex

Step 7. A device on dash.vcon.io must become online: Online device

Step 8. Update firmware on the SAMD21 microcontroller remotely. In the Arduino IDE, copy/paste the following sketch. This is a standard Blink sketch with the only extra addition that enables ublox NINA module:

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  // Important: enable NINA module! Otherwise cloud connection drops.
  pinMode(NINA_GPIO0, OUTPUT); digitalWrite(NINA_GPIO0, HIGH);
  pinMode(NINA_RESETN, OUTPUT); digitalWrite(NINA_RESETN, HIGH);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);
  delay(100);
}

Choose Sketch → Export compiled Binary, to compile this sketch. Choose Sketch → Open Sketch folder. You should see a Blink.ino.nano_33_iot.bin file there, which is a binary firmware.

In order to remotely flash it, we need to convert this binary file into a .hex file. In your Arduino installation, find Nano 33 IoT bootloader file, samd21_sam_ba_arduino_nano_33_iot.bin. Copy both the bootloader and compiled firmware to the cli/ folder. Run the following command from the command prompt to create nano33.hex:

./OS/cli mkhex 0 samd21_sam_ba_arduino_nano_33_iot.bin 0x2000 Blink.ino.nano_33_iot.bin > nano33.hex

NINA module updates SAMD21 microcontroller over the SWD interface. To make that possible, we need to physically wire two pins together, on the bottom of the Nano 33 IoT board. The SAMD21 SWDIO pad needs to be connected to the PA6 pin, and SWDCLK pad - to PA5 pin. See on the picture: Soldering SWDIO - PA6, SWDCLK - PA7

And now, a remote firmware update (set APIKEY and ID):

curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/ota --data-binary @nano33.hex

An LED on a board must start blinking. Congratulations, now we could remotely update Nano 33 IoT firmware at any time!

Below, see a demo video that demonstrates how VCON is used to update Arduino Nano:

Step 9. Remotely control an Arduino board. Blinking LED is exciting, but how about remote commands? We want to control our Nano 33 IoT from a command line, from Web UI or from a mobile phone. That is no problem.

Let's teach our Arduino to turn any GPIO pin low or high. In the 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 with the following content:

#include <mjson.h>

// Expected params: {"pin":13, "value": 1}
static void gpio_write_rpc(struct jsonrpc_request *r) {
  double pin, val;
  if (mjson_get_number(r->params, r->params_len, "$.pin", &pin) &&
      mjson_get_number(r->params, r->params_len, "$.val", &val)) {
    pinMode(pin, OUTPUT);
    digitalWrite(pin, val);
    jsonrpc_return_success(r, "true");
  } else {
    jsonrpc_return_error(r, JSONRPC_ERROR_BAD_PARAMS, "pin, val required", 0);
  }
}

void setup() {
  // Important: enable NINA module! Otherwise cloud connection drops.
  pinMode(NINA_GPIO0, OUTPUT); digitalWrite(NINA_GPIO0, HIGH);
  pinMode(NINA_RESETN, OUTPUT); digitalWrite(NINA_RESETN, HIGH);

  SerialHCI.begin(115200);  // NINA and SAMD21 communicate over SerialHCI, see https://github.com/arduino/ArduinoCore-samd/blob/master/variants/nano_33_iot/variant.cpp 
  jsonrpc_init(NULL, NULL);
  jsonrpc_export("gpio.write", gpio_write_rpc, NULL);
}

static int wfn(const char *frame, int frame_len, void *userdata) {
  (void) userdata; // Unused
  return SerialHCI.write(frame, frame_len);
}

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

This Arduino sketch implements a JSON-RPC server over the serial line SerialHCI, which is connected to the NINA module. Arduino firmware reads JSON frames from the serial, process them, and writes replies back. We could implement any number of any management functions we want. This example firmware implements a gpio.write function that could turn any GPIO pin on or off. Since SAMD21 microcontroller, our Host MCU, is connected to the VCON module (ublox NINA), we can call these functions over the cloud.

Compile this sketch, convert to .hex, and remotely update your Arduino Nano 33 IoT using the procedure described in Step 8. In your terminal, enter the following command to list all RPC functions exported by the Arduino firmware (change APIKEY and ID):

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/host.rpc.list
{"id":4,"result":["gpio.write","rpc.list"]}

We see that two functions are available: a built-in rpc.list, and our custom gpio.write. Let's turn pin 13 (a built-in LED) on:

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/host.gpio.write -d "{\"pin\":13,\"val\":1}"
{"id":5,"result":true}

An LED turns on! Congratulations - now we can control Arduino from anywhere. We can read sensor values, drive motors, or send any other custom commands. And those commands are available over the standard authenticated REST API.

Step 10. Send data. Now we can remotely update Arduino firmware, and remotely control it - the only one missing bit left - how would we send data from the Arduino to the outside world? That is done by means of Notifications. A device can send a JSON-RPC notification frame with any adata to the cloud. A cloud has a special WebSocket endpoint, which receives all notifications from all devices. Let's demonstrate that!

We'll modify the firmware from Step 9 by sending a custom notification peiodically - every second. In the notification, we'll send the current device uptime in milliseconds:

#include <mjson.h>

// Expected params: {"pin":13, "value": 1}
static void gpio_write_rpc(struct jsonrpc_request *r) {
  double pin, val;
  if (mjson_get_number(r->params, r->params_len, "$.pin", &pin) &&
      mjson_get_number(r->params, r->params_len, "$.val", &val)) {
    pinMode(pin, OUTPUT);
    digitalWrite(pin, val);
    jsonrpc_return_success(r, "true");
  } else {
    jsonrpc_return_error(r, JSONRPC_ERROR_BAD_PARAMS, "pin, val required", 0);
  }
}

void setup() {
  // Important: enable NINA module! Otherwise cloud connection drops.
  pinMode(NINA_GPIO0, OUTPUT); digitalWrite(NINA_GPIO0, HIGH);
  pinMode(NINA_RESETN, OUTPUT); digitalWrite(NINA_RESETN, HIGH);

  SerialHCI.begin(115200);  // NINA and SAMD21 communicate over SerialHCI, see https://github.com/arduino/ArduinoCore-samd/blob/master/variants/nano_33_iot/variant.cpp 
  jsonrpc_init(NULL, NULL);
  jsonrpc_export("gpio.write", gpio_write_rpc, NULL);
}

static int wfn(const char *frame, int frame_len, void *userdata) {
  (void) userdata; // Unused
  return SerialHCI.write(frame, frame_len);
}

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

  static unsigned long prev;
  unsigned long now = millis();
  if (now - prev > 1000) {
    // This will generate the following event by dash.vcon.io:
    // {"name": "myevent", "data": MILLISECONDS}
    mjson_printf(wfn, NULL, "{%Q:%Q,%Q:%lu}\n", "method", "cloud.myevent", "params", now);
    prev = now;
  }
}

Remotely update your Arduino with that firmware.

The board now sends custom notification events to the cloud. Let's catch them using a simple NodeJS application. Create a new 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', function(msg) { console.log('Got error:', msg.toString()) });
  ws.on('message', function(msg) { console.log('Got message:', msg.toString()) });
  ws.on('close', function() { 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":"myevent","did":4,"data":263901}
Got message: {"name":"myevent","did":4,"data":263902}
Got message: {"name":"myevent","did":4,"data":263903}

Congratulations! You've just got your custom data reported from your board. Your catcher.js application could save data in any database - for example, Google Firebase, or do any other custom data processing. This is the way to integrate VCON with your own cloud infrastructure. Obviously, you could use any other language to implement a catcher - for example, Python or Go. For example, if your cloud infrastructure reside on AWS, host your catcher application on AWS too, and you can directly save messages to the Dynamo DB or any other part of AWS cloud. Similarly for Microsoft Azure, Google Cloud, or your own private cloud infrastructure - the approach is the same.

Olimex ESP32 POE

In this guide, we are going to flash VCON firmware to the Olimex ESP32 POE board, and configure it as a BLE bridge. Expected result:

  • Catch BLE advertisements from a nearby beacons and forward them to the cloud
  • Collect BLE data from the cloud in real time and print it as its arrives
  • Use cloud API to command a POE board to send a custom BLE beacon

Olimex POE BLE bridge diagram

If any step of this guide fails, please follow the Troubleshooting section.

Step 1. Get a Olimex ESP32 POE board and connect it to your workstation over the USB cable. IMPORTANT: make sure that Ethernet cable is unplugged, otherwise you can burn your board. A serial port must appear on your workstation, e.g. COM3 on windows or /dev/cu.wchusbserial1420 on Mac.

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

Step 3. In the unzipped folder, edit fs/config.json file. Make it look like this:

{
  "dash": {"pass": "CLOUD_DEVICE_PASSWORD"},
  "wifi": {"mode": 0},
  "host": {"mode": 0},
  "ble": {"mode": 2},
  "eth": {"mode": 1, "addr": 0, "clk": 3, "mdc": 23, "mdio": 18, "pwr": 12}
}

Change CLOUD_DEVICE_PASSWORD to the actual password. To get it, login to https://dash.vcon.io, add a new device if needed, then choose "action" → "Copy device password to clipboard". Save config.json when done.

Step 4. Download https://vcon.io/downloads/fw/4m.full.hex into the unzipped cli/ directory

Step 5. 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 -fs fs flash 4m.full.hex
Linux
./linux/cli -p COMPORT -fs fs flash 4m.full.hex
MacOS
./macos/cli -p COMPORT -fs fs flash 4m.full.hex

Step 6. Unplug USB cable. Plug in Ethernet cable. The lights on the board must switch on. If they not, then the POE (Power Over Ethernet) is probably not enabled, and it is safe to plug in USB cable to power the board. A device on dash.vcon.io must become online: POE dashboard

Step 7. The board now sends BLE notification events to the cloud. Let's catch them using a simple NodeJS application. 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', function(msg) { console.log('Got error:', msg.toString()) });
  ws.on('message', function(msg) { console.log('Got message:', msg.toString()) });
  ws.on('close', function() { 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":"ble.adv","did":1,"data":{"rssi":-71,"addr":"ac233fa14bdd","adv":"0201060303e1ff0d16e1ffa1026401dd4ba13f23ac","rsp":""}}
Got message: {"name":"ble.adv","did":1,"data":{"rssi":-72,"addr":"ac233fa14bdd","adv":"0201060303e1ff0d16e1ffa1026401dd4ba13f23ac","rsp":""}}
Got message: {"name":"ble.adv","did":1,"data":{"rssi":-76,"addr":"ac233fa14bdd","adv":"0201060303e1ff0d16e1ffa1026401dd4ba13f23ac","rsp":""}}

Congratulations! You've just got your beacon data via the cloud.

VCON + STM32 Bluepill

In this section, we learn how to remotely update STM32F103C8T6 BluePill board, how to control it remotely, and how to send data from the BluePill. The same approach would work for any other STM32 board.

If any step of this guide fails, please follow the Troubleshooting section.

Step 1. Get any ESP32-based board and connect it to your workstation over the USB cable. A serial port must appear on your workstation, e.g. COM3 on windows or /dev/cu.usbmodem14101 on Mac.

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

Step 4. In the unzipped folder, edit fs/config.json file - set WiFi network name, WiFi password, and cloud password:

{
  "wifi": {"sta": {"ssid": "WIFI_NETWORK", "pass": "WIFI_PASSWORD"}},
  "dash": {"pass": "CLOUD_DEVICE_PASSWORD"},
  "host": {
    "arch": 200,
    "uart": {"rx": 25, "tx": 26},
    "swd": {"io": 21, "clk": 22}
  }
}

To obtain device's cloud password, login to https://dash.vcon.io, add a new device if needed, then choose "action" → "Copy device password to clipboard". Save edited file.

Step 5. Download https://vcon.io/downloads/fw/4m.full.hex into the unzipped cli/ directory

Step 6. 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 -fs fs flash 4m.full.hex
Linux
./linux/cli -p COMPORT -fs fs flash 4m.full.hex
MacOS
./macos/cli -p COMPORT -fs fs flash 4m.full.hex

Step 7. A device on dash.vcon.io must become online: Online device

Step 8. Wire STM32 to the VCON.

VCON pin BluePill pin
3.3V 3.3V
GND GND
IO 21 SWDIO
IO 22 SWDCLK
IO 25 PA9 (UART TX)
IO 26 PA10 (UART RX)

As an example, this picture shows the wiring for ESP32 PICOD4-Kit V3 and Bluepill: PICO-D4 Kit + Bluepill

Step 9. Update STM32 Bluepill firmware remotely. Download a prebuilt STM32 firmware from https://github.com/cesanta/stm32-bluepill/releases/download/1.0/bluepill.hex into the cli/ directory. Run the following command to update STM32 (substitute APIKEY with your API key from the Account tab, and ID with your device ID):

curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/ota --data-binary @bluepill.hex

NOTE: if you want to rebuild the STM32 firmware yourself, please use https://github.com/cesanta/stm32-bluepill

Step 9. Send control commands to the Bluepill board. The bluepill firmware we use is very simple - see main.c. It blinks, and also it implements JSON-RPC over UART using the mjson client library. We have configured VCON module in the RPC bridge mode, and that means we can call any RPC function that Bluepill implements, remotely. Let's do that, call a built-in RPC function rpc.list to see what functions are implemented on a Bluepill (change APIKEY and ID):

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/host.rpc.list
{"id":4,"result":["SetCycles","rpc.list"]}

We see that there are rpc.list and SetCycles. Let's call SetCycles to change the blinking interval to blink much faster:

$ curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/host.SetCycles -d "{\"period\":30000}"
{"id":5,"result":null}

The board must start to blink faster. This demonstrates how we can remotely trigger absolutely any piece of functinality on the Host MCU.

VCON + Arduino Nano

In this section, we learn how to remotely update a classic Arduino Nano, how to control it remotely, and how to send data from it. The same approach would work for any other AVR board - like Uno, Pro Mini, Mega, etc.

If any step of this guide fails, please follow the Troubleshooting section.

Step 1. Get any ESP32-based board and connect it to your workstation over the USB cable. A serial port must appear on your workstation, e.g. COM3 on windows or /dev/cu.usbmodem14101 on Mac.

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

Step 4. In the unzipped folder, edit fs/config.json file - set WiFi network name, WiFi password, and cloud password:

{
  "wifi": {"sta": {"ssid": "WIFI_NETWORK", "pass": "WIFI_PASSWORD"}},
  "dash": {"pass": "CLOUD_DEVICE_PASSWORD"},
  "host": {
    "arch": 300,
    "rst": 25,
    "uart": {"rx": 33, "tx": 32},
    "spi": {"mosi": 23, "miso": 19, "clk": 18, "freq": 100000}
  }
}

To obtain device's cloud password, login to https://dash.vcon.io, add a new device if needed, then choose "action" → "Copy device password to clipboard". Save edited file.

Step 5. Download https://vcon.io/downloads/fw/4m.full.hex into the unzipped cli/ directory

Step 6. 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 -fs fs flash 4m.full.hex
Linux
./linux/cli -p COMPORT -fs fs flash 4m.full.hex
MacOS
./macos/cli -p COMPORT -fs fs flash 4m.full.hex

Step 7. A device on dash.vcon.io must become online: Online device

Step 8. Wire Arduino to the VCON

NOTE: Pay attention to the voltage level. If you're using 3v3 Arduino, there is nothing to worry about. If you're using 5v Arduino, make sure to avoid 5V signals on ESP32 pins.

VCON pin Arduino pin
5V RAW
GND GND
IO 33 RX
IO 32 TX
IO 23 SPI MOSI
IO 19 SPI MISO
IO 18 SPI CLOCK
IO 25 RESET

As an example, this picture shows the wiring for ESP32 PICOD4-Kit V3 and Arduino Pro Mini clone: PICO-D4 Kit + AVR

Step 9. Reprogram Arduino remotely. Start Arduino IDE. Choose a standard Blink example: File → Examples → Basics → Blink. Choose Tools → Board → Arduino Uno. Now, compile: choose Sketch → Export compiled binary. When done, choose Sketch → Open Sketch Folder - you should see a compiled .hex file in your Arduino folder, Blink.ino.standard.hex.

Open https://dash.vcon.io, and choose action → Update firmware. In a dialog, set Target to Host MCU, and Method to .hex file upload. Click on choose .hex file, and in the file selection dialog, navigate to your Arduino sketch folder, click on your compiled .hex file and click Open.

Your should see a firmware update progress bar appear, and when done, your Arduino should start blinking.

Step 10. Remote control. Let's teach our Arduino to turn any GPIO pin low or high using remote command. In the Arduino IDE, choose "Sketch" → "Include Library" → "Manage libraries". In the dialog search field, enter "mjson". You should see a "mjson" library. Click on "Install" to install that library. When installed, create a new sketch with the following code:

#include <mjson.h>

// Expected params: {"pin":13, "value": 1}
static void gpio_write_rpc(struct jsonrpc_request *r) {
  double pin, val;
  if (mjson_get_number(r->params, r->params_len, "$.pin", &pin) &&
      mjson_get_number(r->params, r->params_len, "$.val", &val)) {
    pinMode(pin, OUTPUT);
    digitalWrite(pin, val);
    jsonrpc_return_success(r, "true");
  } else {
    jsonrpc_return_error(r, JSONRPC_ERROR_BAD_PARAMS, "pin, val required", 0);
  }
}

void setup() {
  Serial.begin(115200);
  jsonrpc_init(NULL, NULL);
  jsonrpc_export("gpio.write", gpio_write_rpc, NULL);
}

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

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

Compile this firmware and remotely update your Arduino just like described in a previous step. When done, your Arduino must stop blinking. But we can now turn an LED on and off remotely! Let's turn in on (change APIKEY to your API key, and ID to your device ID):

curl -su :APIKEY https://dash.vcon.io/api/v3/devices/ID/rpc/host.gpio.write -d "{\"pin\":13,\"val\":1}"

You should see an LED turning on. Repeat the previous command, setting val to 0. An LED should go off. This demonstrates how you can remotely control your Host MCU - like, get sensor data, drive motors, or perform any other action.

VCON + Nano + AWS IoT

In this section, we learn how to:

  • Make Arduino Nano communicate with AWS IoT over the serial port
  • Use AWS IoT MQTT messages to turn LED on, off, and get LED status

If any step of this guide fails, please follow the Troubleshooting section.

Step 1. Prepare the VCON + Arduino setup.

We will use an exact the same wiring as in the previous guide, VCON + Arduino Nano. Therefore, complete steps 1 to 9 inclusive of the VCON + Arduino Nano, then return back here.

At this point, we have a classic Arduino board connected to the cloud. We can remotely update the Arduino board to any firmware of our choice.

Step 2. Flash an Arduino firmware.

Create a new sketch with the following contents:

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
    if (character == '2') Serial.write(digitalRead(LED_BUILTIN) ? "1\n" : "0\n");
  }
}

Load this firmware to the Arduino remotely. Let us take a closer look at what this firmware does.

In the loop() function, we read characters from the UART port:

  • If we receive "0", we turn the built-in LED off,
  • If we receive "1", we turn the built-in LED on,
  • If we receive "2", we print the current status of the LED to the UART.

To summarise, this simple sketch implements a very simple Arduino LED control over the UART. In the following steps, we make Arduino serial data available over the AWS IoT MQTT:

  • We'll send the UART output data (TX) to one MQTT topic,
  • We'll read another MQTT topic and write data to the UART input (RX)

Therefore, by writing data into the RX topic, we'll write data to the Arduino serial input, and we can read Arduino serial output by subscribing and reading the TX topic.

Let's go ahead and configure AWS IoT.

Step 3. Configure AWS IoT.

Login to the AWS IoT console. In the left menu, click on "Manage" / "Things". Click on "Create" button to create a new thing. Chooose "Create a single thing". Give it a name "vcon". Click "Next". Click on "Create certificate". Download a "certificate for this thing", and rename it to cert.pem. Download a private key, and rename it to key.pem. Click on "Activate" to activate this certificate. Click on "Attach a policy". Select a policy for the new thing, and click to "Register thing".

If you don't have a policy yet, create a new permissive policy:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": "*"
    }
  ],
  "Version": "2012-10-17"
}

Step 4. Configure VCON for AWS IoT.

Copy cert.pem and key.pem to the cli/fs/ directory. Open cli/fs/config.json file with VCON configuration. Add an extra section in it, mqtt, and set the $.host.mode attribute to 2, which means "use transparent MQTT/UART bridge". Your config.json file should look like this:

{
  "wifi": {"sta": {"ssid": "WIFI_NETWORK", "pass": "WIFI_PASSWORD"}},
  "dash": {"pass": "CLOUD_PASSWORD"},
  "mqtt": {"rx":"vcon/rx", "tx": "vcon/tx","url": "mqtts://cert.pem:key.pem!AWSADDRESS:8883"},
  "host": {
    "arch": 300,
    "mode": 2,
    "rst": 25,
    "uart": {"rx": 33, "tx": 32},
    "spi": {"mosi": 23, "miso": 19, "clk": 18, "freq": 100000}
  }
}

You need to find out an AWSADDRESS - an address of AWS IoT server. In AWS IoT console, click on "vcon" thing, and choose "Interact" on the left hand side menu. The address will be printed in the HTTPS section, and look like this:

xxxxxxxxx-xxxxx.iot.xxxxxx.amazonaws.com

Copy it, and replace AWSADDRESS in the config.json file. Save config file and reflash your VCON module using a command outline in Step 6 of the previous guide.

Congrats! Now VCON creates an extra connection to AWS IoT, forwarding the data to/from Arduino UART port to the vcon/rx and vcon/tx MQTT topics.

Step 5. Remote control Arduino via Aws IoT MQTT.

Go to AWS IoT Console, choose "Test" in the left hand side menu. Type vcon/# in the "Subscription topic" textbox, click on "Subscribe to topic".

Type vcon/rx into the "Publish" textbox, and type 1 into the text area. Click on "Publish to topic" to send a character 1 into the Arduino UART:

AWS test

You should see an LED turning on.

Now publish character 2 in the same way. This way we ask Arduino, what is the status of an LED. We should see Arduino replying with message 1, telling us that the LED is on.

Congratulations! Now we can easily control a classic Arduino board via AWS IoT, and remotely update it at any time.

Module

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 @4m.hex

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)

VCON module advertises itself as a BLE device, and exposes a subset of RPC services (those allowed by a $.rpc.safe configuration variable) via a BLE service. Open https://vcon.io/app/ in any Bluetooth-enabled browser, like Google Chrome or Microsoft Edge:

BLE app

Click on a "Choose device" button and select a VCON device nearby:

BLE app chooser

This app also provides a handy UI for calling RPC services over BLE. On the following screenshot, a sys.info RPC service is called. Note that the available list of RPC services is restricted, and filtered by the $.rpc.safe configuration variable. This is the same list that the Host MCU can call:

BLE app RPC call

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
host.* any Forward RPC frame to the Host MCU, stripping host. prefix from the method name
cloud.* any Forward RPC frame to the cloud, stripping cloud. 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)
$.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
$.device.id string An arbitrary device identification. Used only in the BLE adv forwarding, and passed as a gw BLE adv frame parameter
$.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
$.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
$.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

ESP32 Pinout Reference

GPIO # Reset state Analog Notes
0 input enabled, pulled up yes selects boot mode
1 input enabled, pulled up TX, boot debug output
2 input enabled, pulled down yes
3 input enabled, pulled up RX
4 input enabled, pulled down yes
5 input enabled, pulled up outputs PWM signal at boot
12 input enabled, pulled down yes boot fail if pulled high
13 input enabled yes
14 input enabled yes outputs PWM signal at boot
15 input enabled, pulled up yes outputs PWM signal at boot
16 input enabled
17 input enabled
18 input enabled
19 input enabled
20 input enabled
21 input enabled
22 input enabled
23 input enabled
25 input disabled yes
26 input disabled yes
27 input enabled yes
32 input disabled yes
33 input disabled yes
34 input disabled yes input only
35 input disabled yes input only
36 input disabled yes input only
37 input disabled yes input only
38 input disabled yes input only
39 input disabled yes input only

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 (RX and TX pins)
  • Physically Reboot the ESP32
  • Create an email to support@cesanta.com, and include the following information:
    • 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

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 file data with a new firmware. Example: curl -su :test ttps://dash.vcon.io/api/v3/devices/42/ota --data-binary @PATH_TO_FIRMWARE.hex
GET /devices/:id/bye Disconnect device. If, for various reasons, a device gets stuck, force reconnection

Pinouts

Arduino Pro Mini

Arduino Pro Mini pinout

Arduino Nano

Arduino Nano pinout

Arduino Nano 33 IoT

Arduino Nano 33 IoT pinout

ESP32-PicoD4-Kit v4

ESP32 PicoD4-Kit v4 pinout