Embedded stream January 3rd-10th 2021 Platinum sponsors: Gold sponsors: Silver sponsors
←
→
Page content transcription
If your browser does not render page correctly, please read the page content below
TABLE OF CONTENTS Chapter 1 – Intro to embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 The Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Too Much Information!!! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Chapter 2 – micro:bit basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Using the micro:bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Our first program (variables, the micro:bit display and sleep) . . . . . . . . . . . . . . 4 Working with the display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Working with Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 More scrolling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 The REPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Printing to the console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Using loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Buttons and if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Was it pressed? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 The for loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 The range function - counting in a loop . . . . . . . . . . . . . . . . . . . . . . . . . 11 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Debugging Your Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Where to go from here . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Chapter 3 – Electronics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Voltages and Currents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Relating voltage and current: resistance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Series and Parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Analog and Digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Aside: Ground . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Voltage Dividers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Calibrating Sensors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Pull-up and down resistors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Does it matter if I use pull-up or down? . . . . . . . . . . . . . . . . . . . . . . . . . 20 Level shifting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Why do we even need this? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Light Emitting Diodes (LEDs) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Powering an LED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 More notes on current limiting resistors . . . . . . . . . . . . . . . . . . . . . . . . . 22 Chapter 4 – Program State, Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Program States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 High Scores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Detecting Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Program modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 Example Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Chapter 5 – Grove . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Grove and micro:bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Summary Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Grove Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Other non-Grove Components: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Pulse Width Modulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Built-in PWM Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Servo Motors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Regular Motors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 NCSS Embedded Stream i
Continuous Rotation Servo Motors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Standard Servos (angle) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Controlling a servo motor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Extra: Stepper motors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Chapter 6 – Radio, Files, Plotting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Radio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Groups and addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Plotting with Mu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Chapter 7 – Robotics with the micro:bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Getting Started with bit:bots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Neopixels & programmable LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Programmable LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Colour mixing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Extra: Other types of programmable LED . . . . . . . . . . . . . . . . . . . . . . . . 49 Line following . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Theory of Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Control Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Ultrasonic distance sensing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Chapter 8 – Filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Windowed Averaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Exponential Averaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 The downside of filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Chapter 9 – Integrated circuits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Pin numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Datasheets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Digital signalling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 UART . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 SPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 I²C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Chapter 10 – Pyboard, Quokka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Uploading code to the pyboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Programming on the pyboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Programming the Quokka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 More info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Chapter 11 – Learn more . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Electronics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Mathematics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Other . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Lab Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 About the labs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Installing Mu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Lab 1 – Monday morning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Lab 2 – Monday afternoon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Lab 3 – Tuesday morning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Lab 4 – Tuesday afternoon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Lab 5 – Wednesday morning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Lab 6 – Wednesday afternoon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Lab 7 – Thursday morning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Lab 8 – Thursday afternoon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 NCSS Embedded Stream ii
CHAPTER NCSS Embedded Stream Chapter 1 – Intro to embedded Electronic devices are everywhere in modern life. From light bulbs to toasters to computers and cell- phones, we rely on the principles of electronics to survive in modern society. As technology develops, perhaps the more surprising development is that even seemingly simple items are no longer just basic electronic devices, but rather are becoming embedded devices that incorporate small microcontrollers to make them operate in increasingly smart ways. A simple light bulb is no longer just a wire that glows, it’s a complicated circuit that includes a computer to monitor and control the brightness and power consumption of the light. Toasters and kettles contain little microcontrollers to allow fine adjustment of temperature and cooking time. Even the charger for your phone or laptop contains a little computer to ensure safe charging of your device and to improve the efficiency with which it can operate. And this is WAY before we even start to consider the proliferation of internet enabled devices that are permeating our households. So what is an embedded device? It’s a small, programmable computer, tightly integrated with a set of sensors and mechanical parts to enable it to perform a specific task. As microcontrollers have become smaller, cheaper and more efficient, we’ve been able to put them into an ever increasing number of places, and to do ever more complicated tasks with them. And this is exactly what the focus of this course is going to be! Learning how to design simple electronic devices to perform some specific task of choice. Since we are talking about embedded design, the course focusses not just on software development, but includes a certain amount of electronics as well. We don’t want to get stuck in the land of pure software work. An embedded engineer is often plugging and unplugging, working with motors, sensors, lights and speakers to make devices that interact with the real world. Of course, this also brings along with it a measure of pain…. Over the course of the coming days you will deal not only with buggy code, but faulty wiring and the crazy interaction between the messy real world and the devices you are making. But ultimately, that’s why we love embedded development so much. As painful as it is to deal with the real world, making devices that we can touch, see and hear, whether it’s for a specific purpose, art, or fun, brings along with it a huge amount of satisfaction. We’re super excited to get started! The Project By the end of the camp, you and a group of 3/4 other team members will have developed: • an interactive art piece or game that uses a small microcontroller, a host of sensors and various lights and outputs • a 3-5 minute video that introduces your project and your team, and goes through the process of developing your final project. In order to successfully accomplish these tasks you will learn not only about programming and electronics, but about how to work together as a group to juggle completing the various tasks before the deadline on Saturday night. Just like a real project there are a lot of things to figure out, tight deadlines to meet, compromises you’ll have to make, and limited resources to manage (you can’t hire more people ). You’ll learn about the techniques that real tech companies use to manage teams and, by the end, have taken a project from idea to realization. One of the most important lessons to learn over the course of the camp is that engineering real products is not a solo effort, but will require you to work with others to optimally utilize the talents and skills of everyone in your group, and that effective team management plays as much of a role in making something as being a boss coder. As the course progresses, you will get more information about exactly what sort of project you and your team will be working on. Too Much Information!!! Given the amount of material that we need to cover in a very limited amount of time, the academic portion of the camp is going to be quite jam packed. There will be two 30-minute-to-1-hour lectures per day, as well as two 3-hour lab sessions to put theory into practice. Since there is so much to cover, the lectures will move through the content quite quickly. This year, we are also going to have opportunities for some in-lab lectures that will go through some of the key concepts in a bit more detail. In addition to this, this booklet serves as both a reference for the material covered during the lectures, and a source of additional information. If you want to double check something that was previously covered, it should be available in this book. Owen Brasier/Sebastian Pauka/Alex McCulloch 1
CHAPTER NCSS Embedded Stream Don’t be concerned if you don’t understand everything in the lectures or labs first time around. Every- thing progresses at an incredibly fast pace. If you do get stuck, ask one of the lecturers (Owen, Seb or Alex), your group leader (Steven, Teigan or Emma), or one of the many tutors for help. The only silly question, is the one that doesn’t get asked! Chances are, if you have a question, you’re not the only one in the room who has the same issue. The tutors are very happy to help, and the lecturers are more than happy to go through things in more detail, but we can only go through things if you ask us questions! Not only are we here to help you understand concepts, but we are also more than happy to help you find tricky bugs in your program, or figure out wiring issues in your bots. Feel free to talk to us about what being at university is like, how we like Computer Science as a subject, and how we ended up in computing. We’ll be here all week! Finally, don’t forget to experiment. One of the best things about embedded is that you can wire things up and see how they react in the real world. Although parts of the course are online, the joy of embedded is in seeing lights flash, motors spin and speakers beep. Feel free to grab anything from the drawers over the course of the week and play around with them. If you want help getting something to work ask. Owen Brasier/Sebastian Pauka/Alex McCulloch 2
CHAPTER NCSS Embedded Stream Chapter 2 – micro:bit basics The BBC micro:bit is a small, handheld, embedded microcontroller, which we will be using for the duration of the camp. Apart from being very friendly, it has a huge number of peripherals that we can use: a compass, an accelerometer, a thermometer, a radio for chatting between 2 micro:bits, two buttons and a 5x5 display. More importantly, it can run Python! The aim of the next few days will be to get up to speed on the various features of the micro:bit. (a) (b) Figure 1: A micro:bit To get started we’re going to be working through the micro:bit Crash Course, (https://groklearning. com/course/ncss-2021-embedded/), on the Grok Learning platform. For those of you who have done the NCSS Challenge, this will be a very familiar interface, with the key difference that our code will now be running on an emulated micro:bit in the browser. The crash course will go through everything you need to play around with the micro:bit, as well as getting you up to speed on Python. In addition to the course, we give a brief description of the peripherals of the micro:bit below, and go through some key Python concepts, which you can use as a reference if you ever get stuck. Getting Started If you’re using the micro:bit emulator on Grok, you don’t need anything more to get started. If you’re playing around with a real micro:bit, you’ll need a couple of other things to get going. When you plug the micro:bit in to your computer, it will show up as a drive connected to your computer. The micro:bit expects you to copy a .hex file which contains the compiled program that you want to run. There’s a couple of ways to get this file. If you are using the Grok interface to write your programs, you can hit the “Download” button in the top right corner of the editor to download the hex file that corresponds to your written program. Then, copy and paste that file onto the micro:bit drive that appears. Owen Brasier/Sebastian Pauka/Alex McCulloch 3
CHAPTER NCSS Embedded Stream Figure 2: The Grok Learning Interface Alternatively, you can use the Mu editor - a simple editor for Python that interfaces directly with the micro:bit, and is able to upload code directly to the micro:bit. During the micro:bit labs, this will be the main editor we use. The editor can be downloaded from https://codewith.mu/en/. By pressing the Flash button in the editor, we can upload the code to the micro:bit. Once the upload is completed, the micro:bit will automatically reset and start running your new program. Figure 3: The micro:bit buttons in the Mu editor Note: If this set of buttons doesn’t appear in your copy of Mu, you may not be running in the “micro:bit” mode. You can change the target in Mu by pressing the Mode button in the top left corner of the editor. Using the micro:bit Plug your micro:bit into the computer with a USB cable, open up Mu and let’s start coding! Our first program (variables, the micro:bit display and sleep) Let’s start by showing an image on the display. from microbit import * # Show an image on the 5 x 5 display display.show(Image.HAPPY) What’s happening here? The first line is to import the microbit module, which gives us access to the functions and constants we’re going to use. The third line is a comment line. In Python, all comments start with a # character and allow us to add some descriptive text to our code. display.show is a function that displays an image, while Image.HAPPY is the image we want to display. It looks like a happy face :) There are a long list of in-built images in the microbit documentation: https://microbit-micropython. readthedocs.io/en/latest/tutorials/images.html. To show multiple images, we need to sleep in between instructions. Without it, the micro:bit runs so fast that the images will flash faster than our eyes can see. The parameter of sleep is in milliseconds, so sleep(1000) means sleep for 1 second. So someone’s life story told in 3 seconds might look like this: Owen Brasier/Sebastian Pauka/Alex McCulloch 4
CHAPTER NCSS Embedded Stream from microbit import * # Show an image on the 5 x 5 display display.show(Image.HAPPY) # Wait for a number of milliseconds sleep(1000) display.show(Image.SAD) sleep(1000) display.show(Image.SKULL) sleep(1000) display.clear() In the above code, we are sleeping for 1 second after each image. If we wanted to change this time, we would have to change our code in 3 different places. Rather than write our code like this, we can store the amount of time we want to sleep in a variable, and reuse that value multiple times in our code: from microbit import * # Set the wait time wait_time = 2000 # Show an image on the 5 x 5 display display.show(Image.HAPPY) # Wait for a number of milliseconds sleep(wait_time) display.show(Image.SAD) sleep(wait_time) display.show(Image.SKULL) sleep(wait_time) display.clear() Now, we can change all of the the delay after each of the images while only changing a single number in our code! At various points in the program we can change or manipulate the values we store in variables. For example, here is a program that doubles the wait time after we show the SKULL image. On line 13 of our program we multiply the value stored in the variable wait_time and store it back into that same variable. from microbit import * # Set the wait time wait_time = 2000 # Show an image on the 5 x 5 display display.show(Image.HAPPY) # Wait for a number of milliseconds sleep(wait_time) display.show(Image.SAD) sleep(wait_time) display.show(Image.SKULL) wait_time = wait_time * 2 sleep(wait_time) display.clear() Working with the display The display module does more than just setting pre-built images. It’s also possible to individually control each LED, including making them brighter and darker. We can programmatically do this with the display.set_pixel function. Owen Brasier/Sebastian Pauka/Alex McCulloch 5
CHAPTER NCSS Embedded Stream The display works on a grid with (0, 0) at the top left of the display: Figure 4: A happy micro:bit For example, to control a single pixel we can write: from microbit import * display.clear() #set_pixel(x,y,brightness) 3rd light across and 4th down because we count from 0 display.set_pixel(2, 3, 9) The above code will do two things. First it clears the display, getting rid of any previously displayed image. Then, since each pixel has a brightness from 0 to 9, the code above sets the pixel at coordinate (2, 3) to full brightness. As another example the code below will turn off the previous pixel, and light up the middle pixel, at coordinate (2, 2), at half brightness instead: from microbit import * display.set_pixel(2, 3, 0) display.set_pixel(2, 2, 5) Working with Text We can also use the display.show function to print out some text: from microbit import * # Show text on the 5 x 5 display one letter at a time display.show('Hi NCSS') That code flashes letters, and I find it ends up being a bit hard to read. Much better to scroll text instead: from microbit import * # Scroll text on the 5 x 5 display display.scroll('Hello there, this text is scrooooooolling') Much like with numbers above, we can also store strings (that is text data) in variables as well. from microbit import * text_data = 'Hello there, this text is scrooooooolling' # Scroll text on the 5 x 5 display display.scroll(text_data) Owen Brasier/Sebastian Pauka/Alex McCulloch 6
CHAPTER NCSS Embedded Stream Whenever we want to store text data, we need to wrap it in either ', or " characters. This tells Python that the data that follows should be stored as text and not interpreted as code. We can also add different strings together in order to make longer strings. For example, to print our name we might write code that looks like: name = "Sebastian" display.scroll("Hi " + name + ". Welcome to NCSS!") This code will scroll Hi Sebastian. Welcome to NCSS! across the screen. One final caveat to keep in mind, text and numbers behave differently. By wrapping strings in ' or " characters, we’re telling Python that values stored there should be treated as text. For example, in the below code: text_data_1 = "1" text_data_2 = "2" final_value = text_data_1 + text_data_2 the value stored in final_value will be "12", and not 3, since we’ve told Python that both of those values are strings. We can convert from strings into numbers by running the int function on the text. If our code was instead: final_value = int(text_data_1) + int(text_data_2) the values stored in text_data_1 and text_data_2 are converted to numbers before being added, so final value after running this code will be 3. We can also do the opposite conversion, taking a number variable and turning it into text. For example: number_data_1 = 1 number_data_2 = 2 final_value = str(number_data_1) + str(number_data_2) In this case, final value will be "12" as before. More scrolling To scroll integers, we need to convert them to a string using the str function before scrolling: from microbit import * num = 50 # Scrolling text with joining display.scroll('The answer is: ' + str(num)) We might want to repeatedly scroll text: from microbit import * # Scrolling text with repetition display.scroll('LOLOLOLOLOLOLO', loop=True) That text will keep repeating, to speed it up we can use the delay key word argument (called a kwarg). from microbit import * # Scrolling text with repetition and change of speed display.scroll('LOLOLOLOLOLOLO', delay=50, loop=True) The delay specifies the delay in milliseconds between each frame. Find more info on scrolling text in the docs: https://microbit-micropython.readthedocs.io/en/latest/dis- play.html?#microbit.display.scroll. Lots of those options are available in display.show too! Owen Brasier/Sebastian Pauka/Alex McCulloch 7
CHAPTER NCSS Embedded Stream The REPL In addition to uploading scripts to the micro:bit and running them, we can also interactively run single lines of Python code on the micro:bit. The easiest way to access the REPL on the micro:bit is using the Mu editor, where we can press the REPL button in the menu bar. This will stop the currently running program and bring up a prompt, into which you can type Python expressions. Note: REPL stands for “Read, Evaluate, Print, Loop” For example, try bringing up the REPL and type the following lines: >>> display.show(Image.HAPPY) Opening the REPL causes whatever code is uploading to stop, however we can also restart our running code, either by pressing the “reset” button on the micro:bit, or pressing in the REPL window. Note, you won’t be able to upload code to the micro:bit while the REPL window is open. If you want to upload a new program, you will have to close the REPL window first. Printing to the console It’s annoying to try and look at a scrolling error message on the LED display. Instead of printing to the display, we can output straight to the REPL window. Upload the following code to your micro:bit. # Output text to the console print('Hello, World!') That will print messages to the serial console so you can get an actual output, to do some debugging. In Grok, look at the “Output” tab to view the serial output in the simulator. In Mu to see what the output looks like, you can open the REPL window, and either hit the “reset” button on your micro:bit or press in the REPL window to start your program going again. Your message will show up in the window: Figure 5: Printing to the REPL If we want to print a number inside of a string, just like with the display, we need to convert that number into a string: answer = 42 print('The answer to life the universe and everything is ' + str(answer)) Using loops In embedded systems, we generally want them to run for as long as power is being supplied to the.. So we really want them to do things forever. The while loop is a language feature we can use to do this. It comes in the form while :, when the condition evaluates to True then the loop repeats. Owen Brasier/Sebastian Pauka/Alex McCulloch 8
CHAPTER NCSS Embedded Stream At the beginning of each loop, the condition is checked to see if the loop continues. Let’s take a look a program that does this: from microbit import * # Run this animation forever while True: display.show(Image.HEART) sleep(500) display.show(Image.HEART_SMALL) sleep(500) The code that we want to run is indented to indicate that it is all part of the while loop. Un-indented code will not be run as part of the loop. For example, in the following code: while True: print("Hello, World!") sleep(500) print("Goodnight, Moon.") The phrase “Hello, World!” will be printed indefinitely, while “Goodnight, Moon.” will never be reached. In addition, in both the above loops, since True is always True (duh…), the above code will repeat forever. It is an infinite loop. We can also write a loop that will terminate once a certain action has taken place. For example, in the following code: i = 0 while i < 5: print("The number was: " + str(i)) i += 1 print("Done") the condition i < 5 is true until i == 5, so this code will print: The number was: 0 The number was: 1 The number was: 2 The number was: 3 The number was: 4 Done Buttons and if statements The microbit module gives us button_a and button_b objects to use the buttons. To simply see if the button is pressed down right now, use the is_pressed() function, as the name would imply. from microbit import * # Show a heart while the button is pressed while True: display.clear() if button_a.is_pressed(): display.show(Image.HEART) sleep(1000) Notice the use of the if statement. Like the while loop, the if statement will check whether the condition is True, and run the indented code if it is. In the above code, the function display.show(Image.HEART) is run if button_a.is_pressed() is True. Since sleep(1000) isn’t indented relative to the if statement, it will always be run. We can connect an if statement with an else statement, which will run if the condition is False: Owen Brasier/Sebastian Pauka/Alex McCulloch 9
CHAPTER NCSS Embedded Stream from microbit import * while True: if button_a.is_pressed(): display.show(Image.HEART) else: display.show(Image.GHOST) The elif statement lets us add even more options! from microbit import * while True: if button_a.is_pressed(): display.show('A') elif button_b.is_pressed(): display.show('B') else: display.clear() What would happen here is both button_a and button_b are pressed? Was it pressed? While the is_pressed() function will tell us whether a button is currently being pressed, often what we want to do is check whether a button was pressed at some point in the past. For this purpose we have the was_pressed() function The was_pressed() checks: between this time and the last time we checked, was the button pressed? One thing to remember: the was_pressed() function will return True ONCE for a button press. If we run it again without releasing the button, it will subsequently return False. For example, we can write a simple stopwatch with a reset button like so: count = 0 while True: # Show the current count, and increment it display.show(count) count += 1 # If button a was pressed at some point, reset the count if button_a.was_pressed(): count = 0 # Wait 1 second before continuing sleep(1000) If we were to replace was_pressed with is_pressed, we wouldn’t be able to detect any button presses that occurred during the sleep. In other words, we would have a pretty rubbish reset button…. Lists So far, we’ve stored strings and numbers in variables. In Python we can also store lists of values. To create a list, just as we wrap strings in quotes ("), we wrap lists in square brackets ([). Let’s create a list of the first 10 prime numbers: primes = [2,3,5,7,11,13,17,19,23,29] We can access values inside a list by indicating its position inside square brackets. Note that in Python, and indeed in almost all programming languages, the positions of values starts from 0. To add the first two prime numbers together we can write: first_two_primes = primes[0] + primes[1] display.scroll(str(first_two_primes)) Owen Brasier/Sebastian Pauka/Alex McCulloch 10
CHAPTER NCSS Embedded Stream We can also add and remove items from a list. To add a few more primes into our list we can use the append function: primes.append(31) primes.append(37) To remove primes from the list, we can use the remove function if we know which value we want to remove, or the pop function if we know its position. primes.pop(0) # Remove the first prime number primes.remove(11) # Remove prime number 11 After these two lines, our list will contain: [3,5,7,13,17,19,23,29,31,37]. If we’re ever in any doubt, we can print the list to the console, which will give you the contents of the list: print(primes) We can also always get the length of a list using the len function, which will return the number of items in a given list as a number: list_length = len(primes) print("The number of primes I know is: " + str(list_length)) There are more things that we can do with lists that are documented in the Python language. When you have a chance you can find this documentation at: https://docs.python.org/3.4/tutorial/ datastructures.html. Note that this also includes some additional data structures that you might find helpful later on. The for loop Aside from while loops, Python also allows us to loop over lists of things. For those of you that have done some programming before, this is a for-each type loop. Note that this is different from the for loops you may have seen in other languages, such as C. The syntax for a for loop is: for in : # do something with each value This loop will loop over each value in the given list, and put the value into the variable given. For example, if we are working with the list of primes we made before, we can do something like: for prime in primes: print("One of the primes I know is: " + str(prime)) This will print out each of the primes that is in the list. The range function - counting in a loop One of the really common patterns that we run into when we’re coding is we want to loop up to a certain number of times. In the above code, we used a while loop to accomplish this. i = 0 while i < 5: print("The number was: " + str(i)) i += 1 print("Done") Now that we know about the for loop, we can simplify this code quite a bit by using a list and a for loop: for i in [0,1,2,3,4]: print("The number was: " + str(i)) print("Done") This will print out exactly the same count as before, but we can do even better using the range function, which creates a list of numbers for us, rather than requiring us to do it manually: Owen Brasier/Sebastian Pauka/Alex McCulloch 11
CHAPTER NCSS Embedded Stream for i in range(5): print("The number was: " + str(i)) print("Done") We can use these loops like this to replace most code that requires a range of values to be used. For example, if we wanted to turn on the fourth row of the micro:bit display we can write: from microbit import * for x in range(5): display.set_pixel(x, 4, 9) The equivalent code is: from microbit import * display.set_pixel(0, 4, 9) display.set_pixel(1, 4, 9) display.set_pixel(2, 4, 9) display.set_pixel(3, 4, 9) display.set_pixel(4, 4, 9) but that’s pretty boring to write (and more error-prone). So use a for loop instead. Note: The range function can actually do a whole lot more than count from 0 to n, you can set start, and step values too. If you’re curious, the full documentation is available here: https://- docs.python.org/3.4/library/stdtypes.html#range Functions So far we’ve been using lots of functions in Python like sleep or print. These functions are defined by someone else, and we can just call that function to use it. But you can also write your own functions just like these ones! Say we just wanted to show the HAPPY image, sleep for 1 second, the clear the display. from microbit import * def happy(): display.show(Image.HAPPY) sleep(1000) display.clear() happy() # Call the function The def keyword defines the function happy, and calling the function by using the name and parentheses (in this case happy()), runs the lines of code inside the function. It’s possible to send information into a function, via a parameter or argument (sometimes we abbreviate this and call it an arg). Let’s look at an example: from microbit import * def happy(ms): display.show(Image.HAPPY) sleep(ms) display.clear() happy(300) # Call the function, sleep for 300 ms happy(500) # Call it again, sleep for 500 ms this time Here we defined that there is a parameter to the function happy called ms, and each time we call this function, we use the value of that is passed for ms. The argument ms behaves like a variable, but importantly it only exists within the function itself. In the above example, the first time we call happy, ms is set to 300, the second time we call happy, ms is set to 500. Owen Brasier/Sebastian Pauka/Alex McCulloch 12
CHAPTER NCSS Embedded Stream We can also get information back from functions by using the return statement. For example, we can write a function that converts from celcius to farenheit. from microbit import * # Conversion functions for C->F and F->C def celcius_to_fahrenheit(c): farenheit = c * 9 / 5 + 32 return farenheit # Shows the current temperature in C and F def show_temperature(): c = temperature() f = celcius_to_fahrenheit(c) print("micro:bit temperature in celcius: " + str(c) + "C") print("micro:bit temperature in farenheit: " + str(f) + "F") # Update the temperature once per second while True: show_temperature() sleep(1000) Debugging Your Code Apart from being a great way to quickly test out the functionality of the micro:bit, the REPL also allows you to find errors in your code. For example, let’s upload this buggy code to our micro:bit. from microbit import * for i in range(10): display.show(i) sleep(1000) display.show(done) This above code has an error in it, we’ve forgotten to put quotes around the string done. As a result it will crash once it finishes counting to 10. The micro:bit will scroll the error message across the display, however this is usually slow and difficult to read. We can get the same information by opening the REPL and restarting our program, either by pressing the “reset” button on the micro:bit, or by pressing in the REPL window. Once we run into the error, the full error message will be output to the console. Figure 6: A Python traceback in the REPL Owen Brasier/Sebastian Pauka/Alex McCulloch 13
CHAPTER NCSS Embedded Stream Where to go from here Although we’ve covered a heap of content here, there are far more features available in both Python and micro:bit than we can cover here. If you would like more information, you can: • The micro:bit documentation for features of the micro:bit https://microbit-micropython.readthe- docs.io/en/latest/index.html • The MicroPython documentation http://docs.micropython.org/en/latest/ • The Python documentation https://docs.python.org/3.4/reference/index.html Note that although MicroPython is based on Python 3.4, there are a number of differences. The Python documentation is excellent at describing the built-in functions, and most of what you will find there is valid for MicroPython as well, there are some differences, that you can read about here: http://docs.micropython.org/en/latest/genrst/index.html Owen Brasier/Sebastian Pauka/Alex McCulloch 14
CHAPTER NCSS Embedded Stream Chapter 3 – Electronics Since we’re learning all about embedded development, we need to spend some time learning about electronics. Electronics is a huge field, and encompasses a large number of topics, so it’s impossible for us to cover every topic exhaustively. Rather than trying to exhaustively cover the field of electronics, we’ll try to introduce enough topics that you can put together simple circuits, hook components together and start to understand what is going on with the embedded devices you see around you. Since there is such a large number of topics to cover, don’t worry if this doesn’t all make sense on your first read-through. Most of the content you will have a chance to learn along the way. But since we can’t cover everything in the limited time we have, this section is meant to be a reference to come back to as the program progresses. Voltages and Currents To build any electronic device, we need to have a way of sending energy and signals between controllers, sensors and outputs. The way we send those signals is by sending electrons from one place to another. The movement of electrons through a circuit is called current. The units of current is amps (A) and a larger value means a greater number of electrons are flowing. The way to create a current is to apply a voltage. A difference in voltage between two points in a circuit will cause current to flow. Specifically, when we’re talking about electronics (as opposed to chemistry), electrons flow along paths of positive voltage (i.e. from higher to lower voltage). The units of voltage are volts (V). We can draw an analogy between voltages and currents, and water pressures and flows. A voltage is like a water pressure. If there is a pressure difference between two points, water will flow (like current) between those two points to try and equalize those pressures. There are two things we want to keep track of in our electronic circuits: voltage and current. Two handy things to keep in mind are: • Voltage is across things • Current flows through things Relating voltage and current: resistance Now that we’ve established that an electron flow is current and that electrons flow from regions of high voltage to low voltage, the next question you might ask is: how much current (how many electrons) will flow when I connect two regions together. A higher voltage difference should cause a higher current to flow (or reversing that statement, a higher current means a higher voltage difference). What if they were just related by a simple constant? The relation might look like: V =I ×R V I= R (1) V R= I And in fact, this is Ohm’s famous law: The voltage across a device will be the current through the device times the resistance of the device. Or equivalently, the current through the device is equal to the voltage divided by the resistance. We’ve had to introduce a new constant: resistance, to describe how current flows through various objects. Like its name suggests, resistance resists current flow. The unit of resistance is an Ohm (Ω). What do we expect the resistance of various objects to be? Well again, there are a couple of things we might guess: • A thinner wire should carry less current than a thicker wire. So the thinner wire should have a higher resistance, just like a thick pipe can carry more water than a thin pipe. • A longer wire should carry a smaller current than a shorter wire. So the long wire should have a higher resistance, just like it’s easier to push water through a short hose than a long hose. Owen Brasier/Sebastian Pauka/Alex McCulloch 15
CHAPTER NCSS Embedded Stream Once again, the voltage is the resistance times the current. Remember this equation! Seriously. It’s the foundation of everything. Series and Parallel The next question we might ask is what do resistors do when we start connecting them together. There are two ways that we can connect them together: • Back to back, such that current passes through each of the resistors in series: Figure 7: Series Resistors • In parallel, such that the current can choose between multiple paths: Figure 8: Parallel Resistors Before we start dropping equations, let’s again try and figure out what they should do intuitively. • If we put two resistors back to back, in series, they will both oppose the flow of electrons, so the current should drop. The resistance of the circuit should go up. • If we put two resistors in parallel, then the current can choose between two paths, so the total amount of current that can flow should go up. The resistance of the circuit should go down. The simplest way we might think about implementing the series case is to have all the resistances add together, and this is indeed what happens. The total series resistance is given by: Vtot = V1 + V2 + · · · + VN (2) In parallel the situation is slightly more difficult as we have to take into account the multiple paths that current can flow. The equation becomes: 1 Vtot = 1 1 1 (3) V1 + V2 + ··· + VN Although this looks like a complicated equation, you can still draw some intuitive conclusions. If we have two parallel resistors with equal resistance, then since we’ve doubled the number of paths that electrons can take through equal resistances, the total resistance halves: V Vtot = (4) 2 Try to plug two resistors into the above formula and see if you can get the same result… Owen Brasier/Sebastian Pauka/Alex McCulloch 16
CHAPTER NCSS Embedded Stream Analog and Digital Now that we’ve defined and explained the key terms that we need to understand simple electronics, let’s investigate what the signalling for sensors and microcontrollers actually looks like. We can break the world of sensors down into two types: analog and digital sensors. You may already be familiar with the concept of digital logic, this is what microcontrollers and computers use internally. Digital circuits are either on or off. We don’t have ranges, it’s a binary world. On or off. 0s or 1s. High or low. That’s it. On the micro:bit, we read digital values from pins using the read_digital() function. For example, to read the digital value on pin0, we run pin0.read_digital(). Analog deals with continuous values, or ranges of values. So when we want to measure ranges of, for example brightnesses, sound levels etc. we are often talking about analog sensors. On the micro:bit, we read analog values using the read_analog() function. For example, to read the analog value on pin0, we run pin0.read_analog(). So if we’re talking about things like “pressing a button” or “turning on a light”, we’re talking about digital values. If we’re measuring a range, like “how bright it is”, we’re talking about analog values. When we’re dealing with communication between sensors and microcontrollers we will almost always be signalling with voltages. Both digital and analog signals need to map onto the voltages that our microcontroller uses. When using digital circuits we usually define 0/low as ground: 0V, and 1/high as the supply voltage. For a micro:bit that’s 3.3V. All digital signals that our micro:bit can read or produce swing between these two ranges. For analog circuits, we still can usually only read voltages between these two ranges (0V - 3.3V), but any value in that range is valid. 0V will represent the minimum value, for example absolute silence for a sound sensor, and 3.3V will represent the maximum value, for example the loudest sound that the sensor can sense. Aside: Ground One potentially confusing point about voltages is that voltages are relative. You can sort of think about voltages as heights. We can talk about a height difference between two points on a mountain, but if we want to talk about a universal height, we need to define where zero sits. For heights, we choose sea level as our reference point, and call heights referenced to this point altitude. In electronics, things are usually a little bit harder; there isn’t a universal point we can reference everything to. It’s up to us to pick this point. Once we have, this point is called ground and by definition we say it is always at zero. Since ground is just a point we choose, we have to make sure that whenever we are working with grounds we are consistent with our choice. Once ground is connected to microcontrollers and sensors, those sensors will assume that the ground they see is the same ground that every other connected component sees. So it’s important to connect all the different grounds together, otherwise our signalling won’t work. With disconnected grounds, the definition of logic high that one chip has might be different to the definition of logic high that the other chip has. Voltage Dividers A voltage divider is one of the most common circuits that exist! It looks like this: Figure 9: A voltage divider Given a voltage input, it produces a fraction of the input as the output. Owen Brasier/Sebastian Pauka/Alex McCulloch 17
CHAPTER NCSS Embedded Stream The equation for the output is: R2 Vout = Vin (5) R1 + R2 The voltage divider produces a smaller voltage using the ratio of resistors, which we can calculate from the equation above. For example we can make a simple volume control by changing the ratios of R1, R2 such that the output voltage being fed to our headphones is reduced. At NCSS, the main way we’ll be using voltage dividers is to read in analog sensors, like a joystick, flex sensor, or any sensor that changes its own resistance. It’s hard for us to read resistance directly, but the micro:bit is really good at measuring voltages. So by adding another resistor that is known and applying an input voltage that is known, by measuring the output voltage, we can calculate the resistance of the sensor with our voltage divider! What value resistor should we pick for any given sensor? The aim of the voltage divider circuit should be to maximize the change in voltage when our sensor measures some change. If we look at how the output voltage of a voltage divider varies as a function of the sensor resistance, what we find is that we get the most sensitivity to changes in resistance when the value of the resistor in the voltage divider is set to: (6) p Ropt = Rmin × Rmax In practice this means we should measure the resistance of the sensor at the two extremes of what we want to measure (for example in a bright room, and a dark room if we were using a resistive light sensor), and choose a resistor value that we have that occurs between these two extremes, as close as possible to the optimum. Figure 10: Optimizing the sensitivity of a voltage divider Calibrating Sensors How do we know what the resistance means in the real world? That depends on the sensor. Usually, looking up the datasheet will tell us what it means. But if it’s something like a light sensor, we can calibrate the sensor ourselves by measuring the voltage at lots of light, and measuring it again at no light, and getting a threshold of what we want to call enough light to trigger an action (like turn on a light, because it’s dark). Let’s see how you might do that in practice! First, the code to calibrate the sensor (which is R2 in our voltage divider circuit). pin0 is Vout in the circuit diagram, and Vin is the micro:bit power at 3.3V. from microbit import * while True: Owen Brasier/Sebastian Pauka/Alex McCulloch 18
CHAPTER NCSS Embedded Stream sensor = pin0.read_analog() print(sensor) sleep(50) then once we have a threshold value, we write a simple if statement to trigger the action! from microbit import * threshold = 600 # Measured during calibration while True: sensor = pin0.read_analog() if sensor > threshold: pin1.write_digital(1) # turn on a light else: pin1.write_digital(0) # turn it off Pull-up and down resistors A pull-up or pull-down resistor is a resistor that “pulls” a point that is floating to a positive voltage (pull up), or to ground (pull down). This is used in digital circuits because everything has to be either on or off. Say we wanted to connect a button to a controller (we do want to do that, a lot), how do we wire it up? For starters, it’s possible to wire it directly to the positive rail like this: Figure 11: A floating switch. This probably won’t work! But how do we know what the voltage is at the pin when the switch is open (not pressed)? We don’t! The pin is floating, which means we can’t tell what the voltage at the pin will be. When something is supposed to be either “on” or “off”, floating is the opposite of what we want, because we have no idea what it is. So what we do is use a resistor to pull the voltage to a known state when it isn’t connected to anything. Figure 12: A pull-down resistor on a switch When the switch is open, the resistor pulls the voltage at the pin to 0V, and when it is closed, this connects the pin directly to high (3.3V), so we never have an undefined voltage. This is called a pull- down resistor since it pulls the voltage down to 0V Owen Brasier/Sebastian Pauka/Alex McCulloch 19
CHAPTER NCSS Embedded Stream We can also invert the behavior of the switch by changing around the order of the switch and the resistor. Figure 13: A pull-up resistor on a switch In this case the opposite is true, when the switch is open the voltage at the pin is 3.3V, and when the switch is closed, the voltage is 0V. For this reason it is called a pull-up resistor. Does it matter if I use pull-up or down? No, it’s just a matter of preference. It’s much more important to know what each of them means (and write your code accordingly), and that you need to use one of them when you’re connecting a button (or basically anything) to a digital input. Level shifting Digital circuits have a signal level that they use as their “on” voltage level. On the micro:bit, a pin1.write_digital(1) will set the voltage on pin1 to 3.3V, that’s what “on” means. But some other circuits might use a signal of 5V to decide if something is “on”. For example, the neopixel LEDs want 5V power and 5V digital signals, which the micro:bit simply can’t provide. So what we do in this case, is use a level-shifter to transform the waveform. Rather than building a circuit to do this ourselves (which you could do using a few transistors), we use an integrated circuit (or chip) designed for this purpose. One example is the 74AHCT125, which converts a 3.3V input signal to a 5V output signal (as shown in the diagram below). Figure 14: Level shifting a signal from 3.3V to 5V Level shifting can be generalised in that it is shifting from any voltage level to any other voltage level. It is quite common the shift the other way (5V→3.3V where you might use 74LVC245 chip to do this). Why do we even need this? Some devices are only designed to work at a certain voltage. A 1 might be defined as a voltage above a threshold, and if the logic level coming in is below that value, our device may not detect a 1 that we send. If a logic level is too high, it might be a value greater than a device will accept, so you could damage the components. It’s important to send a value to a device that it expects, so it’s important to know the voltage of your microcontroller (on the micro:bit it is 3.3V), and the voltage the device operates at. Owen Brasier/Sebastian Pauka/Alex McCulloch 20
CHAPTER NCSS Embedded Stream For example, neopixels (WS2812B is the name of the chip), are designed to run at 5V, the reason is because extra voltage is required to produce the full range of colours from the LED, if you supply less, some colours will not be as bright, so the white looks more like an off-brown. Light Emitting Diodes (LEDs) Almost every electronic device has an LED in it. Whether it’s just the power light, or some sort of status indicator, or perhaps the device needs to produce a lot of light, they are everywhere. Although there are many types of lights that can be used in circuits, we almost exclusively use LEDs because of their excellent power efficiency, small size, and ease of use. Theory LEDs are a type of diode, which is a semiconductor device that contains a special junction that only allows current to pass in one direction. The semiconductor junction in an LED is specially built such that when current is flowing, the band gap of the semiconductor results in electron transitions that emit photons of specific wavelengths. This is how LEDs have different colours, red LEDs produce photons that have a wavelength of approximately 650nm, green are 520nm, etc. By combining different colours (usually the primary colours, red, green, and blue) we can produce a whole rainbow of different colours. There also exist LEDs that produce light outside the visible range, for example, ultraviolet (~300nm) and infrared (~900nm). Infrared LEDs are used in television remote controls or night vision applications, and ultraviolet has a wide range of uses from skin therapy to sterilization and even in the production of white light using phosphors. This is how LED light bulbs you use in your house work, they are often UV LEDs which cause a phosphor to glow. Powering an LED Diodes are “non-linear” or “non-ohmic” devices, which means that Ohm’s law doesn’t apply to them. When they are conducting, they have very little resistance, and in the other direction they have a very high resistance. Something with a very low resistance effectively looks like a short circuit, which means that a lot of current will flow and potentially damage the LED or the thing that is powering it (e.g. the micro:bit pin). So what we need is a way to ensure that only a specific current flows, which is why every time we use an LED, we also need a current-limiting resistor. Figure 15: A current limiting resistor on an LED *Note that in the symbol for an LED, the bar points towards the cathode (which should be connected to the lower voltage, i.e. ground). Typically we choose a desired brightness for our LED, and look up the LED’s data sheet to find out what current it needs. Then we choose a resistor using Ohm’s Law (V=IR) to make that work. Remember that devices in series will all have the same current flowing through them, so the current flowing through the LED will be the same as what flows through the resistor. Here’s the formula to figure out what resistor to use for a desired current: Owen Brasier/Sebastian Pauka/Alex McCulloch 21
CHAPTER NCSS Embedded Stream Vres = Vpin − Vled Vres R= I (7) Vpin − Vled R= I The Vled is the forward voltage drop across the LED. This is a property of the LED and it will be in the data sheet. Typically it’s around 2 volts for a red LED. So if we have an LED that is rated for 20mA at full brightness and has a forward drop of 2 volts, then we get: Vres = 3.3 − 2 = 1.3V (8) 1.3V R = = 65Ω (9) 0.02A If we wanted it to be less bright, then for 10mA, the same calculations would yield 130 Ohms. Resistors only come in specific values, so just round up to the nearest one (e.g. 68 Ohms or 150 Ohms). But unless you’re adjusting for a very specific brightness, what matters more is that you have the resistor at all. So most of the time people just use whatever resistor they have spare above about 300 Ohms (330 Ohms and 470 Ohms are extremely commonly used resistors). Summary: If you want to use an LED, you must use a current limiting resistor. In almost all cases, a 330 ohm resistor will do the trick. More notes on current limiting resistors • Each LED requires its own resistor. Can you see why? • The resistor can be on either side of the LED, it doesn’t matter. • Sometimes you might choose to “invert” the LED by connecting it to VCC instead, then when you turn off the pin, the LED will turn on. • The micro:bit also has current limiting resistors for its built-in LED grid. They’re really tiny! Can you find them and measure their resistance? • Blue LEDs typically have a higher forward voltage drop, so will usually require a lower-value resistor for the same brightness. This is because the wavelength of blue light is much shorter than that of red light, so the photons that make up blue light are more energetic. The voltage drop across an LED is directly proportional to the energy of emitted photons. Owen Brasier/Sebastian Pauka/Alex McCulloch 22
You can also read