Skip to content

6. Embedded programming

Hero Shot

Assignment :

Individual assignment:

  • write a program for a microcontroller development board that you made, to interact (with local input &/or output devices) and communicate (with remote wired or wireless devices)
  • extra credit: use different languages &/or development environments
  • extra credit: connect external components to the board

Group assignment:

  • browse through the data sheet for your microcontroller
  • compare the performance and development workflows for other architectures

Group Work

You can find our group page here and this week group assignement here

Comparing Micro-controller

We chose to make a table to compare 2 micro-controller chips using datasheets and vendor documentations :

Feature RP2040 ESP32 - S3
Processor Dual-Core ARM Cortex MO+ Xtensa Dual-core 32bits LX7
Clock speed 133 Mhz 2.4 GHz
Memory 264kb SRAM & 2MB Flash 512Kb SRAM & 384 Kb ROM
GPIO pins 30 30
Serial Interface Brown-out Detect/Reset, DMA, I2S, LCD, POR, PWM, WDT ADC, I2C, I2S, SDIO, SPI, PWM, UART
Analog inputs 4 20
Connectivity None Bluetooth 4.2 & WiFi 802.11b/g/n
Cost 70ct 1.85$
Size 7x7 mmm 5x5 mm

Here’s a representation of the I/O pins for each chips :

Our boards use XIAO RP2040 which is a micro-controller board using the RP2040 chip. The functionalities are slightly different such as an USB-C connector for programming and power supply.

Writing a program

Hardware

As a complete newcomer in electronics, I had a lot of informations to explore to understand the Neil’s course. I read some documentations in english and in french :

For our programming, we use a micro-controller. Micro-controller is like tiny computer with much less power. It’s a micro-chip that contains microprocessors, RAM (temporary memory), ROM (long-term memory) and peripherals with internal bus (electrical connexions to send and receive datas) to interact together.

I had troubles with my board, making all thing functionning as I wished. I spend lot of time debugging, first by using basic programming to test elements. All my LED cold blink as tested in Week04.
When I tested the button example, it only worked with the LED on pin 1. To check what’s wrong, I add the digitalRead example to the button to verify the response on the button. It worked normally for pin 1 LED, but for the other the LEDs remained off but the serial monitor displayed “1” when the button was pushed and stayed at 1 after releasing it. It returned to 0 by moving it slightly. I concluded I had a problem with the button and solder a new one, but the problems still occured. Manipulating the board for visual check, a wire has become unsoldered. I decided to make a new board from scratch cause I had also LEDs not lighting at the same level. Hopefully, we had already mill a PCB in prevision of that kinf of issue.

I was more confident in my soldering skill and it went quite okay. After testing it, I stil had a problem with the button not working. I learned to test it with the multimeter an evreything seemed alright. My Co-student Jean-Côme help me with visual check and find that my soldering on the pin 27 wasn’t good enough. Resoldering it solve the problem.

Programming

Using Arduino IDE, I try several examples to comprehend base programming. We also had a base programming workshop, in which we use some examples and were teached about the meaning of the code. I checked Arduino Language reference page to understand and describe the code.

Example test

First we define values:

This line set the number of the LED pin :

const int pinLed = 26 ;
- const stands for constant and means that the value won’t change.
- int stands for integers and means the data types is a number - pinLed can be replaced by another word like led1 if you need more led for example. - the number stand for the actual pin your LED is connected on the micro-controller

We can set this constant with another comand :

#define led1 26
The compiler will replace any mention of led1 with the value 26 at compile time.

Don’t use ; at the end of the line. It might generate errors.

For variables, you don’t write const

int buttonState = 0
This line codes for the button status, pushed or not pushed and can change during the program.

Then, we define setup for our program. This part will be run only once.

void setup() {
  pinMode(led1, OUTPUT);
}
In this case :

  • “Void” means no information is expected to be extracted from this code, if it’s called by another code.
  • pinMode is used to configure the pin with the pin number and its type, input or output.
  • the pin number here refers to the constant we define earlier, so the pin 26.

So, in a little more complex code, here from a button example :

const int buttonPin = 27;  // the number of the pushbutton pin
const int led1 = 1;   // the number of the LED pin

// variables will change:
int buttonState = 0;  // variable for reading the pushbutton status

void setup() {
   // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // initialize the LED pin as an output:
  pinMode(led1, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
}
All this mean :

  • Serial is used for communication between the computer and the microcontroller. It is used in this code to have a feedback on the serialmonitor.
  • Serial.begin is used to define the speed of that communication. It is set at 9600 bps by defaults.
  • buttonPin constant is screated at value 27, and led1 constant is created at 1
  • a variable named buttonState is created with initial value at 0
  • pin 1 is set as an output through the led1 constant and pin 27 is set as an input through the buttonPin constant

Now the main code :

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    // turn LED on:
    digitalWrite(led1, HIGH);
  } else {
    // turn LED off:
    digitalWrite(led1, LOW);
  }

  Serial.println(buttonState);
  delay(1);
}

  • ``void loop” define the main code that will run repeatedly.
  • buttonState is a new variable. digitalRead is a function that will read the value of digital pin, whether the current is passing or not. It will set the variable as HIGH or LOW and equivalent 1 or 0 if the button is pushed or not. -if is a structure to check a condition (here (buttonState == HIGH), == comparing the 2 variables and returning ‘true’ if they’re equal) and executes the following statement if the condition is ‘true’. -else complete the if structure to define statement if the the previous condition returns ‘false’
  • digitalWrite is set as statements and is a function that will command if a pin will be powered or not, HIGH or LOW.
  • Serial.println is function to print data (here the buttonState value, ‘1’ or ‘0’) in the serial port like the serialMonitor in ArduinoIDE.
  • delay is a time function to pause the program, to let a command to be executed for examples. Here it’s probably to limit the number of prints in the serial monitor. It is set on this example at 1 ms.

Aternating LEDs

Now starting with a specific code. Using chat GPT, I write the prompt :

write a code for arduino : with a button, pushed once, light a led ; pushed twice, light a second led ( but don’t light the first) , pushed 3 times light the third led.

Chat GPT provided this code I will explain part after part:

const int buttonPin = 27;  // the number of the pushbutton pin
const int ledPin1 = 26;    // the number of the first LED pin
const int ledPin2 = 0;    // the number of the second LED pin
const int ledPin3 = 1;    // the number of the third LED pin
This part define the constant. The numbers match the Quentorres board connexions.

int buttonState = 0;  // variable for reading the pushbutton status
int lastButtonState = 0; // variable to store the previous state of the button
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

In this part, we found the previous buttonState variable, plus a new variable to emplement the number of push “lastButtonState”.
unsigned long is used for variables that need big number (4 bytes or 32 bits) and is used for 2 new variables : lastDebounceTime and debounceDelay. Those ares used to avoid spurious transition when pressing the button. The delay is set at 50 ms and will be compare to the lastDebounceTime using a function that we will see further.

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  // Initialize serial communication:
  Serial.begin(9600);
}
This part defines all LEDs as outputs and the Button as an input. Serial is probably used for for feedback and debugging.

Now the main code :

void loop() {
  // read the state of the pushbutton value:
  int reading = digitalRead(buttonPin);

  // check if the button state has changed
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  // if a certain amount of time has passed since the last change
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // if the button state has changed
    if (reading != buttonState) {
      buttonState = reading;

      // check if the button is pressed
      if (buttonState == HIGH) {
        // increment LED count
        static int ledCount = 0;
        ledCount++;

        // turn off all LEDs
        digitalWrite(ledPin1, LOW);
        digitalWrite(ledPin2, LOW);
        digitalWrite(ledPin3, LOW);

        // light LEDs according to button presses
        if (ledCount == 1) {
          digitalWrite(ledPin1, HIGH);
          Serial.println("First LED on");
        } else if (ledCount == 2) {
          digitalWrite(ledPin2, HIGH);
          Serial.println("Second LED on");
        } else if (ledCount >= 3) {
          digitalWrite(ledPin3, HIGH);
          Serial.println("Third LED on");
          // reset LED count after lighting the third LED
          ledCount = 0;
        }
      }
    }
  }

  // save the current button state for comparison in the next loop iteration
  lastButtonState = reading;
}
It first create a variable reading for checking the buttonPin value.

Then it creates a new value for lastDebounceTime. If reading is different from previous lastButtonState, lastDebounceTime is state as millis (a function that return the number of milliseconds since the starting of the program).

Then, if a change occurs in the reading (and different of buttonState) in a time (millis-lastDebounceTime) superior to the debounceDelay, it will change the buttonState to match reading. Thats how debounce works to avoid spurious transitions.

then it can check the buttonState, and create a variable ledCount that increment 1 each time the button is pushed.

For each value of ledCount, it starts LEDs off and turns a specific LEDS on. If the counter reaches 3, it also reset the counter to 0.

It finally set the lastButtonState to match reading value`.

As I wanted the LED to turn off when the cycle reaches an end, I added a fourth state to the led counter for turning off the third LED. I also modified the comparators >= for == on the the ledCount 3 condition because there is now a superior value.

You can find the complete code in this ino file (the ino file needs to be in a folder of the same name to be open with ArduinoIDE).

Press duration button

For this test, I also used chatGPT 3.5 with the prompt :

write a code for arduino : if I hold the button 1 it lights the first LED ; if i hold it longer til 2s, the second leds is added; 3s the third LED is added and 4s all leds turns off

const int buttonPin = 27;    // the number of the pushbutton pin
const int ledPin1 = 26;      // the number of the first LED pin
const int ledPin2 = 0;      // the number of the second LED pin
const int ledPin3 = 1;      // the number of the third LED pin

int buttonState = 0;        // variable for reading the pushbutton status
int lastButtonState = 0;    // variable to store the previous state of the button
unsigned long pressStartTime = 0; // variable to store the time when button was pressed
unsigned long pressDuration = 0;  // variable to store the duration of button press

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  // Initialize serial communication:
  Serial.begin(9600);
}

void loop() {
  // read the state of the pushbutton value:
  int reading = digitalRead(buttonPin);

  // check if the button state has changed
  if (reading != lastButtonState) {
    // reset the debouncing timer
    pressStartTime = millis();
  }

  // if the button is pressed
  if (reading == HIGH) {
    pressDuration = millis() - pressStartTime;

    if (pressDuration >= 1000 && pressDuration < 2000) {
      digitalWrite(ledPin1, HIGH);
    } 
    else if (pressDuration >= 2000 && pressDuration < 3000) {
      digitalWrite(ledPin2, HIGH);
    } 
    else if (pressDuration >= 3000 && pressDuration < 4000) {
      digitalWrite(ledPin3, HIGH);
    } 
    else if (pressDuration >= 4000) {
      digitalWrite(ledPin1, LOW);
      digitalWrite(ledPin2, LOW);
      digitalWrite(ledPin3, LOW);
    }
  } 
  else {
    // if the button is released, turn off all LEDs
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
  }

  // save the current button state for comparison in the next loop iteration
  lastButtonState = reading;
}
Like in the previous code, ChatGPT generated ``unsigned long” variables to measure time. One for the millis when the button is pressed first and one for press duration that is defined by comparing actual millis with button already pushed with the millis when the button was pushed.

The condition compare pressDuration to specific values to turn LEDs on: between 1 and 2 s, between 2 and 3, 3 and 4 then 4 or more.

For more reactivity, I changed the condition value to start 1s faster. Also, I deleted the last else structure to keep the LEDs on after releasing the button.

The final program is available as ino file (the ino file needs to be in a folder of the same name to be open with ArduinoIDE).

Potentiometer test

For my final project, I’d like to use a potentiometer or a selector. I chose to test potentiometer programming for now.

To test a potentiometer on my board, I first needed to define the 3 pin of the potentiometer. Using a multimeter, I measured the resistance between the pin. Between the external, I had a fixed resistance of 9.64 Ohm even if I turned the potentiometer. This means those are the positive and the negative terminals.

Photo

Measuring between the central pin and an external one, I can observe changes on the resistance. If, when i turn the potentiometer clockwise, the resistance is raising, it’s connected to the positive terminal. If it’s lowering, it’s the negative terminal

Checking on documentation, I found that there is 4 analog pins (ACD inputs) on the RP2040. A0 and A1 are already used for a LED and the button as GPIO 26 and 27. Section 2.9.5 describe that this pin can be powered between 1.8V and 3.3V.

To connect it to my board, I added connexion header on it to have a better access at the 3.3V pin.

A2 and A3 are available but A3 is link to a 1000 ohms resistor. Potentiometer being a variable resistor, I thought using the pin without resistor was more pertinent. But after testing the two of them, they both works well.

I made this setup.

ChatGPT provided me this code to this prompt and some specification to correct issues:

make an arduino code for a potentiometer lighting 1 then 2 then 3 led as it is turn.

const int potPin = A2;  // analog pin for the potentiometer
const int ledPin1 = 26;  // the number of the first LED pin
const int ledPin2 = 0;  // the number of the second LED pin
const int ledPin3 = 1;  // the number of the third LED pin
const int numLEDs = 3;  // number of LEDs

void setup() {
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
}

void loop() {
  // Read the potentiometer value
  int potValue = analogRead(potPin);

  // Map the potentiometer value to the range of LEDs
  int ledLevel = map(potValue, 0, 1023, 0, numLEDs + 1);

  // Turn on the LEDs based on the mapped value
  digitalWrite(ledPin1, ledLevel >= 1);
  digitalWrite(ledPin2, ledLevel >= 2);
  digitalWrite(ledPin3, ledLevel >= 3);

  delay(100);  // Adjust delay as needed to control the responsiveness
}
potPin constant define the Analog pin on A2 and don’t need to be set as input in the setup part.
In the main program, analogRead will get a value between 0 and 1023 from the potentiometer and make a variable from it. Then it creates from it a ledLevel variable with the map functions. This functions will make corresponding potValue (between 0 and 1023) and ledLevel (between 0 and 4 “pinLEd+1”)

Then it will turn on LED for each ledLevel, corresponding to the potentionmeter value.

The final code is here

Serial Analog test

I also test AnalogInOutSerial example to have a monitor feedback of the value of the potentiometer.

I change the code a bit to turn on all LEDs with the fade.

The final code is here

Class Archive

ino file needs to be in a folder of the same name to be open with ArduinoIDE

Impressions of the week

I found the data sheets quite overwhelming and I still have lots af questions about it. There’s a lot of acronyms I don’t understand yet and some I misinterpreted. I still have a lot to acquire in this department. I did soldering again and I was more comfortable this time. For programming, I took lot of time to understand what was written, and I feel quite confident to modify it for what I want. Intricated functions continues to make my brain hurts but I somehow understand what I’m doing. Having a good results on the board is rather satisfactory.