ESP32 + a small OLED as a desk display
Build a desk sign that shows who sits at the desk today, all by itself. The ESP32 asks the Hotdesk API every half a minute and draws the result on a 0.96″ OLED. The whole thing costs ~€7 and needs no soldering.
1What you need
- ESP32 DevKit (e.g. ESP32-WROOM-32, 30-pin) — any ~€5 clone will do,
- 0.96″ SSD1306 OLED, 128×64, the I2C variant (4 pins: GND, VCC, SCL, SDA) — ~€2,
- 4 female-to-female jumper wires (dupont),
- a micro-USB / USB-C cable (depending on the board) for power and flashing.
The screen is powered from the board's 3V3 pin — no power supply or breadboard needed. Once the code is flashed, any USB charger will run it.
2Wiring — 4 cables
The I2C version of the OLED connects to the ESP32 with four wires. We use the ESP32's default I2C pins: GPIO 21 (SDA) and GPIO 22 (SCL).
| OLED pin | ESP32 pin | Role |
|---|---|---|
GND | GND | ground |
VCC | 3V3 | 3.3 V power |
SCL | GPIO 22 | I2C clock |
SDA | GPIO 21 | I2C data |
GND and VCC swapped (it varies by manufacturer). Always read the labels printed on the screen's board — don't trust product photos. Reversed power can damage the screen.3Generate an API token
The GET /api/hotdesk/desk/:code endpoint is token-protected. You'll find (or generate) the token in the panel: Admin → Hotdesk → API tab. The full API documentation lives there too, with a "Download .md" button.
Test the token from your computer before flashing anything to the ESP32:
curl "https://your-hotdesk.example/api/hotdesk/desk/A1?token=YOUR_TOKEN"
The response looks like this:
{ "code": "A1", "label": "By the window", "occupied": true, "person": { "id": 16, "name": "Piotrek" } }
A1 is the desk code from the floor plan — every desk has one. It's case-insensitive.
4Set up the Arduino IDE
- Install Arduino IDE 2.x.
- File → Preferences → Additional boards manager URLs — add:
https://espressif.github.io/arduino-esp32/package_esp32_index.json - Boards Manager → search for
esp32(Espressif) → Install. - Library Manager → install these libraries:
Adafruit SSD1306(pulls inAdafruit GFX),ArduinoJson(Benoît Blanchon).
- Select the ESP32 Dev Module board and the USB port. If the port doesn't show up, install the
CP210xorCH340driver (depends on the clone).
5Code — flash and forget
Fill in the four constants at the top (Wi-Fi, desk code, token) and flash. The ESP32 connects to Wi-Fi, polls the API every 30 seconds and shows the result. If the network drops, it switches to retrying on its own.
// Hotdesk desk display — ESP32 + SSD1306 128x64 (I2C)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ==== SETTINGS ==============================================
const char* WIFI_SSID = "YourWiFiName";
const char* WIFI_PASS = "wifi-password";
const char* API_HOST = "https://your-hotdesk.example";
const char* DESK_CODE = "A1"; // desk code from the floor plan
const char* API_TOKEN = "YOUR_TOKEN"; // Admin → Hotdesk → API
const unsigned long POLL_MS = 30000; // poll every 30 s
// ============================================================
Adafruit_SSD1306 display(128, 64, &Wire, -1);
unsigned long lastPoll = 0;
void show(const String& line1, const String& line2, bool big) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println(line1);
display.drawLine(0, 12, 127, 12, SSD1306_WHITE);
display.setTextSize(big ? 2 : 1);
display.setCursor(0, 26);
display.println(line2);
display.display();
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA, SCL
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // screen's I2C address
show("Hotdesk", "Connecting WiFi...", false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(300);
show("Hotdesk", "WiFi OK", false);
}
void poll() {
WiFiClientSecure client;
client.setInsecure(); // skips cert verification — see section 6
HTTPClient http;
String url = String(API_HOST) + "/api/hotdesk/desk/" + DESK_CODE;
http.begin(client, url);
http.addHeader("X-Hotdesk-Token", API_TOKEN);
int code = http.GET();
if (code == 200) {
JsonDocument doc;
deserializeJson(doc, http.getStream());
String desk = doc["code"].as<String>();
bool occupied = doc["occupied"];
String who = occupied ? doc["person"]["name"].as<String>() : String("FREE");
show("Desk " + desk, who, true);
} else if (code == 401) {
show("Error 401", "Bad token", false);
} else if (code == 404) {
show("Error 404", "No desk " + String(DESK_CODE), false);
} else {
show("HTTP error", String(code), false);
}
http.end();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
show("Hotdesk", "WiFi retry...", false);
WiFi.reconnect();
delay(2000);
return;
}
if (millis() - lastPoll >= POLL_MS || lastPoll == 0) {
lastPoll = millis();
poll();
}
delay(100);
}
6Common problems
The screen shows nothing
- Check the pin order on the module (section 2 — GND/VCC are sometimes swapped).
- Some modules use I2C address
0x3Dinstead of0x3C— change it indisplay.begin(...). You can verify it with an "I2C scanner" sketch.
Error 401 on the screen
- The token is wrong or was regenerated in the panel — copy the current one from Admin → Hotdesk → API.
Error 404
- There's no desk with the code
DESK_CODE— check the codes on the floor plan.
What about setInsecure()?
- The sketch skips TLS certificate verification — the data is just "who sits at the desk", and it keeps the code simple (Let's Encrypt certificates rotate every ~2 months). If you want full verification, load the root CA (ISRG Root X1) via
client.setCACert(...).
Full API documentation (all endpoints, response formats): Admin → Hotdesk → API tab. Happy building! 🔧