מעבדת מיקרו-מחשבים

✦ בנוי על ידי פהד גאנם ✦

T = ton + toff   |   f = 1T   |   D = tonT
DHT22
LDR
LM35
SG90
OLED 0.96″
ILI9341
500 ms
500 ms
1000 ms
1.00 Hz
50 %
0
// 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(" %");
}

T = 14 = 0.25 s = 250 ms
ton = 0.25 · 250 = 62.5 ms  →  63 ms   |   toff = 250 − 63 = 187 ms
f = 1000280 = 3.57 Hz

R = VCC − VLEDILED = 3.3 − 2.010 mA = 130 Ω   |   Itotal,max = 7 · ILED
0
0
6
52 mA
// 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);
}

R = 3.3 − 2.08 mA = 162.5 Ω
I8 = 7 · 7.2 = 50.4 mA

tchar = 4 · 9 · 2fSCL   |   addrrow0 = 0x00   |   addrrow1 = 0x40
0
0
0
0.36 ms
#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);
}

nbits = 4 · 2 · 9 = 72
tchar = 72100000 = 0.72 ms
tscreen = 32 · 0.92 = 29.4 ms  →  10 · 29.4 = 294 mss = 29 %

D = tpulse20 ms   |   duty = D · (216 − 1)   |   tpulse = 0.5 + α180 · 2.0 ms
1200 ms
90°
1.50 ms
7.5 %
0.25 V
// 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(" %");
}

Dmin = 0.520 = 2.5 %   |   Dmax = 2.520 = 12.5 %
nmin = 0.025 · 65535 = 1638   |   nmax = 0.125 · 65535 = 8192
Δα8 = 18025.5 = 7.1°   |   Δα16 = 1806554 = 0.027°
Vavg = 0.075 · 3.3 = 0.248 V

Vout = VCC · RfixRsens + Rfix   |   T = V10 mV/°C   |   Rfix,opt = Rmin · Rmax
50 %
10.0 kΩ
1.65 V
2048
50 %
100 %
// 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);
}

R = 1 · 200 = 14.1 kΩ  →  15 kΩ
VLDR = 3.3 · 151 + 15 = 3.09 V   |   V'LDR = 3.3 · 15200 + 15 = 0.23 V
ΔV = 3.09 − 0.23 = 2.86 V  →  2.863.3 = 87 %

Δt > tbounce   |   nfalse ≈ nbounce − 1   |   fmax = 1Δt
2.0 ms
40 ms
0
0
0
0
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);
  }
}

Δcount = 7
Δt = 30 ms  >  3 ms
fmax = 100030 = 33 Hz
fmax = 1000300 = 3.3 1s

nbytes = w · h · bpp8   |   t = nbytes · bits/bytefclk
SSD1306 · 128×64
ILI9341 · 240×320
400 kHz
0
0
23 ms
0.2 ms
#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);
}

nOLED = 128 · 64 · 18 = 1024   |   nTFT = 240 · 320 · 168 = 153600
t = 1024 · 27400000 = 69 ms  →  14 fps
t = 153600 · 840 · 106 = 30.7 ms  →  33 1s
t = 153600 · 27400000 = 10.4 s

bit = thigh > 45 µs1   |   RH = b0·256 + b110   |   sum = b0+b1+b2+b3
24.5 °C
55.0 %
0
#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
}

RH = 0x02 · 256 + 0x2410 = 512 + 3610 = 54.8 %
T = 0x01 · 256 + 0x0F10 = 256 + 1510 = 27.1 °C
0x02 + 0x24 + 0x01 + 0x0F = 2 + 36 + 1 + 15 = 54 = 0x36  ✓
t = 28 · 80 + 12 · 120 = 2240 + 1440 = 3680 µs = 3.68 ms
🏠