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 GitHubhttps://arduino.esp8266.com/stable/package_esp8266com_index.json
weather/temperature.
| Board | WiFi chip | WiFi include | Extra library |
|---|---|---|---|
| ESP32 (all variants) | built-in | #include <WiFi.h> | none |
| ESP8266 — NodeMCU, Wemos D1 Mini, etc. | built-in | #include <ESP8266WiFi.h> | none |
| Uno WiFi Rev2 | NINA-W102 | #include <WiFiNINA.h> | ArduinoHttpClient |
| MKR WiFi 1010 | NINA-W102 | #include <WiFiNINA.h> | ArduinoHttpClient |
| Nano 33 IoT | NINA-W102 | #include <WiFiNINA.h> | ArduinoHttpClient |
| Nano RP2040 Connect | NINA-W102 | #include <WiFiNINA.h> | ArduinoHttpClient |
Every sketch that includes VirtuinoCloud.h has access to all of the following methods.
r.ok before using the result.bool — false on network error, wrong API key, unknown field, or a field with no value yet.float or int. e.g. 23.4 / 23String. e.g. "23.4"char[] — ISO 8601 UTC timestamp. e.g. "2024-06-15T14:30:00Z"String: {"value":"23.4","time":"2024-06-15T14:30:00Z"}count records, newest first. Returns "[]" on error.count ≤ 50 on ESP8266. Server max: 5000.true only when the value was stored — false on no connection, an error, or a field that does not exist.publish=true also pushes to the MQTT broker — live dashboard widgets update instantly (Essential+ plan)./ — add("temperature", …) writes to weather/temperature. With no path, give full names to add().true = publish to MQTT. Max 16 fields per block.true only when every field was stored — if one does not exist, the others are stored and it returns false. Resets the queue so beginWrite can be called next loop.A-Z a-z 0-9 . _ : -200 OK · 404 no field with that name (after a write: a field was not stored — create it in Console → Fields) · 403 read-only or wrong API key · 429 too many writes · 0 or negative = no connection. The Debug Monitor in the Console shows the same requests live.cloud.write("weather", "temperature", 23.4) writes to weather/temperature, exactly like the full name.
Start here. No sensor and no wiring needed — the sketch makes up a value around 25 and sends it every 3 seconds, so a gauge or chart on your dashboard starts moving straight away. Fill in your WiFi and API key, upload, and you are done.
demo_device/demo_field_1, which every new account already has.
If yours does not, create it in Console → Fields with that exact name and the sketch runs unmodified.
#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#include <VirtuinoCloud.h>
const char* SSID = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY = "YOUR_API_KEY"; // Console → Keys & Sub Users
VirtuinoCloud cloud(API_KEY);
void setup() {
Serial.begin(115200);
cloud.setClientId("hello-board"); // optional: the name in My Virtuino World
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWiFi connected");
}
void loop() {
float sensor_value = 25 + random(-5, +5); // replace with a real sensor read
// The field's full name, exactly as in Console → Fields
bool ok = cloud.write("demo_device/demo_field_1", sensor_value);
if (ok) {
Serial.print("Uploaded "); Serial.println(sensor_value);
} else {
// 404 = no field with that name, 403 = read-only or wrong API key, 0 = no connection
Serial.print("Upload failed — HTTP "); Serial.println(cloud.lastStatus());
}
delay(3000); // upload every 3 seconds
}#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";
VirtuinoCloud cloud(API_KEY);
void setup() {
Serial.begin(115200);
cloud.setClientId("hello-nano"); // optional: the name in My Virtuino World
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWiFi connected");
}
void loop() {
float sensor_value = 25 + random(-5, +5); // replace with a real sensor read
bool ok = cloud.write("demo_device/demo_field_1", sensor_value);
if (ok) {
Serial.print("Uploaded "); Serial.println(sensor_value);
} else {
Serial.print("Upload failed — HTTP "); Serial.println(cloud.lastStatus());
}
delay(3000);
}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.
weather/temperature, weather/humidity, weather/pressure.#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#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";
#if defined(ESP32)
#define DHT_PIN 4
#else
#define DHT_PIN D4
#endif
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.
// "weather" is the path: it is joined to every field below with a "/",
// so the values go to weather/temperature, weather/humidity, weather/pressure.
cloud.beginWrite("weather");
// 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.printf("Upload failed — HTTP %d\n", cloud.lastStatus());
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";
#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("weather"); // → weather/temperature, weather/humidity
cloud.add("temperature", temperature, true);
cloud.add("humidity", humidity, true);
bool ok = cloud.send();
if (ok) Serial.println("Uploaded");
else { Serial.print("Failed — HTTP "); Serial.println(cloud.lastStatus()); }
delay(30000);
}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.
home/relay1 and home/relay2.#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#include <VirtuinoCloud.h>
const char* SSID = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY = "YOUR_API_KEY";
#if defined(ESP32)
const int RELAY1 = 26; // GPIO connected to relay module 1 (HIGH = ON)
const int RELAY2 = 27; // GPIO connected to relay module 2
#else
const int RELAY1 = D1;
const int RELAY2 = D2;
#endif
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("home/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("home/relay2");
if (r2.ok) digitalWrite(RELAY2, r2.asInt() ? HIGH : LOW);
// 200 with no value = the field exists but nothing was written to it yet
if (!r1.ok && !r2.ok) Serial.printf("Read failed — HTTP %d\n", cloud.lastStatus());
delay(5000); // poll every 5 seconds
}#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 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("home/relay1");
if (r1.ok) digitalWrite(RELAY1, r1.asInt() ? HIGH : LOW);
VirtuinoResult r2 = cloud.read("home/relay2");
if (r2.ok) digitalWrite(RELAY2, r2.asInt() ? HIGH : LOW);
if (!r1.ok && !r2.ok) { Serial.print("Read failed — HTTP "); Serial.println(cloud.lastStatus()); }
delay(5000);
}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.
motor/speed (slider 0–255) and motor/direction (toggle 0/1).#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#include <VirtuinoCloud.h>
const char* SSID = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY = "YOUR_API_KEY";
#if defined(ESP32)
const int PWM_PIN = 18; // ENA pin on L298N (speed)
const int DIR_PIN = 19; // IN1 pin on L298N (direction)
#else
const int PWM_PIN = D5;
const int DIR_PIN = D6;
#endif
VirtuinoCloud cloud(API_KEY);
void setup() {
Serial.begin(115200);
pinMode(DIR_PIN, OUTPUT);
// analogWrite() works on both boards: 0–255 (ESP32 core 2.x/3.x, ESP8266 core 3.x)
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 the slider widget on the dashboard
VirtuinoResult spd = cloud.read("motor/speed");
if (spd.ok) {
int pwm = constrain(spd.asInt(), 0, 255);
analogWrite(PWM_PIN, pwm);
Serial.print("Speed: "); Serial.println(pwm);
}
// Read direction: 0 = forward, 1 = reverse
VirtuinoResult dir = cloud.read("motor/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 <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 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("motor/speed");
if (spd.ok) analogWrite(PWM_PIN, constrain(spd.asInt(), 0, 255));
VirtuinoResult dir = cloud.read("motor/direction");
if (dir.ok) digitalWrite(DIR_PIN, dir.asInt() ? HIGH : LOW);
delay(2000);
}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.
2024-06-15T14:30:00Z. If omitted, the server records the time of arrival.weather/temperature and weather/humidity (the same as Example 2).#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#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";
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("weather"); // → weather/temperature, weather/humidity
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 <WiFiNINA.h>
#include <VirtuinoCloud.h>
const char* SSID = "YOUR_WIFI_SSID";
const char* PASSWORD = "YOUR_WIFI_PASSWORD";
const char* API_KEY = "YOUR_API_KEY";
VirtuinoCloud cloud(API_KEY);
// WiFi.getTime() returns Unix epoch seconds (UTC) from the WiFiNINA module's NTP.
// Converts it to ISO 8601 "2024-06-15T14:30:00Z" without any time library.
void getTimestamp(char* buf, size_t len) {
unsigned long ep = WiFi.getTime();
unsigned long days = ep / 86400UL, secs = ep % 86400UL;
// days since 1970-01-01 → year/month/day (civil-from-days algorithm)
long z = (long)days + 719468L;
long era = z / 146097L;
unsigned long doe = (unsigned long)(z - era * 146097L);
unsigned long yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
long y = (long)yoe + era * 400L;
unsigned long doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
unsigned long mp = (5 * doy + 2) / 153;
unsigned int d = doy - (153 * mp + 2) / 5 + 1;
unsigned int m = mp < 10 ? mp + 3 : mp - 9;
if (m <= 2) y++;
snprintf(buf, len, "%04ld-%02u-%02uT%02lu:%02lu:%02luZ",
y, m, d, secs / 3600UL, (secs / 60UL) % 60UL, secs % 60UL);
}
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWiFi connected");
// The module needs a few seconds to get the time from NTP (0 until then)
Serial.print("Waiting for time");
while (WiFi.getTime() == 0) { delay(1000); Serial.print("."); }
Serial.println(" done");
}
void loop() {
char ts[25];
getTimestamp(ts, sizeof(ts)); // e.g. "2024-06-15T14:30:00Z"
cloud.beginWrite("weather"); // → weather/temperature
cloud.add("temperature", 23.4f, true);
cloud.add("time", ts);
Serial.print(cloud.send() ? "Uploaded at " : "Failed at "); Serial.println(ts);
delay(60000);
}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.
count ≤ 50 on ESP8266 and ≤ 20 on WiFiNINA boards. ESP32 handles 500+ records comfortably.weather/temperature — upload some values first with Example 2 or 5.#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#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";
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"}, ...] (newest first)
// Returns "[]" on error or if the field has no data yet
String history = cloud.readHistory("weather/temperature", 50);
// Parse with ArduinoJson — allocate ~100 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 if (cloud.lastStatus() != 200) {
Serial.printf("Read failed — HTTP %d\n", cloud.lastStatus());
} else {
Serial.println("No records — upload some data first (Example 2 or 5)");
}
}
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";
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) {
// 20 records keep the JSON small enough for boards with little RAM
String history = cloud.readHistory("weather/temperature", 20);
DynamicJsonDocument doc(3072);
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.print("Avg: "); Serial.print(sum / n, 2);
Serial.print(" over "); Serial.print(n); Serial.println(" records");
}
}
fetched = true;
}
delay(1000);
}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.
thermostat/setpoint, thermostat/temperature, thermostat/heater_state.thermostat/setpoint | Gauge → thermostat/temperature | LED → thermostat/heater_state
#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#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";
#if defined(ESP32)
const int DHT_PIN = 4;
const int HEATER = 26; // relay controlling the heater (HIGH = ON)
#else
const int DHT_PIN = D4;
const int HEATER = D1;
#endif
DHT dht(DHT_PIN, DHT22);
VirtuinoCloud cloud(API_KEY);
float setpoint = 22.0f; // kept between loops: a failed read keeps the last good value
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(HEATER, OUTPUT);
cloud.setClientId("thermostat"); // optional: the name in My Virtuino World
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
VirtuinoResult sp = cloud.read("thermostat/setpoint");
if (sp.ok) setpoint = sp.asFloat();
// 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
// → thermostat/temperature, thermostat/heater_state
// publish=true → live dashboard widgets update immediately
cloud.beginWrite("thermostat");
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 <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 int DHT_PIN = 2;
const int HEATER = 4;
DHT dht(DHT_PIN, DHT22);
VirtuinoCloud cloud(API_KEY);
float setpoint = 22.0f;
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("thermostat/setpoint");
if (sp.ok) setpoint = sp.asFloat();
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("thermostat");
cloud.add("temperature", actual, true);
cloud.add("heater_state", heaterOn ? 1.0f : 0.0f, true);
cloud.send();
delay(10000);
}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.
station/temperature, station/humidity, station/pressure, station/altitude, station/light, station/alarm_threshold.#if defined(ESP32)
#include <WiFi.h> // ESP32
#else
#include <ESP8266WiFi.h> // ESP8266
#endif
#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";
#if defined(ESP32)
const int DHT_PIN = 4;
const int LIGHT_PIN = 34; // ADC pin for photoresistor (0–4095)
const float LIGHT_MAX = 4095.0f;
const int BUZZER = 25; // alarm buzzer
#else
const int DHT_PIN = D4;
const int LIGHT_PIN = A0; // the only ADC pin on ESP8266 (0–1023)
const float LIGHT_MAX = 1023.0f;
const int BUZZER = D5;
#endif
DHT dht(DHT_PIN, DHT22);
Adafruit_BMP280 bmp; // I2C: SDA=21, SCL=22 on ESP32 · SDA=D2, SCL=D1 on ESP8266
VirtuinoCloud cloud(API_KEY);
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(BUZZER, OUTPUT);
cloud.setClientId("weather-station"); // optional: the name in My Virtuino World
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) * 100.0f / LIGHT_MAX; // → 0–100 %
if (isnan(temperature) || isnan(humidity)) {
Serial.println("DHT22 read failed"); delay(5000); return;
}
// ── Check alarm threshold from dashboard ──────────────────────────────────
VirtuinoResult threshold = cloud.read("station/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 ────────────────────────
// "station" is the path → station/temperature, station/humidity, ...
cloud.beginWrite("station");
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 <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 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; // 0–1023 → 0–100 %
if (isnan(temperature) || isnan(humidity)) { delay(5000); return; }
VirtuinoResult threshold = cloud.read("station/alarm_threshold");
if (threshold.ok && temperature > threshold.asFloat())
digitalWrite(BUZZER, HIGH);
else
digitalWrite(BUZZER, LOW);
cloud.beginWrite("station"); // → station/temperature, ...
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);
}