© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
C. BellBeginning MicroPython with the Raspberry Pi Picohttps://doi.org/10.1007/978-1-4842-8135-2_6

6. Project: Hello, World! MicroPython Style

Charles Bell1  
(1)
Warsaw, VA, USA
 

Here, we are at the most fun part of this book – working on MicroPython projects! It is at this point that we have learned how to write in MicroPython and now know a lot more about the hardware and even how to use discrete electronics and breakout boards.

This chapter represents an introduction to building MicroPython projects. As such, there are a few more things we need to learn including techniques and procedures for installing and running our projects on our MicroPython boards. This chapter will introduce those things you need to make your MicroPython projects successful. Thus, the chapter is a bit longer and should be considered required reading even if you do not plan to implement the project.

As you will see, the format for all the project chapters is the same; an overview of the project is presented followed by a list of the required components and how to assemble the hardware. Once we have a grasp of how to connect the hardware, we then see how to connect everything and begin writing the code. Each chapter will close with how to execute the project along with a sample of it running and suggestions for embellishing the project.

Before we jump into our first electronics project for the Pico, let’s discuss a few best practices and other practical advice for developing projects. These apply to all projects in this chapter and likely any future project you may have in mind.

Getting Started with Pico Projects

If you have never worked with microcontrollers before, you have no knowledge of building electronics projects, or you are not familiar with the hardware on the Pico, you may be wondering how to get started building such projects.

In this section, we will see some helpful tips and best practices for how to get started working with the Pico hardware. Most of this advice applies to any electronics or microcontroller project. They are included here for the beginner and those that need a refresher.

One Step at a Time!

Another very common mistake beginners make is sitting down and wiring all of their electronics together and then writing all their code in one pass without testing anything ahead of time. This creates a situation where if something doesn’t work, it can be masked by a host of problems.

For example, if there is some logic error or data produced is incorrect, it may cause other parts of the project to fail or produce incorrect results. This is made worse when the project doesn’t work at all – there are too many parts to try and diagnose what went wrong. This often places beginners in a desperate situation of confusion and frustrations. You students out there know exactly what I am talking about.

This can be avoided easily by building your project one step at a time. That is, build your project one aspect at a time. For example, if you’re working with LEDs to signal something, get that part working first. Similarly, if you’re reading data from a sensor, ensure you can do that correctly in isolation before wiring it all together and hoping it all works. Even the very experienced can make this mistake, but they are more equipped to fix it if something goes wrong (and they know better, but it’s a “do as I say not as I do” situation). We will build the examples in this book one step at a time. Some are small enough that there may be only one step, but the practice is one you should heed for any project you undertake.

Some Assembly Required

Some vendors offer Pico and breakout boards with and without headers soldered. Not soldering the headers saves on production and in some cases shipping costs and makes the boards a bit cheaper. If you know how to solder (or know someone who does), you may be able to save a little going with the boards without headers.

Another reason you may want a board without headers is if you want to install your board in a project enclosure or some other form of embedded installation. In this case, having the headers soldered may take up more space that you have or make the completed project a bit bulkier.

You may also encounter some add-on boards, breakout boards, or other discrete components that are not soldered with headers (or connectors). If you want to use these, you may have to solder the header or connector yourself. For example, most of the breakout boards from Adafruit (adafruit.com) and SparkFun (sparkfun.com) do not come with the headers soldered.

Handle with Care!

You should consider your Pico as a very sensitive device susceptible to electrostatic discharge (ESD). Unless you place your board in a case or on a nonmetal surface, you should handle your board carefully, always placing it on a nonconductive surface before powering it on. ESD can be caused by many things (think back to when you were a child with sneakers on carpet). This discharge can harm the board. Always ensure you handle your board so that ESD is controlled and minimized.

You should also never move the board when it is powered on. Why? The board has components soldered on with many pins exposed on both sides. Should any two or more of those pins touch something that conducts electricity, you can risk damaging the board.

Also, always store your board in an ESD safe container – one that is expressly made to store electronics. Your average, everyday inexpensive plastic box should be avoided (many generate static electricity when handled). However, if you do not have a container made for electronics, you can use static-free bags to place the board in while it is being stored. Many of the boards and components you buy come in such packaging. So, don’t throw it away!

You should take care to make sure your body, your workspace, and your project are grounded to avoid electrostatic discharge (ESD). ESD can damage your electronics – permanently. The best way to avoid this is to use a grounding strap that loops around your wrist and attaches to an antistatic mat like these: uline.com/BL_7403/Anti-Static-Table-Mats.

Finally, be extra careful when connecting your USB cable to your board. The micro-USB connector is prone to breakage (more so than other connectors). In most cases, it is not the cable that breaks but the connector on the board itself. When this happens, it can be very difficult to repair (or may not be repairable). It is also possible that the cable itself will stop working or only work when you hold the cable in place. If this happens, try a new cable, and if that fixes the problem, throw the old one away. If it does not fix the problem, it may be the connector on the board. Fortunately, extra care when plugging and unplugging the cable can avoid these issues. For example, always plug the micro-USB side first and use the full-sized USB end to plug and unplug from your PC. The fewer times you use the micro-USB connector, the less chance you have of damaging it.

Now, let’s get started on our very first MicroPython project!

Overview

In this chapter, we will design and build a MicroPython clock. We will use both an SPI and I2C breakout board. We will use a small organic light-emitting diode (OLED) display that uses an SPI interface and a hardware-based real-time clock (RTC) that uses the DS1307 chip and a battery for keeping time while the project is turned off. Rather than simply connecting to a network time protocol (NTP) server on the Internet, we will use the hardware-based RTC and display the current date and time on the small OLED display. This not only keeps the project a bit smaller but also demonstrates how to use an RTC for projects that may not be connected to the Internet.

While the Pico has a hardware RTC, it must be initialized each time you connect the board to your PC (via Thonny or rshell), making it less than ideal for a project that you power on periodically. To make it possible for the Pico to know what time it is even after being powered off and on again without connecting to your PC, we will use an external RTC.

As you will see, there is a fair amount of wiring needed, and an understanding of the hardware capabilities is required to write the code. Which is why we spent time in previous chapters talking about the firmware and various low-level hardware control. You will need those skills and knowledge to complete this project.

While a clock may sound rather simple, this project will walk you through all the steps needed to assemble the hardware and write the code. Further, the project is small and simplistic, so we can focus on the process, which we can then apply to more advanced projects. In fact, we will see that even a relatively simple project can have an unexpected level of difficulty. But don’t worry, as this chapter documents all the things you need to do to complete the project.

The sources for this project are many. The following links include background data used for this project including documentation and links to the MicroPython library (also called a driver) we will need to download for this project:
Note

The Adafruit MicroPython libraries are marked as deprecated, which only means no one is actively maintaining them; however, they will work with your Pico board without modification.

Notice the sites used. A good practice is to start with the Adafruit and MicroPython learning, blobs, and forums. Then check out the libraries. That is, do the research first and find all the references you can. If you find nice tutorials like those from Adafruit or SparkFun, you may want to download them to your PC or tablet or print them out for later reading. More importantly, take the time to read the references so that you understand as much as you can before you start working with the hardware or writing your code. You can save yourself a lot of time by understanding simple things like how to wire your board to the device and how the library is expected to be used.

Which Library Do I Use?

You may encounter a situation where you find more than one library for the hardware you want to use. In fact, I found several libraries for the OLED display. The differences among them are subtle, and it appears at least one does not support text, another is written for a specific platform, and another is written in C++ for the Pico.

The one listed earlier is the best one to use. Even so, it needs some minor changes for use. I will show you those changes, and, as you will see, they are not too difficult to spot and fix (e.g., when MicroPython throws exceptions, it will show you the source of the issue).

If you encounter a similar situation – having more than one library to choose from – you may want to try each until you find one that works best for your hardware and project. Sometimes, and in this case it is true, one library may not be viable, or another may be lacking features you need. The trick is to find the library that works best with the least amount of modification.

Now let’s see what components are needed for this project, and then we will see how to wire everything together.

Required Components

Table 6-1 lists the components you will need in addition to your Pico board. You can purchase the components separately from Adafruit (adafruit.com), SparkFun (sparkfun.com), or any electronics store that carries electronic components. Links to vendors are provided should you want to purchase the components. When listing multiple rows of the same object, you can choose one or the other – you do not need both. Also, you may find other vendors that sell the components. You should shop around to find the best deal. Costs shown are estimates and do not include any shipping costs.
Table 6-1

Required Components

Component

Qty

Description

Cost

Links

OLED display

1

ssd1306-based SPI display

$17.5

www.adafruit.com/product/661

RTC breakout board

1

RTC module with battery backup

$15.95

www.sparkfun.com/products/12708

$7.50

www.adafruit.com/product/3296

Breadboard

1

Prototyping board, full-sized

$5.95

www.sparkfun.com/products/12615

$5.95

www.adafruit.com/product/239

Jumper wires

11

M/M jumper wires, 7” (set of 30)

$2.25

www.sparkfun.com/products/12615

M/M jumper wires, 6” (set of 20)

$1.95

www.adafruit.com/product/1950

Coin cell battery

1

CR1225 (SparkFun RTC)

$1.95

www.sparkfun.com/products/337

  

CR1220 (Adafruit RTC)

$0.95

www.adafruit.com/product/380

The OLED breakout board used in this project is a small module from Adafruit. It has a tiny but bright display that you can mount on the breadboard. The resolution is 128 pixels wide by 32 pixels high. The OLED breakout board comes without headers installed, but they are easy to add if you know how to solder (now might be a good time to practice), or you can get a friend to help you. Figure 6-1 shows the Adafruit OLED SPI breakout board.
Figure 6-1

Monochrome 128x32 SPI OLED graphic display (courtesy of adafruit.com)

There are several OLED breakout boards available, and so long as they have the SPI interface and use the ssd1306 controller chip (the description will tell you this), you can use an alternate OLED display. The reason we need to use one with that controller chip is because the library is written for that controller. Other controller chips will require a different library.

The RTC breakout board used in this project is a DS1307 breakout board from Adafruit. The board also comes without headers installed (but includes them), nor does it come with a battery, so you must purchase a CR1220 coin cell battery. Adafruit has those as well if you want to save yourself a trip to the store. Figure 6-2 shows the RTC breakout board.
Figure 6-2

DS1307 real-time clock assembled breakout board (courtesy of adafrui.com)

There are several DS1307 RTC clocks available. In fact, SparkFun has one, or you can build your own! See the sidebar “Building Your Own RTC Module” for more details. Fortunately, the library we will use supports breakout boards with DS1307, DS3231, or PCF8523 RTC chips.

Tip

Small, discrete components like LEDs, resistors, etc. and even jumper wires and breadboards can be found in the kits mentioned in Chapter 2 – the Adafruit Parts Pal (www.adafruit.com/product/2975) or the SparkFun Beginner Parts Kit (www.sparkfun.com/products/13973). I recommend one of these kits.

Now, let’s see how to wire the components together.

Set Up the Hardware

This project has a lot of connections. There are seven needed for the OLED and four needed for the RTC. To help keep things easier, we will plan for how things should connect. We will use a full-sized breadboard to mount the breakout boards making the connections easier. We will use male/male jumper wires to make these connections via a breadboard.

But first, we will learn what connections are needed for each component and where they need to be connected to our board, writing them down to keep things straight. Doing this small amount of homework will save you time later (and a small bit frustration).

As you will see, mapping out the connections like this makes it easy to check the connections. This table along with a wiring drawing is the tool you will see in this book and other example projects on the Internet or elsewhere. Thus, learning how to read maps and wiring drawings is a skill you should have to make your project successful.

Table 6-2 shows the connections needed for this project. Traditionally, we use black for ground (negative) and red for power (positive) at a minimum, but you can use whatever color wires you want. We will start with physical pin 40 and work our way down to the lowest number pin used. As you will see in the drawing, this is working clockwise.
Table 6-2

Connections for the MicroPython Clock

Physical Pin

GPIO Num/Function

Breakout Board

Pin Label

40

VBUS

RTC

VCC

37

GND

RTC

GND

37

GND

OLED

GND

26

GP20

OLED

RST

25

GP19

OLED

DATA

24

GP18

OLED

CLK

22

GP17

OLED

D/C

21

GP16

OLED

CS

10

GP9

RTC

SCL

9

GP8

RTC

SDA

Wow, that’s a lot of connections! As we saw in Chapter 5, a breadboard allows us to plug our components in and use jumper wires to make the connections. This simplifies wiring the project and allows you to move things around if you need to make more room.

When plugging in components, always make sure the pins are mounted parallel to the center channel. Recall breadboards have the pins wired together in rows perpendicular to the center channel. This allows you to make more than one connection to the component (or pin on the board).

Caution

Never plug or unplug jumper wires when the project is powered on.

Finally, always make sure you wire your project, carefully double-checking all the connections – especially power, ground, and any pins used for signaling (will be set to “high” or “on”) such as those pins used for SPI interfaces. Most importantly, never plug or unplug jumper wires when the project (or your board) is powered on. This will very likely damage your board or components.

For this project, I mounted the Pico on the left side of the breadboard with the OLED in the center above the center channel and the RTC module on the right below the channel. Notice the RTC board uses a different power connection. The OLED board uses 3.3V and the RTC board 5V. Always check the power requirements of your components before powering on the project. Double-check and triple-check your connections.

Figure 6-3 shows the wiring drawing for the MicroPython clock project.
Figure 6-3

Wiring the clock project (full-sized breadboard)

If you do not have a full-sized breadboard, you can use two of the more popular half-sized breadboards and clip them together. If you look closely, you will see nubs on two sides and corresponding notches on the other.

Note

While you can use a half-sized breadboard for most of the projects in this book, a full-sized breadboard is a bit easier to use. The choice is mainly about being able to connect the wires without the breakout boards too closely placed.

If you’d rather use a single half-sized breadboard, you can, but the wiring will get a bit complex as shown in Figure 6-4.
Figure 6-4

Wiring the clock project (half-sized breadboard)

Caution

Always double- and triple-check your connections, especially all power and ground connections. Be sure to examine the power connections to ensure the correct power (3V or 5V) is being connected to the components correctly. Connecting the wrong voltage can damage the component.

If you chose a different RTC board than the one shown in the drawing, be sure to adjust the connections as needed. For example, the SparkFun DS1307 breakout board has the pins in a different order, so don’t go by this drawing alone – especially if you use alternative components!

Once again, always make sure to double-check your connections before powering the board on. Now, let’s talk about the code we need to write. Don’t power on your board just yet – there is a fair amount of discussion needed before we’re ready to test the project.

Write the Code

Now it’s time to write the code for our project. Since we are working with several new components, I will introduce the code for each in turn. The code isn’t overly complicated but may not be as clear as some of the code from previous projects. Let’s begin with a look at the design of the project.

Design

Once you have the hardware sorted out and how to connect the components to your board, it is time to start designing the code. Fortunately, this project is small enough to make the design simple. In short, we want to display the time on our OLED once every second. Thus, the “work” of the code is to read the date and time from the RTC and then display it on the OLED. The following lists the steps that summarize how to design and implement the code for this project or any project for that matter:
  • Libraries: We will need to select and import libraries for the RTC and OLED.

  • Setup: We will need to set up the interfaces for I2C and SPI.

  • Initialize: We will need to initialize object instances for the classes in the libraries.

  • New functions: We will write some helper functions to better organize the code.

  • Core code: We will write the core code for the project.

  • Test the breakout boards: We will take an extra step to test each breakout board separately before trying to execute the entire code.

  • Copy to the Pico: Name the file main.py and copy the file to the Pico.

These elements are what we will use for most of the projects in this book, and, indeed, it is a good pattern to follow for all your MicroPython projects. The new function step allows us to wrap the operational portion of the code in a separate function to make it easy to call from the main function. We’ll see more about this later when we execute and test the project.

Now that we know how the project code will be implemented, let’s review the libraries needed.

Libraries Needed

Recall from earlier we need two libraries: one for the OLED display and another for the RTC. The library for the OLED display is found at https://github.com/adafruit/micropython-adafruit-ssd1306, and the library for the RTC can be found at https://github.com/adafruit/Adafruit-uRTC.

Go ahead and download both libraries now. You should be able to go to the sites and click the Clone or Download link and then the Download Zip button to download the files to your PC. Then, open the location of the downloaded files and unzip them. You should find the following files:
  • ssd1306.py: The OLED display library

  • urtc.py: The RTC library

Ordinarily, we would create a new folder for each project we want to place on our Pico, but since we will be making this project run on the Pico when it is booted (powered on), the main code file will have to be named main.py and placed in the root folder. The Pico will automatically execute this file on boot unless you connect it to your PC.

However, we will be creating a new folder to place the library files (drivers) for the breakout boards. Once you have your Pico connected, create a new folder named project1 in the root folder by right-clicking the Pico in Thonny and selecting New directory.... Then, double-click the project1 folder on the Pico and upload the library files to that folder. You should see a folder structure and file list similar to Figure 6-5.
Figure 6-5

New project folder and files (Thonny)

Now that we have the libraries copied, let’s look at the code we will need to write.

Planning the Code

Now that we have our design and have downloaded and modified the libraries, we can begin writing the code. Rather than show you a long listing and say, “comprehend or perish,” let’s walk through all the parts of the code first so that we understand each part. As we walk through the code, feel free to test the parts yourself, but if you prefer to wait until the end to test the code, you can. Some of this will be familiar and perhaps rudimentary to those who’ve worked the examples so far in this book, but a little refresher never hurts. Let’s begin with a look at the import section.

Imports

The import section for the project comes before all other statements but after the comment block at the top of the file. You should also include some level of documentation at the top of the file to explain what the code does. You don’t have to write a lengthy tutorial – just a short statement or so that describes the program including your name and other information. This is important if you want to share your code with others and if you ever go back to the code later to reuse it.

If you want to type in the code as we go along, you can open a new file named main.py on your PC with Thonny or your favorite code (or text) editor. We will copy the file to the Pico in a later step.

The following shows the imports for this project.
# Import libraries
from project1.urtc import DS1307
from project1.ssd1306 import SSD1306_SPI
from utime import sleep
from machine import SPI, Pin, SoftI2C

Notice we specified project1 in the first two imports because we placed these libraries (drivers) in the project1 folder. The main.py file will be copied to the root of the Pico filesystem. Also, we use the SoftI2C library instead of I2C because the RTC library doesn’t work well with the I2C library. Recall, we discussed these differences in Chapter 5.

Setup

Next, we need to set up the interfaces for I2C and SPI for use in the RTC and ssd1306 libraries. That is, the classes in those libraries need object instances of the interfaces passed to the constructor. The code we will use is like the code we saw in previous examples. The following shows the interface setup code:
    sda = Pin(8)
    scl = Pin(9)
    # Software I2C (bit-banging) for the RTC
    i2c = SoftI2C(sda=sda, scl=scl, freq=100000)
...
    # SPI for the OLED
    spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))

Notice we use different parameters for the SPI, and we specify pins for the I2C. You can use other pins if you’d like, but just remember to use the correct pins when you wire the components together.

Initialize

Next, we initialize object instances for the classes in the libraries. This is the point where you need to read the documentation for each library to understand what is needed to initialize the objects. For the ssd1306 library, the class constructor requires the number of pixels (the resolution is the number of pixels in rows and columns) for the display, the interface instance (SPI from the last section), and the pins we will use for the D/C, RST, and CS pins. For the RTC library, we need only pass in an interface instance (SoftI2C from the last section). The following shows how to do both steps:
    # Initialize class instance variables for RTC, OLED
    rtc = DS1307(i2c)
    #start_datetime = (2021, 08, 12, 5, 14, 54, 22)
    #rtc.datetime(start_datetime)
    oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20), cs=Pin(16))

Notice there are some commented out lines in there. When we first use the RTC or when we replace the battery, we must initialize the date and time. We can use the library features to do this. In this case, we simply call the datetime() function for the RTC instance passing in a tuple containing the new start date and time – the order of the tuple elements is shown in the following. Once set, we do not need to run it again. In fact, running it again will reset the RTC, and we don’t need to do that. Thus, we leave this code commented out for normal operation and uncomment it when we need to reset the RTC. When you run your project for the first time, uncomment this code supplying the correct current date and time but later comment it out.

New Helper Functions

Now that all the setup and initialization code are figured out, we can create a few helper functions to allow us to organize the code.

Recall we want the project to read the date and time from the RTC and display it on the OLED once every second. Thus, we expect to see some sort of loop that performs these two steps. However, we must again refer to the library documentation where we find that the RTC returns data as a tuple (year, month, day, weekday, hour, minute, second, millisecond). This means we must format the date and time to make it easier for humans to read and to fit on the small OLED screen. This is a perfect candidate for a helper function.

Let’s create a function named write_time() that takes an instance of the OLED display and the RTC and then read the date and time with the datetime() function (with no parameters) and print it to the OLED screen using the text() function, which takes a starting column (called the X position in the documentation) and row (Y position) for the location on the screen to print the message when the show() function is called. This is the essence of the project. Placing it in a separate function allows you to isolate the behavior and make it easier to maintain or modify the code – because the “core” is in one place:
# Display the date and time
def write_time(oled, rtc):
    # Get datetime
    dt = rtc.datetime()
    # Print the date
    oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]),
 0, 0)
    # Print the time
    oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]),
 0, 10)
    # Print the day of the week
    oled.text("Day:  {0}".format(get_weekday(dt[3])), 0, 20)
    # Update the OLED
    oled.show()
Notice we use the text() function and the format() function to take the data from the RTC and format it in an expected format that most clocks use: HH:MM::SS and MM/DD/YYYY. Notice there is an additional function here named get_weekday(). This function takes the number of the day of the week as returned from the RTC and returns a string for the name of the day. The following shows the code for this function:
# Return a string to print the day of the week
def get_weekday(day):
    if day == 1: return "Sunday"
    elif day == 2: return "Monday"
    elif day == 3: return "Tuesday"
    elif day == 4: return "Wednesday"
    elif day == 5: return "Thursday"
    elif day == 6: return "Friday"
    else: return "Saturday"
There is one more function added – a function to clear the screen. This function simply blanks the screen to allow us to overwrite the screen with new data. Normally, this is not needed, but it is a good practice to clear the screen in case the library doesn’t do it for you. In this case, it does now. This function is named clear_screen() and is shown in the following. It simply uses the fill() and show() functions from the ssd1306 library. Passing in 0 for the fill() function tells the library to fill the screen with no data (blank or off):
# Clear the screen
def clear_screen(oled):
    oled.fill(0)
    oled.show()

Core Code

Now we are ready to code the new main() function for the project. We have our helper functions developed, so we need only call them and wait for a second on each pass. We will use a main() function so that when the script is executed (the name check will fail if the code is imported from another script), the main() function is called. We do this with the following code:
if __name__ == '__main__':
    try:
        main()
    except (KeyboardInterrupt, SystemExit) as err:
        print(" bye!")
        sys.exit(0)

We use a try...except block so that we can capture the keyboard interrupt (CTRL+C) so that we can stop it. This construct is typical of how we would write scripts that are intended to be executed.

Recall, when a Python script is loaded (read), each line of code is executed. If we place all of our code in functions (or a class), we need some way to start execution in a controlled manner. The preceding code accomplishes this task, and we will use it in all of our projects. It does not matter what you call the function for the core code, but main is a common practice.

Next, we can create the main() function. The following shows the function with the setup and initialization code discussed earlier:
def main():
    sda = Pin(8)
    scl = Pin(9)
    # Software I2C (bit-banging) for the RTC
    i2c = SoftI2C(sda=sda,scl=scl,freq=100000)
    # SPI for the OLED
    spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
    # Initialize class instance variables for RTC, OLED
    rtc = DS1307(i2c)
    #start_datetime = (2021,08,12,5,14,54,22)
    #rtc.datetime(start_datetime)
    oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20),       cs=Pin(16))
    for i in range(10):
        clear_screen(oled)
        write_time(oled, rtc)
        sleep(1)

Notice how “clean” this function is – we can see only three statements: clear the screen, show the time, and wait for one second.

Let’s put this code together with the import section and the helper functions. Listing 6-1 shows the complete code for the main.py file.
#
# Beginning MicroPython
#
# Chapter 06 - Digital Timepiece
#
# This example uses an I2C real time clock (RTC) to store the current
# date and time. It displays the time, date, and day of the week on
# an SPI OLED.
#
# Dr. Charles Bell
#
# Import libraries
from project1.urtc import DS1307
from project1.ssd1306 import SSD1306_SPI
from utime import sleep
from machine import SPI, Pin, SoftI2C
# Return a string to print the day of the week
def get_weekday(day):
    if day == 1: return "Sunday"
    elif day == 2: return "Monday"
    elif day == 3: return "Tuesday"
    elif day == 4: return "Wednesday"
    elif day == 5: return "Thursday"
    elif day == 6: return "Friday"
    else: return "Saturday"
# Display the date and time
def write_time(oled, rtc):
    # Get datetime
    dt = rtc.datetime()
    # Print the date
    oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0)
    # Print the time
    oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]), 0, 10)
    # Print the day of the week
    oled.text("Day:  {0}".format(get_weekday(dt[3])), 0, 20)
    # Update the OLED
    oled.show()
# Clear the screen
def clear_screen(oled):
    oled.fill(0)
    oled.show()
def main():
    sda = Pin(8)
    scl = Pin(9)
    # Software I2C (bit-banging) for the RTC
    i2c = SoftI2C(sda=sda,scl=scl,freq=100000)
    # SPI for the OLED
    spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
    # Initialize class instance variables for RTC, OLED
    rtc = DS1307(i2c)
    #start_datetime = (2021,08,12,5,14,54,22)
    #rtc.datetime(start_datetime)
    oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20),       cs=Pin(16))
    for i in range(10):
        clear_screen(oled)
        write_time(oled, rtc)
        sleep(1)
if __name__ == '__main__':
    try:
        main()
    except (KeyboardInterrupt, SystemExit) as err:
        print(" bye!")
        sys.exit(0)
Listing 6-1

Completed Code for the MicroPython Clock (main.py)

Take some time to read through the code so you can see how it is organized. We will use this as a template in future projects. You can save it to your PC, but don’t execute it on the Pico yet because we need to test the code for the breakout boards.

Test the Breakout Boards

Now that we have planned the code and know how to code each of the parts, we have one more thing to do – test the breakout boards separately. We do this by wiring one breakout board and testing it, then powering off and unwiring that breakout board, and then wiring the other breakout board and testing it.

This is a good practice to get into the habit of doing for one primary reason. You will save yourself a lot of grief by testing the individual parts of the project – especially the hardware – one at a time. This not only makes it easier to narrow down any issues, but it also ensures you can identify the source of the problem. That is, if you plugged all the hardware in and wired everything and wrote the code, deployed it, then powered it on, and nothing works, how do you know which part is to blame? This is one of my mantras: build and test one piece at a time.

Tip

Testing code one part at a time is a familiar pattern to me, and it is highly recommended you adopt the process yourself, that is, coding a part of the project at a time and testing each individually.

For this project, there are two parts – the RTC and the OLED. Let’s see how to test them individually. The code presented is intended to be run via a REPL console via Thonny.

Test the RTC Breakout Board

To test the RTC, use the following code. Listing 6-2 is a condensed form of the code we saw in Listing 6-1 with only the bare minimum code added. You can name this file test_rtc.py if you’d like to save it, but we will execute the code via the REPL console.
from project1.urtc import DS1307
from utime import sleep
from machine import Pin, SoftI2C
sda = Pin(8)
scl = Pin(9)
i2c = SoftI2C(sda=sda,scl=scl,freq=100000)
rtc = DS1307(i2c)
start_datetime = (2021,08,16,8,11,0,0)
rtc.datetime(start_datetime)
for i in range(0,10):
    # Get datetime
    dt = rtc.datetime()
    print(" Test:", i+1)
    # Print the date
    print("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]))
    # Print the time
    print("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]))
    sleep(3)
Listing 6-2

Test Code for the RTC Breakout Board (test_rtc.py)

Notice we set the date and time in this test. When you run this for yourself, you should change the date and time tuple to include the current date and time when you run the test. What you should see in the REPL console is a tuple representing the date and time. It should be the same as what you set since the code will execute much less than a second from the time you set it to the time you query the RTC. Go ahead and reenter that last statement several times to ensure the time changes as you’d expect. That is, wait a few seconds and try it again – several seconds should have elapsed. Listing 6-3 shows what the output should look like for a successful test.
Test: 1
Date: 08/16/2021
Time: 11:00:00
Test: 2
Date: 08/16/2021
Time: 11:00:03
Test: 3
Date: 08/16/2021
Time: 11:00:06
Test: 4
Date: 08/16/2021
Time: 11:00:09
Test: 5
Date: 08/16/2021
Time: 11:00:12
Test: 6
Date: 08/16/2021
Time: 11:00:15
Test: 7
Date: 08/16/2021
Time: 11:00:18
Test: 8
Date: 08/16/2021
Time: 11:00:21
Test: 9
Date: 08/16/2021
Time: 11:00:24
Test: 10
Date: 08/16/2021
Time: 11:00:27
Listing 6-3

Test RTC Output

If any of the statements fail, be sure to check your wiring and look for any typos. Also, ensure you are using the correct, modified version of the libraries (and that you have copied them to the board).

Test the OLED Breakout Board

To test the OLED, use the following code. Listing 6-4 is a condensed form of the code we saw in Listing 6-1 with only the bare minimum code added. You can name this file test_oled.py if you’d like to save it, but we will execute the code via the REPL console.
from project1.ssd1306 import SSD1306_SPI
from machine import Pin, SPI
from utime import sleep_ms
spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
oled = SSD1306_SPI(128, 32, spi, dc=Pin(17), res=Pin(20), cs=Pin(16))
for i in range(40):
    for j in range(32):
        oled.fill(0)
        oled.show()
        oled.text("HELLO WORLD", i, j)
        oled.show()
        sleep_ms(100)
Listing 6-4

Test Code for the OLED Breakout Board (test_oled.py)

When you run this code, you should see the screen blank (it should be blank from the start), then display the hello message in different places in the range of the OLED screen, and may “scroll” off the screen (can you spot why?).

If you do not see any output, power off your board, check all the connections, and verify the correct pins are used and that you have the correct modified version of the library copied to your board.

Tip

The OLED breakout boards from Adafruit (and presumably others) come with a protective cover over the lens. You can and should leave that in place to ensure the lens does not get damaged. Plus, the OLED is bright enough to see through the protective cover.

OK, now we’re ready to execute and test the completed project.

Execute

We are finally at the point where we can copy all the files to our board and execute the project code. There are several recommended steps in this process as shown in the following. You should follow this process each time you want to deploy and test a project:
  1. 1.

    Double-check all hardware connections (wiring).

     
  2. 2.

    Connect your Pico to your PC.

     
  3. 3.

    Copy the libraries and code file to the board.

     
  4. 4.

    Test the code, fix any issues found, and recopy the file(s) if needed.

     
  5. 5.

    Disconnect and reconnect the board.

     

The first step cannot be overstated. Always check your wiring connections every time before you power on the board. This is in case curious hands have wandered by and “examined” your project or you’ve moved it, or some other event has occurred to unplug wires. It never hurts to be extra careful.

Next, we connect the Pico to our PC to power on the board and check for any issues. Yes, this is the smoke test! Simply make sure all LEDs that are supposed to illuminate do (like those on the board) and that things that should not be on are off. For example, if you see a solid bar on the OLED when you power it on, that’s not a good sign. If ever in doubt, disconnect the Pico and check your connections. If things still aren’t right, disconnect everything and test your board. Sometimes, a damaged component can cause strange behavior.

Next, we copy all the libraries and code we want to use to the board. Recall, we copy the libraries for the RTC and OLED to a folder on the Pico named project1 and copy the main.py file to the root folder on the Pico.

Caution

Be sure to uncomment out the lines to initialize the RTC on your first execution. You can comment them out immediately after your test (be sure to do so!).

At this point, the code isn’t running, but we can execute it via Thonny. Simply click the Run button and watch your code run! If everything is connected correctly, and the code is correct, you will see the date and time appear on the OLED. You should see something like Figure 6-6, which shows the project running in all its glory.
Figure 6-6

A MicroPython clock!

Notice in the figure I am using two half-sized breadboards connected together, which is the same size as a full-sized breadboard. Notice all of the wiring. If we used a single half-sized breadboard, the wiring would be a snaggle that could obscure the OLED. Ever the neat freak, I’ve zip-tied the wiring so that it is out of the way.

If something doesn’t work, go back and check your code. If you left the two lines of code to initialize the RTC uncommented, you may see the same date and time appear each time you run the code. Be sure to comment those out on subsequent executions. Or, if you forgot to uncomment out those lines, you may see some strange date and time values.

It is at this point that you should be basking in the wonder of your first successful MicroPython hardware project. Take some time to bask in your delight of a job well done.

However, we’re not done. There’s one more step. Disconnect your Pico and exit Thonny and then reconnect it to your PC to power it on again. If the date and time show up after a few seconds, you’ve done it! You have successfully created a project you can package and run anywhere you want, and so long as the coin cell battery has a charge, it won’t lose time.

Taking It Further

This project has a lot of potential for embellishment. If you liked the project, you should consider taking time to explore some embellishments. Here are a few you may want to consider. Some are easy and some may be a challenge:
  • Use a different RTC.

  • Calculate AM/PM and display it.

  • Use a larger display and display the Julian date.

  • Use a light sensor to dim the display in direct sunlight.

  • Add a speaker and implement an alarm feature (hint: some RTCs have this feature).

  • Format the date and time using different world standards such as YYYY/MM/DD.

Of course, if you want to press on to the next project, you’re welcome to do so, but take some time to explore these potential embellishments – it will be a good practice.
  • DS1307 chip

  • Coin cell battery breakout board

  • 3V coin cell battery

  • 32.768kHz crystal

  • (2) 1K resistor

If you’re thinking this project is rudimentary now that we have solved the problems with the libraries, consider this: most sensor-based projects and indeed most projects that generate data that must be associated with a date and time when the events are sampled. Thus, using an RTC to read the date and time will be a consideration for many IoT projects.

Building Your Own RTC Module

If you’re like me and like to tinker, you can build your own RTC module using an RTC DS1307 chip, two resistors, a crystal, and a coin cell battery breakout board. You can find these components at most online electronics stores such as Adafruit (www.adafruit.com), SparkFun (www.sparkfun.com), and Mouser (www.mouser.com). The component list is as follows:

That’s it! The following shows how to connect the components on a breadboard.

See www.learningaboutelectronics.com/Articles/DS1307-real-time-clock-RTC-circuit.php for an example walk-through for assembling this side project.

If you plan to build a lot of projects that use an RTC, buying these components in bulk and wiring up your own RTC 1307 module may be more cost-effective. Plus, it ups the cool factor of your kit.

Summary

Working with hardware such as breakout boards and the libraries we need to talk to them over specialized interfaces such as I2C and SPI can be a challenge. Sometimes, like we saw in this chapter, you need the Soft version of the SPI or I2C libraries. The reason for this is the growing array of boards that vendors are creating specialized versions of the MicroPython firmware that may not work 100% with the Pico.

The trick then is understanding why the changes are necessary and taking the time to make the changes yourself. It is so easy to just give up when something doesn’t work – don’t do that! Take your time and understand the problem and then solve it systematically.

In this chapter, we saw a detailed walk-through of a MicroPython clock. We used an OLED display to display time we read from an RTC. Along the way, we learned how to plan our projects, make hardware connections, and write code for use in deploying on our Pico.

In the next chapter, we will explore a project that uses more low-level hardware in the form of discrete components such as LEDs, resistors, and buttons. These are the building blocks you will need to form more complex solutions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.239.11.178