Skip to content

Open Book Documentation

joeycastillo edited this page Oct 18, 2022 · 5 revisions

Table of Contents

  1. Introduction
  2. Board Setup
    1. Soldering Strategies
    2. Assembling the Board
    3. Required Support Software
    4. Flashing the Bootloader
    5. Testing the Hardware
    6. Burning Font Data
    7. Installing the E-Paper Display
  3. Understanding the Hardware
    1. Software and the Open Book library
    2. Power Supply
    3. Battery and Charging
    4. USB Port
    5. Enable Switch
    6. SAM D51 Microcontroller
      1. Pin Assignments
    7. Debug Port
    8. Reset Button
    9. Red LED
    10. QSPI Flash
    11. E-Paper Display
    12. MicroSD Card Slot
    13. Button Register
    14. Lock Button
    15. Accessory Ports
    16. Babel Flash Chip
    17. Audio Combo Jack
    18. +Mic Preamp
    19. Neopixel
    20. Feather Connector
    21. Next Steps: MVBook
  4. Notes
    1. Enclosures
    2. E-Paper Waveforms
  5. Conclusions and Future Work

Introduction

Hi there, and congrats on taking the plunge and deciding to build your own Open Book! The fact that you've sought out this kit and brought it into your life probably signals that you understand the gist of its purpose, but to recap: this is an open hardware device for reading that you are going to build yourself and understand from bottom to top. For the moment, this guide is broken up into three parts:

  • First there's the board setup guide, which offers guidance on how to assemble the board, flash the bootloader, install font data and test the peripherals.
  • Second, there's a rather lengthy deep dive into each of the Open Book's subsystems, explaining the workings of each block of functionality and even the purpose of each component. This section will also outline the software support available for each subsystem in both Arduino / C++ and in CircuitPython, as applicable.
  • Finally, the last section includes notes, errata, and ideas for future work.

This guide aims to walk you through each of the systems and even each of the components on the board. The goal is to, at the other end, equip you with both a fuller understanding of how this gadget works, and the knowledge that you can build your own gadget in the future, whether based on this design or cut from whole cloth.

What You Will Need

It's tough to know exactly what to put in this list, because there are many different ways to put this thing together. Having said that, there are some broad requirements, and some specific items that can fulfill those requirements, so I'll outline them here and then go into more specifics in the sections where we need those things.

  • All of the parts on the Open Book Bill of Materials (BOM), as well as the Open Book printed circuit board (PCB).
  • Tools necessary for soldering the parts on to the board. If you are going to seek out tools to build this, I suggest reading the "Soldering Strategies" section for a breakdown of the options available to you.
  • A multimeter. The linked one on Adafruit's site is is quite nice, but you can also pick one up for cheap at your local hardware store.
  • A device for burning the bootloader. In the 'Flashing the Bootloader' section, I mention a few pro-level debuggers that can do the job. But this guide offers specific instructions for using a SAM D21 dev board to burn the bootloader, so I suggest you get a Feather M0 Express ($19.95) or a Trinket M0 ($8.95); they're awesome boards, and you can use them for other projects afterwards.
  • Some optional items that enable functionality on the book: a MicroSD card for flashing the global font data, a Lithium-Polymer battery for making the device portable, and an external 1/8" headset if you want to use the Open Book's audio features.

BOM Notes

With the exception of the e-paper display, you can for the most part just go to the Open Book's Kitspace page and 1-click order the parts you need from Digikey. But there are a couple of parts that you can swap out to either save a few bucks or gain a bit of extra functionality:

  • The MicroSD slot in the 1-click BOM lacks card detect pins, so you won't get that functionality from it. Adafruit sells a better one that uses the same footprint and does have card detect. It's also slightly cheaper.
  • The Feather connectors on the BOM are rather ludicrously expensive; one of them alone costs more than the SAM D51 microcontroller on the board! I have taken to ordering two of these 2x20 surface mount headers and cutting them down to size, which saves $4 per board.
  • If the listed part numbers for some passive components are out of stock, or you find alternatives that are cheaper, you can swap in parts with the same values. Just be extra careful with the 4.7µF capacitor and the 1µF capacitors in the e-paper display block; for the most part, they need to be rated for 25 volts.
  • When shopping for the e-paper display, you may be tempted by Good Display's tri-color EPD devices, which add either red or yellow to the standard black and white. I suggest you don't do it. The long refresh time (10-15 seconds) required for tri-color display modes makes this sort of display poorly suited for book reading.
  • Finally, the 24-pin flex connector. The part on the BOM does work, but in my experience, Good Display tends to include a very high quality 24-pin connector for each display you order from them. I would recommend using that connector over the one in the BOM, but it's there just in case.

Board Setup

The Open Book comes to you as a bare PCB. Coaxing life into this inanimate sheet of copper and fiberglass will require that you solder dozens of small parts onto the board. The vast majority of those parts are surface mount technology, or SMT. If you have assembled surface mount boards before, no problem! Use whatever method makes the most sense for you. If you have not, there are several strategies that I have used to assemble the Open Book; I'll outline them here, so you can choose one that you're most comfortable with.

Soldering Strategies

First off, you can solder this board together using a fine-tipped soldering iron and either solder paste or thin solder. Collin Cunningham of Adafruit has an excellent video on the topic; that video covers soldering all of the kinds of devices you'll find on the Open Book board. You'll need the following tools on hand:

I've also found that some plain old high magnification reading glasses, like you might find at the drugstore, help immensely here as well.

While you can assemble the board completely using this method, I've found that using hot air for some parts makes life much easier. Specifically, I'm talking about the microSD slot; it's possible but difficult to maneuver a soldering iron in the little slotted area, but I feel more comfortable putting down some solder paste and hitting it with hot air, as depicted in this video by Antti Kupila.

To do this hot air reflow, you would need:

The hot air soldering station is also very useful if you need to rework, i.e. removing a chip that was placed in the wrong orientation: you can blow hot air at the component and remove it with tweezers.

Speaking of hot air, the final method for soldering a board like the Open Book is the one I prefer the most: the reflow oven. This method involves placing all the solder paste and all the components all at once, and then baking the board at high temperature to set everything in its place. I inherited one of these from a colleague, but you can also DIY one, as shown in this tutorial from Dan Cogliano. I've even seen folks reflow on a skillet or with a spoon and a lighter!

All of these (with the possible exception of that last one), are valid strategies for assembling the Open Book. Whichever one you choose, there are a few things to be aware of:

  • Be aware of the polarity of diodes and LEDs. Diodes have a gray line on their plastic body that should face the same direction as the line in the diode symbol on the silkscreen. LEDs have a marking, usually green, indicating the cathode; this should match with the dotted marking on the silkscreen.
  • Be aware of the different types of diodes and MOSFETs used in this design. Q1 and Q2 look nearly identical, but have very different electrical properties.
  • Similarly, the Zener diodes in the 'Extra Ports' block are easy to mix up with the Schottky diodes in the 'E-Paper Display' block. Make sure to put the correct diodes in the correct places.
  • When placing IC's, be conscious of the pin one alignment; in most cases, there is either a dot or a notch on each chip that should match with the dot or notch on the board's silkscreen.
  • The shift register may be a little different: the silkscreen on the board depicts a notch instead of a pin 1 dot. The pin 1 dot should be on the same side as the notch, the right side.

Assembling the board

Using whichever method you have chosen, place and solder all of the surface mount components, but stop just short of placing any through-hole components or the e-paper screen.

Inspect all the parts for bridged pins, and use a multimeter to check for short circuits between the power and ground nets. To do this, set your multimeter to continuity mode, and touch one probe to GND in the Feather header. Then touch the other probe to each of VBAT, VBUS and 3V3, also in the Feather header. You shouldn't hear any beeps. If you do hear a beep, that signals a short circuit; you should not power up the board until you address it.

  • If 3V3 is shorted to ground, use your magnifying device to look for solder bridges on the fine-pitched pins of the flex connector and the SAM D51; there are a handful of spots where a power pin neighbors a ground pin. Also check the two bottom right pins of R13. If you find a bridged pin, remove any excess solder with wick and / or flux.
  • If either VBAT or VBUS are shorted to ground, inspect the right side of R6 and the right side of U2. Remove any excess solder.

At this point it's wise to take one last look at the USB connector to make sure that none of these pads are bridged. In particular, use your multimeter to make sure that there are no short circuits between the three pins on the right. If any of these pins have a short, do not power up the board until you address it. This space is admittedly tricky. You may be able to get in there with solder wick or flux, but personally I've found it easier to use hot air to remove the port entirely and re-place it.

Finally — very important — double check that you have soldered the two through holes on either side of the Micro-USB connector! This is important to the structural stability of the USB port; I once ripped a USB port, pads and all, completely off of the PCB after having forgotten to secure these posts. This kills the board.

Once all the power nets are in order and the USB port is secure, plug the board in to a USB power brick, and use your multimeter to measure 3V3 and VBUS: in the appropriate DC voltage measurement mode, touch the black probe to GND in the Feather header, and the red probe to each of VBUS and 3V3. You should see 3.3 volts on 3V3, and 5 volts on VBUS.

Troubleshooting

  • If you see 5 volts on VBUS and significantly more than 3.3 volts on 3V3, unplug the device immediately, and check again for solder bridges on the USB port's pads. A bridged pad from VBUS to D- could cause this condition. Unfortunately it could also fry the microcontroller's USB interface. (you'll know this for sure if, later in this guide, you are able to flash the bootloader but can't connect to the board over USB, in which case you'll have to desolder and replace the SAM D51)
  • If you see 5 volts on VBUS but less than 3.3 volts on 3V3, double check the orientation of diode D1: the gray line indicating polarity should be facing left, away from the USB port. If it is backwards, desolder it and flip it around.
  • If you do not see 5 volts on VBUS, jiggle the cable and see if it helps. Either way, unplug the board and double check that all of the pins of the Micro-USB connector are soldered fully, reworking if need be.

At this point, you can solder the through-hole buttons (they go in from the top, facing toward the screen) and the Enable switch (which goes in from the bottom, with the metal case exposed on the backside).

Now we can move on to the software portion of this section.

Required Support Software

We will be working with the Arduino IDE for this portion of the guide; if you do not already have Arduino on your machine, download and install it now. You will also need to have both Adafruit and Oddly Specific SAMD board support installed. Follow the instructions here to get this set up. The two board manager URLs are:

If you have trouble finding the Oddly Specific board support package, search for 'Open Book' in the Boards Manager search box; it should be the only result.

In addition to the board support package, you will need several libraries installed. Most of them, we will install from the Arduino Library Manager:

  • Adafruit DAP Library
  • Adafruit BusIO
  • Adafruit GFX Library
  • Adafruit EPD
  • Adafruit SPIFlash
  • SDFat - Adafruit Fork
  • Adafruit NeoPixel
  • Adafruit MP3
  • arduino-menusystem (optional; needed for MVBook example in Part 2)

Finally, you will need to install the following two libraries by copying them to your Arduino libraries folder:

You can either use git to clone these repositories in your Arduino libraries folder, or download them as zip files and move the unzipped folders to the libraries folder. Note that the zip files may unzip as folders named "The-Open-Book-master" or "babel-master"; rename them to remove the "-master", so they are named "The-Open-Book" and "babel", and copy them to the libraries folder.

When you have all of these libraries installed, restart the Arduino IDE.

Flashing the Bootloader

The SAM D51 you soldered onto this board is, as you read this, blank; there's no user code running on it. Eventually, we want to be able to plug it in with USB and get code on it that way, but there's one stop on the way to that, and that's flashing (aka burning) the bootloader. The bootloader is a small program that resides right at the beginning of the SAM D51's Flash memory; it's the first piece of code that runs when powered up.

We are going to install a program called the UF2 bootloader, from Adafruit and Microsoft. It uses the SAM D51's native USB interface to offer two different ways to get code onto the chip: USB serial emulation, which lets you upload code from Arduino, and USB mass storage, which lets you write a UF2 file by dragging it onto the board like a thumb drive. But since there's no bootloader on there now, we will need to flash it using the Serial Wire Debug, or SWD interface.

You can access this interface using a SWD cable and the 2x5 header in the "Debug Port" block, if you populated it; if not, you can use five pogo pins and a steady hand on the five debug pads underneath it (this has been my preferred method). If nothing else, you can solder wires to these pads, and connect them to the device you're going to use for programming.

Speaking of which: you're going to need a second device to burn the bootloader! If you have a J-Link, Atmel-ICE, or other SWD-capable debugging tool, you can use it and Atmel Studio to burn the bootloader. But those gadgets are expensive, so instead, we're going to walk through using another dev board to burn the bootloader.

In the Arduino IDE, go to File -> Examples -> Open Book, and find the sketch called BurnBootloader. This sketch is not designed to run on the Open Book, but rather to run on a SAM D21 board like the Feather M0, which we'll call the "host board".

  1. Connect the Open Book's RESET, SWCLK and SWDIO to pins 10, 11 and 12 of the host board (leave 3V and GND alone).
  2. Plug the Open Book in to power, and the host board in to your computer
  3. Run the sketch on the host board, and open the serial monitor; you should see it erase the Open Book's flash chip, and then burn the bootloader!

You'll know it worked when you double tap 'Reset' on the Open Book: the red LED should begin to pulse, and the Neopixel should glow red or green.

Troubleshooting

  • If you open the serial monitor and find that the BurnBootloader program cannot connect to the Open Book, double check the Open Book's Enable switch; it should be switched toward the inside of the board. If it is switched the other way, toward the mounting hole, the board is powered down; switch it in the other direction and try again.
  • If you still cannot connect, double check that that pin 10 is connected to RESET, 11 to SWCLK and 12 to SWDIO; if any of these wires are swapped, it will not connect.
  • If you are using any host board other than a Feather M0, try again using a Feather M0. In theory this sketch should work with other host boards, but I experienced issues using an M4 board, and these instructions were tested specifically with a Feather M0.

Once you see the BurnBootloader sketch succeed, you're done with the debug interface! From now on we can talk directly to the Open Book via its own USB port.

Testing the Hardware

Note that this step requires a few external parts, if you want to test functionality that depends on them:

If you don't have one or more of these items on hand, no worries! You can choose to skip any test.

  1. In the Arduino IDE, go to File -> Examples -> Open Book, and find the sketch called OpenBook_SystemTest.
  2. Plug the battery in to the Open Book, and double tap the Reset button; you should see the red LED pulse.
  3. Plug the Open Book in to your computer, make sure its serial interface is selected, and run the sketch.
  4. Open the serial monitor; this is where we'll execute the various phases of the test. Follow the prompts, testing each of the subsystems. If there are any tests you wish to skip, send the letter 'S' out over the serial monitor and it will move on.

After the board test is done, you will get a report about what passed and failed. If everything you expected to pass, passed? You're almost done!

Troubleshooting

Depending on what failed its test, some suggestions:

  • In general, double check all the pins of the SAM D51; make sure none are either bridged to a neighboring pin, or not connected with solder. If you see a joint that is not connected, unplug both USB and battery, re-solder the pin and try again. Otherwise:
  • Buttons: double check the placement of the shift register U5. You should have a beveled edge along the top of the chip and a dot at the top right indicating pin 1.
  • SD Card: If you get the message "Card failed, or not present", double check the connections of the MicroSD socket.
  • SD Card Detect: Some MicroSD card slots lack card detect contacts. If this test fails, it's not the end of the world; it's a nice-to-have feature but far from a hard requirement.
  • Babel or QSPI Flash Chip: If either fails to be recognized, check the placement of U6 or U4, respectively. In both cases, the dot indicating pin 1 position should be at the top right. If it is not, you can remove this chip this using hot air and tweezers, and re-place it correctly
  • Mic Preamp: double check the placement of U7. The dot on this one is tiny, but it should be on the top right. Also make sure that none of the pins are bridged; this component has a finer pitch than some of the others.

Burning Font Data

This step requires the MicroSD card from the last step, as well as a binary blob called babel.bin; it's the language support file that contains all the glyphs and Unicode data that the Open Book will use to display texts. You can find it in the babelconvert subfolder of the Babel library, or download it here.

  1. Use your computer to copy the babel.bin file to your MicroSD card, and eject the drive
  2. Plug the MicroSD card in to the MicroSD socket.
  3. Go to File -> Examples -> Babel Universal Character Library and load the BurnBabelBurn sketch.
  4. Run the sketch, and open the Serial Monitor (Tools -> Serial Monitor)

At this point you will see a menu of options for actions you can take.

  1. First, enter E and press 'Enter'. This will erase the Flash chip.
  2. Next, enter W and press 'Enter'. You will see a series of messages indicating the writing of a page to Flash. Wait until they finish scrolling; you will see "Done!" appear when the process is complete.
  3. Finally, enter V and press 'Enter'. This will verify that the burned data matches the blob on the SD card.

If the image verifies, you're done! You can delete the babel.bin file from the SD card; the Flash chip will retain this data indefinitely. Now all that's left is the screen!

Troubleshooting

  • If the verification fails a few pages in, return to step 1 (enter 'E') and try the process again.
  • If you have just gotten done soldering the chip, wait until it cools down completely; I've experienced errors when I tried to flash the Babel image right after pulling the board from the oven.
  • If the Babel verification process fails more than three times, try using a different brand of MicroSD card; I have had issues with off-brand cards, but no problems at all with Sandisk cards.

Installing the E-Paper Display

Finally! For this last test, we are going to install the sketch before attaching the screen. In the Arduino IDE, go to File -> Examples -> Open Book, and find the sketch called OpenBook_ScreenTest.

  1. Plug the Open Book in to your computer, make sure its serial interface is selected, and run the sketch.
  2. Unplug the device.
  3. Carefully take the e-paper display out of any protective packaging it's in. Remember, it's made of very thin glass!
  4. Thread the screen's flex cable through the hole up top, and secure it in the flex connector.
  5. Taking care to avoid damaging the flex connector, turn the device over so you can see the screen, and power up the device by plugging it in to power.

If you see the screen refresh, you're done! Secure the screen to the front side of the Open Book with double sided tape.

Troubleshooting

  • If you do not see the screen refresh, double check that the screen's flex cable is seated correctly; re-seat the cable if necessary.
  • If the screen image looks weak, gray or shows lots of noise or static, double check the orientation and type of the diodes in the E-Paper Display block. All three diodes should bear a tiny marking "B3" (not "CM") on the top of their cases, and the gray line which indicates polarity should be facing upward, toward the top of the board.
    • If one or more of the diodes is facing the wrong direction, desolder it and flip it around.
    • If one or more of the diodes bears the marking "CM", you've placed one of the Zener diodes here instead of the Schottky diodes that belong in this spot. Find the Schottky diode (again, look for the B3 marking). You may have put it in the "Extra Ports" block. Desolder and swap them out.

Next Steps

In this section, we walked through the basic steps required to assemble the board and bring it to life for the first time. In the process of assembling and testing the board, you probably gleaned some sense of how the board works in broad strokes. In this next section, though, we will delve deeper into each section of the board: the purpose of each subsystem, the components that comprise them, their underlying workings and how they interact with each other.

Understanding the Hardware

As you may have noticed, the board is laid out as a series of subsystems, each surrounded by a dotted line. This documentation is going to use these divisions of purpose as a sort of road map to explaining and understanding the Open Book; we'll start with the power supply and the battery, move on to the microcontroller that drives the Open Book's functions, then talk about each of the peripherals and the circuitry that supports them.

Some of these peripherals will be simple; the Babel block is just a Flash chip and a single decoupling capacitor. Other peripherals will be more complex; to support the e-paper display, for example, there are a number of capacitors, diodes and an inductor required to generate the higher voltages that the screen needs.

Whether simple or complex, we will discuss all of these blocks so that no corner of the board is left a mystery.

Software and the Open Book library

Throughout this document, after introducing the hardware, the guide will delve a little bit into the software support for that block of functionality. Some blocks, like the Enable switch, have no software component at all. For others, like the QSPI Flash block, well-defined open source software exists to support their functionality, so it will only be necessary to point toward the required library or firmware. In a couple of cases, the block requires some more custom logic (Babel) or allows for more advanced usage if you understand how the software driver works (the e-paper screen). In those cases, the guide will offer more detail.

The Open Book C++ library aims to seamlessly support most of the Open Book's peripherals. In all cases, this guide will close with a list of the best options for software support for that block.

Anyway, let's get to it! Our first stop on the board is the power supply.

Power Supply

This power supply circuit is basically identical to the power supply on Adafruit's Feather M4 Express. Many thanks to Limor and the whole Adafruit team for all their awesome open source hardware designs!

The heart of this block is U1, an AP2112K-3.3 voltage regulator. It has five pins, but there are only four with connections:

  • VIN - This is the input power pin, and can accept any voltage from around 3.5 volts to a maximum of 6.5 volts. We are going to supply it with the higher of either the USB port's 5 volts (VBUS) or the battery's voltage (VBAT), which varies but is nominally about 3.7 volts. We'll call the power rail connected to this pin V+.
  • GND - the ground pin. Tied to the board's common ground plane, which is connected to both the USB and battery ground.
  • EN - A signal to the regulator that turns it on or off. Anything above 1.5 volts on this pin, and the regulator will supply power to the board; below 0.4 volts, it will shut down.
  • VOUT - This is the output power pin. It provides regulated power at 3.3 volts, and powers just about everything on the board. We can draw up to 600 mA from this regulator.

That's the main event. Then in the immediate vicinity, you can see the input and output capacitors: C1 is a 10µF capacitor between the regulator input and ground, and C2 and C3 are 10µF and 1µF capacitors between the regulator output and ground. These capacitors ensure a steady voltage level going into and coming out of the regulator.

Next, we have R1, which is a 10KΩ pull-up resistor. This resistor pulls the AP2112K's Enable pin to V+, that higher-of-either that we were talking about earlier. How do we get V+? This is also borrowed from Adafruit's latest Feathers: a transistor switch. D1, Q1 and R2 sort of conspire to make sure that only VBAT or VBUS is powering the regulator at any given time.

Q1 is a DMG341 P-channel enhancement mode MOSFET. The battery (VBAT) is connected to the source, and V+ is connected to the drain. The gate, we tie to VBUS. Current only flows from the the source (VBAT) to the drain (V+) when there is a negative voltage at the gate. The premise being, when there is a positive voltage at the gate (i.e. we are plugged in to USB), no current from the battery will flow to V+.

That's pretty good, but we need some help at the gate. When we aren't plugged in to USB, the gate isn't at 0 volts, it's floating. So R2 helps out, pulling the gate low when VBUS is disconnected. Now we know that the gate is at 0V when we're not plugged in, and at just about 5V when we are.

At this point, we have the USB voltage switching the battery on and off, but no current flowing from VBUS to V+. That's where D1, an MBR120 Schottky power diode, completes the triumvirate of power. It allows current to flow from VBUS to V+, while preventing any current from going the other way.

(You may have noticed R10 and an LED in this block. We'll get to those in a bit; aesthetically, they belonged there, but there wasn't room for a dotted line to declare them a separate block.)

Battery and Charging

Technically, you could complete only the steps above (and omit Q1 and R2!) and you'd have a device that's only powered by USB. But since this is intended for use as a portable device, I doubt most people would do that.

Aside from R2, which we already covered, the battery block's components encompass three separate bits of functionality that we'll deal with in turn: battery charging, charge indicator LED's, and power monitoring.

C4 is pretty simple, a 10µF capacitor to keep the voltage on VBAT stable.

From there, charging couldn't be easier: U2 is the MCP73831T-2ACI/OT constant current/constant voltage lithium-ion charge management controller. It has five pins, all of which we make use of:

  • STAT - This is a tri-state output that we use to run our charging status LED's.
  • VSS - Ground pin, tied to the board's common ground.
  • VBAT - The battery that we want to charge.
  • VDD - Power to the charging IC. Maximum 7 volts, but we're going to power it with VBUS which is 5 volts.
  • PROG - We connect a resistor between this pin and ground to select the rate of charge.

Let's start with PROG, because it's important: R3 is a 10K resistor connecting PROG to GND. You may have noticed a note on the board advising that you can use resistors of different values to select the charge rate. The 10K resistor selects a constant current charge rate of 100mA, which is a safe number for any battery you want to use (especially the 400mAh battery I recommend for this project, which should not be charged at a rate of more than 400mA). In theory you could swap it for a 5K resistor to charge at a rate of 200 mA. But then you would have to be very careful not to plug in a battery like this one that doesn't support the faster rate of charge.

There are two reasons I suggest not messing too much with this resistor: first, since power is coming from USB, you shouldn't be drawing more than 500 mA from a USB port to begin with. But second, lithium-ion batteries are, if not scary, certainly worthy of respect. A laptop battery contains roughy the stored energy of a hand grenade, according to Randall Munroe; I don't want anyone to risk catching on fire just to charge their book a little faster.

Anyway! On to the rest of the circuit. R4 and R5 are current limiting resistors for the CHG (yellow) and FUL (green) LEDs, respectively. R4 connects in series between STAT and the cathode of CHG. The anode of CHG is connected to VBUS, so when STAT goes low, current flows from VBUS through the yellow LED into STAT, to indicate that the battery is charging.

R5 connects in series between STAT and the anode of FUL; its cathode is connected to GND. This way, when STAT goes high, current flows from STAT through the green LED into ground, indicating that the battery is full.

When USB power is not plugged in, the battery is neither charging nor full; in this case, STAT goes into a high-impedance state, and no current flows through either LED, which turns them both off.

So far, none of these signals are connected to the microcontroller; at this point, in software, we have no way of knowing whether we are plugged in to USB or on battery power, nor do we know the battery level. R6 fixes this — and it's the first part on the board that actually talks to the microcontroller!

R6 is a pair of voltage dividers. A voltage divider, simply put, applies a voltage difference over a pair of resistors in series. The voltage at the node in the middle equals some fraction of the total voltage difference, depending on the values of the resistors; if they are equal in value, the output voltage will be half of the input voltage. R6 does just this: it's an array of four 100KΩ resistors that split VBAT and VBUS down the middle, and tie the resulting signals to pins A6 and A11. We need to split these voltages in half because the subjecting the pins of the SAM D51 to more than 3.6 volts will damage them. Also, the chip can't measure a voltage higher than its input voltage of 3.3 volts anyway.

There is no specific software support for this; you can just do an analog read of these two pins. A6 will give an analog reading corresponding to half the current battery voltage, and A11 will read roughly 2.5 volts if plugged in to USB, or 0 if running on battery power.

USB Port

While the USB data lines have to do with the microcontroller, the power coming from USB is essential to getting any of this to work, so it probably makes sense to cover it here. There are five pins, plus the shield. First, let's deal with the pins:

  • VBUS - on the far right, as you look down at the bottom side of the board, this is the 5 volts from the host device or your power brick.
  • D- and D+ - these are the USB data wires, a differential pair. This means that whenever one is high, the other is low, which makes for a really reliable way to send data over a wire. These pins connect directly to the SAM D51's USB pins, which means that the USB interface in your laptop is connected more or less directly to the microchip in the book you've built. Neat!
  • Pin 4 is the USB ID pin. It's not connected.
  • The last pin, on the left, is GND. Connects to the board's common ground plane.

The last bit is parts R27 and C36, an RC filter that has to do with the USB cable shield. The shield is braided wire around the signal wires, and it connects to the metal connector at the end of the cable. No signal is intended to flow over the shield. It's common to connect the shield directly to the ground plane, but doing this can turn the USB shield into an antenna, picking up noise from the environment. To mitigate this effect, we have 1MΩ resistor R27, and 1nF capacitor C36. We connect the USB shield to the ground plane via this RC filter, in the hopes of mitigating any noise that the USB shield might pick up.

Enable Switch

This is going to be a short section. The enable switch is a single-pole, single-throw switch that connects the voltage regulator's EN pin to either ground on one side, or nothing at all on the other. When connected to the 'nothing' side, the EN pin is still pulled up to V+ by R1, so the regulator is on, supplying 3.3 volts to the board. When EN is switched to ground, the 3.3 volt regulator will shut down, de-powering just about everything on the board.

There are two exceptions. First, the battery charging circuit will continue to run, if plugged in to USB power. Second: both the VBAT and VBUS pins will continue to provide power to any attached FeatherWings, unless they have additional circuitry to switch them off.

Now that we have power squared away, it's time to look at the microcontroller that will power all the peripherals to come.

SAM D51 Microcontroller

The SAM D51 microcontroller at the heart of the Open Book is a powerhouse as microcontrollers go: a 32-bit ARM Cortex M4 running at 120 MHz, with 192 kilobytes of RAM. The specific device we're using is called the ATSAMD51J19A-AUT. Some of those letters and numbers tell us important things about the device; SAM is the product line, for example and D51 is the specific chip, but then there's the J, which means that this chip has 64 pins, and the 19, which means it has 512 KB of Flash memory. The last A, after the hyphen, specifies the package type; A means it's a TQFP or Thin Quad Flat Pack package. It means the chip has little metal leads coming off of the sides, like computer chips in the movies.

What about those 64 leads? Well, they're connected to all our power and peripherals. Let's talk about power first.

Power Pins

Pins 21, 34, 48 and 56 are power inputs to the chip. All of these are connected to the 3.3 volt power output from the regulator. C6, C9, and C10 are 1µF decoupling capacitors for the power pins, and C5 is a 10µF capacitor as suggested in the datasheet's schematic checklist. All told, these capacitors ensure a steady 3.3 volt power supply to the chip.

Next, there's pins 7, 22, 33, 47 and 54. These are ground pins and as you might expect, tie directly to the board's ground plane.

Pin 8 is VDDANA. The thing with having all these components on the board is that as various parts draw more or less power, say when the processor is crunching a number or an LED is blinking particularly blinkily, there can be fluctuations in the voltage of the 3.3 volt power rail. The regulator does its best, and all our decoupling capacitors try to keep the voltage level stable, but these variations can manifest as noise, and when we're dealing with analog signals, that noise is no good. This is where FB1 comes in. It's a ferrite bead, and its purpose is to attenuate all that high frequency noise on the 3.3 volt rail. On the other end, we get a clean signal called AVCC (further stabilized by 10µF capacitor C11 and 1µF capacitor C12), and we feed that to the VDDANA pin.

Finally in power, we have pin 53, VDDCORE. This one might look confusing if you were just looking at the schematic. VDDCORE connects pin 53 to capacitors C7 and C8, but nothing else. Where does the VDDCORE voltage come from? The answer is that the SAM D51 has an internal 1.2V voltage regulator that it uses for many of its internal peripherals, its memory and the processor core. 10µF C7 and 1µF C8 help to keep that 1.2 volt power rail steady.

Understanding the SAM D51

The data sheet for the SAM D51 is over 1,200 pages long, and even that doesn't cover everything about the device. For the purposes of understanding how your Open Book works, though, there are three major concepts that will help: Peripherals, SERCOMs and pin muxing.

Peripherals

Microcontrollers like the SAM D51 are designed to be versatile. While I might want to buy this chip to put in an e-book reader, someone else might want to use it to drive a wall of colorful LED's, measure data from sensors, or pilot a robot. Since the makers of the chip want to support as many use cases as possible, they put a variety of different peripherals onto the chip's silicon. We don't use all of them. But the ones we do use, we'll talk about here.

First off, there's PORT. This isn't a peripheral per se, but rather the controller for all of our pins. We can use PORT to grab a pin and use it as general purpose input or output (GPIO). This is how the red LED works! The PORT controller also has handy features like enabling pull-up or pull-down resistors (the Lock button uses this) and setting the drive strength (how much current will flow). You make use of this with the Arduino digitalRead and digitalWrite functions; in CircuitPython, you'll use digitalio.

That's all well and good, but what if we wanted to, say, talk to a computer with USB? While we might be able to implement USB from scratch with nothing but flashing wires on and off, the SAM D51 makes it easy: it includes a USB peripheral! By assigning two pins to act as the USB D- and D+ pins, the USB peripheral can do all the low level flashing of wires, and expose a higher level interface that we can use. Now, in practice, even this higher level interface is still pretty low level; the Open Book ends up using the USB peripheral via software like the UF2 bootloader, CircuitPython's USB support, or an Arduino library like Adafruit's TinyUSB. Still, it makes sense for you to know that underneath it all, this is how it works.

More peripherals: the Analog Digital Converter (ADC) allows us to read analog values from pins — instead of a 1 or a 0, an analog value from 0 volts to a maximum of 3.3 volts. Software support for this comes from either the Arduino core's analogRead function or CircuitPython's analogio module.

The Digital Analog Converter (DAC) lets us output a true analog signal, which is useful for a lot of things, but it's especially useful for generating audio. The Arduino core has support for analog output, but there are also useful higher level libraries like Talkie and Adafruit_MP3 that make it even better for audio. CircuitPython support comes from the analogio and audioio modules.

The Timer Counter (TC) and Timer Counter for Control (TCC) are very versatile peripherals, but for our purposes we mostly use them for pulse-width modulation, or PWM. This is often used to control motors or LEDs; we use it to set the brightness of the red LED, and most of the pins on the Feather header also support PWM. Software support for PWM comes from the Arduino core's analogWrite function; in CircuitPython, it's the pulseio module.

The External Interrupt Controller (EIC) lets an external signal (like the lock button) interrupt execution of our code so that we can handle that external condition. It also allows us to wake up from a low power sleep mode when an interrupt is triggered. Software support for this comes from the Arduino core's attachInterrupt function; CircuitPython does not currently support interrupts.

The Quad SPI or QSPI peripheral lets us connect to a device that supports the QSPI protocol (it's like SPI, but uses four data lines so it's faster). We use it for the 2 megabyte QSPI Flash chip. Software support on Arduino comes from the Adafruit_SPIFlash library; CircuitPython uses the QSPI interface interface internally to seamlessly display the contents of the Flash chip as a USB drive.

The last peripheral to mention is SERCOM, but it's versatile enough, and our uses for it are so varied, that it needs its own section.

SERCOMs

The SERCOM peripheral is a sort of a polyglot. Where the USB peripheral could only speak USB and the QSPI peripheral could only speak QSPI, the SERCOM peripheral can talk to several different kinds of devices with many different parameters. It also helps that there are five of them on our SAMD51 J variant. In the end, we use four, speaking three different protocols:

The Serial Peripheral Interface, or SPI, is a one- or two-way communication interface that requires one wire to act as a clock (SCK), and one or both of the following two wires: Serial Data Out (SDO) which the microcontroller uses to talk to a peripheral, and Serial Data In (SDI) which a peripheral uses to talk back to the microcontroller.

NOTE: Your board's silkscreen may contain deprecated names for the SPI data signals. In the past, these signals were called MOSI and MISO, referring to 'master' and 'slave' devices. This guide initially tried to dance around the problematic terminology by calling the devices Main and Secondary, but as of mid-2020, consensus in the hardware community is building around using either SDO and SDI as described above, or COPI and CIPO (Controller Out, Peripheral In and Controller In, Peripheral Out). Future boards' silkscreens will adopt one of these two alternatives, but in the meantime: MOSI=SDO=COPI, and MISO=SDI=CIPO.

  • SERCOM1 is a two-way SPI bus, the board's main SPI interface. Both the Babel flash chip and the MicroSD card are connected to this interface, and it is shared with the Feather header's SCK, SDO and SDI pins.
  • SERCOM4 is a one-way SPI bus (it only has a SDO line, no SDI) connected to the e-paper display. The microcontroller uses this interface (along with some other GPIO signals) to communicate with the e-paper display.

Multiple SPI devices can share a single SPI bus by way of an active-low Chip Select pin; in the case of SERCOM1, we can set SDCS low and BCS high, and we'll only be talking to the SD card; or we can set BCS low and SDCS high to talk to the Babel Flash chip. We can also set both CS lines high and talk to an SPI device plugged in to the Feather header. It is a very versatile communication interface.

Next up: the Inter-Integrated Circuit bus, or I²C bus, is a two-way communication bus that uses two wires: a clock (SCL) and a data (SDA) line.

  • SERCOM2 is an I²C interface. While the Open Book does not include any I²C devices on board, these two pins are routed out to the Feather header's SCL and SDA pins for use with FeatherWings. They're also shared with the 4-pin STEMMA port.

Like SPI, I²C supports talking to multiple devices, but in this case it uses addresses. You can send a command to a device that resides at a given address, and all the devices that are not addressed will ignore it.

Finally: the Universal Asynchronous Receiver and Transmitter or UART is a serial interface that uses two wires, TX and RX, to transmit and receive data.

  • SERCOM5 is the UART, and again, there is no device connected to the UART pins; they are made available on the Feather's TX and RX pins for use with FeatherWings.

Unlike SPI or I²C, the UART can only communicate to one other device: the Open Book's TX line goes to the RX line of the device you're talking to, and the Book's RX line goes to its TX line. You can use this to communicate with a GPS unit, a computer like a Raspberry Pi, or even another Open Book!

Having described all of these peripherals and SERCOMs, you might wonder, how does one decide which pins to use for which purposes? The answer is the third and final piece you'll need to grok the way we use this microcontroller: pin muxing.

Pin Multiplexing

There are 51 IO pins on the J-variant of the SAM D51. Some functions, like GPIO, you can assign to any pin. Others, like USB, have their assignments set in stone. The concept of assigning a physical pin to one internal function or another is called pin multiplexing, or muxing.

This is also a good time to talk about how we identify the pins. When discussing a microcontroller board's pin names and numbers, there are three identifiers that matter.

First off, the pad name. This is a name that the manufacturer assigns to the pin; it's the most constant identifier, and when we talk about what a pin is capable of, we talk about it using its pad name. For example, let's take PA02. If you look at the "I/O Multiplexing and Considerations" table on page 32 of the SAM D51 data sheet, you'll see some entries in the grid to the right: "EIC/EXTINT[2]", "ADC0/AIN[0]", and "DAC/VOUT[0]". This means that in addition to being general purpose IO, you can also assign one of the following functions to the pin:

  1. External interrupt 2 on the external interrupt controller.
  2. Analog input 0 on ADC0.
  3. Voltage output 0 on the DAC.

The reason we pin these functions to PA02 has to do with the second identifier that matters: the physical pin number. This has to do with how the signals are laid out on the chip's package: pin numbering starts at #1, usually denoted by a dot on the surface of the chip, and runs sequentially, counter-clockwise, all the way around the package. The physical pin number usually has nothing at all to do with the pad name, and it might vary dramatically from one package to another. Let's take that same table and that same pin, PA02. Looking to the left of the pad name, you'll see that on a 64-pin SAM D51 like ours, PA02 is pin 3. But on the 128-pin variant, PA02 is on pin 9, and on the 100 pin variant it's on pin 7. The function of PA02 doesn't change; all that matters is that it's found in a different place.

The last identifier that matters is the IDE pin number. This, again, has nothing to do with the pad name or the pin number. When creating a board definition for Arduino or CircuitPython, we end up creating a user-friendly identifier that you end up using in your code. In the case of PA02, I've given it the user-friendly name of A0, partly because it's the first DAC-capable analog pin, but mostly to match with Adafruit's M4 Feather boards, where PA02 is also A0.

So now you know: when you send a signal out on A0, it's being handled by the microcontroller's pad PA02, and if you wanted to poke at it with your multimeter, you would find it physically present at the microcontroller's pin 3.

Is this, strictly speaking, necessary for day-to-use of the Open Book? Probably not. But I think it's important not to hide these implementation details in header files. I want you to know how the thing works, and this component is arguably the most important worky bit of the whole thing.

Pin Assignments

With all that as background, here are all the peripherals and the pins they are connected to.

IDE Pin Number Pad Name Physical Pin Function Notes
0 PB17 40 UART RX SERCOM5/PAD[1]
1 PB16 39 UART TX SERCOM5/PAD[0]
4 PA14 31 SD Chip Select (SDCS)
5 PA16 35 Feather Pin 5
6 PA18 37 Feather Pin 6
8 PA15 32 Neopixel
9 PA19 38 Feather Pin 9
10 PA20 41 Feather Pin 10
11 PA21 42 Feather Pin 11
12 PA22 43 Feather Pin 12
13 PA23 44 LED / Feather Pin 13
24 PA12 29 I²C SDA SERCOM2/PAD[0]
25 PA13 30 I²C SCL SERCOM2/PAD[1]
26 PB22 49 SPI SDI SERCOM1/PAD[2]
27 PB23 50 SPI SDO SERCOM1/PAD[3]
28 PA17 36 SPI SCK SERCOM1/PAD[1]
30 PA24 45 USB D-
31 PA25 46 USB D+
32 PA03 4 AREF Permanently tied to 3.3V
35 PB10 23 QSPI SCK
36 PB11 24 QSPI CS Pulled up to 3.3V
37 PA08 17 QSPI IO0
38 PA09 18 QSPI IO1
39 PA10 19 QSPI IO2
40 PA11 20 QSPI IO3
41 PB15 28 E-Paper SDO SERCOM4/PAD[3]
42 PB13 26 E-Paper SCK SERCOM4/PAD[1]
43 PB12 25 Button Latch
44 PB07 10 E-Paper CS (ECS)
45 PB05 6 E-Paper DC (EDC)
46 PA00 1 E-Paper Reset (ERST)
47 PA01 2 E-Paper Busy (EBSY)
48 PB31 60 Mic Shutdown Also supports SWO via a solder jumper
49 PB30 59 Button Data
51 PA27 51 Lock button Active low; requires internal pull-up
52 PB06 9 Babel Chip Select (BCS)
53 PB14 27 Button Clock
A0 PA02 3 Headphone Right Shared with Feather header; aka digital pin 14
A1 PA05 14 Headphone Left Shared with Feather header; aka digital pin 15
A2 PB08 11 Feather pin A2 aka digital pin 16
A3 PB09 12 Feather pin A3 aka digital pin 17
A4 PA04 13 Feather pin A4 aka digital pin 18
A5 PA06 15 Feather pin A5 aka digital pin 19
A6 PB01 62 VBAT monitor aka digital pin 20
A7 PB04 5 Raw mic input aka digital pin 21
A8 PB03 64 STEMMA port A8 aka digital pin 2 / digital pin 22
A9 PB02 63 STEMMA port A9 aka digital pin 3 / digital pin 23
A10 PA07 16 Amplified mic input aka digital pin 54
A11 PB00 61 VBUS monitor aka digital pin 50
- PA30 57 SWCLK Debug port; not available in Arduino or CircuitPython
- PA31 58 SWDIO Debug port; not available in Arduino or CircuitPython

From here, let's just walk the board!

Debug Port

We used the Serial Wire Debug or SWD pins earlier. There is not much to say about it, except to say that with the right kind of debug interface and software support on your desktop, it's a powerful tool for debugging, even though we only used it to flash the bootloader. Two notes, though.

First: in the interest of understanding the board, let's address R7. It's a pull-up resistor on the SWCLK pin (PA31), to ensure that even in a noisy environment, the SWCLK pin remains high when a debugger is not connected.

Second: the SAM D51 that we are using has a pin (PB31) that supports Single Wire Output, or SWO; it's a debug protocol that lets you output strings of data, kind of like a console output. The SWO pin on the header is connected to a solder jumper pad near the headphone jack; this is because, by default, PB31 is the microphone preamp shutdown control. By cutting the trace between PB31 and SHDN, you can reroute it to the SWD header and get this functionality on your debugger. If you do this, you may also want to tie SHDN to either the ENA or DISA pad to keep the mic preamp on (or off).

Reset Button

The reset button is connected to the SAM D51's !RESET pin via RC filter R9 and C13. R8 pulls !RESET high whenever the button is not pressed.

Pressing reset once resets the board. If using the UF2 bootloader, a double tap puts it into bootloader mode.

Red LED

I promised we would get to this! The red blinkenlight is labeled LED, and it's right next to the USB plug. Next to it: R10, a current limiting resistor in series between PA23/D13 and LED's anode.

QSPI Flash

As you were assembling the board, you probably noticed that there were two identical chips in two different places. The GD25Q16C chip in this block (U4) is connected to the SAM D51's QSPI interface.

C14 is a decoupling capacitor that serves much the same purpose as the capacitors at each of the SAM D51's power pins: ensuring a steady 3.3 volts to the QSPI Flash chip.

R11 is a pull-up for the QSPI chip select line. It ensures that the CS line is not left floating at boot. This is very important since, at least in CircuitPython world, this chip contains all of your code! You don't want it getting any errant signals.

E-Paper Display

This is the most complex part of the board, and probably the one you most want to understand. So before we get into the electronics of it all, let's digress for a moment to understand how e-paper works.

E-Paper: Theory of Operation

When you are looking at a page of text on the Open Book's e-paper screen, what you're really seeing is a layer of ink in capsules. The ink is either sucked to the back of the screen (creating an area of white), or pushed toward the front of the screen (creating an area of black).

But really, there are three layers of stuff allowing this to happen. On the front, the layer closest to your eyes, is a thin electrode covering the entire screen. And then on the back, there's a grid of electrodes, 120,000 of them, that the e-paper screen can address as pixels. Applying a voltage difference across the electrodes on the back and the common electrode on the front causes the particles of ink to get shoved around, and the result is a pattern of ink on the screen that resembles the shape of the electrodes / pixels that did the shoving.

In theory, that sounds simple enough. In practice, there are a couple of issues with this. For one thing, if you shoved the ink in the same direction repeatedly, it would be hard to get it to go back to where you want it, leading to ghosting, the phenomenon where a residual image remains on-screen after it's been cleared. That, we can solve in software; we can make sure to push the ink back and forth when we refresh the screen, to keep the ink from getting too settled in.

The bigger problem from an electronics perspective is that the voltages required to actually shove the ink around are relatively high, especially compared to the 3.3 volts we have available on our board. We'll need voltages as high as 16 volts and as low as -16 volts to get the job done. Lucky for us, the e-paper panel in this project (the GDEW042T2) includes the circuitry necessary to control a boost converter. All we have to do is supply some supporting circuitry to help it along.

The E-Paper Display Block

Basically all of the circuitry in this block is based on the Adafruit eInk Breakout Friend and the reference circuit for the GDEW042T2. To a certain extent, this is just following the suggested schematic, but it is helpful to know what these parts are for.

The inductor L1, N-channel MOSFET Q2, resistor R12, Schottky diodes D2-D4 and capacitors C22-C23 all encompass the power boost and inverter circuit. Honestly I find it a bit complicated, and I don't want to misstate anything, but the important thing to note is that L1 stores energy, and Q2 repeatedly switches L1's connection to ground; it's basically a cycle of storing energy and releasing it. This has the effect of boosting the voltage at C23 to 16 volts (VGH), and inverting it to -16 volts at C22 (VGL). R12 is a current sense resistor, part of the feedback loop that the on-board boost control circuit uses to control the switching.

Then, there's that bank of capacitors on the left. With the exception of C17, all of these capacitors are connected to various power pins on the e-paper display's flex connector; the e-paper panel generates a variety of voltages that it needs to operate the screen, and these capacitors keep those voltages stable.

C17 on the other hand is the usual decoupling capacitor, this time for the 3.3 volt power input to the display panel.

E-Paper Signals

In addition to the two SPI wires (SCK and SDO), there are a few other signals to and from the e-paper display:

  • ECS is the e-paper Chip Select, an output from the microcontroller that we set low when we're talking to the display.
  • EDC is the Data / Command line. Also a signal from the microcontroller, this selects whether the data we are sending to the display is data (like to put on the screen) or a command (bytes that tell the screen what to do)
  • ERST is another signal from the microcontroller to the display; we can pulse this line low to reset the e-paper driver chip.
  • EBSY is the lone signal from the display to the microcontroller; the display pulls this line low when it is busy (i.e. updating the screen) so that we know to wait to send it more commands or data.

E-Paper Display Drivers

The E-paper display panel we are using here is the GDEW042T2, but the controller inside of it is an IL0398 E-Paper Driver IC. It's that data sheet that has the good stuff, commands to turn the various components on and off, transmit data to the screen, and customize everything from the voltages applied to the electrodes to the color of the border to the lookup tables that define the update waveforms.

These update waveforms, incidentally, are the key to driving an e-paper display. The panel ships with some default lookup tables, which cause the screen to flash black and white and inverted over the course of a second, before settling into a final 1-bit image. By sending different lookup tables to the display, we can coax it into displaying an image faster, or we can convince it to quickly update only a partial area of the screen. We can even coax 2-bit grayscale out of it, a four-color display mode that with some dithering can display simple drawings and photographs, if at an admittedly low resolution.

More information about update waveforms can be found in the Notes section at the end, but for the moment, the important thing to understand is that there are five different lookup tables that describe what happens when the screen updates:

  • LUT1 is the set of voltages and timings applied to the common electrode.
  • LUTWW is the set of voltages and timings applied to pixels that are currently white, and will remain white after the screen update.
  • LUTBW is the same but for black pixels that will turn white in the update.
  • LUTBB, same, for black pixels that remain black.
  • LUTWB, same again, for white pixels that are turning black.

The Open Book Arduino library includes an Adafruit_EPD-compatible driver for the IL0398 called OpenBook_IL0398. It includes three sets of custom waveforms, which you can set by calling the driver's init method with the waveform as an argument:

  • OPEN_BOOK_DISPLAY_MODE_QUICK is the update mode I use for turning pages of a book. It quickly flashes the screen black, then white, then inverted, and finally to the final values. This mode has somewhat less contrast than the factory waveform, likely because it's holding the pixels at their assigned voltages for a shorter amount of time.
  • OPEN_BOOK_DISPLAY_MODE_GRAYSCALE uses the four states intended for partial refresh (WW, BW, BB, WB) to coax four shades of gray out of the display. This mode also changes some settings related to the display's frame rate and voltage.
  • OPEN_BOOK_DISPLAY_MODE_PARTIAL is available, but you shouldn't need to call init with it. This one is used in conjunction with the displayPartial method. It's intended to update a small area of the screen, such as a selection indicator.

Other than this, you can interact with the Open Book's e-paper display the same way you would any other Adafruit_EPD-compatible display.

MicroSD Card Slot

This is another short section, because MicroSD is pretty self-explanatory! It never ceases to amaze me that the same protocol that I use to drive an e-paper display can drive the MicroSD card that I use to shoot megapixels worth of photos. It's just SCK, SDO, SDI and a chip select pin. Software support for the MicroSD comes from the Adafruit fork of SDFat, but it's not baked in to the Open Book library just in case you want to use a different library for whatever reason.

From a hardware perspective, the only thing remotely interesting about this block is that the decoupling capacitor is 10µF instead of 1µF; I saw in a datasheet that we might want a larger value to buffer the current inrush that happens when a card is inserted, so, more µF's.

Oh, and the card detect signal. This is cool: it's just a physical switch on the backside of the connector! You can see it, pair of little metal tabs, making contact when there's no card in the slot, and being pushed up out of the way when the card is inserted. You can read this signal via the high bit of the button shift register; it's pulled up so high means there is a card, and low means no card present. Speaking of:

Button Register

Another acknowledgment: I got a Pybadge last year, and I loved the buttons; looked into how Adafruit did it and thought it was super rad. This block of functionality is borrowed directly from their design; again, many thanks!

Each of the buttons (and the SD card detect, as mentioned) are tied to input pins on U5, a 74HC165 parallel-in, serial-out shift register. To read them, we set the latch line low, pulse the clock eight times, and on the data line we can read out eight bits representing the state of each button.

The buttons are active high, meaning that pressing a button connects it to 3.3 volts. R13 and R14 are 10KΩ resistor arrays that pull the buttons down when not pressed, although one of the signals on R13 (the card detect) is pulled up instead. Other than that, there's C26, a 1µF decoupling capacitor, and that's it for that!

As for software support, the Open Book library includes a method on the Open Book class that reads the states of the buttons. Note though that for cross-platform compatibility with the E-Book Wing (which uses the high bit for the Lock button), it exposes the state of the Lock button instead of the card detect signal. There is a separate method (OpenBook::sdCardState) to read the card detect signal.

In CircuitPythonland, you can use the gamepadshift module to read the states of the buttons and the SD card detect signal.

Lock Button

The Lock button is tied directly to a GPIO pin on the SAM D51. I picked PA27 (51 in IDE-speak), because while PA27 doesn't do much in terms of peripherals, it can be an external interrupt, and it happens to be on a different interrupt channel from any of the Feather pins. The thinking here is that if your e-book is sitting idle for a while, you could display an image like a screen saver, set all your peripherals to a low power mode, and then put the SAM D51 into a sleep mode. Pressing the lock button could then wake the device up.

Software support for this button involves the Open Book library, as mentioned above; readButtons will stuff the lock button's state into the expected place in the buttons byte. Alternatively, you could use just a plain old digitalRead in Arduino, or a digitalio.DigitalInOut in CircuitPython.

Using the lock button as an interrupt pin involves using the attachInterrupt function in the Arduino core. For sleep mode operation, use the Adafruit_SleepyDog library.

Accessory Ports

The three ports on the side of the board are also borrowed from the Pybadge. The I²C port is a four-pin STEMMA-compatible JST-PH connector that provides power, ground and I²C signals. There are no pull-ups on the Open Book, but all the STEMMA and STEMMA-QT boards that Adafruit makes have pull-ups, and I think most Sparkfun Qwiic boards do as well, so you should be able to just plug and play.

Ports A8 and A9 are three-pin STEMMA compatible JST-PH ports, providing power, ground and a signal that can be used as an analog input, digital IO, or an external interrupt.

C27 is a 10µF capacitor that's kind of just there for the external ports, on the off chance that you have a power hungry gadget plugged in to one of them.

Ports A8 and A9 also have current limiting resistors (R15 and R16) in between the microcontroller pin and the external port, both to protect the microcontroller and so you can run something like an LED right off of the pin. They also have 3.6V Zener diodes (D5 and D6) ready to limit any higher-than-expected voltage on the pin, to protect the microcontroller. These too are cribbed from the Pybadge; it's a really well-considered bit of safety for these external ports.

Babel Flash Chip

I'm hesitant to even call this the "Babel Flash Chip," since it's just a GD25Q16C SPI Flash chip like any other, but I needed a way to distinguish its purpose on the board.

The SAM D51 microcontroller has 512 KB of Flash. Which is big, but not big enough to store all the languages of the world. The 2MB QSPI Flash chip could swing it, but it would leave little room for anything else like, y'know, your code. You could store font data on a MicroSD card, but now the operating system for your e-book is depending on a removable device that could disappear at any time.

The Babel Flash chip is my solution. It's two megabytes dedicated to nothing but language support: Unifont glyphs for the entire basic multilingual plane, glyph data for accents, diacritics and non-spacing marks, right-to-left support, word wrapping info, case mapping and even Arabic shaping.

Earlier you burned a binary blob on to this chip; while more information about the file format can be found in the Babel source code, the gist is that it starts off with some header info and a set of lookup tables. These lookup tables let you locate metadata and a graphical representation for any code point in the basic multilingual plane in constant time.

The other component in this block, familiar story: 1µF decoupling capacitor C28.

In terms of software support, the Babel repository is the place to look for this. The Arduino library and the CircuitPython module are ahead of each other in some places and behind in others; the Arduino library has robust text layout in both LTR and RTL modes and formatting in bold and italic. The CircuitPython module on the other hand has working Arabic shaping, but less done in terms of typesetting and display.

More on this soon.

Audio Combo Jack

I miss headphone jacks on modern gadgets. Seriously, it's such a useful, simple, obvious connector. An analog signal that moves a diaphragm to make sounds. You could hand this to an alien with no ears and they'd still figure out how to make it work. But I digress.

J1 is the jack, and it has four conductors. Starting from the tip of the jack, there's the left audio signal (A1), then the right audio signal (A0), analog ground (separated from the board's ground plane by FB2), and the raw mic input signal (A7).

The raw mic input on A7 isn't terribly useful for audio, as the inline electret mic doesn't actually output enough of a voltage to be useful. This signal is made available to the microcontroller mostly for the sake of inline buttons; the iPhone's headset shorts the mic input to ground when the button is pressed, meaning you can use the headset as an extra button. Just watch for this pin shorting suddenly to ground.

+Mic Preamp

This circuit is also borrowed from an Adafruit design, the NeoTrellis M4. It's similar in many ways to the reference design in the datasheet, but some component values differ; I figured they had tuned their circuit to work well with the SAM D51's ADC.

U7 is a MAX4468 microphone preamp with a shutdown pin to allow the amplifier to be turned off for power-saving or privacy reasons. The top row of resistors and capacitors represent the mic bias circuit, responsible for filtering the signal on the way to the op amp's non-inverting input. The bottom row of resistors and capacitors set the gain of the op amp, which in our case is about 100x.

Software support for this is technically built in; you can read the amplified mic signal on A10. But my hope for this was for it to be used for voice control, and it works, at least at a proof-of-concept level, using machine learning with TensorFlow Lite. The state of the art is changing, but this guide from Ladyada herself is an excellent place to start. There's also a demo in the Open Book repository (Open_Book_Heart_Of_Darkness_TensorFlow) that lets you read Joseph Conrad via a voice-activated accessibility mode.

Neopixel

The WS2812 Neopixel serves as a status LED for the bootloader and for CircuitPython, or you can control it directly using Adafruit's Neopixel libraries for Arduino or CircuitPython. There's not much more to add except, again, a 1µF decoupling capacitor, to buffer any current inrush from making it real bright real quick.

Software support: Adafruit Neopixel library for Arduino; for CircuitPython, use the neopixel module.

Feather Connector

Finally, we've reached the final block of functionality on the board, and this one is good. Because the Open Book adheres to the Feather standard, you can plug just about any FeatherWing into it and it'll, y'know, do things! Get new features and such.

In addition to matching the Feather spec with placement of pins like I²C, SPI and Serial, the Feather header also retains exactly the same pin muxing as the Feather M4 Express. This means that all the pins have the same capabilities in the same places. Check out the Feather M4 Express pinout here.

Most of the pins on the Feather header are untouched by the peripherals and parts on the Open Book board, but a few are shared:

  • A0 and A1 are shared with the headphone jack, since they are the only DAC-capable pins.
  • SDO, SDI and SCK are shared with the two SPI devices on the board: the MicroSD card and the Babel Flash chip.
  • 13 is shared with the LED, but that's something that's common to most Feather boards anyway.

That's it! All the other pins are available for you to use for whatever. I have not tested a ton of FeatherWings, but one that I have tested and that I find very useful is the Adafruit AirLift FeatherWing. This wing adds WiFi to the Open Book, using pins 11, 12 and 13 as well as the shared SPI pins.

Next Steps: MVBook

Now that you know how all the parts of the Open Book work, let's run a sketch that puts some of the pieces together!

The MVBook software is a simple menu system for displaying a few works, stored in a specific format, and allowing the user to select and page through them. It's not as full-featured as I hope it someday will be, but it is my hope that as we build more of a community around this hardware, we can advance better software support together.

The MVBook example folder includes a zip file called books.zip with a handful of public domain works you can copy to an SD card. The MVBook example folder also includes a Python script called bookbinder.py that formats plain text files to function with MVBook. Ideally, the canonical format for reading will be plain text files, but for the moment, some of the work of pagination and embedding metadata needs to happen on a computer.

  1. Copy the works you want to read with MVBook to an SD card and insert it into the Open Book.
  2. Go to File -> Examples -> Open Book and load the Open_Book_MVBook sketch.
  3. Run the sketch!

You should see the screen refresh and display a listing of the books on the MicroSD.

Controls

  • Use the direction pad's "Up" and "Down" arrow keys to navigate the list of works.
  • Use the direction pad's "Select" button in the middle to select a work in the list and enter reading mode.
  • When reading a work, use the "Previous Page" and "Next page" buttons on the far left and right to navigate the work.
  • To return to the main menu while reading a work, press the center "Select" button.

Troubleshooting

  • If the MVBook sketch does not run, ensure that you completed the steps in "Burning the Babel Flash chip" above, as MVBook depends on Babel for text rendering.
  • If the interface is not responding, especially after you have left the device alone for for a while, ensure that the board has power by plugging it in or pressing the reset button. The e-paper display will retain whatever text was on the screen last even after the battery dies, which can be confusing.

Notes

Enclosures

The Open Book wants to be portable, but with all of these exposed components on the back, it's not exactly something you want to toss into your purse or backpack where other metal objects could cause a short. Not without a case, at least! The minimum viable enclosure is this 3D-printable case. Note that it does not completely prevent ingress of small objects; there are holes for the buttons, SD card, ports and optionally, the Feather headers. Still, it does give me a level of comfort to toss it in its own pocket in a satchel where I know there are no small metal objects.

My printer isn't the best so I prefer to print the front piece with supports; it only needs them for the holes in the sides, and they are easy to punch out. But you may be able to get away with no supports. As for the back piece, there are two options there: one with holes for the Feather headers, and one without.

The enclosure is designed to be held together with M2.5 screws and hex standoffs; I got them from this kit, but I bet you could find them at McMaster:

  • 4x M2.5 x 8mm long F-F hex standoffs
  • 4x M2.5 x 4mm screws
  • 4x M2.5 x 6mm screws

Once you have printed the enclosure, follow these steps to assemble it:

  1. Insert the board into the enclosure, taking care to fit the buttons through the holes on the front.
  2. The four standoffs match with the four mounting holes in the corners. One by one, place each standoff in each corner, and screw a 6mm screw in from the top.
  3. Once all screws are in place, put the backplate on, making sure that the holes for the Feather headers are aligned with the Feather.
  4. Secure the backplate using the four 4mm screws.

The Laser Cut Option

You can take that same design in Tinkercad, move the parts so that they intersect the workplane, and export SVG's appropriate for use with a laser cutter. Laser-cut 1/16" acrylic or wood works well for this; personally, I love the look of basswood, with a stain + polyurethane in the color of your choice. You can use the same screws and standoffs, but note that this option provides even less protection against ingress, as it leaves the sides open.

E-Paper Waveforms

Credit where credit is due: I learned a lot of what's in this section from Waveshare's public domain drivers for their version of this e-paper display, as well as Ben Krasnow's video on the topic. I also recognize that this section may not be complete and may even contain some misunderstandings or errors on my part; if I got something wrong, please let me know!

This section is most useful if you want to hack on the display driver, either to improve its refresh rate or improve display modes like grayscale and partial refresh. But it's also worth stating that you can permanently damage your display by editing these waveforms. Tinker at your own risk!

As mentioned above, e-paper displays work by applying voltage differences between an electrode on the top of the display and a grid of electrodes on the bottom, which attracts black ink to either the front of the display or the back. Some displays also add a third pigment, either red or yellow; when combined with an additional voltage level and a longer refresh cycle, this can add a third color to the mix. Our display doesn't have that feature, but the red voltage level will come up in a bit.

The IL0398 E-Paper Driver is highly configurable. For example, when explaining the boost circuit above, I mentioned that it boosted the 3.3 volts input to the display to ±16 volts. That's true, but it's also not set in stone; by editing a value in the power setting register, we can select four different voltage levels from ±13 to ±16. This same register allows us to select high and low voltage levels for our pixels, ranging from ±2.4 to ±11 volts in 0.2 volt increments. All the details are on are page 12 of the datasheet.

In the Open Book Arduino library, the OpenBook_IL0398::init method sets the values in the power register (IL0398_POWER_SETTING). By default it selects 16 volts on VGH and -16 volts on VGL — don't worry, we'll explain these abbreviations in a moment — 11 volts VDH for black and white pixels, -11 volts VDL for black and white pixels, and 3 volts VDH for red pixels; this is just the default value; we have no red pixels. You may notice that the init method sets the red pixel voltage to 6.5 volts in grayscale mode; this is from the Waveshare driver, where we use the red voltage to coax two additional shades of gray out of the panel.

(There is one other power bit a bit lower, the IL0398_VCM_DC_SETTING. This sets the value of VCM_DC. We'll get to it shortly.)

After the power register is set up, the next item in init to pay attention to is the PLL control (IL0398_PLL). The values for this register are on page 16 of the datasheet, and they relate to the display's frame rate. "Frame rate," you say! "This display would be lucky to display two frames per second!" Well, the frame rate we're referring to here isn't aboout how many frames you see. Remember: when we refresh the screen, we need to push the ink forward and backward several times by setting voltage levels on the electrodes for intervals of time. When we write out our update waveforms, we do it in terms of the number of frames, which translates through this register to a duration of time.

At this point, we might as well skip ahead in init to the displayMode switch. Here, you'll see different lookup tables set for the three custom modes: quick, partial, and grayscale. Note that for this next bit, the documentation on these lookup tables is not in the IL0398 datasheet, but it is identical to the format described in the IL0373 datasheet; look on pages 15 and 16 for the details.

When you update the screen, the controller walks through a series of steps or states. The panel has some programmed in, but we can also specify our own. It's not much more complicated than that: these tables are just instructions for which voltages to set and when. Each six-byte line in the lookup table describes voltages and timings for four phases, and there are six lines.

The first byte in each line is the voltage level select. A series of four two-bit values:

D7 D6    D5 D4    D3 D2    D1 D0

This is where the voltage levels set up in the power register come in: for the WW, WB, BW and BB lookup tables, 0 means GND, 1 means VDH (11V), and 2 means VDL (-11 volts). At each phase, each pixel's voltage will go to that value.

That handles the pixels. But what about the electrode on the front? That's where the LUT1 lookup table comes in: in that table, 0 means VCM_DC, which is close to 0 volts; 1 is VDH+VCM_DC, which is close to the pixel high voltage, and, 2 is VDL+VCM_DC, which is close to the pixel low voltage.

The next four bytes on each line describe the number of frames to hold these values. The final entry on the line is the number of times to repeat phases 1 through 4. Each line after the first describes another set of voltages and durations for the update. If you see a row full of zeroes, nothing happens, since it's holding the desired states for zero frames, zero times.

Now you can see how these lookup tables translate to the update behavior that you see on the screen! The voltage differences pull the ink toward the back or push it to the front, and by manipulating the voltage and the timing, you can program the display to do many different kinds of updates.

Conclusions and Future Work

Congratulations on building your own Open Book! You are now one of the first people in the world to have one.

While MVBook works well as a proof of concept, I'm hoping that by the time you read this, work will already have begun on more robust software for reading books, which is the device's primary use case. However, the Open Book is capable of more; here are some ideas for future projects:

  • The dual DACs on the headphone jack, along with the DMA-capable playback of Adafruit_MP3, make an audiobook player an obvious use case.
  • For mobility impaired users, the pairing of the powerful SAM D51 and the MAX4488 microphone preamp opens the door to for voice control using machine learning! There's a demo of this in the Open Book repository, but it may need some tweaking; look for Open_Book_Heart_Of_Darkness_TensorFlow.
  • The STEMMA ports along the left side offer a lot of flexibility! You could add a Neopixel strip to either the A8 or A9 ports to make a reading light, a potentiometer as a volume control, or even more buttons!
  • The STEMMA I²C port (left side center) lets you plug in all manner of sensors; for accessibility (or just to read on the treadmill), you could add a gesture sensor to turn pages with a flick of the hand (you'll also want a STEMMA to QT cable).
  • You can also add an AirLift FeatherWing to add WiFi to your book! Sync up with RSS feeds or weather data to add connectivity to the book.
  • Add a clock with the the DS3231 Precision RTC FeatherWing

There are so many possibilities. One that keeps coming to mind is adding an Ultimate GPS FeatherWing and some OpenStreetMap data to make an open navigation device of some sort. Is such a thing even possible? Who knows?

Maybe you?