Combine the week into one device that senses, acts, and connects to the cloud, then demo it.
TODO 1/2/3 markers to make it yours.
By the end of this project, you will:
In a team, build a device that does all three:
| Idea | Sense | Act | Cloud |
|---|---|---|---|
| Smart room monitor | DHT11 + LDR | OLED readout | Ubidots charts |
| Motion security node | PIR | Buzzer | Telegram alert |
| Greenhouse minder | DHT11 + LDR | LED warning | Ubidots gauge + event |
| Cloud appliance | button | relay | Ubidots switch + Telegram |
| Comfort meter | DHT11 | RGB LED green/yellow/red | Ubidots dashboard |
TODO 1/2/3 markers.DEVICE_LABEL so your data does not mix with another team's.The starter is Project 11 with the project-specific parts hollowed out. It compiles and runs as it stands, publishing a constant 0.0, so you can confirm the cloud half works before you touch a sensor. Three TODO markers say where your code goes.
#include "UbidotsEsp32Mqtt.h" // TODO 1: add your sensor library includes here, e.g.: // #include <Adafruit_Sensor.h> // #include <DHT.h> float readMySensor() { // TODO 1: replace this with a real reading from your sensor. // return analogRead(SENSOR_PIN); // potentiometer / LDR // return dht.readTemperature(); // DHT11 return 0.0; }
Everything about your measurement is behind one function. That is deliberate: the rest of the sketch never needs to know whether the number came from an ADC pin, a DHT11, or a calculation across three sensors. Change readMySensor() and the whole device follows.
There are three places to touch for a new sensor, and forgetting the third is the usual bug: the include, the object (for example DHT dht(DHTPIN, DHTTYPE);), and the begin() call in setup().
if (isnan(t)) { ... } and skip the publish rather than sending a bad number.// TODO 2: add every variable you want to see in the dashboard. ubidots.add(VAR_SENSOR, value); ubidots.publish(DEVICE_LABEL);
One add() per variable, then a single publish(). Add a second reading by adding a second const char *VAR_... at the top and a second add() here. Rename VAR_SENSOR to what you are actually measuring (soil, lux, temperature): that name is what appears in Ubidots, so a good one makes the dashboard explain itself.
void callback(char *topic, byte *payload, unsigned int length) { ... float v = atof(value); // TODO 3: decide what a command means for your project. digitalWrite(OUTPUT_PIN, (v >= 0.5) ? HIGH : LOW); Serial.printf("[CLOUD] command -> %.1f\n", v); }
The payload arrives as a number, so a command does not have to be on/off. v could be a brightness for ledcWrite() (Project 3), a target temperature to compare against, or an index selecting which message to show on the OLED (Project 10). Keep the callback short: set a variable or write a pin, and let loop() do anything slow.
| Code | What it does |
|---|---|
connectToWifi() + setup() + reconnect() | Gets you onto the network and the broker. |
subscribeLastValue(DEVICE_LABEL, VAR_COMMAND) | Listens for cloud commands and picks up the current value at boot. |
if (!ubidots.connected()) in loop() | Reconnects and re-subscribes after a Wi-Fi drop. |
millis() - lastPublish > PUBLISH_EVERY_MS | Publishes on a timer without blocking. |
ubidots.loop() | Services MQTT so your callback() actually fires. |
Serial.printf("[PUB] ...") | Your fastest debugging tool. Keep printing while you build. |
| Criteria | Points |
|---|---|
| Sensor reads real, sensible values | 2 |
| Actuator responds correctly | 2 |
| Data reaches the cloud | 3 |
| Full loop works live at the demo | 2 |
| Clear explanation of the idea | 1 |
Bonus (up to 2): a cloud alert/event, a clean OLED display, or a genuinely creative idea.
millis(), not delay(), so Wi-Fi and MQTT stay responsive.Look back at the week. Every project was one idea, and the capstone is where they stop being separate:
millis(), never delay() (Project 4). Not a style rule: it is what lets a device do two things at once, and every networked project since has depended on it.The engineering habits matter as much as the APIs: check that hardware started, guard against bad readings, keep credentials out of your code's public life, print enough on Serial to see what is happening, and keep a version that works before adding the next thing.
That is a real IoT skill set. The next device you build is the same shape, with different parts.