Week 14

Interface and Application Programming

Learning outcomes

  • Implement a User Interface (UI) using programming and explore protocols to communicate with a microcontroller board that you designed and made.
Week 4 cover

Assignment requirements

Group assignment

  • Compare as many tool options as possible
  • Document your work on the group work page and reflect on your individual page what you learned

Individual assignment

  • Write an application for the embedded board that you made, that interfaces a user with an input and/or output device(s)

Progress status

Group work Done

Compare tools and document your findings

Individual work Done

Write and I/O application

Documentation Done

Upload source files

1) Introduction

Improve new skills and fill knowledge gaps


  • Input/Ouput devices
  • Networking & commnication
  • Design an application
Step right image

2) Group assignment - Compare tools options and document them

For more details visit Fab Lab Peru Week 14 Group assignment


Problems

- We didn't know what to do

- New programs and applications to learn

- Shortage of time to test the procedures several times


Solutions

- Review previous assignments and repeat several times

- A lot of time in the computer is needed to learn and practice the new skills

Running 1
Installing Processing
Running 1
Reviewing group assignments
Running 1
Coding at Ardino Ide & Processing
Running 3
Running both Processing and Arduino IDE
Running 4
We need calories, fat, carbohydrates, cola - Not healthy food at that time 🤣🤣🤣

  
          // Arduino code for controlling an LED with Processing commands  
    
          #define LED_PIN D6

            bool blinkMode = false;
            bool ledState = false;
            unsigned long lastBlinkTime = 0;

            void setup() {
              Serial.begin(115200);
              Serial.setTimeout(20);

              pinMode(LED_PIN, OUTPUT);
              digitalWrite(LED_PIN, LOW); // apagado
            }

            void loop() {

              if (Serial.available()) {
                String command = Serial.readStringUntil('\n');
                command.trim();

                if (command == "ON") {
                  blinkMode = false;
                  digitalWrite(LED_PIN, HIGH);
                  Serial.println("ON");
                }

                else if (command == "OFF") {
                  blinkMode = false;
                  digitalWrite(LED_PIN, LOW);
                  Serial.println("OFF");
                }

                else if (command == "BLINK") {
                  blinkMode = true;
                  Serial.println("BLINK");
                }
              }

              if (blinkMode) {
                if (millis() - lastBlinkTime > 300) {
                  lastBlinkTime = millis();
                  ledState = !ledState;
                  digitalWrite(LED_PIN, ledState ? HIGH : LOW);
                }
              }
            }
  


          // Processing code to control the LED on the designed PCB
          
            import processing.serial.*;

            Serial myPort;
            String status = "OFF";

            void setup() {
              size(430, 500);
              smooth();
              textAlign(CENTER, CENTER);

              println(Serial.list());
              myPort = new Serial(this, "COM3", 115200);
              myPort.clear();
            }

            void draw() {
              background(245);

              fill(0);
              textSize(28);
              text("LED Control", width/2, 60);

              fill(120);
              textSize(14);
              text("ON / OFF / BLINK", width/2, 90);

              // STATUS
              fill(255);
              rect(50, 120, 330, 100, 20);

              fill(0);
              textSize(18);
              text("Status", width/2, 150);

              fill(status.equals("ON") ? color(0,200,100) :
                  status.equals("BLINK") ? color(0,120,255) :
                  color(150));

              textSize(28);
              text(status, width/2, 190);

              // BOTONES
              drawButton(60, 270, 90, 70, "ON");
              drawButton(170, 270, 90, 70, "OFF");
              drawButton(280, 270, 90, 70, "BLINK");
            }

            void drawButton(float x, float y, float w, float h, String label) {
              boolean hover = mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h;

              noStroke();
              fill(hover ? color(80) : color(50));
              rect(x, y, w, h, 15);

              fill(255);
              textSize(16);
              text(label, x + w/2, y + h/2);
            }

            void mousePressed() {

              if (mouseY > 270 && mouseY < 340) {

                if (mouseX > 60 && mouseX < 150) {
                  send("ON");
                  status = "ON";
                }

                if (mouseX > 170 && mouseX < 260) {
                  send("OFF");
                  status = "OFF";
                }

                if (mouseX > 280 && mouseX < 370) {
                  send("BLINK");
                  status = "BLINK";
                }
              }
            }
            void send(String cmd) {
              myPort.write(cmd + "\n");
              println("Sent: " + cmd);
            }


Swimming 2
Serial monitor screenshot
Swimming 2
We used Putty to communicate with the Xiao ECP 32 C3 with HC-SR04 sensor
Swimming 2
We connected the serial port to connect Putty and the Xiao ECP 32 C3 with HC-SR04 sensor
Swimming 2
Putty view of the serial connection
  
          // Arduino code for HC-SR04 sensor and Putty serial communication

            #define TRIG_PIN D5
            #define ECHO_PIN D6

            void setup() {
              Serial.begin(115200);
              pinMode(TRIG_PIN, OUTPUT);
              pinMode(ECHO_PIN, INPUT);
            }

            void loop() {
              // Trigger the sensor
              digitalWrite(TRIG_PIN, LOW);
              delayMicroseconds(2);
              digitalWrite(TRIG_PIN, HIGH);
              delayMicroseconds(10);
              digitalWrite(TRIG_PIN, LOW);

              // Read the echo time
              long duration = pulseIn(ECHO_PIN, HIGH);

              // Calculate distance in cm
              float distance = (duration / 2.0) * 0.0343;

              // Send distance to serial monitor
              Serial.print("Distance: ");
              Serial.print(distance);
              Serial.println(" cm");

              delay(1000); // Wait before next reading
            }
   
Running 4
A holiday day. Very late after the group meeting 😇😇😇

3) Individual assigment

Problems

- Define the project

- How to control Input/Output devices

- Not enough experience

Solutions

- Review previous materials

- Explore solutions at Web Resources

- Test & Error for hours

Swimming 2
We used a Xiao ECP 32 C3, DHT Sensor (temperature & humidity) and a 1.3 OLED IIC display
Swimming 2
We used a Xiao ECP 32 C3, DHT Sensor (temperature & humidity) and a 1.3 OLED IIC display
Swimming 2
We runned a code and we had the information in the serial monitor
Swimming 2
This is the screenshot with the results
Camera 1
We used a 1.3 OLED display for control, for example temperature: 81.7 °F
Camera 2
We used a 1.3 OLED display for control, for example humidity: 57%
              
          // Arduino code for DHT11 sensor and OLED display

                    #include 
                    #include 
                    #include 

                    #define DHTPIN  D2
                    #define DHTTYPE DHT11

                    DHT dht(DHTPIN, DHTTYPE);

                    U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
                    // U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

                    int currentMode = 0;  // 0 = Hello, 1 = humidity, 2 = temp C, 3 = temp F

                    void showHello() {
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(22, 20, "DHT11 Ready");
                      u8g2.drawHLine(0, 25, 128);
                      u8g2.setFont(u8g2_font_ncenB08_tr);
                      u8g2.drawStr(10, 40, "0 - Hello Fab Lab");
                      u8g2.drawStr(10, 52, "1 - Humidity");
                      u8g2.drawStr(10, 64, "2 - Temp (C)  3 - Temp (F)");
                      u8g2.sendBuffer();
                    }

                    void showFabLab() {
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB14_tr);
                      u8g2.drawStr(18, 24, "Hello");
                      u8g2.setFont(u8g2_font_ncenB18_tr);
                      u8g2.drawStr(8, 54, "Fab Lab!");
                      u8g2.sendBuffer();
                    }

                    void showHumidity(float h) {
                      char valStr[10];
                      dtostrf(h, 4, 1, valStr);
                      strcat(valStr, " %");

                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(28, 14, "Humidity");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB24_tr);
                      u8g2.drawStr(14, 52, valStr);
                      u8g2.sendBuffer();
                    }

                    void showTempC(float t) {
                      char valStr[10];
                      dtostrf(t, 4, 1, valStr);
                      strcat(valStr, " C");

                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(18, 14, "Temperature");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB08_tr);
                      u8g2.drawStr(44, 32, "Celsius");
                      u8g2.setFont(u8g2_font_ncenB24_tr);
                      u8g2.drawStr(14, 62, valStr);
                      u8g2.sendBuffer();
                    }

                    void showTempF(float t) {
                      float f = (t * 9.0 / 5.0) + 32.0;
                      char valStr[10];
                      dtostrf(f, 4, 1, valStr);
                      strcat(valStr, " F");

                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(18, 14, "Temperature");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB08_tr);
                      u8g2.drawStr(30, 32, "Fahrenheit");
                      u8g2.setFont(u8g2_font_ncenB24_tr);
                      u8g2.drawStr(14, 62, valStr);
                      u8g2.sendBuffer();
                    }

                    void setup() {
                      Serial.begin(115200);
                      dht.begin();
                      u8g2.begin();

                      showHello();

                      Serial.println("DHT11 Monitor ready.");
                      Serial.println("Send 0 = Hello Fab Lab | 1 = Humidity | 2 = Temp C | 3 = Temp F");
                    }

                    void loop() {
                      if (Serial.available() > 0) {
                        char input = Serial.read();

                        if (input == '0') {
                          currentMode = 0;
                          showFabLab();
                          Serial.println("Mode: Hello Fab Lab");
                        } else if (input == '1') {
                          currentMode = 1;
                          Serial.println("Mode: Humidity");
                        } else if (input == '2') {
                          currentMode = 2;
                          Serial.println("Mode: Temperature (Celsius)");
                        } else if (input == '3') {
                          currentMode = 3;
                          Serial.println("Mode: Temperature (Fahrenheit)");
                        } else if (input != '\n' && input != '\r') {
                          Serial.println("Invalid. Send 0, 1, 2 or 3.");
                        }
                      }

                      // Sensor modes refresh every 2 seconds
                      if (currentMode == 1 || currentMode == 2 || currentMode == 3) {
                        float humidity    = dht.readHumidity();
                        float temperature = dht.readTemperature();

                        if (isnan(humidity) || isnan(temperature)) {
                          Serial.println("DHT11 read error!");
                          u8g2.clearBuffer();
                          u8g2.setFont(u8g2_font_ncenB08_tr);
                          u8g2.drawStr(10, 35, "Sensor error!");
                          u8g2.sendBuffer();
                          delay(2000);
                          return;
                        }

                        if (currentMode == 1) {
                          showHumidity(humidity);
                          Serial.print("Humidity: ");
                          Serial.print(humidity, 1);
                          Serial.println(" %");
                        } else if (currentMode == 2) {
                          showTempC(temperature);
                          Serial.print("Temperature: ");
                          Serial.print(temperature, 1);
                          Serial.println(" C");
                        } else if (currentMode == 3) {
                          showTempF(temperature);
                          float f = (temperature * 9.0 / 5.0) + 32.0;
                          Serial.print("Temperature: ");
                          Serial.print(f, 1);
                          Serial.println(" F");
                        }
                        delay(2000);
                      }
                    }
                          
            
Camera 1
We generated a code for Processing and repeated the serial monitor procedure
Camera 2
Option 1 - Humidity
Camera 2
Option 3 - ° F (Fahrenheit)
Camera 2
An other Processing button layout
                

        // Processing code for a DHT11 monitor with a custom UI

                  import processing.serial.*;
                  Serial myPort;
                  String serialMsg = "Waiting for data...";
                  int hoveredBtn = -1;
                  int activeBtn  = -1;

                  String[] labels   = {"Hello Fab Lab", "Humidity", "Celsius", "Fahrenheit"};
                  String[] icons    = {"★", "~%", "C", "F"};
                  String[] sublabel = {"Welcome screen", "Relative humidity", "Temperature", "Temperature"};
                  color[]  btnBase  = {#534AB7, #0F6E56, #185FA5, #993C1D};
                  color[]  btnLight = {#7F77DD, #1D9E75, #378ADD, #D85A30};
                  color[]  btnDark  = {#26215C, #04342C, #042C53, #4A1B0C};

                  PFont fontBold;
                  PFont fontRegular;
                  PFont fontSmall;

                  int COLS   = 2;
                  int ROWS   = 2;
                  int PAD    = 24;
                  int BTN_W  = 160;
                  int BTN_H  = 110;
                  int GRID_X;
                  int GRID_Y = 140;

                  void setup() {
                    size(420, 520);
                    smooth();
                    GRID_X = (width - (COLS * BTN_W + PAD)) / 2;

                    fontBold    = createFont("Arial Bold", 14, true);
                    fontRegular = createFont("Arial", 13, true);
                    fontSmall   = createFont("Arial", 10, true);

                    try {
                      myPort = new Serial(this, "COM6", 115200);
                      myPort.bufferUntil('\n');
                    } catch (Exception e) {
                      println("Serial port error: " + e.getMessage());
                    }
                  }

                  void draw() {
                    background(24, 24, 28);

                    // Header background
                    noStroke();
                    fill(36, 36, 42);
                    rect(0, 0, width, 110);

                    // Title
                    textFont(fontBold);
                    fill(230);
                    textSize(22);
                    textAlign(CENTER, TOP);
                    text("DHT11 Monitor", width / 2, 22);

                    // Subtitle
                    textFont(fontRegular);
                    fill(130);
                    textSize(12);
                    text("XIAO ESP32C3  ·  COM6", width / 2, 54);

                    // Status pill
                    color pillC = (myPort != null) ? color(15, 110, 86) : color(153, 60, 29);
                    String pillT = (myPort != null) ? "● Connected" : "● Disconnected";
                    drawPill(width / 2, 80, pillT, pillC);

                    // Section label
                    textFont(fontSmall);
                    fill(110);
                    textSize(11);
                    textAlign(LEFT, TOP);
                    text("SELECT MODE", GRID_X, GRID_Y - 22);

                    // Grid buttons
                    hoveredBtn = -1;
                    for (int i = 0; i < 4; i++) {
                      int col = i % COLS;
                      int row = i / COLS;
                      int x   = GRID_X + col * (BTN_W + PAD);
                      int y   = GRID_Y + row * (BTN_H + PAD);

                      boolean hovered = (mouseX >= x && mouseX <= x + BTN_W &&
                                        mouseY >= y && mouseY <= y + BTN_H);
                      if (hovered) { hoveredBtn = i; cursor(HAND); }

                      drawCard(x, y, BTN_W, BTN_H, i, hovered, activeBtn == i);
                    }
                    if (hoveredBtn == -1) cursor(ARROW);

                    drawSerialBox();
                  }

                  void drawCard(int x, int y, int w, int h, int i, boolean hovered, boolean active) {
                    color base = active  ? btnLight[i] :
                                hovered ? btnLight[i] : btnBase[i];
                    color dark = btnDark[i];

                    // Shadow
                    noStroke();
                    fill(0, 60);
                    rect(x + 4, y + 6, w, h, 14);

                    // Card body
                    fill(base);
                    rect(x, y, w, h, 14);

                    // Top shine
                    fill(255, active ? 60 : hovered ? 45 : 25);
                    rect(x, y, w, h / 3, 14, 14, 0, 0);

                    // Active bottom bar
                    if (active) {
                      fill(255, 120);
                      rect(x + 20, y + h - 6, w - 40, 4, 2);
                    }

                    // Number badge
                    fill(dark);
                    ellipse(x + 22, y + 22, 26, 26);
                    textFont(fontBold);
                    fill(255, 200);
                    textSize(11);
                    textAlign(CENTER, CENTER);
                    text(str(i), x + 22, y + 22);

                    // Icon (top right)
                    textFont(fontBold);
                    fill(255, active ? 255 : 200);
                    textSize(active ? 22 : 20);
                    textAlign(RIGHT, TOP);
                    text(icons[i], x + w - 14, y + 12);

                    // Main label
                    textFont(fontBold);
                    fill(255);
                    textSize(14);
                    textAlign(LEFT, TOP);
                    text(labels[i], x + 12, y + 52);

                    // Sub label
                    textFont(fontRegular);
                    fill(255, 160);
                    textSize(10);
                    text(sublabel[i], x + 12, y + 72);
                  }

                  void drawPill(int cx, int cy, String label, color c) {
                    textFont(fontSmall);
                    textSize(11);
                    float tw = textWidth(label);
                    int pw = (int)tw + 24;
                    int ph = 22;
                    noStroke();
                    fill(c, 60);
                    rect(cx - pw / 2, cy - ph / 2, pw, ph, ph / 2);
                    fill(c);
                    rect(cx - pw / 2 + 1, cy - ph / 2 + 1, pw - 2, ph - 2, ph / 2);
                    fill(255);
                    textAlign(CENTER, CENTER);
                    text(label, cx, cy);
                  }

                  void drawSerialBox() {
                    int bx = PAD;
                    int by = GRID_Y + ROWS * (BTN_H + PAD) + 10;
                    int bw = width - PAD * 2;
                    int bh = 70;

                    noStroke();
                    fill(36, 36, 42);
                    rect(bx, by, bw, bh, 10);

                    // Left accent bar
                    fill(activeBtn >= 0 ? btnBase[activeBtn] : color(80));
                    rect(bx, by, 4, bh, 10, 0, 0, 10);

                    // Label
                    textFont(fontSmall);
                    fill(110);
                    textSize(10);
                    textAlign(LEFT, TOP);
                    text("SERIAL OUTPUT", bx + 16, by + 10);

                    // Message
                    textFont(fontRegular);
                    fill(210);
                    textSize(13);
                    text(serialMsg, bx + 16, by + 30);
                  }

                  void mousePressed() {
                    for (int i = 0; i < 4; i++) {
                      int col = i % COLS;
                      int row = i / COLS;
                      int x   = GRID_X + col * (BTN_W + PAD);
                      int y   = GRID_Y + row * (BTN_H + PAD);

                      if (mouseX >= x && mouseX <= x + BTN_W &&
                          mouseY >= y && mouseY <= y + BTN_H) {
                        sendCommand(i);
                      }
                    }
                  }

                  void sendCommand(int mode) {
                    activeBtn = mode;
                    if (myPort != null) {
                      myPort.write(str(mode));
                      println("Sent: " + mode);
                    }
                    switch (mode) {
                      case 0: serialMsg = "Sent 0  ->  Hello Fab Lab";    break;
                      case 1: serialMsg = "Sent 1  ->  Humidity";         break;
                      case 2: serialMsg = "Sent 2  ->  Temperature (C)";  break;
                      case 3: serialMsg = "Sent 3  ->  Temperature (F)";  break;
                    }
                  }

                  void serialEvent(Serial p) {
                    String incoming = p.readStringUntil('\n');
                    if (incoming != null) {
                      incoming = trim(incoming);
                      if (incoming.length() > 0) {
                        println("ESP32 >> " + incoming);
                        serialMsg = incoming;
                      }
                    }
                  }
                
            
Camera 2
I added a SG90 - Micro servo 9 gram - Devices
Camera 2
Schematic diagram with wires
Camera 2
Schematic diagram with wires
                

        // Arduino code for DHT11 sensor, OLED display, servo motor and ON/OFF switch monitor with a custom UI

                    #include 
                    #include 
                    #include 
                    #include 

                    #define DHTPIN    D2
                    #define DHTTYPE   DHT11
                    #define SERVO_PIN D9

                    DHT dht(DHTPIN, DHTTYPE);
                    Servo myServo;

                    U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
                    // U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

                    int  currentMode  = 0;
                    int  servoAngle   = 0;
                    bool sweeping     = true;
                    bool servoEnabled = true;

                    unsigned long lastSensorRead = 0;
                    unsigned long lastServoMove  = 0;
                    const unsigned long SENSOR_INTERVAL = 2000;
                    const unsigned long SERVO_INTERVAL  = 15;

                    // ──────────────────────────────────────────
                    // OLED display functions
                    // ──────────────────────────────────────────

                    void showFabLab() {
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB14_tr);
                      u8g2.drawStr(18, 24, "Hello");
                      u8g2.setFont(u8g2_font_ncenB18_tr);
                      u8g2.drawStr(8, 54, "Fab Lab!");
                      u8g2.sendBuffer();
                    }

                    void showHumidity(float h) {
                      char valStr[10];
                      dtostrf(h, 4, 1, valStr);
                      strcat(valStr, " %");
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(28, 14, "Humidity");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB24_tr);
                      u8g2.drawStr(14, 52, valStr);
                      u8g2.sendBuffer();
                    }

                    void showTempC(float t) {
                      char valStr[10];
                      dtostrf(t, 4, 1, valStr);
                      strcat(valStr, " C");
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(18, 14, "Temperature");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB08_tr);
                      u8g2.drawStr(44, 32, "Celsius");
                      u8g2.setFont(u8g2_font_ncenB24_tr);
                      u8g2.drawStr(14, 62, valStr);
                      u8g2.sendBuffer();
                    }

                    void showTempF(float t) {
                      float f = (t * 9.0 / 5.0) + 32.0;
                      char valStr[10];
                      dtostrf(f, 4, 1, valStr);
                      strcat(valStr, " F");
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(18, 14, "Temperature");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB08_tr);
                      u8g2.drawStr(30, 32, "Fahrenheit");
                      u8g2.setFont(u8g2_font_ncenB24_tr);
                      u8g2.drawStr(14, 62, valStr);
                      u8g2.sendBuffer();
                    }

                    void showServoOff() {
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(28, 14, "Servo Status");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB18_tr);
                      u8g2.drawStr(22, 48, "STOPPED");
                      u8g2.sendBuffer();
                    }

                    void showServoOn() {
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB10_tr);
                      u8g2.drawStr(28, 14, "Servo Status");
                      u8g2.drawHLine(0, 18, 128);
                      u8g2.setFont(u8g2_font_ncenB18_tr);
                      u8g2.drawStr(18, 48, "RUNNING");
                      u8g2.sendBuffer();
                    }

                    // ──────────────────────────────────────────
                    // Sensor read + display
                    // ──────────────────────────────────────────

                    void readAndDisplay() {
                      float humidity    = dht.readHumidity();
                      float temperature = dht.readTemperature();

                      if (isnan(humidity) || isnan(temperature)) {
                        Serial.println("DHT11 read error!");
                        u8g2.clearBuffer();
                        u8g2.setFont(u8g2_font_ncenB08_tr);
                        u8g2.drawStr(10, 35, "Sensor error!");
                        u8g2.sendBuffer();
                        return;
                      }

                      if (currentMode == 1) {
                        showHumidity(humidity);
                        Serial.print("Humidity: ");
                        Serial.print(humidity, 1);
                        Serial.println(" %");
                      } else if (currentMode == 2) {
                        showTempC(temperature);
                        Serial.print("Temperature: ");
                        Serial.print(temperature, 1);
                        Serial.println(" C");
                      } else if (currentMode == 3) {
                        showTempF(temperature);
                        float f = (temperature * 9.0 / 5.0) + 32.0;
                        Serial.print("Temperature: ");
                        Serial.print(f, 1);
                        Serial.println(" F");
                      }
                    }

                    // ──────────────────────────────────────────
                    // Servo sweep
                    // ──────────────────────────────────────────

                    void updateServo() {
                      if (!servoEnabled) return;
                      if (millis() - lastServoMove < SERVO_INTERVAL) return;
                      lastServoMove = millis();

                      myServo.write(servoAngle);

                      if (sweeping) {
                        servoAngle++;
                        if (servoAngle >= 180) sweeping = false;
                      } else {
                        servoAngle--;
                        if (servoAngle <= 0) sweeping = true;
                      }
                    }

                    // ──────────────────────────────────────────
                    // Setup
                    // ──────────────────────────────────────────

                    void setup() {
                      Serial.begin(115200);
                      dht.begin();
                      u8g2.begin();

                      myServo.setPeriodHertz(50);
                      myServo.attach(SERVO_PIN, 500, 2400);
                      myServo.write(0);

                      showFabLab();
                      Serial.println("DHT11 Monitor ready.");
                      Serial.println("0 = Hello Fab Lab | 1 = Humidity | 2 = Temp C | 3 = Temp F");
                      Serial.println("5 = Servo OFF      | 6 = Servo ON");
                    }

                    // ──────────────────────────────────────────
                    // Loop
                    // ──────────────────────────────────────────

                    void loop() {
                      updateServo();

                      if (Serial.available() > 0) {
                        char input = Serial.read();

                        if (input == '0') {
                          currentMode = 0;
                          showFabLab();
                          Serial.println("Mode: Hello Fab Lab");

                        } else if (input == '1') {
                          currentMode = 1;
                          Serial.println("Mode: Humidity");
                          readAndDisplay();
                          lastSensorRead = millis();

                        } else if (input == '2') {
                          currentMode = 2;
                          Serial.println("Mode: Temperature (Celsius)");
                          readAndDisplay();
                          lastSensorRead = millis();

                        } else if (input == '3') {
                          currentMode = 3;
                          Serial.println("Mode: Temperature (Fahrenheit)");
                          readAndDisplay();
                          lastSensorRead = millis();

                        } else if (input == '5') {
                          servoEnabled = false;
                          myServo.write(0);        // return to rest
                          delay(500);              // wait to reach position
                          myServo.detach();        // cut signal — no jitter
                          servoAngle = 0;
                          sweeping   = true;
                          showServoOff();
                          Serial.println("Servo: OFF");

                        } else if (input == '6') {
                          servoEnabled = true;
                          servoAngle   = 0;
                          sweeping     = true;
                          myServo.attach(SERVO_PIN, 500, 2400);
                          myServo.write(0);
                          delay(300);
                          showServoOn();
                          Serial.println("Servo: ON");

                        } else if (input != '\n' && input != '\r') {
                          Serial.println("Invalid. Send 0, 1, 2, 3, 5 or 6.");
                        }
                      }

                      // Auto-refresh sensor display every 2 seconds
                      if (currentMode >= 1 && currentMode <= 3 &&
                          millis() - lastSensorRead >= SENSOR_INTERVAL) {
                        lastSensorRead = millis();
                        readAndDisplay();
                      }
                    }
            
            
Camera 2
Arduino Ide and Processing views
Camera 2
Schematic diagram
                    
        // Processing code for a DHT11, 1.3 OLED IIC, Micro Servo 9 gram, and ON/OFF switch monitor with a custom UI

                    import processing.serial.*;

                    Serial myPort;

                    // ── Serial data ──────────────────────────
                    String serialMsg   = "Waiting for data...";
                    float  tempC       = 0;
                    float  tempF       = 0;
                    float  humidity    = 0;
                    boolean hasData    = false;

                    // ── Graph history ────────────────────────
                    int    MAX_POINTS  = 60;
                    float[] histTempC  = new float[MAX_POINTS];
                    float[] histHumid  = new float[MAX_POINTS];
                    int    histIndex   = 0;
                    boolean graphFull  = false;

                    // ── UI state ─────────────────────────────
                    int     activeBtn  = -1;
                    int     hoveredBtn = -1;
                    boolean servoOn    = false;
                    boolean swHovered  = false;

                    // ── Fonts ────────────────────────────────
                    PFont fontBold;
                    PFont fontRegular;
                    PFont fontSmall;

                    // ── Layout constants ─────────────────────
                    int W         = 440;
                    int H         = 720;
                    int PAD       = 20;
                    int CARD_W    = (W - PAD * 2 - 12) / 2;
                    int CARD_H    = 90;
                    int GRID_X    = PAD;
                    int GRID_Y    = 390;

                    // Toggle
                    int SW_W      = 60;
                    int SW_H      = 30;
                    int SW_X;
                    int SW_Y      = 560;

                    // Colors
                    color BG        = color(24, 24, 28);
                    color PANEL     = color(36, 36, 42);
                    color TEXT1     = color(230, 230, 230);
                    color TEXT2     = color(140, 140, 148);
                    color TEXT3     = color(90, 90, 100);
                    color BLUE      = color(55, 138, 221);
                    color TEAL      = color(29, 158, 117);
                    color[] CBASES  = {color(83,74,183), color(15,110,86), color(24,95,165), color(153,60,29)};
                    color[] CLIGHTS = {color(127,119,221), color(29,158,117), color(55,138,221), color(216,90,48)};
                    color[] CDARKS  = {color(38,33,92), color(4,52,44), color(4,44,83), color(74,27,12)};

                    // ─────────────────────────────────────────
                    void setup() {
                      size(440, 720);
                      smooth();
                      SW_X = (W - SW_W) / 2;

                      fontBold    = createFont("Arial Bold", 14, true);
                      fontRegular = createFont("Arial", 13, true);
                      fontSmall   = createFont("Arial", 10, true);

                      for (int i = 0; i < MAX_POINTS; i++) {
                        histTempC[i] = 0;
                        histHumid[i] = 0;
                      }

                      try {
                        myPort = new Serial(this, "COM6", 115200);
                        myPort.bufferUntil('\n');
                      } catch (Exception e) {
                        println("Serial error: " + e.getMessage());
                      }
                    }

                    // ─────────────────────────────────────────
                    void draw() {
                      background(BG);

                      drawHeader();
                      drawLiveCards();
                      drawGraph();
                      drawModeButtons();
                      drawServoSection();
                      drawSerialBox();
                    }

                    // ── Header ───────────────────────────────
                    void drawHeader() {
                      fill(PANEL);
                      noStroke();
                      rect(PAD, 14, W - PAD * 2, 80, 10);

                      textFont(fontBold);
                      fill(TEXT1);
                      textSize(20);
                      textAlign(CENTER, TOP);
                      text("DHT11 Monitor", W / 2, 24);

                      textFont(fontRegular);
                      fill(TEXT2);
                      textSize(11);
                      text("XIAO ESP32C3  ·  COM6", W / 2, 50);

                      // Status pill
                      color pc = (myPort != null) ? color(15,110,86) : color(153,60,29);
                      String pt = (myPort != null) ? "  Connected" : "  Disconnected";
                      drawPill(W / 2, 76, pt, pc);
                    }

                    // ── Live cards ───────────────────────────
                    void drawLiveCards() {
                      textFont(fontSmall);
                      fill(TEXT3);
                      textSize(10);
                      textAlign(LEFT, TOP);
                      text("LIVE READINGS", PAD, 108);

                      int cw = (W - PAD * 2 - 16) / 3;
                      int cy = 122;
                      int ch = 62;

                      drawLiveCard(PAD,           cy, cw, ch,
                        hasData ? nf(tempC, 1, 1) + "C" : "--",
                        "Temperature", BLUE);
                      drawLiveCard(PAD + cw + 8,  cy, cw, ch,
                        hasData ? nf(tempF, 1, 1) + "F" : "--",
                        "Temperature", BLUE);
                      drawLiveCard(PAD + (cw+8)*2, cy, cw, ch,
                        hasData ? nf(humidity, 1, 1) + "%" : "--",
                        "Humidity", TEAL);
                    }

                    void drawLiveCard(int x, int y, int w, int h, String val, String lbl, color c) {
                      fill(PANEL);
                      noStroke();
                      rect(x, y, w, h, 8);
                      fill(c);
                      textFont(fontBold);
                      textSize(18);
                      textAlign(LEFT, TOP);
                      text(val, x + 10, y + 12);
                      fill(TEXT3);
                      textFont(fontSmall);
                      textSize(10);
                      text(lbl, x + 10, y + 38);
                    }

                    // ── Graph ────────────────────────────────
                    void drawGraph() {
                      textFont(fontSmall);
                      fill(TEXT3);
                      textSize(10);
                      textAlign(LEFT, TOP);
                      text("HISTORY GRAPH", PAD, 198);

                      int gx = PAD;
                      int gy = 212;
                      int gw = W - PAD * 2;
                      int gh = 80;

                      fill(PANEL);
                      noStroke();
                      rect(gx, gy, gw, gh + 20, 8);

                      // Grid lines
                      stroke(50, 50, 58);
                      strokeWeight(0.5);
                      for (int i = 1; i < 4; i++) {
                        float ly = gy + (gh / 4.0) * i;
                        line(gx + 8, ly, gx + gw - 8, ly);
                      }

                      // Temp line
                      int points = graphFull ? MAX_POINTS : histIndex;
                      if (points > 1) {
                        float minT = 0, maxT = 50;
                        float minH = 0, maxH = 100;

                        noFill();
                        stroke(BLUE);
                        strokeWeight(1.5);
                        beginShape();
                        for (int i = 0; i < points; i++) {
                          int idx = graphFull ? (histIndex + i) % MAX_POINTS : i;
                          float x = map(i, 0, MAX_POINTS - 1, gx + 8, gx + gw - 8);
                          float y = map(histTempC[idx], minT, maxT, gy + gh - 4, gy + 4);
                          vertex(x, y);
                        }
                        endShape();

                        // Humidity line dashed
                        stroke(TEAL);
                        strokeWeight(1.5);
                        for (int i = 0; i < points - 1; i++) {
                          if (i % 3 == 0) {
                            int idx0 = graphFull ? (histIndex + i)     % MAX_POINTS : i;
                            int idx1 = graphFull ? (histIndex + i + 1) % MAX_POINTS : i + 1;
                            float x0 = map(i,     0, MAX_POINTS - 1, gx + 8, gx + gw - 8);
                            float x1 = map(i + 1, 0, MAX_POINTS - 1, gx + 8, gx + gw - 8);
                            float y0 = map(histHumid[idx0], minH, maxH, gy + gh - 4, gy + 4);
                            float y1 = map(histHumid[idx1], minH, maxH, gy + gh - 4, gy + 4);
                            line(x0, y0, x1, y1);
                          }
                        }
                      }

                      // Legend
                      noStroke();
                      fill(BLUE);
                      rect(gx + 10, gy + gh + 7, 18, 2, 1);
                      fill(TEXT2);
                      textFont(fontSmall);
                      textSize(10);
                      textAlign(LEFT, CENTER);
                      text("Temp", gx + 32, gy + gh + 8);

                      fill(TEAL);
                      rect(gx + 80, gy + gh + 7, 18, 2, 1);
                      fill(TEXT2);
                      text("Humidity", gx + 102, gy + gh + 8);

                      fill(TEXT3);
                      textAlign(RIGHT, CENTER);
                      text("last 60s", gx + gw - 8, gy + gh + 8);
                    }

                    // ── Mode buttons ─────────────────────────
                    void drawModeButtons() {
                      textFont(fontSmall);
                      fill(TEXT3);
                      textSize(10);
                      textAlign(LEFT, TOP);
                      text("SELECT MODE", PAD, GRID_Y - 18);

                      String[] labels   = {"Hello Fab Lab", "Humidity", "Celsius", "Fahrenheit"};
                      String[] sublbls  = {"Welcome screen", "Rel. humidity", "Temperature", "Temperature"};
                      String[] icons    = {"*", "%", "C", "F"};

                      hoveredBtn = -1;
                      for (int i = 0; i < 4; i++) {
                        int col = i % 2;
                        int row = i / 2;
                        int x   = GRID_X + col * (CARD_W + 12);
                        int y   = GRID_Y + row * (CARD_H + 10);

                        boolean hov = (mouseX >= x && mouseX <= x + CARD_W &&
                                      mouseY >= y && mouseY <= y + CARD_H);
                        boolean act = (activeBtn == i);
                        if (hov) { hoveredBtn = i; cursor(HAND); }

                        drawCard(x, y, CARD_W, CARD_H, i,
                                labels[i], sublbls[i], icons[i], hov, act);
                      }
                      if (hoveredBtn == -1 && !swHovered) cursor(ARROW);
                    }

                    void drawCard(int x, int y, int w, int h, int i,
                                  String lbl, String sub, String icon,
                                  boolean hov, boolean act) {
                      color base = (hov || act) ? CLIGHTS[i] : CBASES[i];
                      color dark = CDARKS[i];

                      noStroke();
                      fill(base);
                      rect(x, y, w, h, 10);

                      fill(255, act ? 55 : hov ? 40 : 20);
                      rect(x, y, w, h / 3, 10, 10, 0, 0);

                      if (act) {
                        fill(255, 100);
                        rect(x + 18, y + h - 5, w - 36, 3, 2);
                      }

                      fill(dark);
                      ellipse(x + 20, y + 20, 24, 24);
                      textFont(fontSmall);
                      fill(255, 180);
                      textSize(10);
                      textAlign(CENTER, CENTER);
                      text(str(i), x + 20, y + 20);

                      textFont(fontBold);
                      fill(255, act ? 255 : 200);
                      textSize(16);
                      textAlign(RIGHT, TOP);
                      text(icon, x + w - 12, y + 8);

                      textFont(fontBold);
                      fill(255);
                      textSize(13);
                      textAlign(LEFT, TOP);
                      text(lbl, x + 10, y + 48);

                      textFont(fontRegular);
                      fill(255, 150);
                      textSize(10);
                      text(sub, x + 10, y + 66);
                    }

                    // ── Servo section ────────────────────────
                    void drawServoSection() {
                      textFont(fontSmall);
                      fill(TEXT3);
                      textSize(10);
                      textAlign(LEFT, TOP);
                      text("SERVO CONTROL", PAD, SW_Y - 18);

                      fill(PANEL);
                      noStroke();
                      rect(PAD, SW_Y - 2, W - PAD * 2, 60, 10);

                      // Label
                      textFont(fontBold);
                      fill(TEXT1);
                      textSize(13);
                      textAlign(LEFT, CENTER);
                      text("SG90 Servo", PAD + 14, SW_Y + 16);

                      // Status text
                      textFont(fontRegular);
                      textSize(11);
                      fill(servoOn ? TEAL : TEXT3);
                      text(servoOn ? "RUNNING" : "STOPPED", PAD + 14, SW_Y + 36);

                      // Toggle switch
                      int tx = W - PAD - SW_W - 14;
                      int ty = SW_Y + 14;
                      swHovered = (mouseX >= tx - 6 && mouseX <= tx + SW_W + 6 &&
                                  mouseY >= ty - 6 && mouseY <= ty + SW_H + 6);
                      if (swHovered) cursor(HAND);

                      drawToggle(tx, ty, SW_W, SW_H, servoOn, swHovered);
                    }

                    void drawToggle(int x, int y, int w, int h, boolean on, boolean hov) {
                      int r = h / 2;
                      color track = on ? (hov ? color(29,158,117) : color(15,110,86))
                                      : (hov ? color(70,70,80)   : color(50,50,60));
                      noStroke();
                      fill(track);
                      rect(x, y, w, h, r);

                      fill(255, 18);
                      rect(x, y, w, h / 2, r, r, 0, 0);

                      textFont(fontSmall);
                      textSize(9);
                      fill(255, on ? 180 : 50);
                      textAlign(LEFT, CENTER);
                      text("ON", x + 9, y + h / 2);
                      fill(255, on ? 50 : 180);
                      textAlign(RIGHT, CENTER);
                      text("OFF", x + w - 9, y + h / 2);

                      int kd = h - 6;
                      int kx = on ? x + w - kd - 3 : x + 3;
                      int ky = y + 3;
                      fill(235);
                      ellipse(kx + kd / 2, ky + kd / 2, kd, kd);
                      fill(255, 160);
                      ellipse(kx + kd / 2 - 2, ky + kd / 2 - 3, kd / 3, kd / 3);
                    }

                    // ── Serial box ───────────────────────────
                    void drawSerialBox() {
                      int bx = PAD;
                      int by = SW_Y + 72;
                      int bw = W - PAD * 2;
                      int bh = 56;

                      fill(PANEL);
                      noStroke();
                      rect(bx, by, bw, bh, 10);

                      color accent = activeBtn >= 0 ? CBASES[activeBtn] : color(80);
                      fill(accent);
                      rect(bx, by, 4, bh, 10, 0, 0, 10);

                      textFont(fontSmall);
                      fill(TEXT3);
                      textSize(10);
                      textAlign(LEFT, TOP);
                      text("SERIAL OUTPUT", bx + 14, by + 10);

                      textFont(fontRegular);
                      fill(TEXT1);
                      textSize(12);
                      text(serialMsg, bx + 14, by + 28);
                    }

                    // ── Pill ─────────────────────────────────
                    void drawPill(int cx, int cy, String lbl, color c) {
                      textFont(fontSmall);
                      textSize(11);
                      float tw = textWidth(lbl);
                      int pw = (int)tw + 24;
                      int ph = 20;
                      noStroke();
                      fill(red(c), green(c), blue(c), 55);
                      rect(cx - pw/2, cy - ph/2, pw, ph, ph/2);
                      fill(c);
                      rect(cx - pw/2 + 1, cy - ph/2 + 1, pw - 2, ph - 2, ph/2);
                      fill(255);
                      textAlign(CENTER, CENTER);
                      text(lbl, cx, cy);
                    }

                    // ── Mouse ────────────────────────────────
                    void mousePressed() {
                      // Mode buttons
                      for (int i = 0; i < 4; i++) {
                        int col = i % 2;
                        int row = i / 2;
                        int x   = GRID_X + col * (CARD_W + 12);
                        int y   = GRID_Y + row * (CARD_H + 10);
                        if (mouseX >= x && mouseX <= x + CARD_W &&
                            mouseY >= y && mouseY <= y + CARD_H) {
                          activeBtn = i;
                          sendCmd(str(i));
                          switch (i) {
                            case 0: serialMsg = "Sent 0  ->  Hello Fab Lab";   break;
                            case 1: serialMsg = "Sent 1  ->  Humidity";        break;
                            case 2: serialMsg = "Sent 2  ->  Temperature (C)"; break;
                            case 3: serialMsg = "Sent 3  ->  Temperature (F)"; break;
                          }
                          return;
                        }
                      }

                      // Toggle switch
                      int tx = W - PAD - SW_W - 14;
                      int ty = SW_Y + 14;
                      if (mouseX >= tx - 6 && mouseX <= tx + SW_W + 6 &&
                          mouseY >= ty - 6 && mouseY <= ty + SW_H + 6) {
                        servoOn = !servoOn;
                        if (servoOn) {
                          sendCmd("6");
                          serialMsg = "Sent 6  ->  Servo ON";
                        } else {
                          sendCmd("5");
                          serialMsg = "Sent 5  ->  Servo OFF";
                        }
                      }
                    }

                    void sendCmd(String cmd) {
                      if (myPort != null) myPort.write(cmd);
                      println("Sent: " + cmd);
                    }

                    // ── Serial event ─────────────────────────
                    void serialEvent(Serial p) {
                      String raw = p.readStringUntil('\n');
                      if (raw == null) return;
                      raw = trim(raw);
                      if (raw.length() == 0) return;

                      println("ESP32 >> " + raw);
                      serialMsg = raw;

                      // Parse "Humidity: 58.2 %" 
                      if (raw.startsWith("Humidity:")) {
                        String[] t = splitTokens(raw, ": %");
                        if (t.length >= 2) {
                          humidity = float(t[1]);
                          hasData  = true;
                          addHistory();
                        }
                      }

                      // Parse "Temperature: 26.4 C" or "Temperature: 79.5 F"
                      if (raw.startsWith("Temperature:")) {
                        String[] t = splitTokens(raw, ": ");
                        if (t.length >= 3) {
                          float val = float(t[1]);
                          if (t[2].equals("C")) {
                            tempC = val;
                            tempF = val * 9.0 / 5.0 + 32.0;
                            hasData = true;
                            addHistory();
                          } else if (t[2].equals("F")) {
                            tempF = val;
                            tempC = (val - 32.0) * 5.0 / 9.0;
                            hasData = true;
                            addHistory();
                          }
                        }
                      }
                    }

                    void addHistory() {
                      histTempC[histIndex] = tempC;
                      histHumid[histIndex] = humidity;
                      histIndex++;
                      if (histIndex >= MAX_POINTS) {
                        histIndex = 0;
                        graphFull = true;
                      }
                    }
                
            
Running 4
Add 8. Open - servo 180° - 9. Close - servo 0°
Running 4
Screenshot of Processing with Add 8. Open - servo 180° - 9. Close - servo 0°
                  
        // Arduino code for a DHT11, 1.3 OLED IIC, Micro Servo 9 gram, and ON/OFF switch monitor
        // Open - servo 180° and close - servo 0° with a custom UI
                  #include 
                  #include 
                  #include 
                  #include 

                  #define DHTPIN    D2
                  #define DHTTYPE   DHT11
                  #define SERVO_PIN D9

                  DHT dht(DHTPIN, DHTTYPE);
                  Servo myServo;

                  U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
                  // U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

                  int  currentMode  = 0;
                  int  servoAngle   = 0;
                  bool sweeping     = true;
                  bool servoEnabled = false;

                  unsigned long lastSensorRead = 0;
                  unsigned long lastServoMove  = 0;
                  const unsigned long SENSOR_INTERVAL = 2000;
                  const unsigned long SERVO_INTERVAL  = 15;

                  // ──────────────────────────────────────────
                  // OLED display functions
                  // ──────────────────────────────────────────

                  void showFabLab() {
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB14_tr);
                    u8g2.drawStr(18, 24, "Hello");
                    u8g2.setFont(u8g2_font_ncenB18_tr);
                    u8g2.drawStr(8, 54, "Fab Lab!");
                    u8g2.sendBuffer();
                  }

                  void showHumidity(float h) {
                    char valStr[10];
                    dtostrf(h, 4, 1, valStr);
                    strcat(valStr, " %");
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB10_tr);
                    u8g2.drawStr(28, 14, "Humidity");
                    u8g2.drawHLine(0, 18, 128);
                    u8g2.setFont(u8g2_font_ncenB24_tr);
                    u8g2.drawStr(14, 52, valStr);
                    u8g2.sendBuffer();
                  }

                  void showTempC(float t) {
                    char valStr[10];
                    dtostrf(t, 4, 1, valStr);
                    strcat(valStr, " C");
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB10_tr);
                    u8g2.drawStr(18, 14, "Temperature");
                    u8g2.drawHLine(0, 18, 128);
                    u8g2.setFont(u8g2_font_ncenB08_tr);
                    u8g2.drawStr(44, 32, "Celsius");
                    u8g2.setFont(u8g2_font_ncenB24_tr);
                    u8g2.drawStr(14, 62, valStr);
                    u8g2.sendBuffer();
                  }

                  void showTempF(float t) {
                    float f = (t * 9.0 / 5.0) + 32.0;
                    char valStr[10];
                    dtostrf(f, 4, 1, valStr);
                    strcat(valStr, " F");
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB10_tr);
                    u8g2.drawStr(18, 14, "Temperature");
                    u8g2.drawHLine(0, 18, 128);
                    u8g2.setFont(u8g2_font_ncenB08_tr);
                    u8g2.drawStr(30, 32, "Fahrenheit");
                    u8g2.setFont(u8g2_font_ncenB24_tr);
                    u8g2.drawStr(14, 62, valStr);
                    u8g2.sendBuffer();
                  }

                  void showServoOff() {
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB10_tr);
                    u8g2.drawStr(28, 14, "Servo Status");
                    u8g2.drawHLine(0, 18, 128);
                    u8g2.setFont(u8g2_font_ncenB18_tr);
                    u8g2.drawStr(22, 48, "STOPPED");
                    u8g2.sendBuffer();
                  }

                  void showServoOn() {
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB10_tr);
                    u8g2.drawStr(28, 14, "Servo Status");
                    u8g2.drawHLine(0, 18, 128);
                    u8g2.setFont(u8g2_font_ncenB18_tr);
                    u8g2.drawStr(18, 48, "RUNNING");
                    u8g2.sendBuffer();
                  }

                  void showServoAngle(int angle) {
                    u8g2.clearBuffer();
                    u8g2.setFont(u8g2_font_ncenB10_tr);
                    u8g2.drawStr(22, 14, "Servo Position");
                    u8g2.drawHLine(0, 18, 128);
                    char angStr[10];
                    sprintf(angStr, "%d deg", angle);
                    u8g2.setFont(u8g2_font_ncenB18_tr);
                    u8g2.drawStr(10, 48, angStr);
                    u8g2.sendBuffer();
                  }

                  // ──────────────────────────────────────────
                  // Sensor read + display
                  // ──────────────────────────────────────────

                  void readAndDisplay() {
                    float humidity    = dht.readHumidity();
                    float temperature = dht.readTemperature();

                    if (isnan(humidity) || isnan(temperature)) {
                      Serial.println("DHT11 read error!");
                      u8g2.clearBuffer();
                      u8g2.setFont(u8g2_font_ncenB08_tr);
                      u8g2.drawStr(10, 35, "Sensor error!");
                      u8g2.sendBuffer();
                      return;
                    }

                    if (currentMode == 1) {
                      showHumidity(humidity);
                      Serial.print("Humidity: ");
                      Serial.print(humidity, 1);
                      Serial.println(" %");
                    } else if (currentMode == 2) {
                      showTempC(temperature);
                      Serial.print("Temperature: ");
                      Serial.print(temperature, 1);
                      Serial.println(" C");
                    } else if (currentMode == 3) {
                      showTempF(temperature);
                      float f = (temperature * 9.0 / 5.0) + 32.0;
                      Serial.print("Temperature: ");
                      Serial.print(f, 1);
                      Serial.println(" F");
                    }
                  }

                  // ──────────────────────────────────────────
                  // Servo continuous sweep
                  // ──────────────────────────────────────────

                  void updateServo() {
                    if (!servoEnabled) return;
                    if (millis() - lastServoMove < SERVO_INTERVAL) return;
                    lastServoMove = millis();

                    myServo.write(servoAngle);

                    if (sweeping) {
                      servoAngle++;
                      if (servoAngle >= 180) sweeping = false;
                    } else {
                      servoAngle--;
                      if (servoAngle <= 0) sweeping = true;
                    }
                  }

                  // ──────────────────────────────────────────
                  // Servo move to target and stop
                  // ──────────────────────────────────────────

                  void moveServoTo(int target) {
                    servoEnabled = false;

                    if (!myServo.attached()) {
                      myServo.attach(SERVO_PIN, 500, 2400);
                    }

                    int step = (target > servoAngle) ? 1 : -1;

                    while (servoAngle != target) {
                      servoAngle += step;
                      myServo.write(servoAngle);
                      if (servoAngle % 10 == 0) {
                        showServoAngle(servoAngle);
                      }
                      delay(15);
                    }

                    showServoAngle(servoAngle);
                    Serial.print("Servo at: ");
                    Serial.print(servoAngle);
                    Serial.println(" deg -- stopped.");

                    delay(400);
                    myServo.detach();
                  }

                  // ──────────────────────────────────────────
                  // Setup
                  // ──────────────────────────────────────────

                  void setup() {
                    Serial.begin(115200);
                    dht.begin();
                    u8g2.begin();

                    myServo.setPeriodHertz(50);
                    myServo.attach(SERVO_PIN, 500, 2400);
                    myServo.write(0);
                    servoAngle   = 0;
                    sweeping     = true;
                    servoEnabled = false;
                    delay(300);
                    myServo.detach();

                    showFabLab();
                    Serial.println("DHT11 Monitor ready.");
                    Serial.println("0=FabLab | 1=Humidity | 2=Temp C | 3=Temp F");
                    Serial.println("5=Servo OFF | 6=Servo ON sweep");
                    Serial.println("8=Open-Move to 180 and stop | 9=Close-Move to 0 and stop");
                  }

                  // ──────────────────────────────────────────
                  // Loop
                  // ──────────────────────────────────────────

                  void loop() {
                    updateServo();

                    if (Serial.available() > 0) {
                      char input = Serial.read();

                      if (input == '0') {
                        currentMode = 0;
                        showFabLab();
                        Serial.println("Mode: Hello Fab Lab");

                      } else if (input == '1') {
                        currentMode = 1;
                        Serial.println("Mode: Humidity");
                        readAndDisplay();
                        lastSensorRead = millis();

                      } else if (input == '2') {
                        currentMode = 2;
                        Serial.println("Mode: Temperature (Celsius)");
                        readAndDisplay();
                        lastSensorRead = millis();

                      } else if (input == '3') {
                        currentMode = 3;
                        Serial.println("Mode: Temperature (Fahrenheit)");
                        readAndDisplay();
                        lastSensorRead = millis();

                      } else if (input == '5') {
                        servoEnabled = false;
                        if (myServo.attached()) {
                          myServo.write(0);
                          delay(500);
                          myServo.detach();
                        }
                        servoAngle = 0;
                        sweeping   = true;
                        showServoOff();
                        Serial.println("Servo: OFF");

                      } else if (input == '6') {
                        servoEnabled = true;
                        sweeping     = true;
                        myServo.attach(SERVO_PIN, 500, 2400);
                        myServo.write(servoAngle);
                        delay(300);
                        showServoOn();
                        Serial.println("Servo: ON -- sweeping");

                      } else if (input == '8') {
                        Serial.println("Open-Moving to 180 deg...");
                        moveServoTo(180);

                      } else if (input == '9') {
                        Serial.println("Close-Moving to 0 deg...");
                        moveServoTo(0);

                      } else if (input != '\n' && input != '\r') {
                        Serial.println("Invalid. Send 0,1,2,3,5,6,8 or 9.");
                      }
                    }

                    // Auto-refresh sensor display every 2 seconds
                    if (currentMode >= 1 && currentMode <= 3 &&
                        millis() - lastSensorRead >= SENSOR_INTERVAL) {
                      lastSensorRead = millis();
                      readAndDisplay();
                    }
                  }
                
        
                  

    // ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    //  Code for Processing with 8-Open - 180° Servo control and 9-Close - 0° Servo Control (Processing 4)
        
                  //  Line formats:
                  //    H:,TC:,TF:
                  //    SERVO:ANGLE:
                  //    SERVO:ON / SERVO:OFF
                  //    MODE:
                  //    ERROR:sensor
                  //    READY
                  // ─────────────────────────────────────────

                  import processing.serial.*;

                  Serial  port;

                  // ── Data ──────────────────────────────────
                  float   humidity  = 0;
                  float   tempC     = 0;
                  float   tempF     = 0;
                  int     servoAngle = 0;
                  float   displayAngle = 0;
                  boolean servoOn   = false;
                  String  mode      = "FabLab";
                  boolean hasData   = false;
                  boolean connected = false;
                  String  portName  = "–";

                  // ── Buttons ───────────────────────────────
                  Button[] buttons;

                  // ── Palette ───────────────────────────────
                  final color BG     = color(18, 18, 20);
                  final color CARD   = color(28, 28, 32);
                  final color BORDER = color(55, 55, 65);
                  final color TEAL   = color(29, 158, 117);
                  final color AMBER  = color(186, 117, 23);
                  final color PURPLE = color(83, 74, 183);
                  final color BLUE   = color(55, 138, 221);
                  final color PINK   = color(212, 83, 126);
                  final color MUTED  = color(130, 130, 140);
                  final color WHITE  = color(230, 228, 222);
                  final color RED    = color(226, 75, 74);
                  final color GREEN  = color(99, 153, 34);

                  PFont mono;
                  PFont sans;

                  // ──────────────────────────────────────────
                  void setup() {
                    size(680, 580);
                    smooth(4);
                    mono = createFont("Monospaced", 12);
                    sans = createFont("SansSerif",  13);

                    // Define all 8 buttons: label, command char, color, x, y, w, h
                    buttons = new Button[] {
                      new Button("FabLab",       '0', PURPLE, 20,  468, 138, 44),
                      new Button("Humidity",     '1', BLUE,   172, 468, 138, 44),
                      new Button("Temp \u00b0C", '2', TEAL,   324, 468, 138, 44),
                      new Button("Temp \u00b0F", '3', TEAL,   476, 468, 138, 44),
                      new Button("Servo sweep",  '4', AMBER,  20,  524, 138, 44),
                      new Button("Servo stop",   '5', MUTED,  172, 524, 138, 44),
                      new Button("Open 180\u00b0",'6', PINK,  324, 524, 138, 44),
                      new Button("Close 0\u00b0", '7', PINK,  476, 524, 138, 44),
                    };

                    println("Available ports:");
                    printArray(Serial.list());

                    // ⚠ Change [0] if your board is on a different port
                    // Mac:  look for /dev/cu.usbmodem...
                    // Win:  look for COM3 / COM4 etc.
                    try {
                      portName = Serial.list()[0];
                      port = new Serial(this, portName, 115200);
                      port.bufferUntil('\n');
                      connected = true;
                    }
                    catch (Exception e) {
                      connected  = false;
                      portName   = "No port — demo mode";
                      // Seed demo values so UI is visible without a board
                      humidity   = 58.0; tempC = 24.5; tempF = 76.1;
                      servoAngle = 45;   hasData = true;
                    }
                  }

                  // ── Serial ────────────────────────────────
                  void serialEvent(Serial p) {
                    String raw = trim(p.readStringUntil('\n'));
                    if (raw == null || raw.length() == 0) return;

                    if (raw.startsWith("H:")) {
                      String[] pts = split(raw, ',');
                      try {
                        humidity = float(pts[0].substring(2));
                        tempC    = float(pts[1].substring(3));
                        tempF    = float(pts[2].substring(3));
                        hasData  = true;
                      } catch (Exception e) {}

                    } else if (raw.startsWith("SERVO:ANGLE:")) {
                      servoAngle = int(raw.substring(12));

                    } else if (raw.equals("SERVO:ON")) {
                      servoOn = true;

                    } else if (raw.equals("SERVO:OFF")) {
                      servoOn = false;

                    } else if (raw.startsWith("MODE:")) {
                      mode = raw.substring(5);
                    }
                  }

                  // ── Send command ──────────────────────────
                  void sendCmd(char c) {
                    if (connected && port != null) {
                      port.write(c);
                    } else {
                      // Demo simulation — no board needed
                      switch(c) {
                        case '0': mode = "FabLab"; break;
                        case '1': humidity = random(35,85); hasData = true; mode = "Humidity"; break;
                        case '2': tempC = random(18,38); tempF = tempC*9/5+32; hasData = true; mode = "TempC"; break;
                        case '3': tempC = random(18,38); tempF = tempC*9/5+32; hasData = true; mode = "TempF"; break;
                        case '4': servoOn = true;  mode = "Servo"; break;
                        case '5': servoOn = false; servoAngle = 0; mode = "Servo"; break;
                        case '6': servoAngle = 180; servoOn = false; mode = "Servo"; break;
                        case '7': servoAngle = 0;   servoOn = false; mode = "Servo"; break;
                      }
                    }
                  }

                  // ── Click ─────────────────────────────────
                  void mousePressed() {
                    for (Button b : buttons) {
                      if (b.contains(mouseX, mouseY)) {
                        b.pressed = true;
                        sendCmd(b.cmd);
                      }
                    }
                  }

                  void mouseReleased() {
                    for (Button b : buttons) b.pressed = false;
                  }

                  // ── Draw ──────────────────────────────────
                  void draw() {
                    background(BG);
                    displayAngle = lerp(displayAngle, servoAngle, 0.1);

                    drawHeader();
                    drawSensorCards();
                    drawServoCard();
                    drawOledCard();
                    drawButtons();
                  }

                  // ── Header ────────────────────────────────
                  void drawHeader() {
                    fill(CARD); noStroke();
                    rect(0, 0, width, 56);
                    fill(TEAL); rect(0, 0, 5, 56);

                    fill(WHITE); textSize(18); textAlign(LEFT, CENTER);
                    text("Fab Lab — DHT11 + Servo + OLED", 20, 28);

                    fill(connected ? GREEN : AMBER);
                    noStroke(); ellipse(width - 18, 28, 10, 10);
                    fill(MUTED); textSize(10); textAlign(RIGHT, CENTER);
                    text(portName, width - 32, 28);
                    textAlign(LEFT, TOP);
                  }

                  // ── Card base ──────────────────────────────
                  void cardBase(int x, int y, int w, int h, color accent) {
                    fill(CARD); stroke(BORDER); strokeWeight(0.5);
                    rect(x, y, w, h, 10);
                    fill(accent); noStroke();
                    rect(x, y + 8, 4, h - 16, 2);
                  }

                  void cardTitle(int x, int y, String label, color c) {
                    fill(c); textFont(mono); textSize(10); textAlign(LEFT, TOP);
                    text(label.toUpperCase(), x, y);
                  }

                  // ── Sensor cards ──────────────────────────
                  void drawSensorCards() {
                    drawValueCard(22,  102, 188, 180, "Humidity",
                      hasData ? nf(humidity,1,1) : "--", "%", BLUE,
                      humidity > 70 ? "HIGH" : (humidity < 30 ? "DRY" : "OK"),
                      humidity > 70 ? RED : (humidity < 30 ? AMBER : GREEN));

                    drawValueCard(222, 102, 188, 180, "Temperature",
                      hasData ? nf(tempC,1,1) : "--", "\u00b0C", TEAL,
                      tempC > 35 ? "HOT" : (tempC < 15 ? "COLD" : "OK"),
                      tempC > 35 ? RED : (tempC < 15 ? BLUE : GREEN));

                    drawValueCard(422, 102, 232, 180, "Temperature",
                      hasData ? nf(tempF,1,1) : "--", "\u00b0F", PURPLE,
                      tempF > 95 ? "HOT" : (tempF < 59 ? "COLD" : "OK"),
                      tempF > 95 ? RED : (tempF < 59 ? BLUE : GREEN));
                  }

                  void drawValueCard(int x, int y, int w, int h,
                                    String label, String val, String unit,
                                    color accent, String status, color statusCol) {
                    fill(CARD); stroke(BORDER); strokeWeight(0.5);
                    rect(x, y, w, h, 10);
                    fill(accent); noStroke();
                    rect(x, y+8, 4, h-16, 2);

                    fill(MUTED); textSize(10); textAlign(CENTER, TOP);
                    text(label, x+w/2, y+12);

                    fill(WHITE); textSize(40); textAlign(CENTER, CENTER);
                    text(val, x+w/2, y+h/2 - 8);

                    fill(accent); textSize(13); textAlign(CENTER, TOP);
                    text(unit, x+w/2, y+h/2+26);

                    fill(statusCol); noStroke();
                    rect(x+w/2-28, y+h-26, 56, 18, 9);
                    fill(BG); textSize(10); textAlign(CENTER, CENTER);
                    text(status, x+w/2, y+h-17);
                    textAlign(LEFT, TOP);
                  }

                  // ── Servo card ────────────────────────────
                  void drawServoCard() {
                    int x = 22, y = 294, w = 300, h = 160;
                    fill(CARD); stroke(BORDER); strokeWeight(0.5);
                    rect(x, y, w, h, 10);
                    fill(AMBER); noStroke();
                    rect(x, y+8, 4, h-16, 2);

                    fill(MUTED); textSize(10); textAlign(CENTER, TOP);
                    text("Servo — D9", x+w/2, y+12);

                    int cx = x+w/2, cy = y+118, r = 60;

                    // Track arc
                    stroke(BORDER); strokeWeight(2); noFill();
                    arc(cx, cy, r*2, r*2, PI, TWO_PI);

                    // Progress arc
                    stroke(AMBER); strokeWeight(4);
                    float endRad = map(displayAngle, 0, 180, PI, TWO_PI);
                    arc(cx, cy, r*2, r*2, PI, endRad);

                    // Needle
                    float rad = map(displayAngle, 0, 180, PI, TWO_PI);
                    stroke(WHITE); strokeWeight(2.5);
                    line(cx, cy, cx+cos(rad)*r, cy+sin(rad)*r);
                    fill(AMBER); noStroke(); ellipse(cx, cy, 9, 9);

                    // Angle labels
                    fill(MUTED); textSize(9); textAlign(CENTER, TOP);
                    text("0\u00b0",   cx-r-4, cy+6);
                    text("180\u00b0", cx+r-8, cy+6);

                    // Degree readout
                    fill(WHITE); textFont(mono); textSize(22); textAlign(CENTER, TOP);
                    text(int(displayAngle)+"\u00b0", cx, cy+10);

                    // Status pill
                    color pc = servoOn ? GREEN : MUTED;
                    fill(pc); noStroke();
                    rect(cx-38, y+h-28, 76, 20, 10);
                    fill(BG); textSize(10); textAlign(CENTER, CENTER);
                    text(servoOn ? "SWEEPING" : "STOPPED", cx, y+h-18);
                    textAlign(LEFT, TOP);
                  }

                  // ── OLED preview ──────────────────────────
                  void drawOledCard() {
                    int x = 334, y = 294, w = 320, h = 160;
                    fill(CARD); stroke(BORDER); strokeWeight(0.5);
                    rect(x, y, w, h, 10);
                    fill(PURPLE); noStroke();
                    rect(x, y+8, 4, h-16, 2);

                    fill(MUTED); textSize(10); textAlign(CENTER, TOP);
                    text("OLED display preview", x+w/2, y+12);

                    // Screen
                    fill(8); stroke(PURPLE); strokeWeight(1);
                    rect(x+20, y+30, w-40, 76, 4);
                    noStroke();

                    // Screen content
                    fill(255); textFont(mono); textAlign(LEFT, TOP);
                    if (mode.equals("FabLab")) {
                      textSize(12); text("Hello",    x+34, y+40);
                      textSize(16); text("Fab Lab!", x+28, y+60);
                    } else if (mode.equals("Humidity")) {
                      textSize(9);  text("Humidity",             x+52, y+34);
                      textSize(18); text(nf(humidity,1,1)+" %",  x+28, y+58);
                    } else if (mode.equals("TempC")) {
                      textSize(9);  text("Temperature · Celsius",   x+28, y+34);
                      textSize(18); text(nf(tempC,1,1)+" C",        x+28, y+56);
                    } else if (mode.equals("TempF")) {
                      textSize(9);  text("Temperature · Fahrenheit", x+22, y+34);
                      textSize(18); text(nf(tempF,1,1)+" F",         x+28, y+56);
                    } else if (mode.equals("Servo")) {
                      textSize(9);  text("Servo Status",             x+38, y+34);
                      textSize(16); text(servoOn ? "RUNNING" : "STOPPED", x+26, y+58);
                    }

                    // Mode badge
                    fill(PURPLE); noStroke();
                    rect(x+20, y+114, w-40, 18, 6);
                    fill(BG); textSize(9); textAlign(CENTER, CENTER);
                    text("MODE: " + mode.toUpperCase(), x+w/2, y+123);
                    textAlign(LEFT, TOP);
                  }

                  // ── Button row ────────────────────────────
                  void drawButtons() {
                    fill(MUTED); textFont(mono); textSize(10); textAlign(LEFT, CENTER);
                    text("CONTROLS", 22, 458);
                    for (Button b : buttons) b.draw();
                  }

                  // ── Button class ──────────────────────────
                  class Button {
                    String  label;
                    char    cmd;
                    color   col;
                    int     x, y, w, h;
                    boolean pressed = false;

                    Button(String label, char cmd, color col, int x, int y, int w, int h) {
                      this.label = label; this.cmd = cmd; this.col = col;
                      this.x = x; this.y = y; this.w = w; this.h = h;
                    }

                    boolean contains(int mx, int my) {
                      return mx >= x && mx <= x+w && my >= y && my <= y+h;
                    }

                    void draw() {
                      color fc = pressed ? lerpColor(col, WHITE, 0.3) : col;
                      fill(fc); noStroke();
                      rect(x, y, w, h, 8);
                      fill(0, 40); noStroke();           // bottom depth edge
                      rect(x, y+h-6, w, 6, 0, 0, 8, 8);
                      fill(BG); textFont(mono); textSize(12); textAlign(CENTER, CENTER);
                      text(label, x+w/2, y+h/2);
                      textAlign(LEFT, TOP);
                    }
                  }
                
        
Video demonstration

4.2) Additional individual assignment

Considerations:

- Purpose and functionality of the application

- UI and user interaction flow

- Communication between the application and the embedded board

- Block diagram with system architecture and data flow

- Communication protocol used

- Important parts of application and embedded code

- Wiring/connections between board and peripherals

- Real-time interaction, testing results, and final output

- Debugging process, challenges faced, and solutions

- Final reflection, learning outcomes, and future improvements

Camera 1
1. Purpose and functionality: This application connects a processing UI running on a laptop to a XIAO ESP32-C3 board via USB serial. It lets the user monitor live temperature and humidity from a DHT11 sensor, view the data on a 1.3" OLED display, and control an SG90 servo motor — all from a single dashboard
Camera 2
2. Screenshots + interaction flow: this picture shows the UI. When the user click a button, ESCP 32 C3 reads it and changes mode, the OLED and the serial monitor update the information
Camera 1
3. Communication between the application and the embedded board: Processing opens COM6 at 115200 baud, sends a single ASCII character ('0'–'3', '5', '6', '8', '9'), the ESP32 reads it with Serial.read() and responds with a line like "Humidity: 57.2 %". Processing parses that line in serialEvent()
Camera 2
4.Communication protocol: Communication uses UART serial at 115200 baud over USB. The protocol is simple ASCII: the UI sends one character, the board responds with the sensor data
Camera 2
5.Wiring/connections: Sreenshot of Arduino Ide code: DHT11 data- D2 - 10kΩ pull-up to 3.3V; OLED SDA - I²C shared bus; OLED SCL- I²C shared bus; SG90 signal- D9P- WM, 500–2400µs pulse
Camera 2
5.Wiring/connections: DHT11 data- D2; OLED SDA- D4; OLED SCL- D5; SG90 - D9
6. Real time interaction:
Camera 2
7.Debugging & Challenges: I had several issues with the communication protocols. I solved it connecting device by device; and debugging the code and testing
Camera 2
8.Reflection & future improvements: I learned different tools of UI design. I love this assigment because I can design interfaces, advance step by step and generate human & device interactions

5) Final project advances

Devices

3D Print body boxes

Review the prototype and considering sharp dimensions

Review acrilic windows

Review waterproof boxes

Camera 1
3D Printed boxes and seals
Camera 2
Box details as tolarence, sharp dimensions, screws and others
Camera 1
Transparent vynil window details
Camera 2
Right and left boxes
Final project page

6) Final results

  • Linked to the group assignment page
  • Documented your process
  • Explained the UI that you made and how you did it
  • Expalined hoy your application communicates with your embedded microcontroller board
  • Explained any problems you encountered and how you solved them
  • Include original source code (or a screenshot of the app code if that's not poosible)
  • Include a hero shot of your application running & communicating with your board

7) References files

We learn how to Interface and Application Programming

Sections