// Experiment 1: no wiring at all. The LED is already on the board.
const int LED = 2; // LED_BUILTIN on most ESP32 boards
const int T_ON = 500; // ms
const int T_OFF = 500; // ms
long blinks = 0;
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
Serial.println("board is alive");
}
void loop() {
digitalWrite(LED, HIGH);
delay(T_ON);
digitalWrite(LED, LOW);
delay(T_OFF);
blinks++;
Serial.print("blink "); Serial.print(blinks);
Serial.print(" T = "); Serial.print(T_ON + T_OFF);
Serial.print(" ms duty = ");
Serial.print(100.0 * T_ON / (T_ON + T_OFF), 0);
Serial.println(" %");
}
// Segments a b c d e f g, one resistor each, common cathode.
const int SEG[7] = {13, 12, 14, 27, 26, 25, 33};
const int SW_A = 4; // switch -> value 1
const int SW_B = 5; // switch -> value 2
const int SW_UP = 18; // direction switch
const int BTN = 19; // reset button
const int MODE = 23; // show switches instead of counter
// One row per digit. Bit 6 is segment a, bit 0 is segment g.
const byte FONT[10] = {
0b1111110, 0b0110000, 0b1101101, 0b1111001, 0b0110011,
0b1011011, 0b1011111, 0b1110000, 0b1111111, 0b1111011
};
int count = 0;
unsigned long last = 0;
void show(int d) {
byte code = FONT[d % 10];
for (int i = 0; i < 7; i++) {
digitalWrite(SEG[i], (code >> (6 - i)) & 1);
}
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < 7; i++) pinMode(SEG[i], OUTPUT);
pinMode(SW_A, INPUT_PULLUP);
pinMode(SW_B, INPUT_PULLUP);
pinMode(SW_UP, INPUT_PULLUP);
pinMode(BTN, INPUT_PULLUP);
pinMode(MODE, INPUT_PULLUP);
}
void loop() {
if (digitalRead(BTN) == LOW) count = 0;
// Time comparison, never delay - the switches must stay responsive.
if (millis() - last > 700) {
last = millis();
if (digitalRead(SW_UP) == LOW) count++; else count--;
if (count > 9) count = 0;
if (count < 0) count = 9;
}
// Pull-up means pressed = LOW, so a closed switch reads 0.
int bits = 0;
if (digitalRead(SW_A) == LOW) bits += 1;
if (digitalRead(SW_B) == LOW) bits += 2;
int value = (digitalRead(MODE) == LOW) ? bits : count;
show(value);
Serial.print("count="); Serial.print(count);
Serial.print(" switches="); Serial.print(bits);
Serial.print(" shown="); Serial.println(value);
}
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// The library carries the whole HD44780 protocol - the 4-bit init, the
// nibbles, the enable edge, the busy waits. All we declare is where the
// display sits on the bus and how big it is.
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
lcd.init(); // wakes the display and puts it into 4-bit mode
lcd.backlight();
// Column first, row second. Everyone gets this the wrong way round once.
lcd.setCursor(0, 0);
lcd.print("ESP32 LAB");
lcd.setCursor(0, 1);
lcd.print("33.9008");
Serial.println("lcd ready");
}
void loop() {
long secs = (millis() / 1000) % 100;
lcd.setCursor(9, 1);
if (secs < 10) lcd.print("0");
lcd.print(secs);
lcd.print("s");
delay(500);
}
// Servo on a 50 Hz channel, RGB on three fast channels.
const int SERVO = 13;
const int R_PIN = 25;
const int G_PIN = 26;
const int B_PIN = 27;
const int DWELL = 1200; // ms at each angle
int angles[3] = {0, 90, 180};
int idx = 0;
unsigned long last = 0;
// 0.5 ms .. 2.5 ms inside a 20 ms period, on a 16-bit counter.
int pulseToDuty(float ms) {
return (int)(ms / 20.0 * 65535.0);
}
void setup() {
Serial.begin(115200);
ledcAttach(SERVO, 50, 16); // one pulse every 20 ms
ledcAttach(R_PIN, 5000, 8);
ledcAttach(G_PIN, 5000, 8);
ledcAttach(B_PIN, 5000, 8);
}
void loop() {
if (millis() - last < DWELL) return;
last = millis();
int a = angles[idx];
idx = (idx + 1) % 3;
float ms = 0.5 + (a / 180.0) * 2.0;
int duty = pulseToDuty(ms);
ledcWrite(SERVO, duty);
// colour follows the angle: red at one end, blue at the other
ledcWrite(R_PIN, 255 - a * 255 / 180);
ledcWrite(G_PIN, a == 90 ? 255 : 0);
ledcWrite(B_PIN, a * 255 / 180);
Serial.print("angle="); Serial.print(a);
Serial.print(" pulse="); Serial.print(ms, 2);
Serial.print(" ms duty="); Serial.print(duty);
Serial.print(" D="); Serial.print(100.0 * ms / 20.0, 1);
Serial.println(" %");
}
// One pin, three very different sensors. Only the last line changes.
const int SENS = 34; // ADC1 - stays usable with Wi-Fi on
const int N = 12; // samples to average
void setup() {
Serial.begin(115200);
analogReadResolution(12);
analogSetAttenuation(ADC_11db);
}
void loop() {
long sum = 0;
for (int i = 0; i < N; i++) sum += analogRead(SENS);
int raw = sum / N;
float volts = raw * 3.3 / 4095.0;
// Pick the line that matches what is actually wired:
float percent = 100.0 * raw / 4095.0; // potentiometer
float degC = volts * 100.0; // LM35: 10 mV per degree
Serial.print("raw="); Serial.print(raw);
Serial.print(" V="); Serial.print(volts, 3);
Serial.print(" pot="); Serial.print(percent, 1);
Serial.print(" % T="); Serial.print(degC, 1);
Serial.println(" C");
delay(200);
}
const int BTN_UP = 4;
const int BTN_DN = 5;
const unsigned long GUARD = 40; // ms of silence that make a press real
volatile int count = 0;
volatile unsigned long tUp = 0, tDn = 0;
volatile long fired = 0, dropped = 0;
void IRAM_ATTR onUp() {
fired++;
// millis() keeps running inside an interrupt. A private counter would not.
if (millis() - tUp < GUARD) { dropped++; return; }
tUp = millis();
count++;
}
void IRAM_ATTR onDown() {
fired++;
if (millis() - tDn < GUARD) { dropped++; return; }
tDn = millis();
count--;
}
void setup() {
Serial.begin(115200);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BTN_UP), onUp, FALLING);
attachInterrupt(digitalPinToInterrupt(BTN_DN), onDown, FALLING);
}
int shown = -1;
void loop() {
// The work happens here, never inside the routine.
if (count != shown) {
shown = count;
Serial.print("count = "); Serial.print(shown);
Serial.print(" fired="); Serial.print(fired);
Serial.print(" dropped="); Serial.println(dropped);
}
}
#include <BluetoothSerial.h>
BluetoothSerial SerialBT;
const int SW = 4;
const int LED = 2;
const unsigned long BEAT = 2000; // ms between heartbeats
int last = -1;
long sent = 0;
unsigned long tBeat = 0;
void setup() {
Serial.begin(115200);
pinMode(SW, INPUT_PULLUP);
pinMode(LED, OUTPUT);
SerialBT.begin("ESP32-A"); // this name appears on the other side
Serial.println("A ready");
}
void loop() {
int s = (digitalRead(SW) == LOW) ? 1 : 0;
// Send on change - and also once in a while, so a lost message heals.
bool changed = (s != last);
bool beat = (millis() - tBeat > BEAT);
if (changed || beat) {
last = s;
tBeat = millis();
sent++;
SerialBT.write('0' + s); // one readable character, not a raw number
Serial.print(changed ? "change -> " : "beat -> ");
Serial.println(s);
}
// The other board answers with the state it actually shows.
if (SerialBT.available()) {
char c = SerialBT.read();
digitalWrite(LED, c == '1'); // our LED mirrors the far display
}
}
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// The library keeps a full 128x64 frame buffer in RAM. Everything you draw
// lands there and nowhere else - the panel only sees it when display() runs.
Adafruit_SSD1306 oled(128, 64);
const int PATTERN = 0; // 0 stripes, 1 checker, 2 wave
long frames = 0;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.setClock(400000);
// begin() sends the whole init sequence for you - addressing mode, charge
// pump, contrast, display on. Skip it and the panel stays dark.
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(SSD1306_WHITE);
Serial.println("oled ready");
}
void loop() {
oled.clearDisplay();
if (PATTERN == 0) {
for (int x = 0; x < 128; x += 16) oled.fillRect(x, 16, 8, 48, SSD1306_WHITE);
} else if (PATTERN == 1) {
for (int x = 0; x < 128; x += 8)
for (int y = 16; y < 64; y += 8)
if (((x / 8) + (y / 8)) % 2) oled.fillRect(x, y, 8, 8, SSD1306_WHITE);
} else {
for (int x = 0; x < 128; x++) {
int y = 40 + (int)(20.0 * sin(x * 0.09));
oled.drawPixel(x, y, SSD1306_WHITE);
}
}
oled.setCursor(0, 0);
oled.print("ESP32 LAB");
// Nothing above reached the bus. This one line ships the whole buffer.
oled.display();
frames++;
Serial.print("frame sent, frames = ");
Serial.println(frames);
delay(400);
}
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
// Four shared wires, and one select line per device.
const int TFT_CS = 5; // the display
const int TFT_DC = 2; // command or data
const int TFT_RST = 4;
const int TCH_CS = 15; // the touch controller, same bus
// The library drives the panel: reset, sleep-out, pixel format, display on.
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
void setup() {
Serial.begin(115200);
// Park the touch controller before anything else talks. Two devices
// holding the bus at once is the classic four-wire failure.
pinMode(TCH_CS, OUTPUT);
digitalWrite(TCH_CS, HIGH);
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.print("ESP32 LAB");
Serial.println("tft ready");
}
void loop() {
// The touch controller gets no library here, and needs none: the same
// four wires, its own select line, three bytes.
digitalWrite(TCH_CS, LOW); // now the controller owns the bus
SPI.transfer(0x90); // ask for the X axis
byte hi = SPI.transfer(0x00);
byte lo = SPI.transfer(0x00);
digitalWrite(TCH_CS, HIGH); // release it - forgetting this is fatal
int x = ((hi << 8) | lo) >> 3;
Serial.print("touch x = ");
Serial.println(x);
delay(300);
}
#include <DHT.h>
// The library carries the one-wire exchange: the wake-up pulse, the two
// presence pulses, the forty bits and the checksum. We only say which pin
// and which sensor.
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
long good = 0, bad = 0;
void setup() {
Serial.begin(115200);
dht.begin();
Serial.println("dht ready");
}
void loop() {
float rh = dht.readHumidity();
float t = dht.readTemperature();
// A failed read does not stop the program - it hands back NaN. Without
// this test the broken value gets printed as if it were a measurement.
if (isnan(rh) || isnan(t)) {
bad++;
Serial.print("read failed bad="); Serial.println(bad);
} else {
good++;
Serial.print("RH="); Serial.print(rh, 1);
Serial.print(" % T="); Serial.print(t, 1);
Serial.print(" C ok="); Serial.println(good);
}
delay(2000); // the DHT22 will not answer faster than once every 2 s
}