HTTP API — Code Examples

All examples communicate with the Virtuino Cloud HTTP REST API using the VirtuinoCloud Arduino library — a lightweight, open-source wrapper that handles HTTPS, JSON serialization and multi-board support so your sketches stay short and readable.

Each example sends or receives data over HTTPS to https://api.virtuino.com/api/data. If you prefer to call the API directly without a library, see the HTTP API Guide.

Supports ESP32, ESP8266, Uno WiFi Rev2, MKR WiFi 1010, Nano 33 IoT and Nano RP2040 Connect. The correct HTTP backend is selected automatically at compile time — no configuration needed.

iliaslamprou/VirtuinoCloud on GitHub

Installation

Arduino IDE → Sketch → Include Library → Manage Libraries
Search for VirtuinoCloud and click Install.
In the same Library Manager, search for ArduinoJson by Benoit Blanchon and install it. Required on all boards.
WiFiNINA boards only (Uno WiFi Rev2, MKR WiFi 1010, Nano 33 IoT, Nano RP2040): also install ArduinoHttpClient by Arduino.
ESP8266 only: make sure your board core is version 3.x.
Tools → Board → Boards Manager → search esp8266 by ESP8266 Community → update.
If the Boards Manager URL is missing, add it in File → Preferences:
https://arduino.esp8266.com/stable/package_esp8266com_index.json
Console setup (required before uploading any sketch):
1. Log in at virtuino.com
2. Console → Devices — create a device and add the fields used in each example
3. Console → API & Connections — copy your API key
The DEVICE string in your sketch must match the device name in the Console exactly.

Supported Boards

BoardWiFi chipWiFi includeExtra library
ESP32 (all variants)built-in#include <WiFi.h>none
ESP8266 — NodeMCU, Wemos D1 Mini, etc.built-in#include <ESP8266WiFi.h>none
Uno WiFi Rev2NINA-W102#include <WiFiNINA.h>ArduinoHttpClient
MKR WiFi 1010NINA-W102#include <WiFiNINA.h>ArduinoHttpClient
Nano 33 IoTNINA-W102#include <WiFiNINA.h>ArduinoHttpClient
Nano RP2040 ConnectNINA-W102#include <WiFiNINA.h>ArduinoHttpClient

API Reference

Every sketch that includes VirtuinoCloud.h has access to all of the following methods.

Read latest value — GET /api/data/device/{d}/field/{f}?latest=true
VirtuinoResult r = cloud.read(device, field)
Fetches the most recent stored value of one field. Always check r.ok before using the result.
r.ok
boolfalse on network error, wrong API key, or field not found.
r.asFloat()  ·  r.asInt()
Returns the value cast to float or int. e.g. 23.4 / 23
r.asString()
Returns the raw value as an Arduino String. e.g. "23.4"
r.time
char[] — ISO 8601 UTC timestamp. e.g. "2024-06-15T14:30:00Z"
r.asJson()
Returns a compact JSON String: {"value":"23.4","time":"2024-06-15T14:30:00Z"}
Read history — GET /api/data/device/{d}/field/{f}?limit={n}
String h = cloud.readHistory(device, field, count)
Returns a JSON array String with the last count records. Returns "[]" on error.
[{"time":"2024-06-15T14:30:00Z","value":"23.4","source":"HTTP"},
{"time":"2024-06-15T14:29:00Z","value":"23.1","source":"HTTP"},...]
Parse with ArduinoJson. Keep count ≤ 50 on ESP8266. Server max: 5000.
Write single field — POST /api/data/write
bool ok = cloud.write(device, field, value)
Uploads one numeric value. Returns true on HTTP 200.
bool ok = cloud.write(device, field, value, true)
publish=true also pushes to the MQTT broker — live dashboard widgets update instantly (Essential+ plan).
bool ok = cloud.write(device, field, value, true, "2024-06-15T14:30:00Z")
5th argument: explicit ISO 8601 UTC timestamp. Omit to use server arrival time.
Block write — multiple fields in ONE HTTP request — POST /api/data/write
cloud.beginWrite(device)
Starts a new block for the given device. Clears any previously queued fields.
cloud.add(field, value)
cloud.add(field, value, true)
Queues a field. Does not send yet. true = publish to MQTT. Max 16 fields per block.
cloud.add("time", "2024-06-15T14:30:00Z")
Optional: sets a shared ISO 8601 timestamp for all fields in the block.
bool ok = cloud.send()
Sends all queued fields in ONE POST. Resets the queue so beginWrite can be called next loop.

Examples

Example 1
Write a single field

The simplest upload: reads a sensor value and posts it to one field every 30 seconds. Use publish:true to also push to the MQTT broker so dashboard widgets update live.

write()
Console setup: Create a device and add a field named temperature.
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";    // Console → API & Connections
const char* DEVICE   = "YOUR_DEVICE_ID"; // must match device name in Console exactly

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = 23.4;   // replace with a real sensor read

    // Basic upload — server records the time of arrival as timestamp
    bool ok = cloud.write(DEVICE, "temperature", temperature);

    // publish=true pushes the value to the MQTT broker immediately,
    // so live dashboard widgets (gauges, charts) update in real time
    // bool ok = cloud.write(DEVICE, "temperature", temperature, true);

    // With an explicit ISO 8601 UTC timestamp (for batch / offline uploads)
    // bool ok = cloud.write(DEVICE, "temperature", temperature, true, "2024-06-15T14:30:00Z");

    Serial.println(ok ? "Uploaded" : "Upload failed — check WiFi and API key");
    delay(30000);   // upload every 30 seconds (max rate: 600 writes/min)
}
#include <ESP8266WiFi.h>   // ESP8266 — use this instead of <WiFi.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = 23.4;

    bool ok = cloud.write(DEVICE, "temperature", temperature);
    Serial.println(ok ? "Uploaded" : "Failed");
    delay(30000);
}
#include <WiFiNINA.h>       // WiFiNINA boards — use this instead of <WiFi.h>
#include <VirtuinoCloud.h>  // also install ArduinoHttpClient from Library Manager

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = 23.4;

    bool ok = cloud.write(DEVICE, "temperature", temperature);
    Serial.println(ok ? "Uploaded" : "Failed");
    delay(30000);
}
Example 2
Block write — multiple fields in one request

Uploads three sensor values (temperature, humidity, pressure) in a single HTTP POST using beginWrite / add / send. Without block write, three fields would require three separate requests and three times the network latency.

beginWrite / add / send
Console setup: Add fields temperature, humidity, pressure to your device.
Extra library: DHT sensor library by Adafruit (Library Manager).
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <DHT.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

#define DHT_PIN 4
DHT dht(DHT_PIN, DHT22);

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = dht.readTemperature();
    float humidity    = dht.readHumidity();
    float pressure    = 1013.0;   // replace with a real barometer read

    if (isnan(temperature) || isnan(humidity)) {
        Serial.println("DHT22 read failed"); delay(5000); return;
    }

    // beginWrite() starts a new block — does NOT send yet
    cloud.beginWrite(DEVICE);

    // add() queues a field value
    // publish=true → also push to MQTT broker for live dashboard widgets
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    cloud.add("pressure",    pressure);         // no publish for this one

    // Optional: set a shared timestamp for all fields in this block
    // cloud.add("time", "2024-06-15T14:30:00Z");

    // send() transmits all queued fields in ONE HTTP POST, then resets the queue
    bool ok = cloud.send();

    if (ok) Serial.printf("Uploaded — T:%.1f  H:%.1f  P:%.1f\n", temperature, humidity, pressure);
    else    Serial.println("Upload failed");

    delay(30000);
}
#include <ESP8266WiFi.h>
#include <DHT.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

#define DHT_PIN D4   // GPIO2 on NodeMCU / Wemos D1 Mini
DHT dht(DHT_PIN, DHT22);

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = dht.readTemperature();
    float humidity    = dht.readHumidity();
    if (isnan(temperature) || isnan(humidity)) { delay(5000); return; }

    cloud.beginWrite(DEVICE);
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    bool ok = cloud.send();
    Serial.println(ok ? "Uploaded" : "Failed");
    delay(30000);
}
#include <WiFiNINA.h>
#include <DHT.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

#define DHT_PIN 2
DHT dht(DHT_PIN, DHT22);

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = dht.readTemperature();
    float humidity    = dht.readHumidity();
    if (isnan(temperature) || isnan(humidity)) { delay(5000); return; }

    cloud.beginWrite(DEVICE);
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    bool ok = cloud.send();
    Serial.println(ok ? "Uploaded" : "Failed");
    delay(30000);
}
Example 3
Read latest value — control a relay

Reads the state of two relay fields from the cloud every 5 seconds and switches the corresponding GPIO pins. A Toggle widget on the dashboard writes 1 (ON) or 0 (OFF) to each field.

read() GPIO output
Console setup: Add fields relay1 and relay2.
Dashboard: Add two Toggle widgets, one connected to each field.
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int RELAY1 = 26;   // GPIO connected to relay module 1 (HIGH = ON)
const int RELAY2 = 27;   // GPIO connected to relay module 2

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    pinMode(RELAY1, OUTPUT);
    pinMode(RELAY2, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    // Read relay 1 state set by the dashboard toggle widget
    VirtuinoResult r1 = cloud.read(DEVICE, "relay1");
    if (r1.ok) {
        digitalWrite(RELAY1, r1.asInt() ? HIGH : LOW);
        Serial.print("relay1="); Serial.print(r1.asString());
        Serial.print("  at "); Serial.println(r1.time);   // ISO 8601 timestamp

        // r1.asJson() → {"value":"1","time":"2024-06-15T14:30:00Z"}
    }

    VirtuinoResult r2 = cloud.read(DEVICE, "relay2");
    if (r2.ok) digitalWrite(RELAY2, r2.asInt() ? HIGH : LOW);

    if (!r1.ok && !r2.ok) Serial.println("Read failed — check WiFi and API key");

    delay(5000);   // poll every 5 seconds
}
#include <ESP8266WiFi.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int RELAY1 = D1;   // GPIO5 on NodeMCU / Wemos D1 Mini
const int RELAY2 = D2;   // GPIO4

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    pinMode(RELAY1, OUTPUT);
    pinMode(RELAY2, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    VirtuinoResult r1 = cloud.read(DEVICE, "relay1");
    if (r1.ok) digitalWrite(RELAY1, r1.asInt() ? HIGH : LOW);

    VirtuinoResult r2 = cloud.read(DEVICE, "relay2");
    if (r2.ok) digitalWrite(RELAY2, r2.asInt() ? HIGH : LOW);

    delay(5000);
}
#include <WiFiNINA.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int RELAY1 = 2;
const int RELAY2 = 3;

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    pinMode(RELAY1, OUTPUT);
    pinMode(RELAY2, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    VirtuinoResult r1 = cloud.read(DEVICE, "relay1");
    if (r1.ok) digitalWrite(RELAY1, r1.asInt() ? HIGH : LOW);

    VirtuinoResult r2 = cloud.read(DEVICE, "relay2");
    if (r2.ok) digitalWrite(RELAY2, r2.asInt() ? HIGH : LOW);

    delay(5000);
}
Example 4
Motor control from dashboard

Reads a speed setpoint (0–255) and a direction flag from the cloud every 2 seconds. Sets PWM on a motor driver (e.g. L298N) and a direction GPIO. A Slider widget on the dashboard controls the speed; a Toggle widget controls the direction.

read() PWM + direction
Console setup: Add fields speed (slider 0–255) and direction (toggle 0/1).
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int PWM_PIN = 18;   // ENA pin on L298N (speed via LEDC PWM)
const int DIR_PIN = 19;   // IN1 pin on L298N (direction)

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    ledcSetup(0, 1000, 8);       // channel 0, 1 kHz, 8-bit resolution (0–255)
    ledcAttachPin(PWM_PIN, 0);
    pinMode(DIR_PIN, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    // Read speed value (0–255) set by slider widget on dashboard
    VirtuinoResult spd = cloud.read(DEVICE, "speed");
    if (spd.ok) {
        int pwm = constrain(spd.asInt(), 0, 255);
        ledcWrite(0, pwm);
        Serial.print("Speed: "); Serial.println(pwm);
    }

    // Read direction: 0 = forward, 1 = reverse
    VirtuinoResult dir = cloud.read(DEVICE, "direction");
    if (dir.ok) {
        digitalWrite(DIR_PIN, dir.asInt() ? HIGH : LOW);
        Serial.println(dir.asInt() ? "Direction: REVERSE" : "Direction: FORWARD");
    }

    delay(2000);   // check every 2 seconds
}
#include <ESP8266WiFi.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int PWM_PIN = D5;   // speed (analogWrite range 0–1023 on ESP8266)
const int DIR_PIN = D6;   // direction

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    pinMode(DIR_PIN, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    VirtuinoResult spd = cloud.read(DEVICE, "speed");
    if (spd.ok) {
        // Dashboard uses 0–255; ESP8266 analogWrite uses 0–1023 — scale it
        int pwm = map(constrain(spd.asInt(), 0, 255), 0, 255, 0, 1023);
        analogWrite(PWM_PIN, pwm);
    }

    VirtuinoResult dir = cloud.read(DEVICE, "direction");
    if (dir.ok) digitalWrite(DIR_PIN, dir.asInt() ? HIGH : LOW);

    delay(2000);
}
#include <WiFiNINA.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int PWM_PIN = 9;
const int DIR_PIN = 8;

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    pinMode(DIR_PIN, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    VirtuinoResult spd = cloud.read(DEVICE, "speed");
    if (spd.ok) analogWrite(PWM_PIN, constrain(spd.asInt(), 0, 255));

    VirtuinoResult dir = cloud.read(DEVICE, "direction");
    if (dir.ok) digitalWrite(DIR_PIN, dir.asInt() ? HIGH : LOW);

    delay(2000);
}
Example 5
Upload with NTP timestamp

Syncs time from an NTP server and attaches an explicit ISO 8601 UTC timestamp to each block upload. Useful when you want precise control over the timestamp shown in charts, or when you are uploading historical data collected offline.

Block write NTP timestamp
Timestamp format: ISO 8601 UTC — 2024-06-15T14:30:00Z. If omitted, the server records the time of arrival.
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <time.h>            // for configTime() and strftime()
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);

// Fills buf with the current UTC time in ISO 8601 format: "2024-06-15T14:30:00Z"
void getTimestamp(char* buf, size_t len) {
    time_t now = time(nullptr);
    strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
}

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");

    // Sync time from NTP — waits until a valid epoch is received
    configTime(0, 0, "pool.ntp.org", "time.nist.gov");
    Serial.print("Syncing NTP");
    while (time(nullptr) < 1000000000UL) { delay(500); Serial.print("."); }
    Serial.println(" done");
}

void loop() {
    float temperature = 23.4;   // replace with sensor read
    float humidity    = 61.0;

    char ts[25];
    getTimestamp(ts, sizeof(ts));   // e.g. "2024-06-15T14:30:00Z"

    // All fields in this block share the same explicit timestamp
    cloud.beginWrite(DEVICE);
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    cloud.add("time",        ts);          // attach the NTP timestamp
    bool ok = cloud.send();

    Serial.printf("%s at %s\n", ok ? "Uploaded" : "FAILED", ts);
    delay(60000);   // upload every minute
}
#include <ESP8266WiFi.h>
#include <time.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);

void getTimestamp(char* buf, size_t len) {
    time_t now = time(nullptr);
    strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
}

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    configTime(0, 0, "pool.ntp.org");
    while (time(nullptr) < 1000000000UL) { delay(500); }
    Serial.println("NTP synced");
}

void loop() {
    char ts[25];
    getTimestamp(ts, sizeof(ts));
    cloud.beginWrite(DEVICE);
    cloud.add("temperature", 23.4f, true);
    cloud.add("humidity",    61.0f, true);
    cloud.add("time", ts);
    Serial.println(cloud.send() ? "Uploaded" : "Failed");
    delay(60000);
}
#include <WiFiNINA.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);

// WiFiNINA provides getTime() which returns Unix epoch seconds
// For production use consider an RTC module for a proper formatted timestamp
void getTimestamp(char* buf, size_t len) {
    unsigned long ep = WiFi.getTime();
    snprintf(buf, len, "epoch-%lu", ep);   // simple form; convert to ISO 8601 if needed
}

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    char ts[32];
    getTimestamp(ts, sizeof(ts));
    cloud.beginWrite(DEVICE);
    cloud.add("temperature", 23.4f, true);
    cloud.add("time", ts);
    Serial.println(cloud.send() ? "Uploaded" : "Failed");
    delay(60000);
}
Example 6
Read history & calculate average

Fetches the last 50 records of a field as a JSON array String, parses each entry with ArduinoJson and computes the average, minimum and maximum values. The same technique can be used to find anomalies, draw local charts, or make decisions based on trend data.

readHistory() ArduinoJson
Memory: Each record uses ~120 bytes of heap inside the JSON document. Keep count ≤ 50 on ESP8266. ESP32 handles 500+ records comfortably.
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <ArduinoJson.h>     // needed to parse the returned JSON array
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);
bool fetched = false;

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected — fetching history...");
}

void loop() {
    if (!fetched) {
        // Returns: [{"time":"...","value":"23.4","source":"HTTP"}, ...]
        // Returns "[]" on error or if the field has no data yet
        String history = cloud.readHistory(DEVICE, "temperature", 50);

        // Parse with ArduinoJson — allocate ~120 bytes × number of records
        DynamicJsonDocument doc(8192);
        DeserializationError err = deserializeJson(doc, history);

        if (err) {
            Serial.print("JSON parse error: "); Serial.println(err.c_str());
        } else {
            JsonArray arr = doc.as<JsonArray>();
            float sum = 0, minVal = 1e9, maxVal = -1e9;
            int n = 0;

            for (JsonObject rec : arr) {
                float v = atof(rec["value"] | "0");
                sum    += v;
                minVal  = min(minVal, v);
                maxVal  = max(maxVal, v);
                Serial.printf("[%2d]  %s  →  %.2f\n", n, (const char*)rec["time"], v);
                n++;
            }

            if (n > 0) {
                Serial.println("────────────────────────────────");
                Serial.printf("Count: %d   Avg: %.2f   Min: %.2f   Max: %.2f\n",
                              n, sum/n, minVal, maxVal);
            } else {
                Serial.println("No records — upload some data first (Example 1 or 2)");
            }
        }
        fetched = true;
    }
    delay(1000);
}
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);
bool fetched = false;

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nConnected — fetching...");
}

void loop() {
    if (!fetched) {
        // Keep count ≤ 50 on ESP8266 to avoid running out of heap
        String history = cloud.readHistory(DEVICE, "temperature", 50);
        DynamicJsonDocument doc(4096);
        if (!deserializeJson(doc, history)) {
            JsonArray arr = doc.as<JsonArray>();
            float sum = 0; int n = 0;
            for (JsonObject rec : arr) { sum += atof(rec["value"] | "0"); n++; }
            if (n > 0) Serial.printf("Avg: %.2f over %d records\n", sum/n, n);
        }
        fetched = true;
    }
    delay(1000);
}
#include <WiFiNINA.h>
#include <ArduinoJson.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

VirtuinoCloud cloud(API_KEY);
bool fetched = false;

void setup() {
    Serial.begin(115200);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nConnected");
}

void loop() {
    if (!fetched) {
        String history = cloud.readHistory(DEVICE, "temperature", 50);
        DynamicJsonDocument doc(4096);
        if (!deserializeJson(doc, history)) {
            JsonArray arr = doc.as<JsonArray>();
            float sum = 0; int n = 0;
            for (JsonObject rec : arr) { sum += atof(rec["value"] | "0"); n++; }
            if (n > 0) Serial.printf("Avg: %.2f over %d records\n", sum/n, n);
        }
        fetched = true;
    }
    delay(1000);
}
Example 7
Thermostat — read setpoint & write back measurements

A complete closed-loop control example: reads a temperature setpoint from the dashboard, measures actual temperature with a DHT22, controls a heater relay using bang-bang logic with hysteresis, and uploads all measurements in one request. Demonstrates read() and beginWrite/add/send working together.

read() Block write Relay
Console setup: Fields needed: setpoint, temperature, heater_state.
Dashboard: Slider widget → setpoint  |  Gauge → temperature  |  LED → heater_state
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <DHT.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int DHT_PIN = 4;
const int HEATER  = 26;   // relay controlling the heater (HIGH = ON)

DHT dht(DHT_PIN, DHT22);
VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    pinMode(HEATER, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    // Step 1 — Read desired setpoint from dashboard (default 22°C if read fails)
    VirtuinoResult sp = cloud.read(DEVICE, "setpoint");
    float setpoint = sp.ok ? sp.asFloat() : 22.0f;

    // Step 2 — Measure actual temperature
    float actual = dht.readTemperature();
    if (isnan(actual)) { Serial.println("DHT22 read failed"); delay(5000); return; }

    // Step 3 — Bang-bang control with 0.5°C hysteresis
    // Heater turns ON below (setpoint - 0.5) and OFF above (setpoint + 0.5)
    // No change inside the 1°C deadband — prevents rapid switching
    bool heaterOn = digitalRead(HEATER);
    if (actual < setpoint - 0.5f) heaterOn = true;
    if (actual > setpoint + 0.5f) heaterOn = false;
    digitalWrite(HEATER, heaterOn ? HIGH : LOW);

    // Step 4 — Upload measurements and heater state in ONE request
    // publish=true → live dashboard widgets update immediately
    cloud.beginWrite(DEVICE);
    cloud.add("temperature",  actual,                  true);
    cloud.add("heater_state", heaterOn ? 1.0f : 0.0f, true);
    bool ok = cloud.send();

    Serial.printf("%s  SP:%.1f  Actual:%.1f  Heater:%s\n",
                  ok ? "OK  " : "FAIL", setpoint, actual, heaterOn ? "ON" : "OFF");
    delay(10000);   // control loop runs every 10 seconds
}
#include <ESP8266WiFi.h>
#include <DHT.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int DHT_PIN = D4;
const int HEATER  = D1;

DHT dht(DHT_PIN, DHT22);
VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    pinMode(HEATER, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    VirtuinoResult sp = cloud.read(DEVICE, "setpoint");
    float setpoint = sp.ok ? sp.asFloat() : 22.0f;

    float actual = dht.readTemperature();
    if (isnan(actual)) { delay(5000); return; }

    bool heaterOn = digitalRead(HEATER);
    if (actual < setpoint - 0.5f) heaterOn = true;
    if (actual > setpoint + 0.5f) heaterOn = false;
    digitalWrite(HEATER, heaterOn ? HIGH : LOW);

    cloud.beginWrite(DEVICE);
    cloud.add("temperature",  actual,                  true);
    cloud.add("heater_state", heaterOn ? 1.0f : 0.0f, true);
    cloud.send();
    delay(10000);
}
#include <WiFiNINA.h>
#include <DHT.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int DHT_PIN = 2;
const int HEATER  = 4;

DHT dht(DHT_PIN, DHT22);
VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    pinMode(HEATER, OUTPUT);
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    VirtuinoResult sp = cloud.read(DEVICE, "setpoint");
    float setpoint = sp.ok ? sp.asFloat() : 22.0f;

    float actual = dht.readTemperature();
    if (isnan(actual)) { delay(5000); return; }

    bool heaterOn = digitalRead(HEATER);
    if (actual < setpoint - 0.5f) heaterOn = true;
    if (actual > setpoint + 0.5f) heaterOn = false;
    digitalWrite(HEATER, heaterOn ? HIGH : LOW);

    cloud.beginWrite(DEVICE);
    cloud.add("temperature",  actual,                  true);
    cloud.add("heater_state", heaterOn ? 1.0f : 0.0f, true);
    cloud.send();
    delay(10000);
}
Example 8
Multi-sensor weather station

A complete weather station sketch that combines multiple sensors: a DHT22 for temperature and humidity, a BMP280 for barometric pressure and altitude, and a photoresistor for light level. All six values are sent in one block write every minute. Also reads an alarm threshold from the dashboard and activates a buzzer if the temperature exceeds it.

Block write (6 fields) read() alarm Buzzer
Console setup: Add fields: temperature, humidity, pressure, altitude, light, alarm_threshold.
Extra libraries: DHT sensor library (Adafruit) · Adafruit BMP280 library.
ESP32
ESP8266
WiFiNINA
#include <WiFi.h>
#include <DHT.h>
#include <Adafruit_BMP280.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int DHT_PIN   = 4;
const int LIGHT_PIN = 34;   // ADC pin for photoresistor (0–4095)
const int BUZZER    = 25;   // alarm buzzer

DHT dht(DHT_PIN, DHT22);
Adafruit_BMP280 bmp;        // I2C: SDA=21, SCL=22 on ESP32

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    pinMode(BUZZER, OUTPUT);

    if (!bmp.begin(0x76)) {   // try 0x77 if 0x76 doesn't work
        Serial.println("BMP280 not found — check wiring");
    }

    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    // ── Read all sensors ─────────────────────────────────────────────────────
    float temperature = dht.readTemperature();
    float humidity    = dht.readHumidity();
    float pressure    = bmp.readPressure() / 100.0f;   // Pa → hPa
    float altitude    = bmp.readAltitude(1013.25f);    // sea-level pressure hPa
    float light       = analogRead(LIGHT_PIN) / 40.95f; // map 0–4095 → 0–100%

    if (isnan(temperature) || isnan(humidity)) {
        Serial.println("DHT22 read failed"); delay(5000); return;
    }

    // ── Check alarm threshold from dashboard ──────────────────────────────────
    VirtuinoResult threshold = cloud.read(DEVICE, "alarm_threshold");
    if (threshold.ok && temperature > threshold.asFloat()) {
        tone(BUZZER, 1000, 500);   // beep for 500 ms
        Serial.printf("ALARM! Temp %.1f°C exceeds threshold %.1f°C\n",
                      temperature, threshold.asFloat());
    }

    // ── Upload all 5 sensor values in one HTTP request ────────────────────────
    cloud.beginWrite(DEVICE);
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    cloud.add("pressure",    pressure,    true);
    cloud.add("altitude",    altitude);
    cloud.add("light",       light,       true);
    bool ok = cloud.send();

    Serial.printf("%s T:%.1f H:%.1f P:%.1f Alt:%.0f Light:%.0f%%\n",
                  ok ? "OK" : "FAIL",
                  temperature, humidity, pressure, altitude, light);

    delay(60000);   // upload every minute
}
#include <ESP8266WiFi.h>
#include <DHT.h>
#include <Adafruit_BMP280.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int DHT_PIN = D4;
const int BUZZER  = D3;

DHT dht(DHT_PIN, DHT22);
Adafruit_BMP280 bmp;   // I2C: SDA=D2, SCL=D1 on NodeMCU / Wemos D1 Mini

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    pinMode(BUZZER, OUTPUT);
    if (!bmp.begin(0x76)) Serial.println("BMP280 not found");
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = dht.readTemperature();
    float humidity    = dht.readHumidity();
    float pressure    = bmp.readPressure() / 100.0f;
    float altitude    = bmp.readAltitude(1013.25f);
    // ESP8266 has one ADC (A0, 0-1V, 0–1023)
    float light       = analogRead(A0) / 10.23f;   // 0–100%

    if (isnan(temperature) || isnan(humidity)) { delay(5000); return; }

    VirtuinoResult threshold = cloud.read(DEVICE, "alarm_threshold");
    if (threshold.ok && temperature > threshold.asFloat())
        digitalWrite(BUZZER, HIGH);
    else
        digitalWrite(BUZZER, LOW);

    cloud.beginWrite(DEVICE);
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    cloud.add("pressure",    pressure,    true);
    cloud.add("altitude",    altitude);
    cloud.add("light",       light,       true);
    Serial.println(cloud.send() ? "Uploaded" : "Failed");

    delay(60000);
}
#include <WiFiNINA.h>
#include <DHT.h>
#include <Adafruit_BMP280.h>
#include <VirtuinoCloud.h>

const char* SSID     = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY  = "YOUR_API_KEY";
const char* DEVICE   = "YOUR_DEVICE_ID";

const int DHT_PIN = 2;
const int BUZZER  = 5;

DHT dht(DHT_PIN, DHT22);
Adafruit_BMP280 bmp;

VirtuinoCloud cloud(API_KEY);

void setup() {
    Serial.begin(115200);
    dht.begin();
    pinMode(BUZZER, OUTPUT);
    if (!bmp.begin(0x76)) Serial.println("BMP280 not found");
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
    Serial.println("\nWiFi connected");
}

void loop() {
    float temperature = dht.readTemperature();
    float humidity    = dht.readHumidity();
    float pressure    = bmp.readPressure() / 100.0f;
    float altitude    = bmp.readAltitude(1013.25f);
    float light       = analogRead(A0) / 10.23f;

    if (isnan(temperature) || isnan(humidity)) { delay(5000); return; }

    VirtuinoResult threshold = cloud.read(DEVICE, "alarm_threshold");
    if (threshold.ok && temperature > threshold.asFloat())
        digitalWrite(BUZZER, HIGH);
    else
        digitalWrite(BUZZER, LOW);

    cloud.beginWrite(DEVICE);
    cloud.add("temperature", temperature, true);
    cloud.add("humidity",    humidity,    true);
    cloud.add("pressure",    pressure,    true);
    cloud.add("altitude",    altitude);
    cloud.add("light",       light,       true);
    Serial.println(cloud.send() ? "Uploaded" : "Failed");

    delay(60000);
}