Lab 01a

The questions below are due on Thursday March 15, 2018; 09:55:00 PM.


Partners: You have not yet been assigned a partner for this lab.
You are not logged in.

If you are a current student, please Log In for full access to the web site.
Note that this link will take you to an external site (https://oidc.mit.edu) to authenticate, and then you will be redirected back to this page.

Goals:

Today we'll hook up several initial components of our embedded system: the ESP32 microcontroller, the OLED display, and a button. The ESP32 is a powerful Wifi-enabled microcontroller development board built onto a small form-factor board. All the necessary documentation for the ESP32 is on our Reference page. You'll also install several core pieces of software for the semester, before establishing communication between the microcontroller and the OLED, and finally implementing a simple state machine.

Code for today can be downloaded from HERE.

You will work with a partner for this and all other labs in 6.08. Your partner should be listed at the top of the page along with a table location to sit at (all computer stations have numbers taped on them). Go find them!

Although you'll sit with your partner at one of the Macs in 38-530, you will use your personal laptops to access the course website and to interface with the microcontroller. The computers in the class do not have the right software on them.

Please use the 6.08 wireless network (id: 6s08, password iesc6s08) when connecting your laptops to WiFi and eventually your labkit.

Questions? In the lower right corner of your page will be a queue control pop up. If at any time you have a question, add yourself to the classroom queue (remember to add a table number) and a staff member will come around to help you.

1) Software

Today we'll be starting to use our embedded system based around the ESP32. For ease of deployment we'll use the Arduino environment and the add-on ESP32 core. You need to install the following three pieces of software on your laptop:

  • First install Arduino (even if you've already got it, install it again to get the most up-to-date distribution). The Arduino IDE can be found here. Download the appropriate distribution for your laptop's operating system. Note: Windows Users do NOT install the Windows App. Install only the regular Windows distribution!
  • Next install the ESP32 core. It is found here. Download the files and follow the appropriate instructions for Windows.

    • Note the Windows distribution can be a bit tricky to install. In the instructions to install the core, there is a slight error. Where it says "open a Git Bash session pointing to ARDUINO_SKETCHBOOK_DIR and execute git submodule update --init --recursive" instead make sure to open it at [ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32
    • For Mac users pay close attention to what the output of copy-pasting that script says. If there is an error or some text about unicode not existing you need to change the word python to python2.7 in that command string.
  • You'll also need to install a cable driver for your laptop (all OS's need to do this). Go to here and install. Your operating system must be somewhat recent for this to be valid so if you get bugs talk to a staff member. Mac Users: After installing, go to System Preferences > Security and click on the messable about enabling Silicon Labs Drivers on your machine!

When you've installed everything you should be able to open up Arduino and in the top right you should be able to see in the menu Tools>Board>ESP32 Dev Module. If you see that, it means you've installed most of the correct software. Assuming this all went through, we can't do anything else until we get some hardware, so move onto the next part.

2) Hardware

Obtain from staff the parts for today, which for reference are:

  • The ESP32 Dev Board
  • The OLED screen + protector
  • 3 mini breadboards with backpiece
  • A kit of wires
  • A micro USB cable
  • A button switch
  • A padded envelope which you can use to hold everything

With parts in hand, the final test which makes sure everything is working, is to take a peek at Tools>port in your Arduino environment. You should see maybe one thing listed. Take note of what is there. Now take your Micro-USB-to-USB cable, connect it to your ESP32 board and then to the computer, and take a fresh peek at Tools>port. You should hopefully see one more thing. On Windows this will often be something like COM3. On Mac/Unix it will probably be USB_to_UART.... If it appeared, then you are 99% good.

Try Now:

Does your ESP32 appear under Tools>port when you connect it to your computer? If not, ask for help from a staff member.

OK we're now ready to start assembly.

Each partner should build up their own system and do their own wiring. This will be yours to use for the duration of the course, so treat it nicely! You will receive a free replacement for the first part that you break or lose. If you break/lose any other parts, you'll have to purchase replacements.

We will construct our circuits on a proto-board (also known as a "breadboard"), which provides an array of holes into which wires and components can be inserted. Certain rows and columns of the holes are electrically connected together, and the holes have spring-loaded clamps in them providing a convenient way to securely connect components together. Specifically, each column of 5 holes is connected internally. If you insert one end of side of a component into one set of the holes in a column of 5, and then insert one side into a second set of holes across the gap, the pins will not be connected together.

If you were to rip the back off your breadboard (DO NOT DO...we ruined the one in the photo so you don't have to!) you could more clearly see which rows and columns are connected.

breadboards side by side

The back of a breadboard, revealing the metal connectors inside. Note how there are many short connectors (with five connecting holes), usually used for linking signals, and only several longer connectors, often called "rails" or "busses" and which we'll use for power distribution.

The strategy in using a breadboard is to place components at conveniently close locations to one another and then use hookup wires to connect the appropriate electrical pins together to enable communication, control, transfer of signals and power, etc.

There are good and bad ways to wire a breadboard, just like there are good and bad ways to write code. The general rule is to keep the wires short and sweet...avoid large loops. Because poor wiring now will have an impact on you through the semester, we will provide suggested wiring and placement schemes in this and many of the labs.

We'll eventually have quite a few components so we're going to attach three breadboards together for our system, but first, we need to break off both the power rails from the middle breadboard. Take one of your breadboards and snap off the rails connected to either side of the protoboard, like this: (use scissors available in class to cut off the excess paper. Take care not to expose the sticky back just yet).

remove the rails

Remove the rails from the central breadboard.

Here's more of a storyboard for your benefit:

board assembly 1

You'll start with (A) three half-breadboards and a backing piece of acrylic. (B) We'll ultimately want all three boards to sit side-by-side linked together, but as they are, they're too large. Lay them out so that they are in line and can order them. (C) Take the middle one and remove one of its inner rails by bending it down, and (D) with scissors cutting it off carefully. (E) Attach the side to the main central board so you'll end up with (F). (G) Repeat for the other side, to (H) ultimately end up with three boards linked together like shown.

Once the boards are together, we'll mount it on a piece of acryllic for stabilization by exposing the sticky breadboard backing:

board assembly 2

We want the three boards to ultimately be attached to the piece of acrylic to add some stabilization. (A) To accomplish this, lie your breadboard set on its face, and carefully remove the adhesive back (B) resulting in an exposed sticky surface. (C) Take the piece of acrylic and while using your eyes to line it up, (D) attach it to the breadboards and apply pressure on contact (E) so that you end up with a stable board assembly like shown.

Now it is time for parts!

2.1) The ESP32

We'll first place our ESP32 into the breadboard. We recommend placing it where shown in the figure below. We'd like to make sure that the red and blue rails are connected to the pins marked 3.3V and 0V/GND on your ESP32, respectively. This is achieved using some short wires like so:

esp connection

Adding two wires to connect where the ESP32's 3.3V and GND pins will be to the rails we want to be 3.3V and Ground.

Then insert the ESP32 like shown:

esp to oled connections

Gently insert the ESP into the breadboard so its pins line up with the wires we just inserted.

The ESP32 has a ton of pins on it in addition to those that channel power. A list is shown below:

esp pinout

A pinmap of most of what we'll use the ESP32 for this semester. Don't worry we'll go over what many of these acronyms mean in good time.

Although we'll want to gradually gain experience hooking up components by referencing their data sheets, today we'll walk you through the connections in detail.

On the ESP32, we'll very often be using pins with labels that look like IO4 or IO15, etc. The "IO" part stands for Input/Output, and the number following that. These pins can often serve several purposes, depending on how they are configured in the setup function of our code. We'll use a subset of these pins now to establish communication with our OLED display.

2.2) The OLED Display

oled

Our nice OLED

The OLED display is an Organic Light Emitting Diode screen. What's nice about an OLED screen is that it is beautiful to look at, and it consumes relatively little power when not displaying text since there is no backlight (we'll investigate this a lot in a few weeks). The OLED has seven connections/pins:

  • power supply (3.3 V) - VCC
  • ground (0 V) - GND
  • five wires that serve as data connections for the SPI communications interface

oled schematic

OLED schematic. This is how we'll hook it up.

We'll learn more about SPI in the future, but for now we just need to know that the SPI set of wires (or "bus" as we call it in EECS) is how the microcontroller will communicate with the OLED screen to tell it what to display. We just need to connect the OLED SPI pins to the corresponding pins on the ESP32.

The OLED screen is fragile! You can break it by bending it, sitting on it, dropping it, etc. This is why we have a custom-made cover for it. Keep the cover on! In addition you may notice the plastic screen film is still in place. That also helps minimize unwanted cracks, although we know it can look ugly.

We recommend placing the OLED to the left side of your setup.

esp to oled connections

We want to place the OLED here.

To connect the OLED to power we'll use short wires to connect its pins back to the available +3.3V and GND power rails.

esp to oled connections

We'll use some more wires to route a power path to the OLED.

Then by paying attention and tracing the pin connections found in the schematic above we add appropriately sized wires for the five data lines. Note the wires in your kit are color-coded based off of length. The end result should be the similar to the image below. Your board does not need to look exactly the same, but if you want any custom-length wires, ask a staff member and they can show you where/how to do that.

esp to oled connections

Wires in place, without and with our OLED in place.

Before moving on, notice how in the left panel of the figure above we've added two extra jumper wires to connect to the lower +/- rails. This means that the second set of +/- rails is now also capable of providing 3.3V and GND as needed. We'll use this in a little bit!

2.2.1) Getting the OLED to Work

We now want to do something with that OLED. In order to do that we'll need to download a library. In the Arduino environment one way to do that is to go up to Sketch > Include Library > Manage Libraries and then search for U8g2. Install this library when it comes up. It has Some really great documentation that we'll use a lot this term.

Then in the starter code for today's lab (linked here for your benefit). Extract the file, open up OLEDtest.ino, make sure that under Tools>board you have ESP32 dev module chosen, and make sure the Tools>port is pointing to the ESP32. Finally click the right arrow to compile and upload to your ESP32 (make sure you're hooked via the USB cable).

If all is right with the world, you should see a bunch of images and other stuff flying by in a repeating loop. If not ask for help!

2.2.2) The Code in Detail

We now should look a little bit through this code and go over some of the main pieces. It will serve as a good example of the high level structure of many of our microcontroller-based programs for this term. Our microcontroller scripts will have a .ino suffix. There are always two primary functions defined in these .ino-type files: setup and loop, both of which take no arguments. When building and compiling code to place on the ESP32 microcontroller, the software will place these two functions into the following general file type:

#include <Arduino.h> //a library that contains basic libraries/functionality we use!
//all outside code gets placed in the following form.

void main(){ //runs once
	setup();
	while (1){ //infinite loop
		loop(); //runs repeatedly because it is in the infinite loop
	}
}

The net result is that setup runs once, and loop runs repeatedly and as fast as we allow it. The speed at which these functions execute is based on what exactly we do within them. Each line of code that we place in a function comes with certain computational cost, and roughly speaking, these computational costs correspond to time.

2.2.3) Communication

Writing code of more than a few lines is very difficult without an ability to debug it. In the Arduino environment, we can gain an insight into what the code is doing using communication up via the USB cable. In the Arduino environment, we achieve this with the Serial library. We initialize Serial communication by saying Serial.begin(115200); in the setup function and can report information up to the computer by calling Serial.print or more often Serial.println and using the Arduino Serial Monitor (Tools > Serial Monitor) to see what pops out.

2.3) Switch

We now have an output (the OLED). Next we'd like to add an input. For this lab we'll add a simple switch. It comes in three pieces and you can snap it together like a LEGO. Before we do that a quick note on schematic notation. Because we will very often use the 3.3V pin and the GND pin to provide power to components, we often don't explicitly show connections when drawing them in schematics. Instead we'll show those connections going to a common point and it is assumed that they are connected "off-page".

vcc and gnd

VCC (which on our board is 3.3V) and GND are used so often we will generally use the shorthand shown above to represent the connection.

OK now let's add in a switch to IO pin 15.

switch spot

The final schematic that we want with our switch.

The switches we can use can be shoved into the breadboard like shown. Take care to position it in the right orientation.

esp to oled connections

There are four pins on these switches. Each side of the switch is connected to two pins.

Earlier in our wiring adventure we connected up the lower +/- breadboard rail to 3.3V and GND and hid that connection underneath the OLED (take a look at Figure 12). This now allows easy wiring of our switch like shown below:

esp to oled connections

Everything all connected! With minimal long wires. Delicious.

3) Our First Input

How do we use this switch on the software side? We need to set it up as a digital input. This is accomplished by calling the pinMode function and specifying the pin number of interest and that we want to set it as an input. In the switch circuit that we just built, when the button is pushed, it connects the two terminals together, such that the ESP32's Pin 15 is now connected to GND, and thus the input that is read is a binary 0, which we often call a "LOW". What is the value when we're not pushing the button? If you think it is "HIGH" that's sorta correct...but also sorta not. A HIGH voltage of 3.3V does not just magically appear. It needs to be placed there just like a voltage of 0V needs to be placed onto the pin. In the current configuration, only one state has a well-defined voltage...namely the pushed state will show up (0V). When the button is unpushed, the state of the digital pin is actually undefined! This can lead to weird behaviors where the digital input randomly reads weird values and your code could interpret this as button pushes and not button pushes. In order to fix this, we therefore need to specify the input as a particular type called a pull-up and this can be done with specifying INPUT_PULLUP for the second argument.

How can we just use a random word like INPUT_PULLUP? It is because in other hidden files that are being used, that variable name is defined as a particular number that the pinMode function knows how to handle.

The result of doing this is that in the unpushed state the inputs to the digital pins are "pulled-up" to 3.3V which is a HIGH voltage, meaning that in both the pushed and unpushed states the state of our switch/digital pin circuit is now well-defined.

3.1) Active High vs. Active Low

You may be a little weirded out because in an unpushed state, our switch is "HIGH", and in a pushed state our switch is "LOW". This configuration is known as "Active Low" and is actually very common in electronics. Much less common in electronics is "Active High" which is when a switch's state is LOW normally and HIGH upon being pushed. From the engineering perspective of our class, we won't worry about the reasons for this bias towards Active Low, but we do need to use this knowledge to know how to interpret signals read in on our digital inputs.

So attached is some starter code.

int input_pin = 15;

void setup(){
    Serial.begin(115200); //initialize serial!
    pinMode(input_pin,INPUT_PULLUP); //sets IO pin 15 as an input which defaults to a 3.3V signal when unconnected and 0V when the switch is pushed
}

void loop(){
    int value = digitalRead(input_pin);
    if (value){
        //executed if pin is HIGH (voltage of 3.3V)
       //code here for printing "unpushed" 
    }else{ 
        //executed if pin is LOW (voltage of 0V)
       //code here to print "pushed" 
    }
}

Copy-paste the code above into Arduino, and update the code shown above to print a message of "pushed" or "unpushed" on the serial monitor when the button is being pushed or unpushed, respectively.

Make sure that you have the correct BAUD (115200) chosen in your Serial monitor, otherwise a bunch of random question marks will fly by.

Checkoff 1:
Show your button-controlled Serial-printer to a staff member.

4) Stateless vs. Stateful

When running this code, when you press a button, your serial monitor displays one thing, and then when you don't press the button, another thing gets printed. At any point in time your system behaves based only upon its current input value and nothing else. It has no state.

State means "state of being" or something along those lines. Basically a system/device/machine in which what it does at any point in time is based not only on its current inputs, but also its past inputs is what we'll call a state machine. Most things in life that do anything interesting can be classified as one form or another of a state machine. Another way of thinking about state machines is that for a given identical input, its response might vary depending on its state, while a stateless system will always act the same way given an input. Me eating food is a state machine...I see food and I eat it...then I see food and I eat it...then I see food and I'm like, I'm good/full. My pet dog growing up was a stateless machine...he'd see food and eat it and then see food and then eat it...and so on...he'd never stop. (obv a joke because eventually he'd have a vomit output).

Anyways one way to visualize state machines is by using a state flow diagram like the one shown below, where upon starting the system begins in State 0. In State 0, if the getInput() function returns a 5, the machine moves into State 1, otherwise, it stays in State 0. If the machine is in State 1, if it gets a 2 as an input it moves to State 0, otherwise it stays in State 1.:

simple fsm example

State machine example.

In code form for our ESP32, this would look like the following.

int state;

void setup(){
  Serial.begin(115200);
  state = 0; //initialize!
}
    

void loop(){
  //handle state transition if it happens
  int input = getInput();
  if (input==5 && state==0){
    state = 1;
  }else if(input==2){
    state = 0;
  }
  //handle output based on state:
  if (state==1){
    Serial.println("Wheeee.");
    //do something corresponding to state 1
  }else{
    Serial.println("Whoooo.");
    //do something correspondign to state 2 
  }
}

If the system just printed "Wheeee.", and getInput() returns a 2 on its next call, what will the next thing printed by the system be?

Following the event in the previous question if call getInput() again returns a 2, what will the next thing printed by the system be?

The key piece here is that there is some sort of variable which "lives" beyond the scope of the function in question. In particular in our situation that is the loop function. Every time loop starts over, any variables declared within it are new, so having a variable outside of its scope allows us to remember information beyond each "run" of loop. We'll obsess over state variables more in the next lab. `

5) Final Assignment

We'd now like to implement a state machine that acts in the following way:

Use the code button_start.ino in the lab01a.zip download for a starting point (compile and upload it first to make sure it works). The system:

  1. Starts displaying one word or image on the OLED (your choice. Feel free to experiment).
  2. When the user presses and then releases the button it starts displaying another image (again your choice, but it needs to be significantly different).
  3. When the user presses and then releases the button again it goes back to displaying the first image.
  4. The system can continue to toggle between these two images based on button presses forever.

A rough state diagram looks like the following:

switch flow diagram

This is a switch flow diagram for our two-image state machine!

Note you may find adding a line like at the end of your loop function delay(20); helpful if your system is acting really weird. Also in coming up with images to create, refer back to the u8g2 documentation!. You can do pretty much anything...

Your final system should look like something similar to the functionality in the video below:

Checkoff 2:
Show your working button state machine to a staff member, discuss your design and code, and answer any questions the staff has about the wiring you carried out. Staff will stress test to see if it works well or not.