An Introduction to Microcontroller Programming

By: Jan Joe S. Carcillar

What are Microcontrollers?

Microcontroller Elements

Elements of a Microcontroller

  1. Processor - responds to instructions, perform basic arithmetic and logic operations.
  2. Memory - Store data the processor ouputs or receives.

    • Program Memory - stores instructions, long term memory
    • Data Memory - stores volatile memory, cleared when there is no power.
  3. I/O peripherals - ports, analog-to-digital or digital-to-analog converters.

Some Popular Microcontrollers

STM32F103C8T6 (Blue Pill)

Some Popular Microcontrollers

ATTINY85

Some Popular Microcontrollers

PIC16F877A

Some Popular Microcontrollers

ATMEGA328P (Arduino Uno)

Some Popular Microcontrollers

Tensilica Xtensa LX6 (ESP32)

Programming Environment

An Integrated Development Environment contains the necessary tools and other helpful features for software development such as:
  • Syntax highlighting
  • Intelligent code completion
  • Testing and debugging
  • Code compiler
  • Arduino IDE
    Serial Monitor
    Serial Plotter

    When microcontrollers first came out, they were programmed using the C language. Now, there are a few programming language than you can work with namely:

    • Python
    • C++
    • Javascript

    Project 0: Blink for Me

    Project 0: Blink for Me

    Materials Needed:
    1. Any Arduino Board
    2. Arduino IDE
    Project Objectives:
    1. Run a simple microcontroller program.
    2. Introduce basic parts of a microcontroller code.
    3. Introduce the process of board programming.
    							
    // the setup function runs once when you press reset or power the board
    void setup() {
    // initialize digital pin LED_BUILTIN as an output.
    	pinMode(LED_BUILTIN, OUTPUT);
    }
    
    // the loop function runs over and over again forever
    void loop() {
    	digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
    	delay(1000);                      // wait for a second
    	digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
    	delay(1000);                      // wait for a second
    }
    							
    						

    Recap

    1. What part of the code runs once after boot up?
    2. What part of the code runs indefinitely?
    3. How many seconds of delay will there be in the code below?
      delay(10000);

    Project 1: Blink Without Delay⚡

    Project 1: Blink Without Delay⚡

    Materials Needed:
    1. Any Arduino Board
    2. Arduino IDE
    Project Objectives:
    1. Know the effect of delays in the program flow.
    2. Introduce timing macros in Arduino.
    3. Perform actions based on a condition.
    							
    
    bool ledState = LOW;
    unsigned long prevMillis = 0;
    unsigned long currentMillis = 0;
    const unsigned long blinkInterval = 1000;
    
    void setup() {
    	pinMode(LED_BUILTIN, OUTPUT);
    }
    
    void loop() {
    	currentMillis = millis(); //get the number of milliseconds the microcontroller has run since boot up
    	if (currentMillis - prevMillis >= blinkInterval){ //if current time exceeds the previous time by the set interval
    		ledState = !ledState; //toggle the state of the LED to its opposite using "!"
    		prevMillis = currentMillis; //set the current time as the previous time to be used in the next iteration
    	}
    	digitalWrite(LED_BUILTIN, ledState); //turn on or turn off the LED depending on the condition above
    }
    							
    						

    Recap

    1. Why is it not a good pratice to add lots of delays in the program?
    2. How does the program know when to switch the LED state?
    3. Why is the highlighted code below needed?
      											
      if (currentMillis - prevMillis > = blinkInterval) {
      	ledState = !ledState;
      	prevMillis = currentMillis;
      }
      											
      										

    CONCEPTS 1: Data Types

    CONCEPTS 1: Data Types

    Array

    An array is a collection of variables that are accessed with an index number.

    							
    //initializing arrays
    int myInts[6]; //values default to 0
    int myPins[] = {2, 4, 8, 3, 6};
    int mySensVals[5] = {2, 4, -8, 3, 2};
    char message[] = "level"; //enclose in double-quotes
    char *messages[] = {"message1", "message2", "message3"}; //* indicates pointers
    
    //assigning value to arrays
    //indexing starts at 0
    //which corresponds to the first element
    myInts[0] = 100;
    myInts[5] = 99;
    //myInts -> {100, 0, 0, 0, 0, 99}
    
    //retrieving values from arrays
    message[4] //-> 'l' (last character)
    message[3] //-> 'e'
    message[2] //-> 'v'
    message[1] //-> 'e'
    message[0] //-> 'l' (first character)
    							
    						

    String

    An Arduino class for strings. This class allows for easier operations with strings.
    							
    //initializing the string object
    String myString = "Hello String"; // using a constant String
    String myString = String('a'); // converting a constant char into a String
    String newString = String("This is a string"); // converting a constant string into a String object
    String myString = String(newString + " with more");  // concatenating two strings
    String myString = String(13); // using a constant integer
    String myString = String(45, HEX); // using an int and a base (hexadecimal)
    String myString = String(255, BIN); // using an int and a base (binary)
    String myString = String(millis(), DEC); // using a long and a base
    String myString = String(5.698, 3); // using a float and the decimal places
    
    //operations on string
    myString.indexOf() //returns index of a character in a string
    myString.length() //returns length of a string
    myString.remove() //remove character at specified location
    myString.replace() //replaces parts of a string
    myString.toDouble() //convert string to double
    myString.toInt() //convert string to integer
    myString.toFloat() //convert string to float
    myString.toLowerCase() //convert string to lower case
    myString.toUpperCase() //convert string to upper case
    myString.trim() //remove leading and trailing spaces
    							
    						

    Recap

    Identify which type is the most suitable for following data description.

    1. A sensor reading that detects if a device is on or off.
    2. A gyroscope reading angles from 0° to 180°
    3. A value from the millis() macro.
    4. The message "turn on motor".
    5. A temperature reading from a sensor.

    CONCEPTS 2: Control Structures

    CONCEPTS 2: Control Structures

    IF Statements

    The use of IF statements is very common in the any field of programming. This allows for actions to happen when a certain condition is met.

    							
    float waterLevel = 3.4;
    float alarmThreshold = 5;
    
    if (waterLevel >= alarmThreshold){
    	activateAlarm(); //📢
    }
    							
    						

    IF-ELSE Statements

    Alternative conditions can also be addressed using the ELSE statement.

    							
    float waterLevel = 3.4;
    float alarmThreshold = 5;
    
    if (waterLevel >= alarmThreshold){
    	//activates when the condition is true
    	activateAlarm(); //📢
    } else {
    	//activates when the above condition is false 🪅
    	playPartyMusic();
    }
    							
    						

    IF-ELSE-IF Statements

    Sometimes there could be many alternative conditions.

    							
    float waterLevel = 3.4;
    float alarmThreshold = 5;
    
    if (waterLevel >= 0.9*alarmThreshold){
    	activateLoudestAlarm();
    } else if (waterLevel >= 0.75*alarmThreshold){
    	activateModerateAlarm();
    } else if (waterLevel >= 0.5*alarmThreshold){
    	activateSoftAlarm();
    } else {
    	//activates when all of the previous conditions are false 🪅
    	playPartyMusic();
    }
    							
    						

    SWITCH Case

    If the IF-ELSE-IF statements only contain checks for equality, the switch case control can be used to provide a more concise syntax.

    							
    //if-else-if statement
    int fanSpeed = 0;
    if (fanSpeed == 0) {
    	turnOffFan();
    } else if (fanSpeed == 1) { //double equals checks for equality
    	setLowFanSpeed();
    } else if (fanSpeed == 2) {
    	setMediumFanSpeed();
    } else if (fanSpeed == 3) {
    	setHighFanSpeed();
    } else {
    	activateBrokenKnobAlarm(); //oh no 😧
    }
    
    //switch case implementation
    int fanSpeed = 0;
    switch (fanSpeed) {
    	case 0:
    		turnOffFan(); break;
    	case 1:
    		setLowFanSpeed(); break;
    	case 2:
    		setHighFanSpeed(); break;
    	case 3:
    		setHighFanSpeed(); break;
    	default: //optional
    		activateBrokenKnobAlarm(); break;  //oh no 😧
    }
    							
    						

    FOR Loop

    FOR loops are control structures that repeats tasks by keeping track of an incrementing variable.

    							
    int NUM_REPEAT = 5;
    String firstPart = "Went over the loop ";
    String message = "";
    
    for (int i = 0; i < NUM_REPEAT; i++) {
    	message = String(firstPart + String(i+1) + " times");
    	print(message);
    }
    							
    						

    FOR Loop

    The way the loop variable changes can also be changed.

    							
    //prints number starting from 1 increasing by 2
    //while the number is less than 10
    for (int i = 1; i < 10; i += 2) {
    	print(i); //output -> 1, 3, 5 , 7, 9
    }
    
    //prints number starting from 15 decreasing by 1
    //while the number is greater than 10
    for (int j = 15; j > 10; j--){
    	print(j); //output -> 15, 14, 13, 12, 11
    }
    
    //prints powers of 2
    //while the value is less than 100
    for (int k = 1; k < 100 ; k = 2*k){
    	print(k); //output -> 1, 2, 4, 8, 16, 32, 64
    }
    							
    						

    WHILE Loop

    Sometimes, you want to repeat something while a condition is true. For this, the WHILE loop can be used.

    							
    int myNumber = 0;
    while (myNumber < 10) {
    	print(myNumber); //output -> 0, 2, 4, 6, 8
    	myNumber += 2;
    }
    							
    						

    WHILE Loop

    Loops can be interrupted using
    continue;
    and
    break;
    keywords.
    							
    int myNumber = 0;
    while (myNumber < 10) {
    	if (myNumber == 2){
    		myNumber += 2;
    		continue; //skip current iteration
    	}
    
    	if (myNumber == 6){
    		break; //end loop here
    	}
    
    	print(myNumber); //output -> 0, 4, 6
    	myNumber += 2;
    }
    							
    						

    DO WHILE Loop

    A WHILE loop where the condition is evaluated after the action. Ensures that at least one iteration happens.

    							
    int myNumber = 0;
    do {
    	print(myNumber); //output -> 0, 2, 4, 6, 8
    	myNumber += 2;
    } while (myNumber < 10);
    							
    						

    Recap

    1. Which action will be peformed in this IF statement?
      											
      int i = 0;
      i -= 1;
      if (i == 0){
      	doAction1();
      } else if (i == 1) {
      	doAction2();
      } else if (i < 2) {
      	doAction3();
      } else {
      	doAction4();
      }
      											
      										
    2. Modify this code such that the strings are printed with correct grammar.
      											
      int NUM_REPEAT = 5;
      String firstPart = "Went over the loop ";
      String message = "";
      
      for (int i = 0; i < NUM_REPEAT; i++) {
      	message = String(firstPart + String(i+1) + " times.");
      	print(message);
      }
      											
      										
    3. What do you think will happen in this WHILE loop?
      											
      int i = 0;
      while (i < 10) {
      	print(i);
      }
      											
      										
    4. Can the switch case control structure be used
      for conditions involving inequalities?
    5. What's the difference between
      continue;
      and
      break;
      ?

    CONCEPTS 3: Boolean Algebra

    CONCEPTS 3: Boolean Algebra

    Using boolean algebra there is way to combine simple conditions, such as equality and inequality checks into a single statement. Arduino has three boolean algebra operators:

    1. AND (&&)
    2. OR (||)
    3. NOT (!)

    AND Code Implementation

    							
    int numLegs = 2;
    String movementMethod = "crawling";
    String conclusion = "";
    
    if (numLegs == 2) && (movementMethod == String("crawling")) {
    	conclusion = String("This could be a human baby. 👶");
    } 
    else if (numLegs == 2) && (movementMethod == String("flying")) {
    	conclusion = String("This could be a bird. 🦜");
    } 
    else if (numLegs = 3) && (movementMethod == String("flying")){
    	conclusion = String("I have no idea what this is. 👽");
    }
    else if (numLegs > 100) && (movementMethod == String("crawling")) {
    	conclusion = String("This could be a millipede. 🪱");
    }
    else {
    	conclusion = String("I need further research. 🔬")
    }
    							
    						

    OR Code Implementation

    							
    bool isRainingHard = false;
    bool isHoliday = false;
    bool isClasses = true;
    
    if (isRainingHard || isHoliday) {
    	isClasses = false; ⛈️
    }
    							
    						

    NOT Code Implementation

    							
    bool isRaining = false;
    bool isHotDay = false;
    bool bringUmbrella = true; //initially true
    
    if (!isRaining && !isHotDay) {
    	bringUmbrella = false;
    }
    							
    						

    Boolean Operators Symbols

    Recap

    1. The two statements below are equivalent. Why?
      											
      bool  isRaining = true;
      
      //first satement
      if (isRaining == true) {
      	//do stuffs here
      }
      
      //second satement
      if (isRaining) {
      	//do stuffs here
      }
      											
      										

    Recap

    1. When does the output of the AND operator become true?
    2. WWhen does the ouput of the OR operator become false?
    3. WWhen does the ouput of the NOT operator become true?

    Project 2: SWITCH DESTROYER 3000

    Project 2: SWITCH DESTROYER 3000

    Materials Needed:
    1. Arduino Uno or Arduino Nano
    2. Arduino IDE
    3. Bread Board and Jumper Wires
    4. 5pcs LEDs, 5pcs 200$\Omega$ Resistors, 1pc 1k$\Omega$ Resistor
    5. 1pc SPST Switch
    Project Objectives:
    1. Introduce the importance of pulldown-resistor for digital inputs.
    2. Introduce reading digital inputs and writing digital outputs.
    3. Learn how to keep track of state changes.
    4. Use arrays, IF statements and FOR loops.
    5. Use the Serial Monitor.
    Project Desription

    A switch will be toggled from OFF📴 state to an ON🔦 state repeatedly. In each switch cycle, an LED is illuminated moving from left to right.

    Before the main thing, let's talk about states.

    In digital circuits, there commonly are two logic states which are HIGH and LOW. But is this actually the case? Try the simple code below.

    							
    	const int inputPin = 12;
    	bool inputState;
    	
    	void setup() {
    		pinMode(inputPin,INPUT);
    		pinMode(LED_BUILTIN,OUTPUT);
    	}
    	
    	void loop() {
    		//read the state of the input pin
    		//the input pin will not be connected to anything
    		inputState = digitalRead(inputPin);
    	
    		//copy whatever state the input pin is
    		//into the LED pin
    		digitalWrite(LED_BUILTIN,inputState);
    	}
    							
    						

    Strange 😲. It seems like the output LED is in the HIGH state. Is it really? Let's look deeper through our Serial tools. Add the following lines.

    							
    	const int inputPin = 12;
    	bool inputState;
    	
    	void setup() {
    		pinMode(inputPin,INPUT);
    		pinMode(LED_BUILTIN,OUTPUT);
    
    		//initialize serial communication
    		//specify 9600 as rate of data transfer
    		Serial.begin(9600);
    	}
    	
    	void loop() {
    		//read the state of the input pin
    		//the input pin will not be connected to anything
    		inputState = digitalRead(inputPin);
    	
    		//print stuff to the serial monitor
    		Serial.print("inputState:"); //print horizontally
    		Serial.println(inputState); //print vertically
    
    		//copy whatever state the input pin is
    		//into the LED pin
    		digitalWrite(LED_BUILTIN,inputState);
    	}
    							
    						

    The input state seems random. This indicates that the input is floating or has no definite state. To fix this, we just need to make the state definite. How so? Add a 1k$\Omega$ resistor between GND and the input pin.

    Here's the plan of attack.

    							
    int toggleCount = 0;
    const int numLEDs = 5;
    const int ledPins[] = {2,3,4,5,6};
    const int togglePin = 12;
    
    void setup () {
    	//1. Set the pin modes to output using a loop
    }
    
    void loop () {
    	/*1. Count how many times the switch was toggled.
    		2. Activate the LED corresponding to the current toggle count.
    		3. If the count exceeds the number of LEDs, stat over.*/
    }
    							
    						

    Here's the code implementation.

    							
    int toggleCount = 0;
    const int numLEDs = 5;
    const int ledPins[] = {2,3,4,5,6};
    const int togglePin = 12;
    bool currentState;
    bool prevState;
    
    char messageBuffer[50];
    
    void setup() {
    	pinMode(togglePin,INPUT);
    	for (int i = 0; i < numLEDs; i++) {
    		pinMode(ledPins[i],OUTPUT);
    	}
    	Serial.begin(9600);
    	prevState = digitalRead(togglePin);
    }
    
    void loop() {
    	sprintf(messageBuffer,"toggleCount = %d, prevState = %d, currentState = %d",toggleCount,prevState,currentState);
    	Serial.println(messageBuffer);
    
    	if (toggleCount == 0) {
    		//turn of last LED
    		digitalWrite(ledPins[numLEDs - 1],LOW);
    	}
    	else if (toggleCount == 1) {
    		//turn off first LED
    		digitalWrite(ledPins[0],HIGH);
    	} else if (toggleCount > 1) {
    		//turn on LED corresponding to count
    		//turn off previous LED 
    		digitalWrite(ledPins[toggleCount - 1],HIGH);
    		digitalWrite(ledPins[toggleCount - 2],LOW);
    	}
    
    	currentState = digitalRead(togglePin);
    	if (currentState != prevState) {
    		if (currentState == HIGH) {
    			toggleCount ++;
    		}
    		if (toggleCount > numLEDs){
    			toggleCount = 0;
    		}
    	}
    	prevState = currentState;
    }
    							
    						

    It's your turn. Modify the existing code and try to replicate this output. Tip: Use a for-loop to turn on and turn off multiple LEDs.

    Recap

    1. What does is it mean for the input to be floating?
    2. What are the possible outputs of the digitalRead function?
    3. What's the required step before one can perform digital read and writes?

    Project 3: TURN UP THE BRIGHTNESS

    Project 3: TURN UP THE BRIGHTNESS

    Materials Needed:
    1. Arduino Uno or Arduino Nano
    2. Arduino IDE
    3. Bread Board and Jumper Wires
    4. 6pcs LEDs, 6pcs 220$\Omega$ Resistors, 1pc 1k$\Omega$ Resistor
    5. 1pc 1k$\Omega$ (and above) Potentiometer
    Project Objectives:
    1. Learn how to read analog inputs.
    2. Learn how to write analog ouputs.
    3. Identify correct pins to use for analog read and writes.
    4. Learn about voltage dividers.
    5. Learn about the map function.
    Project Desription

    A potentiometer will create a voltage divider. The micrcontroller detects the voltage ⚡. The higher the voltage, the higher number of LEDs will be illuminated 🚨. An indicator LED increases and decreases in brightness at the same time.

    Digital vs Analog

    Digital signals are the ones whose value can be represented in two states. ON or OFF, HIGH or LOW, TRUE or FALSE are just some of the terms we're calling those states. Some of these scenarios include:
    1. The sensor output detecting whether the laptop lid is open.
    2. A laser detector that triggers when someone passes through.
    3. The position of a lever switch.

    Digital vs Analog

    Analog signals on the other hand are signals that can take on many smaller values within a given interval. For example:
    1. Amount of charge in the battery of a certain device.
    2. Pressure of water as read from a sensor below a tank.
    3. The altitude of a drone.

    Digital vs Analog

    All electronics are analog down to the physics of how things interact. A layer above that is the digital side of things like the logic gates that allows processing of instuctions handed to the micrcontroller. To switch between analog and digital signals, the following are present in a micrcontroller.

    Arduino Uno Pinout
    Arduino Nano Pinout

    Voltage Divider

    Inside a Potentiometer
    Equivalent Schematic Diagram
    $$V_{out} = \frac{R_1}{R_1 + R_2} \times V_{in}$$

    Analog Measurements

    The value we would get from the ADC are not the actual units of voltage but the digital representation whose scale varies with the number bits in the ADC. The Arduino Uno and Nano have 10-bit ADCs which means the ADC has: $$2^{10} = 1024$$ values to represent 0 to 5V. Hence the output range of analogRead is 0 to 1023.

    Analog Measurements

    The Uno and Nano DACs on the other hand have lower resolution at 8 bits. Hence we could write the voltage from 0V to 5V using: $$2^8 = 256$$ unique symbols. Hence the ouput range is 0 to 255.

    Here's the plan of attack.

    							
    const int numLEDs = 5;
    const int ledPins[] = {2,3,4,5,6};
    
    //should be a DAC pin - one with "~"
    const int indicatorLED = 10;
    
    //any of the analog ADC pins
    const int knobPin = A0;
    
    int inputReading;
    
    void setup () {
    	//1. Set the pin modes to output using a loop
    }
    
    void loop () {
    	/*
    	1. Measure voltage at the potentio meters wiper.
    	2. Map the voltage to the indices of the LEDs
    	3. Map the voltage to the brightness of the indicator LED.*/
    }
    							
    						

    Here's the code implementation.

    							
    const int numLEDs = 5;
    const int ledPins[] = {2,3,4,5,6};
    const int indicatorLED = 10; //must be a DAC pin (look for "~")
    const int knobPin = A0; //must be an ADC pin
    int inputReading;
    int mappedValue;
    int indicatorBrightness;
    
    char messageBuffer[80];
    
    void setup() {
    	pinMode(indicatorLED,OUTPUT);
    	for (int i = 0; i < numLEDs; i++) {
    		pinMode(ledPins[i],OUTPUT);
    	}
    	Serial.begin(9600);
    }
    
    void loop() {
    	inputReading = analogRead(knobPin);
    	mappedValue = map(inputReading,0,1023,0,6);
    	indicatorBrightness = map(inputReading,0,1023,0,255);
    	
    	//print values
    	sprintf(messageBuffer,"inputReading:%d; mappedValue:%d; indicatorBrightness:%d;",inputReading,mappedValue,indicatorBrightness);
    	Serial.println(messageBuffer);
    	
    	//adjust brightness of indicator LED
    	analogWrite(indicatorLED,indicatorBrightness);
    
    	//turn on leds
    	for (int i = 0; i <= numLEDs; i++) {
    		if ( i> 0 && i <= mappedValue) {
    			digitalWrite(ledPins[i-1],HIGH);
    		} else {
    			digitalWrite(ledPins[i],LOW);
    		}
    	}  
    }						
    							
    						

    It's your turn. Modify a single line in the code so that the number of active LEDs decreases as the potentiometer is turned.

    Recap

    1. How do we select pins where we can use analogRead?
    2. How do we select pins where we can use analogWrite?
    3. What does the map function do?
    4. What is the range of values that one can measure from the ADC? Why is this the case?

    PROJECT 4: CONTROL EXCHANGE

    PROJECT 4: CONTROL EXCHANGE

    Materials Needed:
    1. 2pcs Arduino Uno or Arduino Nano
    2. Arduino IDE
    3. Bread Board and Jumper Wires
    4. 5V Power Supply (if power is not enough)
    5. 2pcs Potentiometers
    Project Objectives:
    1. Introduce usage of libraries.
    2. Introduce usage of functions for task division.
    3. Make two boards communicate via serial.
    4. Learn how to control servo motors.
    Project Desription

    Two boards will communicate via serial 💬🗨️. Each will control 🎮 the other board's servo motor position via an analog input through a potentiometer.

    Libraries

    Libraries are collection of codea that perform related tasks. Libaries simplify the workflow of the users by providing tools so that the user will not need to reinvent the wheel each time. Libary files end with a ".h" extension.

    Libraries

    Importing libraries can be done in two ways:
    							
    //used for libraries where the compiler
    //knows the location of the library file
    #include <filename.h>
    
    //used for custom libraries in the working directory
    #include "filename.h"
    							
    						

    Servo Control

    Servo motors are DC motor with a built-in control system for keeping track of its angular position. Show below are the inner workings of a servo motor.

    Servo Control

    The control of servo motors are carried out via Pulse Width Modulation (PWM). This works the same way as as the analogWrite function albeit with more inctricacies with the timing of pulses.

    Servo Control

    The Servo library can be imported as show below. This library comes by default with the Arduino IDE.

    							
    //import the Servo.h libary
    #include <Servo.h>
    
    /*
    Any pins can be used to control servo motors but 
    pins 9 and 10 for Arduino Uno and Nano will not be
    //able to use analogWrite even if there are no
     servos connected when Servo.h is imported.
    */
    							
    						

    Serial Communication

    The SoftwareSerial libary allows two devices to communicate via the serial protocol. The said libary simulates the hardware serial and thus making way for many serial communication channels.

    							
    //import the SoftwareSerial libary
    #include <SoftwareSerial.h>
    							
    						

    Here's the plan of attack.

    							
    #include <SoftwareSerial.h>
    #include <SServo.h>>
    
    const int potPin = A5;   // Potentiometer pin
    const int servoPin = 9;  // Servo motor pin
    
    // SoftwareSerial object for communication
    // Receive Pin (Rx) - 4
    // Transmit Pin (Tx) - 5
    SoftwareSerial mySerial(4, 5); 
    
    // Servo motor object
    Servo servo; 
    
    void setup() {
    	//Initialize serial and servo objects
    }
    
    void loop() {
    	/*
    	1. Create separate function that reads incoming data
    	   The said function will also move the servo to the
    	   location specified by the other board.
    	2. Create a separate function that reads the analog
    	   level of the potentiometer. This function would
    	   map the value to the appropriate range and send the
    	   data to the other board.
    	3. Run each function in succession and display the 
    	   data sent and received.
    	*/
    }
    							
    						

    Here's the code implementation.

    							
    #include <SoftwareSerial.h>
    #include <SServo.h>>
    
    // Pins
    const int potPin = A5;   // Potentiometer pin
    const int servoPin = 9;  // Servo motor pin
    
    // SoftwareSerial object for communication
    SoftwareSerial mySerial(4, 5);  
    
    // Servo motor object
    Servo servo;  
    
    void setup() {
    	// Initialize serial communication
    	Serial.begin(9600);
    	mySerial.begin(9600);
    	servo.attach(servoPin);
    }
    
    //function for receiving data
    int readIncoming() {
    	int receivedValue = mySerial.read();
    	servo.write(receivedValue);
    
    	//this is the ouput of this function
    	return receivedValue;
    }
    
    //function for sending data
    int sendData() {
    	int potValue = analogRead(potPin);
    	int mappedValue = map(potValue, 0, 1023, 0, 180);
    	mySerial.write(mappedValue);
    
    	//this is the ouput of this function
    	return mappedValue; 
    }
    
    void loop() {
    	if (mySerial.available()) {
    		int received = readIncoming();
    		Serial.print("Received:");
    		Serial.print(received);
    		Serial.print("; ");
    	}
    
    	int sent = sendData();
    	Serial.print("Sent:");
    	Serial.print(sent);
    	Serial.println(";");
    	
    	//let the servo catch up
    	delay(10);
    }
    							
    						

    Recap

    1. What do we use software libraries?
    2. In the serial communication protocol, how will one connect the Rx and Tx pins of one board to the Rx and Tx pins of the other?
    3. How are servo motors being controlled?

    PROJECT 5: OLED BUT GOLD

    PROJECT 5: OLED BUT GOLD

    Materials Needed:
    1. Arduino Uno or Arduino Nano
    2. 1pc 0.96in OLED Display
    3. Arduino IDE
    4. Bread Board and Jumper Wires
    5. 1pc Potentiometer
    Project Objectives:
    1. Show how to use and OLED Display.
    2. Create a custom function to draw bar graphs.
    3. Locate SDA and SCL pins for I2C Communication.
    Project Desription

    The micrcontroller would read an analog value and show the value and its corresponding voltage level in an OLED display 🖥️ through text and a bar graph 📶.

    I2C Protocol

    Like the serial communication, I2C is one of the commonly used communication protocol out there. Many devices can communicate over two wires namely SCL (serial clock) and SDA (serial data) lines.

    Arduino Uno SDA and SCL Pins
    Arduino Nano SDA and SCL Pins
    Install needed libraries. Head over to the Library Manager in the IDE sidebar. Search and install the following,
    1. Adafruit GFX Library
    2. Adafruit SSD 1306

    Here's the plan of attack.

    							
    
    #include <Adafruit_SSD1306.h>
    
    #define SCREEN_WIDTH 128
    #define SCREEN_HEIGHT 64
    #define BAR_THICKNESS 15
    
    //oled display object
    Adafruit_SSD1306 myDisplay = Adafruit_SSD1306(128, 64, &Wire);
    
    
    void setup() {
    	//Initialize OLED display
    }
    
    void loop() {
    	/*
    	1. Create a separate function that will 
    	   draw a retangle give two opposite corners.
    	2. Read analog signal and map it to the screen
    	   resolution you want.
    	3. Change bar graph and text values.
    	*/
    }
    							
    						

    Here's the code implementation.

    							
    #include <Adafruit_SSD1306.h>
    
    #define SCREEN_WIDTH 128
    #define SCREEN_HEIGHT 64
    #define BAR_THICKNESS 15
    
    //oled display object
    Adafruit_SSD1306 myDisplay = Adafruit_SSD1306(128, 64, &Wire);
    
    void setup() {
    	// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    	if (!myDisplay.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    	Serial.println(F("SSD1306 allocation failed"));
    	for (;;)
    		; // Don't proceed, loop forever
    	}
    	myDisplay.display();
    	delay(500);
    	myDisplay.setTextSize(1);
    	myDisplay.setTextColor(WHITE);
    	myDisplay.setRotation(0);
    	myDisplay.clearDisplay();
    }
    
    //function for drawing rectangles
    void drawRectangle(int x1, int y1, int x2, int y2) {
    	int max_x = max(x1,x2);
    	int min_x = min(x1,x2);
    	int max_y = max(y1,y2);
    	int min_y = min(y1,y2);
    
    	for (int i = min_x; i <= max_x; i++){
    		for (int j = min_y; j <= max_y; j++) {
    			myDisplay.drawPixel(i,j,WHITE);
    		}
    	}
    }
    
    void loop() {
    	int potValue = analogRead(A0);
    	int barLength = map(potValue,0,1023,0,SCREEN_WIDTH-1);
    	float voltageValue = (potValue/1023.0)*5;
    
    	myDisplay.clearDisplay();
    	//draw bar graph
    	drawRectangle(0,0,barLength,BAR_THICKNESS-1);
    
    	//show analog pin reading
    	myDisplay.setCursor(0,20);
    	myDisplay.println("Reading:");
    	myDisplay.setCursor(0,50);
    	myDisplay.setTextSize(2);
    	myDisplay.println(potValue);
    	myDisplay.setTextSize(1);
    
    	//show corresponding voltage
    	char messageBuffer[4];
    	dtostrf(voltageValue,1,2,messageBuffer);
    	myDisplay.setCursor(70,20);
    	myDisplay.println("Voltage:");
    	myDisplay.setCursor(70,50);
    	myDisplay.setTextSize(2);
    	myDisplay.print(messageBuffer);
    	myDisplay.println(" V");
    	myDisplay.setTextSize(1);
    
    	//update screen
    	myDisplay.display();
    }
    							
    						

    Recap

    1. How many wires are needed to establish a working I2C setup?
    2. What was "dtostrf" for?
    3. What's the result of this code below?
      											
      for (int i = min_x; i <= max_x; i++){
      	for (int j = min_y; j <= max_y; j++) {
      		myDisplay.drawPixel(i,j,WHITE);
      	}
      }