Box IO

Self-hosted IoT for Arduino and ESP32.

Arduino sketch functions

These calls live in the Box IO library. You set them in the sketch. Timers, the send cap, and the poll interval are the ones people change most often. Email, SMS, and the ping watchdog have their own pages.

Calls you can use

CallSet itWhat it does
BoxIO.begin(...) Once in setup() Stores the device key and server, and on ESP32 joins Wi-Fi. Port 5923 is the device hub.
BoxIO.run() Every loop() Fires timers, pulses the ping watchdog, sends queued writes, and asks the server for button and slider commands.
BoxIO.connected() Any time after run() True after the last request to the server finished. False when that request could not connect or timed out.
BoxIO.setTimer(ms, fn) setup(), or later Calls fn every ms milliseconds. Returns an id, or -1 if the interval is 0 or the timer table is full.
BoxIO.deleteTimer(id) The id from setTimer Stops that one timer. Other timers keep running. An unknown id does nothing.
BoxIO.setMaxSendsPerSecond(n) setup() Caps virtualWrite. Default is 30. 0 removes the cap. The highest value kept is 10000.
BoxIO.setMaxSendsPerMinute(n) setup() Replaces the per-second cap with this many virtualWrite calls per minute. Minimum 1, maximum 10000.
BoxIO.setPollInterval(ms) setup() How often run() pulls dashboard commands. Default is 400 milliseconds.
BoxIO.virtualWrite(pin, value) From a timer or loop() Sends an int, long, float, text, or an int or float array. An array goes out as comma-separated values for the grid.
BOX_WRITE(V0) Global, pins 0–31 Runs when the dashboard writes that virtual pin. Read the value with param.asInt(), asFloat(), asBool(), or asStr().
BoxIO.onWrite(pin, fn) setup(), pins 0–31 Same delivery as BOX_WRITE, registered from code instead of a macro.
BoxIO.virtualRead(pin, buf, len) When you need the server’s last value Copies the last value stored for that pin into buf. Returns false when the server does not answer.
BoxIO.setProperty(pin, name, value) Strings, including numbers Changes a widget property such as label, min, max, startDeg, or color.
BoxIO.email / BoxIO.sms After Settings are saved See the email and SMS pages. Those calls are not counted in the virtualWrite cap.
BoxIO.setPingWatchdog setup(), or the dashboard See the ping watchdog page. clearPingWatchdog() stops the sketch copy. A saved widget replaces it on the next sync.

Start the library

  1. Copy arduino/BoxIO to Documents/Arduino/libraries/BoxIO and restart the IDE.
  2. Wi-Fi sketches include BoxIOEsp32.h. An Ethernet shield includes BoxIOEthernet.h or passes an EthernetClient to BoxIO.begin. A Teensy 4.1 uses the Teensy41 example: install QNEthernet, call Ethernet.begin(), then BoxIO.begin with that client. Do not include BoxIOEsp32.h on that board.
  3. Put the device key in BOXIO_AUTH. The host is the Box IO machine. The port is 5923.
  4. Call BoxIO.config(key, host, port) or BoxIO.setAuth and BoxIO.setServer only when you are not passing those arguments to begin.
  5. Leave BoxIO.run() in loop(). Timers do not fire on their own.

Timers

BoxIO.setTimer(1000, sendTemp) calls sendTemp every 1000 milliseconds. The first call is one interval after you register it. Each setTimer adds another timer. The function takes no arguments and returns nothing.

The return value is the id for BoxIO.deleteTimer. It is -1 when the interval is 0 or the table is full. An Uno (ATmega328P or ATmega168) holds 6 timers. Other boards hold 16. To change that, define BOXIO_MAX_TIMERS before the include.

Timers still run when the server is unreachable. A virtualWrite inside the timer follows the send cap below.

#define BOXIO_AUTH "bx_paste_your_device_key_here"
#define WIFI_SSID  "your-ssid"
#define WIFI_PASS  "your-password"
#define BOXIO_HOST "localhost"
#define BOXIO_PORT 5923

#include <BoxIOEsp32.h>

int tempTimer = -1;

void sendTemp() {
  BoxIO.virtualWrite(V1, analogRead(34));
}

void sendStatus() {
  BoxIO.virtualWrite(V2, "ok");
}

void setup() {
  BoxIO.begin(BOXIO_AUTH, WIFI_SSID, WIFI_PASS, BOXIO_HOST, BOXIO_PORT);
  tempTimer = BoxIO.setTimer(1000, sendTemp);
  BoxIO.setTimer(10000, sendStatus);
}

void loop() {
  BoxIO.run();
  // BoxIO.deleteTimer(tempTimer);
}

How often virtualWrite can send

Each virtualWrite opens an HTTP connection. The library allows 30 of those per second. Extra writes are kept: the latest value for each pin goes out on a later BoxIO.run(). setProperty, email, and SMS are not counted.

  1. BoxIO.setMaxSendsPerSecond(10) allows 10 virtualWrite messages per second.
  2. BoxIO.setMaxSendsPerSecond(0) removes the cap.
  3. BoxIO.setMaxSendsPerMinute(30) switches the cap to 30 per minute. Call setMaxSendsPerSecond again to switch back to a per-second cap.

The waiting list holds 4 pins on an Uno and 12 on other boards. A newer write to the same pin replaces the older one. A value that does not fit the waiting slot is not queued: 40 characters on an Uno, 96 on other boards. Define BOXIO_MAX_PENDING before the include if you need more pins waiting.

#define BOXIO_AUTH "bx_paste_your_device_key_here"
#define WIFI_SSID  "your-ssid"
#define WIFI_PASS  "your-password"
#define BOXIO_HOST "localhost"
#define BOXIO_PORT 5923

#include <BoxIOEsp32.h>

void setup() {
  BoxIO.begin(BOXIO_AUTH, WIFI_SSID, WIFI_PASS, BOXIO_HOST, BOXIO_PORT);
BoxIO.setMaxSendsPerMinute(30);
}

void loop() {
  BoxIO.run();

}

How often the board asks for commands

BoxIO.setPollInterval(400) is the default. run() asks the server for button, slider, and input commands that often. A smaller number reacts sooner and uses more radio time. The value is a uint16_t, so the largest interval is 65535 milliseconds. The board also sends a short ping about every 10 seconds. That interval is fixed.

#define BOXIO_AUTH "bx_paste_your_device_key_here"
#define WIFI_SSID  "your-ssid"
#define WIFI_PASS  "your-password"
#define BOXIO_HOST "localhost"
#define BOXIO_PORT 5923

#include <BoxIOEsp32.h>

void setup() {
  BoxIO.begin(BOXIO_AUTH, WIFI_SSID, WIFI_PASS, BOXIO_HOST, BOXIO_PORT);
BoxIO.setPollInterval(250);
}

void loop() {
  BoxIO.run();

}

Commands from the dashboard

BOX_WRITE is a function the library calls when that virtual pin changes. It has to sit at global scope, and the pin has to be a name the compiler can paste into a function: V0 through V31, or the number 0 through 31. A variable does not work in the macro. Pins above 31 can still virtualWrite, but the dashboard cannot call a handler on them.

param.asInt(), asLong(), asFloat(), asBool(), and asStr() read the text the widget sent. asBool() is true when the integer value is not 0.

#define BOXIO_AUTH "bx_paste_your_device_key_here"
#define WIFI_SSID  "your-ssid"
#define WIFI_PASS  "your-password"
#define BOXIO_HOST "localhost"
#define BOXIO_PORT 5923

#include <BoxIOEsp32.h>

void setup() {
  BoxIO.begin(BOXIO_AUTH, WIFI_SSID, WIFI_PASS, BOXIO_HOST, BOXIO_PORT);
pinMode(2, OUTPUT);
}

void loop() {
  BoxIO.run();

}

BOX_WRITE(V0) {
  digitalWrite(2, param.asInt() ? HIGH : LOW);
}

Read a pin back from the server

virtualRead asks the server for the last value stored on that pin and copies it into a buffer you provide. Use it when the sketch needs a value that was set from the dashboard and you are not using BOX_WRITE.

#define BOXIO_AUTH "bx_paste_your_device_key_here"
#define WIFI_SSID  "your-ssid"
#define WIFI_PASS  "your-password"
#define BOXIO_HOST "localhost"
#define BOXIO_PORT 5923

#include <BoxIOEsp32.h>

void setup() {
  BoxIO.begin(BOXIO_AUTH, WIFI_SSID, WIFI_PASS, BOXIO_HOST, BOXIO_PORT);

}

void loop() {
  BoxIO.run();
char value[32];
  if (BoxIO.virtualRead(V3, value, sizeof(value))) {
    Serial.println(value);
  }
}

Change a widget from the sketch

Every setProperty value is a string, including numbers. A non-empty value replaces the saved dashboard property until you edit that same property on the dashboard. Property names for each widget are on that widget’s page. Circle meter start and end use degrees: 0 is straight up, and degrees increase clockwise. 225 to 135 is the lower-left to lower-right gauge.

#define BOXIO_AUTH "bx_paste_your_device_key_here"
#define WIFI_SSID  "your-ssid"
#define WIFI_PASS  "your-password"
#define BOXIO_HOST "localhost"
#define BOXIO_PORT 5923

#include <BoxIOEsp32.h>

void setup() {
  BoxIO.begin(BOXIO_AUTH, WIFI_SSID, WIFI_PASS, BOXIO_HOST, BOXIO_PORT);
BoxIO.setProperty(V2, "label", "Pump");
  BoxIO.setProperty(V2, "min", "0");
  BoxIO.setProperty(V2, "max", "200");
  BoxIO.setProperty(V2, "startDeg", "225");
  BoxIO.setProperty(V2, "endDeg", "135");
}

void loop() {
  BoxIO.run();

}

Email, SMS, and the ping watchdog