Skip to content

Embedded Programming

Group Assignment:

  • demonstrate and compare the toolchains and development workflows
  • for available embedded architectures

Individual Assignment:

  • browse through the data sheet for your microcontroller
  • write a program for a microcontroller,
    • and simulate its operation,
    • to interact (with local input &/or output devices)
    • and communicate (with remote wired or wireless connections)
  • extra credit: test it on a development board
  • extra credit: try different languages &/or development environments

What Microcontroller?

(From ChatGPT, Prompt: Go over these microcontrollers and make a table in MKdocs material explaining their differences: ATtiny412, SAM D21, RP2040, ESP32-C3)

Feature ATtiny412 SAM D21 RP2040 ESP32-C3
Architecture AVR (8-bit) ARM Cortex-M0+ (32-bit) Dual-core ARM Cortex-M0+ (32-bit) RISC-V (32-bit)
Clock Speed 20 MHz Up to 48 MHz Up to 133 MHz 160 MHz
Flash Memory 4 KB Up to 256 KB 2 MB (external QSPI) 400 KB (ROM) + External Flash
SRAM 256 B Up to 32 KB 264 KB 400 KB
EEPROM No (emulated in Flash) No (emulated in Flash) No (must use external) No (must use external)
GPIO Pins 6 Up to 38 30 22
Communication UART, SPI, I²C UART, SPI, I²C, USB UART, SPI, I²C, USB UART, SPI, I²C
USB Support No Yes (USB 2.0) Yes (USB 1.1) Yes (USB 2.0)
Wireless No No No Wi-Fi 4, Bluetooth 5 (LE)
Power Consumption Ultra-low power Low power Moderate Moderate to high
Use Cases Simple embedded tasks, IoT Low-power applications, USB devices General-purpose, hobbyist, machine learning Wireless IoT, Bluetooth projects

I decided to use the Seeed Xiao ESP32-C3 to begin with, but later I also used an ATtiny. Here you can switch between their documentation:

Datasheet

Above is a combined image of several datasheets. Original images can be found here. You can click the image to expand it.

Arduino

Arduino code refers to the programming written for Arduino microcontrollers using the Arduino programming language, which is based on C/C ++. The code is written in the Arduino IDE and uploaded to the Arduino board to control electronic components. (From ChatGPT, Prompt: What is Arduino Code?)

Setting Up Arduino IDE

To get started, I downloaded the Arduino IDE and followed these steps to use Arduino with Seeed XIAO. Next, I got a development board from Mariu Fönn.

Image of board from Mariu Fönn

NeoPixel LED Strip Test

I also got a NeoPixel LED strip and coded a quick blink test using both a little help from ChatGPT and some code I found from the Neopixel Arduino LED tutorial and Maria Fönn.

#include <Adafruit_NeoPixel.h>

// Number of NeoPixels
#define NUMPIXELS 8
#define PIN_NEOPIXEL D0  // Replace with actual GPIO pin
#define NEOPIXEL_POWER 1  // Remove if your board doesn't have a power control pin
const int buttonPin = D7;

int buttonState = 0;

Adafruit_NeoPixel pixels(NUMPIXELS, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);

void setup() {
    Serial.begin(115200);
    pinMode(buttonPin, INPUT_PULLUP);  // Use pull-up resistor

#if defined(NEOPIXEL_POWER)
    pinMode(NEOPIXEL_POWER, OUTPUT);
    digitalWrite(NEOPIXEL_POWER, HIGH);
#endif

    pixels.begin();
    pixels.setBrightness(20);
}

void loop() {
    Serial.println("Hello!");

    buttonState = digitalRead(buttonPin);

    if (buttonState == HIGH) {  // Adjusted for INPUT_PULLUP
        pixels.fill(0xFFFFFF);
        pixels.show();
        delay(500);

        pixels.fill(0x000000);
        pixels.show();
        delay(500);
    }

    delay(50);  // Simple debounce
}

I could press a button and while holding it, the LED blinks.

MicroPython

MicroPython is a lightweight implementation of Python 3 designed specifically for microcontrollers and embedded systems. It provides most of Python’s standard libraries while being optimized for low-memory and low-power environments. (From ChatGPT, Prompt: What is MicroPython?)

Setting Up MicroPython on Seeed Xiao ESP32-C3

I began by following this tutorial from Seeed about how to use the Xiao esp32-c3 with MicroPython. Below are the summarized steps for Windows users:

  1. Install the esptool package:

    pip install esptool
    

  2. Check the COM port your microcontroller is connected to:

    mode
    

  3. Erase the flash memory of your ESP32-C3:

    esptool --chip esp32c3 --port Your_COM_HERE erase_flash
    
    Replace Your_COM_HERE with the COM port identified in the previous step.

  4. Navigate to your downloads folder:

    cd Downloads
    

  5. Download the firmware from this link and write it to the flash memory:

    esptool --chip esp32c3 --port Your_COM_HERE --baud 460800 write_flash -z 0x0 ESP32_GENERIC_C3-20241129-v1.24.1.bin
    
    Replace Your_COM_HERE with the COM port identified earlier and ensure the file name ESP32_GENERIC_C3-20241129-v1.24.1.bin is correct.

Using MicroPython with VS Code

After setting up MicroPython, I tried to set up VS Code with PyMakr following this tutorial. Unfortunately, I was unable to get it to recognize my controller and project. After some debugging, I decided to install Arduino lab for MicroPython, which finally worked! The workflow is a bit different, but I got used to it.

Translating Arduino Code to MicroPython

I used ChatGPT to translate the Arduino code from the previous section to MicroPython. After some troubleshooting, I realized I was using the wrong pins. Once corrected, the code worked perfectly. Here is the working MicroPython code:

import machine
import neopixel
import time

# Configuration
NUMPIXELS = 8
PIN_NEOPIXEL = 2  # Update according to your board
PIN_BUTTON = 20  # Update according to your board

# NeoPixel setup
np = neopixel.NeoPixel(machine.Pin(PIN_NEOPIXEL), NUMPIXELS)

# Button setup (pull-up resistor enabled)
button = machine.Pin(PIN_BUTTON, machine.Pin.IN, machine.Pin.PULL_UP)

# Optional: NeoPixel power pin
# PIN_NEOPIXEL_POWER = 5  # Update according to your board
# power = machine.Pin(PIN_NEOPIXEL_POWER, machine.Pin.OUT)
# power.value(1)  # Turn on NeoPixels

def fill_pixels(color):
    """Set all pixels to the specified color and update the strip."""
    for i in range(NUMPIXELS):
        np[i] = color
    np.write()

# Simple debounce helper
def wait_for_button_release():
    """Wait for the button to be released to avoid bouncing issues."""
    time.sleep(0.02)  # Initial debounce delay
    while button.value() == 0:  # Wait until button is released
        time.sleep(0.01)

# Main loop
while True:
    if button.value() == 0:  # Adjusted for INPUT_PULLUP (pressed = 0)
        print("Button Pressed!")

        fill_pixels((255, 255, 255))  # White
        time.sleep(0.5)

        fill_pixels((0, 0, 0))  # Off
        time.sleep(0.5)

        wait_for_button_release()  # Debounce before next loop

    time.sleep(0.05)  # Prevent excessive CPU usage

Next, I want to learn to code using MicroPython instead of just relying on ChatGPT. But this was a challenge to set up, so for now, I am moving to CircuitPython.

CircuitPython

CircuitPython is an open-source derivative of MicroPython, optimized for Adafruit's microcontroller boards. It is designed to simplify programming for embedded systems and IoT projects. (From ChatGPT, Prompt: What is CircuitPython?)

Setting Up CircuitPython

CircuitPython is the way to go in my opinion, at least with a Wi-Fi-supported board like the ESP32-C3. There is no need to install yet another code editor. Installation is also quite easy. You just go to the CircuitPython Download Page, choose your microcontroller, and let the web installer do its thing. However, at least for me, the Wi-Fi setup did not work on my computer. So, I had to follow this tutorial, which instructed me to install Putty and connect to my microcontroller using serial and run these commands:

f = open('settings.toml', 'w')
f.write('CIRCUITPY_WIFI_SSID = "wifissid"\n')
f.write('CIRCUITPY_WIFI_PASSWORD = "wifipassword"\n')
f.write('CIRCUITPY_WEB_API_PASSWORD = "webpassword"\n')
f.close()

After restarting, I could go to: http://circuitpython.local/code/ and that opened the code editor online. I used ChatGPT to quickly test everything by making it translate the MicroPython code to CircuitPython:

import board
import neopixel
import digitalio
import time

# Configuration
NUMPIXELS = 8
PIN_NEOPIXEL = board.D0  # Update according to your board
PIN_BUTTON = board.D7  # Update according to your board

# NeoPixel setup
np = neopixel.NeoPixel(PIN_NEOPIXEL, NUMPIXELS, brightness=1.0, auto_write=False)

# Button setup (pull-up resistor enabled)
button = digitalio.DigitalInOut(PIN_BUTTON)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.UP

# Optional: NeoPixel power pin
# PIN_NEOPIXEL_POWER = board.D5  # Update according to your board
# power = digitalio.DigitalInOut(PIN_NEOPIXEL_POWER)
# power.direction = digitalio.Direction.OUTPUT
# power.value = True  # Turn on NeoPixels

def fill_pixels(color):
    """Set all pixels to the specified color and update the strip."""
    np.fill(color)
    np.show()

# Simple debounce helper
def wait_for_button_release():
    """Wait for the button to be released to avoid bouncing issues."""
    time.sleep(0.02)  # Initial debounce delay
    while not button.value:  # Wait until button is released
        time.sleep(0.01)

# Main loop
while True:
    if not button.value:  # Adjusted for INPUT_PULLUP (pressed = False)
        print("Button Pressed!")

        fill_pixels((0, 0, 0))  # Off
        time.sleep(0.5)

        wait_for_button_release()  # Debounce before next loop

        fill_pixels((255, 255, 255))  # White
        time.sleep(0.5)

    time.sleep(0.05)  # Prevent excessive CPU usage

Installing Required Libraries

That did not work as the NeoPixel library was not installed, so I googled a little and found this official tool from Adafruit called Circup. So, I installed it by running:

pip install setuptools
pip install circup

Then I ran this command. You will need to change the IP and password, but the same command can be used always as it will read the code.py file and install all required libraries.

$ circup --host 192.168.1.188 --password 1234 install --auto

Now everything worked.

Datasheet

I also used an ATtiny 1614 microcontroller with the Adrianino. Image from Adrián Torres

I used this code from Adrián, which is a simple Button Blink example.

// Fab Academy 2020 - Fab Lab León
// Button + LED                           
// Adrianino
// ATtiny1614 - ATtiny1624
//
// Original code: Neil Gershenfeld 12/8/19
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose, but must
// acknowledge this project. Copyright is retained and
// must be preserved. The work is provided as is; no
// warranty is provided, and users accept all liability.
//

const int ledPin1 = 8; // first light
const int buttonPin = 10; // button pin
int buttonState = 0; // initial state of the button

void setup() { // declaration of inputs and outputs
    pinMode(ledPin1, OUTPUT);
    pinMode(buttonPin, INPUT);
}

void loop() {
    buttonState = digitalRead(buttonPin); // read the state of the button
    if (buttonState == HIGH) { // if we press the button
        digitalWrite(ledPin1, HIGH);
        delay(500);                       
        digitalWrite(ledPin1, LOW);    
        delay(500);
        digitalWrite(ledPin1, HIGH);
        delay(500);                       
        digitalWrite(ledPin1, LOW);    
        delay(500);
        digitalWrite(ledPin1, HIGH);
        delay(2000);                        
        digitalWrite(ledPin1, LOW);    
        delay(1000);
    } else { // if we don't press the button
        digitalWrite(ledPin1, LOW);
    }
}

I used the Quentorres Programmer to program the ATtiny 1614 by following this guide from Adrián, and it worked.

(Video)

Hall Effect Sensor Example

I also tried the Hall Effect Sensor with the following code:

// Fab Academy 2020 - Fab Lab León
// Hall effect
// Adrianino
// ATtiny1614 - ATtiny1624

int sensorPin = 0; // analog input pin to hook the sensor to
int sensorValue = 0; // variable to store the value coming from the sensor

void setup() {
    Serial.begin(115200); // initialize serial communications
}

void loop() {
    sensorValue = analogRead(sensorPin); // read the value from the sensor
    sensorValue = map(sensorValue, 0, 1024, 1024, 0);
    Serial.println(sensorValue); // print value to Serial Monitor
    delay(500); // short delay so we can actually see the numbers
}

I connected it to the programmer, and it also worked.

(Video)