30 June 2021

MCU Internet Clock

While poking around on Adafruit, I found a guide for building an internet clock, and while the dev board used has a ATSAM21G18, it's not using CircuitPython. I eventually found the AirLift series of WiFi boards that have this statement just about everywhere:
Having WiFi managed by a separate chip means your code is simpler, you don't have to cache socket data, or compile in & debug an SSL library.
Sounds pretty promising, but...
It also works great with CircuitPython, a SAMD51/Cortex M4 minimum required since we need a bunch of RAM.
Meaning that I'd need to use the ATSAMD51J19A-A, which isn't a huge deal. While I did find the most compact Airlift breakout board, I kinda wanted just the module so that I can just build my own and such, and though I did find it, half of the product description was a bit concerning.
For advanced users only! This product is just the module - which can be difficult to use. You'll need to solder it onto a board with supporting circuitry (See the Espressif's assembled ESP32 development board for a more complete design). This module is for developers at this time, and is FCC, CE, IC, MIC (Telec), KCC, and NCC certified.

Please note this product is for people who are comfortable compiling via the Tensilica toolchain. There are the beginnings of an Arduino IDE setup, Lua and MicroPython port. We are providing these for the community of advanced hackers, coders and makers who are willing to help make demos, projects and tutorials for the ESP32! Please consider holding off on purchase if you are not planning to contribute to the community coding efforts at this time, thank you! There are no refunds, exchanges, store credit or support in any way for this product.
So yeah, not the best idea (especially when there's a solder pad underneath it that I wouldn't be able to hand solder). The guide for the breakout board makes it seem fairly easy to use with CircuitPython, but I wasn't all that concerned about the code at that time. I think I made a basic footprint for the Airlift breakout board and used vias for the signals before removing the vias later for more flexibility. What was annoying is that the mounting holes of the Airlift is neither y-aligned, nor is it the same x distance from the edge, and I figured they have their reasons for doing so.

I think I started the board off with the seven-segment displays (same ones as the Raspberry Pi clock) and then added the Airlift footprint (that I think still had vias) for the board size, and then had to remove the vias from the Airlift footprint because I was going to need whatever space I could get. I also had a hole in each corner of the board for the standoffs and lens, but later I removed two of them in favour of the simplicity of using the Airlift mounting holes for the same purpose. The USB mini-B jack went on the left side, the button on the right, and the MCU and transistor was plopped in the centre for later placement. For this case, I have PA24 and PA25 facing the USB connector, since I wasn't going to need any of the pins on that side.

Because of the space I would need, I needed SMT headers to connect the Airlift to the board, and also needed the same for the displays. Part of the problem was that I would also need a standoff or space between the two boards, and that I had no way of knowing the insulation height of the header that comes with the Airlift. After a bunch of digging around, I did find a header and socket header combo that came to 10mm. I ended up using the same series of socket headers for the display because it was the one with the smallest pad sizes (which technically are a reference anyway).

Anyway, I think I was planning on the same 74HC595 as the second revision (the smaller but wider package, SSOP), but I didn't have enough space to have everything run properly, so I needed a different solution. I had encountered the MCP23008 on Adafruit sometime before this project, and there was the MCP230xx library that they have, which includes the larger MCP23017. Oh right, I tried the DHVQFN16 package of the 74HC595 on the Raspberry Pi clock before I went to the SSOP/TSSOP package, but I might've tried it here before giving up on it again. Anyway, I think I started with the SSOP package of the MCP23017, but it was still hard to route stuff, so I ended up going with the QFN package. I opted to not connect the exposed thermal pad to anything for that bit of board space, that really only makes it easier to run VCC to Reset for both of them and to A0 for one of them.

A little out of order here (though honestly I think the posts after the backlog and each of those posts aren't fully in chronological order), but because I had learned about multiplexing the signals, it was pretty useful here. I'll go into it here since this would be the slightly better example than the project that kinda sparked the multiplexing. Anyway, while there is a guide from Adafruit on it, it goes the way of Arduino (I almost wanted to type Audino, thanks Pokémon) after kinda explaining it. First we have what are called SERCOM, which is short for SERial COMmunication, and each SERCOM is a module that groups different serial communication protocols together (SPI, I²C, etc). Each SERCOM has pads, which define the signal type for each of the connections to the SERCOM module, and are numbered from zero to three. Each SERCOM module can share pads with other SERCOM modules, but only with modules with the same number (SERCOM 0 can't share pads with SERCOM 1). There is a bit of a caveat, however, in that an MCU pin can be part of two SERCOM modules. Using the ATSAMD21G as an example, PA16 is SERCOM 1 pad 0 by default, but is SERCOM 3 pad 0 alternatively; this means that PA16 can be only be used with SERCOM 1 and SERCOM 3 as pad 0. In terms of I²C, pad 0 is defined as SDA, pad 1 is defined as SCL, pad 2 is defined as SDA_OUT, and pad 3 is defined as SCL_OUT; pad 2 and pad 3 are for 4-wire I²C operation. SPI is trickier than I²C, so let's start with a table I made that condenses the information from the datasheet.

Data Out Pinout
(DOPO)
Data In Pinout
(DIPO)
Address MOSI SCK MISO
0x0 PAD 0 PAD 1 PAD 0
0x1 PAD 2 PAD 3 PAD 1
0x2 PAD 3 PAD 1 PAD 2
0x3 PAD 0 PAD 3 PAD 3
This table is only for ATSAMD21 or ATSAMDA1 devices!

Let's start with explaining some of these acronyms: MISO stands for Main In Secondary Out, MOSI stands for Main Out Secondary In, and SCK stands for Serial ClocK. (Main and secondary used to be master and slave respectively, but well, there's been a push to stop using these terms in favour for others. I think Adafruit uses secondary instead of serial, but I could be wrong, though I'm pretty sure it's where I got secondary from.) Also this table is only for when the MCU is being used to "host" the SPI bus, MOSI and MISO are flipped if the MCU is being used as a "client". Besides the fact that it's obvious that MOSI and SCK are data outputs and thus are in the same column, it's also because the pads to be used need to be on the same address. For example, to have pad 3 as MOSI, SCK can only be pad 1. The MISO pin can be any address, but obviously can't be a pad that's being used by MOSI or SCK. To sum up for this case, SCK can only be pad 1 or pad 3, MOSI can only be pad 0, 2, or 3, and MISO can be any of the unused pads.

The ATSAMD51 is different though, and for pads 0 and 1, the alternate SERCOM pads don't match the default SERCOM pads like the ATSAMD21. For example: PA08 is SERCOM 0 pad 0 as the default, and SERCOM 2 pad 1 as the alternative; PA09 is SERCOM 0 pad 1 as the default and SERCOM 2 pad 0 as the alternative. Weird? Yes. By this, in theory, it wouldn't matter which pin is SDA and SCL for I²C if the pins are on the same default or alternate SERCOMs. Pads 2 and 3 don't exhibit this weird flop between default and alternate SERCOMs, so it's predictable. SPI is kinda weird, but technically easier... Table time!

Data Out Pinout
(DOPO)
Data In Pinout
(DIPO)
Address MOSI SCK MISO
0x0 PAD 0 PAD 1 PAD 0
0x1 Reserved PAD 1
0x2 PAD 3 PAD 1 PAD 2
0x3 Reserved PAD 3
This table is only for ATSAMD5x or ATSAME5x devices!

SCK has to be pad 1, MOSI can be either pad 0 or pad 3, and MISO is, well, whatever's not already used. DOPO addresses 0x1 and 0x3 are reserved and I think it might say somewhere (either the datasheet or somewhere online) the reasoning or use for the reserved addresses. Hopefully I explained this a little clearer than the Adafruit guide, since the guide uses Arduino code to help explain it, and though I can read it, I feel like it's a little messy for someone that's using CircuitPython and trying to multiplex pins. Question you might have is "How do I use this in CircuitPython?", and well, you kinda don't. Certain Adafruit CircuitPython libraries has places to put the respective signals (i2c = busio.I2C(SCL, SDA) for example) and you would just put the pins you want in their corresponding places and CircuitPython takes care of the rest. From what it seems like with the Arduino, you have to actually set which SERCOM the pin will be using (or something like that) before you can tell whatever library that you want your pin to be used for SCK or whatever.

What does this whole SERCOM multiplexing have anything to do with this project? It's because I needed to connect the SPI signals from the Airlift to the MCU in a direct manner without using vias to jump over other signal traces and to leave pins for I²C since I blocked off the SPI and I²C pins on the USB side of the MCU. I put the RTC (DS3231) and the temp sensor (MCP9808) at the bottom of the board and left it there while I worked on the other stuff. Though the Airlift board does have a 3.3-volt voltage regulator, I decided to add another one because I knew that one wouldn't be enough to power everything else.

Now that I think about it, the other clock project I've yet to talk about actually came first, due to the fact that its where I originally have the timezone offset set in the code while having a switch for DST control. The same is true for this project, especially since the time that it gets from NTP is UTC. Anyway, the code takes the timezone offset and adds the inverted status of the DST switch to it for the total offset before it adds it to UTC for the local time. To be able to do date and time maths, Adafruit's version of Python's datetime module is needed, which not only takes up quite a bit of space (30 something kilobytes), but also takes up quite a bit of memory (which isn't really said anywhere but the github page, and more of this will be covered in the other clock project if I remember). The switch for time and temp at full brightness was carried over (the idea, not the part) from the Raspberry Pi clock project, and it sat with the DST switch until I was ready to put them in their final locations.

For flipping the display, I think I started with a third switch, but eventually moved to an accelerometer to make the end usage easier. It wasn't too big of a deal to add the accelerometer, since it has both I²C and SPI interfaces, the I²C traces were already running to the MCP23017 on the "button" side of the board, and there was enough space to do so. I had to hop one of the I²C traces over the other since there wasn't any other way to route the traces, and that I already needed vias to bring the signals up to the top of the board for the MCP23017.

I think I failed to mention the purpose of connecting A0 of one MCP23017 to VCC, but I'll do it now while it's on my mind. The MCP23017 has three addressing pins (A0, A1, A2) that allows the MCP23017 to have eight different addresses, and thus allowing eight MCP23017 to be on the same I²C line. For normal usage, the addressing pins are connected to ground for the default address of 0b100000 (0x20, 32), and when connecting A0 to VCC, the address becomes 0b100001 (0x21, 33). Also with this non-default address, this MCP23017 instance needs to be made with the address, otherwise it would go unnoticed or CircuitPython will raise an error that the device (which is actually the one at the default address) is already in use (or something to that effect).

I think I forgot again that I needed the pull-up resistor and cap for the reset line, so I had to make some late changes to account for it... Or maybe it was the weird pull-up resistor thing on SWDIO for programming the bootloader. With some extra space that would be able to accomodate six switches the size of the reset switch, I wanted to add some buttons, even if I never planned on having them (and thus not knowing what to have them do). Realistically, I could only do three to keep the traces fairly simple (I probably could do one more, but I didn't feel like it was worth the extra work it'd entail), and I already had a placeholder wire that I put in between a couple of traces because I didn't want to lose that bit of routing space, so one of the routes was already partially ran. I later realised that I could do capacitive touch pads instead since the pins I used also support capacitive touch, and that the force needed would be negligible compared to the tactiles that I was using (same as the reset button, but I don't know if I was looking at ones with the same or similar footprint with lower activation force). I had ideas to use a button or two for doing some sort of screen test and maybe to be able to set the timezone offset, but I haven't decided what to do them. I also haven't placed any of the touch pads since I kinda lost interest in them and in the project a bit — the silicon shortage also not helping.

Also, for the RTC's backup battery, I decided to use a CR1225 that has some solder pins welded on, so that I can solder wires to the pins and then solder the wires to the vias in the board. The battery (which would have heatshrink around it) would sit under the display at the centre, since there's no space for a coin cell holder. Routing the positive trace was a bit interesting, since I had to go around and between stuff. I don't think I have anything else to say with the board, so here's the current state of the board:

The CR1225 holder was just a placeholder to be able to connect the vias, it'll eventually be deleted.

I forgot when I did during all of this, but I did get an Airlift to poke around with the libraries and such; I was kinda curious with the WiFiManager class that should make it easier to stay connected. Another Adafruit library I found was their NTP library, which is super simple to use. The NTP object is created with the Airlift object, and .set_time() is called on it to set the MCU's time (the MCU of the board, not the MCU of the Airlift). .valid_time is provided to be able to know if the NTP sync was successful or not. Setting the RTC to the MCU's time is just as easy, as it just takes rtc.datetime=time.localtime() to do so if the sync was successful (probably obvious, but time.localtime() is the time from the MCU). This was another reason that I decided that this project would be better than the Raspberry Pi clock, as I had a lot more unanswered questions with the Raspbery Pi clock that would require testing I didn't feel like putting in the effort for. One of them was if I was still able to access the RTC if it's given to the system, and I had assumed not, so setting the clock via the OS required a shell script and tweaking the list of sudoers was the route I would've needed to go. The other question was if I would even be able to access the I²C line at all, because of the former. With this project, I have a lot less worries about the I²C lines and device access.

I did also use the ATSAMD51J19A-A dev board to test out the automatic syncing of the RTC module I have, and it worked fine since I had to set the RTC module time for the third time. I forgot which project I was working on, but I was using the RTC module to test out the 32kHz output pin and when hooking it up, I didn't realise that I swapped VCC and ground, so while I got nothing, the RTC, board, and backup battery were heating up. I forgot how I realised it, but I did and unplugged the board while heading to the soldering station for one of the fans to cool it down. I was worried that I fried the RTC, but when it was cool enough, it worked just fine and had just lost the set time. I think I had read in the datasheet or somewhere that the 32kHz output only works when the RTC recieves power on VCC, so it was what I was trying to test for, but I didn't have any luck getting a good reading. I had also read that I would need a pull-up resistor for the 32kHz, and I had soldered a 2.2 kilohm resistor according to the post I read (datasheet says anywhere between 1k and 1M) before the test. I also had desoldered one leg of the resistor during the test because I was having troubles getting a reading, but it didn't seem to make a difference. I was using the oscilloscope I use at work because it was more convenient than the one at home (which is some leftover one from work), but got some funky waveform, and tried it on the the oscilliscope that the electrical engineer uses and still got the same weird waveform. I think this is what sparked me to try the one I have at home and either I had the reversed polarity fiasco then or it was before all this, I really can't remember. Regardless though, I ended up getting the frequency metre to read the 32.768kHz it was supposed to read and the wave was square and was close enough to 32kHz. I took the RTC to work again the next day and also got the correct waveform. I know I had some sort of suspicion that it might have something to do with the ground line of the oscilloscope connected to the bench power supply, but it didn't seem to be it. I think with the initial test with the oscilloscopes at work, I did try using the little probe grounding thing with no avail. Dunno what happened, but well, I got what I was looking for in the end. The second time I did the reverse polarity thing was while setting up for the automatic syncing test, and well, I had more of a reason to do it after it cooled down enough.

Anyway, the reasoning for the 32kHz output pin was to see if I might be able to use that instead of having a dedicated crystal circuit for the MCU, and from the post I previously linked, it should be doable. I checked the datasheet for which pin to connect to since one of the two crystal pins is the output and one is the input. I keep forgetting where I read it (it was some Adafruit thing, I know for sure), but the MCU uses the USB pulses for timekeeping on boards that don't have a crystal (or something like that). Since the clock would be connected to some USB charger, it probably wouldn't get the pulses, so I needed to add a crystal in some way. Using the 32kHz output was a lot more space efficient because it would've been a tight fit to get a crystal and its caps near the crystal pins (PA00, PA01). Routing was kinda interesting because I was thinking to redo some traces and kinda take a long way around, but decided to try running it the other way while hugging other traces, which ended up being easier. I only needed to redo the placement of the 6 ground-stiching vias and the via of the 3.3-volt bus on that side.

I don't know if it's the most dense board that I've designed, but it's definitely one with a lot of minimum-width traces. I think that's really all I can say about it until I get boards and stuff. Silicon shortage is also affecting MCP32017 supply as well as the MCU, so double ouch in this case.

While writing this, I (obviously?) had the board layout open and such and was questioning if the extra voltage regulator would really be enough, since the theoretical maximum current draw of the display at 2 volts (their forward voltage) is 640mA. This made me want to try to measure the actual power consumption that the Airlift is using (at the 3.3 volts), and while it should be manageable by lifting the output leg of the regulator and soldering a wire to it, when I actually looked at the physical board, I didn't feel like taking the time to trying to do so. At that point, the friend that helped me name i3rd asked if it might be in the datasheet, and well I hadn't thought to check. While the datasheet doesn't give any meaningful current consumption, I did find that they recommend a minimum of 500mA supplied to the device, and with Adafruit having the 600mA voltage regulator on the Airlift board, it's probably safe to say that I can expect at most that. I did find some voltage regulators that have a maximum output of 700mA and 1A, and probably will use the 1A version so that I really don't have to worry. I was thinking to not have the Airlift's regulator add to the 3.3-volt bus on the board, but it'd probably be for the better that I don't. I might've been fine with 600mA and whatever is left over from the Airlift's regulator, considering that I don't plan on actual 100% brightness for full brightness, but desoldering the regulator to replace it will be annoying.