Tutorial · DIY

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.

1. Parts 2. Wiring 3. API token 4. Arduino IDE 5. Code 6. Troubleshooting

1What you need

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).

ESP32 USB ESP32 DevKit 3V3 GND G21 G22 … Desk A1 Piotrek 0.96″ SSD1306 OLED (I2C) GND VCC SCL SDA 3V3 → VCC (power) GND → GND (ground) GPIO 21 → SDA (data) GPIO 22 → SCL (clock)
Four dupont wires — any colours you like, but stick to the convention: red = power, black = ground.
OLED pinESP32 pinRole
GNDGNDground
VCC3V33.3 V power
SCLGPIO 22I2C clock
SDAGPIO 21I2C data
Mind the pin order! OLED modules sometimes have 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

  1. Install Arduino IDE 2.x.
  2. File → Preferences → Additional boards manager URLs — add:
    https://espressif.github.io/arduino-esp32/package_esp32_index.json
  3. Boards Manager → search for esp32 (Espressif) → Install.
  4. Library Manager → install these libraries:
    • Adafruit SSD1306 (pulls in Adafruit GFX),
    • ArduinoJson (Benoît Blanchon).
  5. Select the ESP32 Dev Module board and the USB port. If the port doesn't show up, install the CP210x or CH340 driver (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);
}
The OLED will show e.g. "Desk A1 / Piotrek" or "Desk A1 / FREE". After the morning seat draw the result updates itself on the next poll.

6Common problems

The screen shows nothing

Error 401 on the screen

Error 404

What about setInsecure()?

Full API documentation (all endpoints, response formats): Admin → Hotdesk → API tab. Happy building! 🔧