Week 2: Embedded Programming

Idea

Build a Lock State Device

Living in New House Dorm is nice, as you get the option os using single-stall bathrooms - the closest thing to living in an apartment. But because it is single-stall, it is always occupied in rush hours. So, I wanted to know, from the comfort of my room, whether the restroom is occupied or not using a light transistor that senses the lock position.

Circuit

It is a simple circuit with two phototransistors. One to sense whether the bathroom light itself is open, as the light closes automatically. Another to sense the lock position. There is also some LEDs for visual feedback.
There are two phototransistors, one for the room and one for the lock. The restroom light automatically turns on using a motion sensor. There is one case when the restroom is occupied, which is when the restroom light is ON and the lock is ON. The phototransistors then both should pick up plenty of light because of the setup of the device.

Programming

It includes multiple of components, which is to monitor the input (light transistors) and control the output (LEDs) and to communicate through WiFi. I tried two chips, Seeed XIAO RP2040 and Raspberry Pi Pico 2040.

Seeed XIAO RP2040

Tutorial


Datasheet


Schematic


Then, I switch back to C and used the RGB feature of the board to create a Rainbow Fade Transition.

ON/OFF LEDs

import time
from machine import Pin

# Define the pins for the LEDs
red_led_pin = Pin(17, Pin.OUT)
green_led_pin = Pin(16, Pin.OUT)
blue_led_pin = Pin(25, Pin.OUT)

# Function to turn on the LEDs
def turn_on_leds():
    red_led_pin.value(1)    # Turn on the red LED
    green_led_pin.value(1)  # Turn on the green LED
    blue_led_pin.value(1)   # Turn on the blue LED

# Function to turn off the LEDs
def turn_off_leds():
    red_led_pin.value(0)    # Turn off the red LED
    green_led_pin.value(0)  # Turn off the green LED
    blue_led_pin.value(0)   # Turn off the blue LED

# Main loop
while True:
    turn_on_leds()
    time.sleep(1)  # LEDs stay on for 1 second
    turn_off_leds()
    time.sleep(1)  # LEDs stay off for 1 second
                    

First, I explored using MicroPython to code the device using a simple blinking LED example.

Rainbow Fade RGB

#include <Adafruit_NeoPixel.h>

int Power = 11;
int PIN  = 12;
#define NUMPIXELS 1

#define VIOLET 0 
#define INDIGO 1
#define BLUE 2 
#define GREEN 3 
#define YELLOW 4 
#define ORANGE 5 
#define RED 6 
#define BLACK 7
#define TRANSITION 10

int color = 0; 
int old_time = 0;
int r = 255; 
int g = 0; 
int b = 0; 

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
    
void setup() 
{
    pixels.begin();
    pinMode(Power,OUTPUT);
    digitalWrite(Power, HIGH);
    Serial.begin(9600); 
    old_time = millis(); 
    color = VIOLET; 
}


void loop() { 
    change_color(); 
}

void change_color() {
    switch(color) {
    case VIOLET:
        if(millis() >= old_time + TRANSITION) {
        old_time = millis(); 
        if(r>148) r = r - 1;
        if(b<211) b = b + 1;
        }
        if(r==148 && g==0 && b==211) {
        color = INDIGO; 
        }
        break; 
    case INDIGO:
        if(millis() >= old_time + TRANSITION) {
            old_time = millis(); 
            if(r>75) r = r - 1; 
            if(b>130) b = b - 1;
        }
        if(r==75 && g==0 && b==130) {
            color = BLUE; 
        }
        break; 
    case BLUE:
        if(millis() >= old_time + TRANSITION) {
            old_time = millis(); 
            if(r>0) r = r - 1; 
            if(b<255) b = b + 1;
        }
        if(r==0 && g==0 && b==255) {
            color = GREEN; 
        }
        break; 
    case GREEN:
        if(millis() >= old_time + TRANSITION) {
            old_time = millis(); 
            if(g<255) g = g + 1; 
            if(b>0) b = b - 1;
        }
        if(r==0 && g==255 && b==0) {
            color = YELLOW; 
        }
        break;
    case YELLOW:
        if(millis() >= old_time + TRANSITION) {
            old_time = millis(); 
            if(r<255) r = r + 1;
        }
        if(r==255 && g==255 && b==0) {
            color = ORANGE; 
        }
        break;
    case ORANGE:
        if(millis() >= old_time + TRANSITION) {
            old_time = millis(); 
            if(g>127) g = g - 1; 
        }
        if(r==255 && g==127 && b==0) {
            color = RED; 
        }
        break;
    case RED:
        if(millis() >= old_time + TRANSITION) {
            old_time = millis(); 
            if(g>0) g = g - 1; 
        }
        if(r==255 && g==0 && b==0) {
            color = VIOLET; 
        }
        break;
    }
    pixels.clear();
    pixels.setPixelColor(0, pixels.Color(r, g, b));
    pixels.show();
}

Raspberry Pi Pico W

As I reached the limitation of Seed Ciao RP2040 in term of communication capabilities, I started learning how to use Raspberry Pi Pico W.

Guide to Connect it to WiFi

Light Sensor Monitor

#include  

#define room_sens_pin 26
#define lock_sens_pin 27

#define RED_LED 5 
#define GREEN_LED 1

#define ON_CUTOFF 8.5

Smoothed <float> roomSens; 
Smoothed <float> lockSens; 


void setup() {
    Serial.begin(9600); 
    pinMode(room_sens_pin, INPUT); 
    pinMode(lock_sens_pin, INPUT);
    pinMode(RED_LED, OUTPUT);
    pinMode(GREEN_LED, OUTPUT); 
    roomSens.begin(SMOOTHED_AVERAGE, 5); 
    lockSens.begin(SMOOTHED_AVERAGE, 5); 
}

void loop() {
    float roomReading = analogRead(room_sens_pin); 
    float lockReading = analogRead(lock_sens_pin); 
    roomSens.add(roomReading); 
    lockSens.add(lockReading);
    roomReading = roomSens.get();
    lockReading = lockSens.get(); 
    Serial.print("Room (up) Sensing"); 
    Serial.println(roomSens.get());
    Serial.print("lock (low) Sensing"); 
    Serial.println(lockSens.get());
    delay(100); 

    if(roomReading > ON_CUTOFF && lockReading > ON_CUTOFF) {
    digitalWrite(RED_LED, HIGH);
    digitalWrite(GREEN_LED, LOW);  
    } else {
    digitalWrite(RED_LED, LOW);
    digitalWrite(GREEN_LED, HIGH); 
    } 
}
                    

The following code is to test WiFi Capabilities. Due to the limited time, I was not able to finish testing and building the HTML to interact with the user and send data. The plan is to use the the Pico W as the server and connect to it using its IP address. In its code, I will add the HTML page that will appear to the user using client.println More things to learn and implement!

WiFi IP Address Printer

#include 
                       
// Replace with your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

void setup() {

// Start the Serial Monitor
Serial.begin(115200);

// Operate in WiFi Station mode
WiFi.mode(WIFI_STA);

// Start WiFi with supplied parameters
WiFi.begin(ssid, password);

// Print periods on monitor while establishing connection
while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    delay(500);
}

// Connection established
Serial.println("");
Serial.print("Pico W is connected to WiFi network ");
Serial.println(WiFi.SSID());

// Print IP Address
Serial.print("Assigned IP Address: ");
Serial.println(WiFi.localIP());

}

void loop() {

delay(2000);

// Print IP Address
Serial.print("Assigned IP Address: ");
Serial.println(WiFi.localIP());

}
                    

WiFi Interactive Page (uncompleted)

#include 
 
// Replace with your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
    
// Set web server port number to 80
WiFiServer server(80);
    
// Variable to store the HTTP request
String header;
    
// Variable to store onboard LED state
String picoLEDState = "off";
    
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
    
void setup() {
    
    // Start Serial Monitor
    Serial.begin(115200);
    
    // Initialize the LED as an output
    pinMode(LED_BUILTIN, OUTPUT);
    
    // Set LED off
    digitalWrite(LED_BUILTIN, LOW);
    
    // Connect to Wi-Fi network with SSID and password
    WiFi.begin(ssid, password);
    
    // Display progress on Serial monitor
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
    
    // Print local IP address and start web server
    Serial.println("");
    Serial.print("WiFi connected at IP Address ");
    Serial.println(WiFi.localIP());
    
    // Start Server
    server.begin();
}
    
void loop() {
    
    WiFiClient client = server.available();   // Listen for incoming clients
    
    if (client) {                             // If a new client connects,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected() && currentTime - previousTime <= timeoutTime) {  // loop while the client's connected
        currentTime = millis();
        if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
            // if the current line is blank, you got two newline characters in a row.
            // that's the end of the client HTTP request, so send a response:
            if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
    
            // Switch the LED on and off
            if (header.indexOf("GET /led/on") >= 0) {
                Serial.println("LED on");
                picoLEDState = "on";
                digitalWrite(LED_BUILTIN, HIGH);
            } else if (header.indexOf("GET /led/off") >= 0) {
                Serial.println("LED off");
                picoLEDState = "off";
                digitalWrite(LED_BUILTIN, LOW);
            }
    
            // Display the HTML web page
            client.println("");
            client.println("");
            client.println("");
    
            // CSS to style the on/off buttons
            client.println("");
    
            // Web Page Heading
            client.println("

Pico W LED Control

"); // Display current state, and ON/OFF buttons for Onboard LED client.println("

Onboard LED is " + picoLEDState + "

"); // Set buttons if (picoLEDState == "off") { //picoLEDState is off, display the ON button client.println("

"); } else { //picoLEDState is on, display the OFF button client.println("

"); } client.println(""); // The HTTP response ends with another blank line client.println(); // Break out of the while loop break; } else { // if you got a newline, then clear currentLine currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } } } // Clear the header variable header = ""; // Close the connection client.stop(); Serial.println("Client disconnected."); Serial.println(""); } }

Micropython vs C/C++

Micropython Documentation

Having used Arduino before as it is widely used made starting to use new microprocessors smoother. All I had to do to install the respective board manager and I am ready to go. For Micropython, I had to install the language first using brew install micropython. Some people in the internet were able to use VSCode to run Micropython using an extension, but that did not work for me. So, I had to install a new IDE, Thonny, to run Micropython. After setting the laptop, you also have to download UF2 firmware on the RP2040 to be able to code on it.

COM vs Drive

I learned that there are two ways to connect to RP2040, which is as a drive and a COM port. You can upload the firmware only when it appears as a drive. To do so, hold on on the Bootload button while connecting. Note that this will erase Micropython firmware on the microprocessor. You can use this to switch between C and Mircopython. Now, you can see it in your file browser as a drive. You can also still upload code to it by Tools/Port/UF2 Board. To connect it as a COM port, plug it normally. You can not upload any firmware in this state, but you can upload your code.

Assignment Description

Individual Assignments:

  1. browse through the datasheet for your microcontroller
  2. write a program for a microcontroller development board to interact (with local input &/or output) and communicate (remotely)
  3. extra credit: use different languages &/or development environments
  4. extra credit: connect external components to the board

Group Assignment:

  1. compare the performance and development workflows for other architectures. Get a feel for the different flavors and what might vibe with you
  2. Everyone should see Arduino and MicroPython with a XIAO. But beyond that there are other languages and toolchains (native C, Rust/Go, Javascript, ...), other processor families (AVR, SAMD, ...), and other architectures (FPGA, PLD, PIO, GPU, ...). Group goal is to sample options beyond what you're doing individually this week.

Would you like to get in touch?

Email me!