Digital Output
Anything that’s simply on or off: relay, LED, buzzer, valve, solenoid. 1 GPIO.
There are two ways to get an ESP32 working with SiliconWit.io. Most people should use browser flashing - it needs no coding and takes a couple of minutes. If you want to write your own firmware instead, skip to Manual Setup with Arduino IDE.
Flash SiliconWit’s generic firmware straight from your browser - no Arduino IDE, no drivers, no code to write. It works for most ESP32 boards over USB.
Requirements: Chrome, Edge, or Opera on desktop or Android (Web Serial API - Safari and Firefox aren’t supported by any site, it’s a browser limitation). On Linux, if your board isn’t detected, run sudo usermod -aG dialout $USER and log back in.
Already have a flashed board and just want to reconnect it (for example, after a factory reset) without erasing and reflashing again? Use Already flashed? Reconnect and set up Wi-Fi on the same page.
If you bought a SiliconWit Module - a unit that arrives already flashed, with a claim code or QR code printed on its label - you don’t need to flash or claim it manually:
SW-XXXX-XXXX, or paste the full claim link/token)The unit fetches its own real device ID and access token from our servers using that claim code and writes them to itself over the same USB connection - nobody, including us, ever has to type or copy the raw access token by hand. The unit stays unclaimed and ready for whoever activates it into their own account; setting up its Wi-Fi here doesn’t claim it into yours.
Every generic-firmware device can optionally stream its boot and debug logs to your browser over the same USB cable, useful when troubleshooting Wi-Fi or sensor issues. It’s off by default - turn it on from the device’s Settings tab, or check Enable Serial Monitor during the flash setup steps. No reflashing needed either way, and you can toggle it any time.
The generic firmware doesn’t need reflashing to support a sensor or actuator - you tell it what’s wired where from the dashboard instead. This is available in three places: the Flash a dev board wizard’s wiring step, the Hardware Wiring section when creating a custom device, and any existing device’s Settings → Hardware Wiring tab.
The firmware reads this configuration once at boot, so power-cycle the board (or send it a restart) after changing it.
Digital Output
Anything that’s simply on or off: relay, LED, buzzer, valve, solenoid. 1 GPIO.
Digital Input
Anything that reports a simple on/off state: motion sensor, button, door switch, float switch. 1 GPIO.
Analog Input
Anything with a variable voltage output: soil moisture, light, potentiometer, current sensor. 1 GPIO, must be GPIO1-10.
PWM Output
Variable output: dimming, motor/fan speed. Not for hobby servos. 1 GPIO.
DS18B20
Waterproof 1-Wire temperature probe. 1 GPIO + pull-up resistor.
DHT22
Temperature + humidity, single-wire timing protocol (also covers AM2302/AM2321). 1 GPIO.
BME280
Temperature + humidity + pressure, I2C. 2 GPIOs (SDA + SCL).
BH1750
Light level (lux), I2C. 2 GPIOs (SDA + SCL), its own bus - see note below.
These 8 types cover the large majority of common sensors and actuators. The first four are general electrical forms, not specific products - anything that fits the shape works, so pick by what your part actually does electrically, not by brand name. The last four are specific chip protocols the firmware speaks directly.
| Type | Wiring | GPIO notes |
|---|---|---|
| Digital output | 3V3 (or 5V, check your module) + GND + 1 signal GPIO | Any free GPIO. Does nothing if unwired, so it’s safe to leave configured |
| Digital input | 3V3/5V + GND + 1 signal GPIO | Any free GPIO. Reads a stable “no motion”/off if unwired (pulled down in firmware) |
| Analog input | 3V3 + GND + 1 signal GPIO | Must be GPIO1-10 (ADC1 only - ADC2 on GPIO11-20 has a documented Wi-Fi coexistence issue, so this firmware doesn’t use it). An unwired pin reads floating noise, not a stable value |
| PWM output | 3V3 (or a driver circuit for higher-power loads) + GND + 1 signal GPIO | Any free GPIO, up to 8 PWM pins total. Outputs 0% duty until a command sets it |
| Type | Wiring | Notes |
|---|---|---|
| DS18B20 | 3V3 + GND + 1 data GPIO + 4.7kΩ pull-up resistor between data and 3V3 | 1-Wire. No reading sent if unwired |
| DHT22 | 3V3/5V + GND + 1 data GPIO | Single-wire timing protocol, not true 1-Wire despite the visual similarity. No reading sent if unwired |
| BME280 | 3V3 + GND + SDA GPIO + SCL GPIO (+ 4.7kΩ pull-ups on both, unless your breakout already has them) | I2C, fixed address 0x76. Only one BME280 supported per device |
| BH1750 | 3V3 + GND + SDA GPIO + SCL GPIO (+ 4.7kΩ pull-ups on both) | I2C, fixed address 0x23. Runs on its own separate I2C bus from BME280 - wiring both sensors together needs 4 GPIOs total (2 pairs), not 2 shared ones, since mixing their driver models on one bus isn’t supported |
A reference for common real-world parts:
| If you have this… | Pick this type | Field/command examples |
|---|---|---|
| Relay module (1/2/4/8-channel) | Digital output | relay, pump, valve |
| LED, buzzer, siren | Digital output | light, buzzer, siren |
| Solenoid valve, lock | Digital output | valve, lock |
| PIR motion sensor (e.g. HC-SR501, AM312) | Digital input | motion |
| Push button | Digital input | button |
| Door/window reed switch | Digital input | door, window |
| Float switch (water level) | Digital input | float_switch |
| Tilt/vibration switch | Digital input | tilt, vibration |
| Capacitive or resistive soil moisture sensor | Analog input | soil_moisture |
| LDR / photoresistor | Analog input | light_level |
| Potentiometer | Analog input | position |
| Current sensor with analog output (e.g. ACS712) | Analog input | current |
| Gas sensor with analog output (e.g. MQ-series) | Analog input | custom field name |
| Dimmable LED strip, DC motor/fan speed | PWM output | brightness, fan_speed |
| DS18B20 waterproof probe | DS18B20 | probe_temp |
| DHT22 / AM2302 / AM2321 | DHT22 | temperature, humidity |
| BME280 breakout board | BME280 | bme280_temp, bme280_humidity, pressure |
| BH1750 / GY-302 light sensor breakout | BH1750 | light_level |
Not supported yet - these need their own driver or a timing/protocol this firmware doesn’t implement:
If you need one of these, contact us or vote/request it on the roadmap - or write your own firmware instead, which can do anything the ESP32 itself is physically capable of.
A common pairing - detect motion, switch something on.
5, type Digital input, field motion4, type Digital output, command relaymotion becomes true, run command relay → trueNot exhaustive - PSRAM and flash pins vary by module - but these are worth avoiding on ESP32-S3 dev boards specifically:
| GPIO | Why |
|---|---|
| 0 | BOOT button, also triggers factory reset (hold 5s at startup). Wiring something here means holding it low can reset the device |
| 3 | Strapping pin (JTAG boot mode). Usually fine once booted, avoid if you see boot issues |
| 19 | Native USB D- on most ESP32-S3 boards. Wiring this can break flashing/provisioning over USB |
| 20 | Native USB D+ on most ESP32-S3 boards. Same risk as GPIO19 |
| 43 | UART0 TX (debug serial output). Wiring this can interfere with logs and flashing |
| 44 | UART0 RX (debug serial input). Same risk as GPIO43 |
| 45 | Strapping pin, sets flash voltage at boot. Must stay floating or LOW at boot |
| 46 | Strapping pin, controls boot messages. Must stay floating or LOW at boot |
The Hardware Wiring page warns inline if you pick one of these.
There are two ways to wipe a device’s Wi-Fi and identity (for example, to hand it to someone else or reprovision it as a different device):
Either way, reconnect using the Already flashed? option on the flash page afterward, even on the same network.
If a device’s saved credentials are ever rejected by our servers (for example, if it was deleted from a dashboard while still running), it now detects this automatically and resets itself into Wi-Fi setup mode the next time it’s powered on with a network connection - no button press needed.
Prefer to write and control your own firmware instead of SiliconWit’s generic one? Use the Define my own device path and program the board yourself.
[ { "name": "temperature", "label": "Temperature", "unit": "°C" }, { "name": "humidity", "label": "Humidity", "unit": "%" }]SWD-XXXXXX) and Access Token from the device pageSee the Field Schema guide for nested fields, roles, and more examples.
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.jsonInstall these libraries via Sketch → Include Library → Manage Libraries:
#include <WiFi.h>#include <WiFiClientSecure.h>#include <PubSubClient.h>
// WiFi credentialsconst char* ssid = "YOUR_WIFI_SSID";const char* password = "YOUR_WIFI_PASSWORD";
// SiliconWit.io MQTT settingsconst char* mqtt_server = "mqtt.siliconwit.io";const int mqtt_port = 8883;const char* device_id = "SWD-XXXXXX"; // Replace with your Device IDconst char* access_token = "your-access-token"; // Replace with your Access Token
WiFiClientSecure espClient;PubSubClient client(espClient);
void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP());}
void reconnect() { while (!client.connected()) { Serial.print("Connecting to MQTT...");
if (client.connect(device_id, device_id, access_token)) { Serial.println("connected"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" retrying in 5 seconds"); delay(5000); } }}
void setup() { Serial.begin(115200); setup_wifi(); espClient.setInsecure(); // For testing - use setCACert() in production client.setServer(mqtt_server, mqtt_port);}
void loop() { if (!client.connected()) { reconnect(); } client.loop();
// Send data every 10 seconds static unsigned long lastMsg = 0; if (millis() - lastMsg > 10000) { lastMsg = millis();
// Create JSON payload String payload = "{\"temperature\":25.5,\"humidity\":60}";
// Publish to telemetry topic String topic = "d/" + String(device_id) + "/t"; client.publish(topic.c_str(), payload.c_str());
Serial.println("Data sent: " + payload); }}For production, use TLS encryption:
#include <WiFiClientSecure.h>
WiFiClientSecure espClient;PubSubClient client(espClient);
void setup() { // ... WiFi setup ...
espClient.setInsecure(); // For testing only // For production, use: espClient.setCACert(root_ca);
client.setServer(mqtt_server, 8883); // TLS port}#include <WiFi.h>#include <PubSubClient.h>#include <DHT.h>
#define DHTPIN 4#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ... WiFi and MQTT setup ...
void loop() { if (!client.connected()) { reconnect(); } client.loop();
static unsigned long lastMsg = 0; if (millis() - lastMsg > 30000) { lastMsg = millis();
float temp = dht.readTemperature(); float hum = dht.readHumidity();
if (!isnan(temp) && !isnan(hum)) { String payload = "{\"temperature\":" + String(temp) + ",\"humidity\":" + String(hum) + "}";
String topic = "d/" + String(device_id) + "/t"; client.publish(topic.c_str(), payload.c_str()); } }}#define uS_TO_S_FACTOR 1000000#define TIME_TO_SLEEP 300 // 5 minutes
void setup() { Serial.begin(115200);
setup_wifi(); client.setServer(mqtt_server, mqtt_port);
if (client.connect(device_id, device_id, access_token)) { // Read and send data String payload = "{\"temperature\":25.5}"; String topic = "d/" + String(device_id) + "/t"; client.publish(topic.c_str(), payload.c_str());
delay(100); // Allow message to send client.disconnect(); }
// Go to sleep esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); esp_deep_sleep_start();}
void loop() { // Never reached}| Problem | Solution |
|---|---|
| WiFi won’t connect | Check SSID/password, ensure 2.4GHz network |
| MQTT connection fails | Verify device ID and access token |
| Messages not appearing | Check topic format matches dashboard |
Enable verbose logging:
Serial.setDebugOutput(true);