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:
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.
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:
-
Install the esptool package:
-
Check the COM port your microcontroller is connected to:
-
Erase the flash memory of your ESP32-C3:
ReplaceYour_COM_HERE
with the COM port identified in the previous step. -
Navigate to your downloads folder:
-
Download the firmware from this link and write it to the flash memory:
Replaceesptool --chip esp32c3 --port Your_COM_HERE --baud 460800 write_flash -z 0x0 ESP32_GENERIC_C3-20241129-v1.24.1.bin
Your_COM_HERE
with the COM port identified earlier and ensure the file nameESP32_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:
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.
Now everything worked.
I also used an ATtiny 1614 microcontroller with the Adrianino. Image from Adrián Torres
Button Blink Example
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)