22 June 2021

3DS Input Redirect Controller

With some mention of a Discord server member having a modded 3DS firmware, I kinda decided to look into it, and after reading the instructions, it seemed pretty easy to do. I grabbed my secondary 3DS (because I pretty much had nothign to lose on it) and gave it a shot; it really was that easy and non-destructive.

One of the features is being able to control the 3DS with a controller connect to a computer over the network (obviously using a programme on the computer as well), which is pretty nice since the 3DS' directional pad is a little annoying for me to use (I guess I have larger than average hands?). For controllers I have the XBox 360 controller, the Horipad, the Joy Cons, or any of the Bluetooth SNES controllers (8Bitdo or Nintendo) to choose from. The Bluetooth SNES controllers are automatically out for not having a joystick (though probably not a huge deal), the Joy Cons I don't want to have to bother pairing/repairing, the Horipad is wired and connected to the switch dock that I don't want to have to bother with, and so that leaves me with the XBox 360 controller that gives me some disassociative issues because its buttons feel much different than the 3DS'.

With learning CircuitPython, I decided to make my own controller that has a few buttons to allow me to run a predefind macro that speeds up what I was doing a lot by hand; it would also have tactile buttons that'd probably feel a little closer to the 3DS than the XBox 360 controller does.

I think while using the Trinket M0 to do some tests, the programme to send the input to the 3DS didn't seem to register any of the buttons in the gamepad library for the directional pad, and while it sucked, I figured out to just emulate the directional pad by sending a couple joystick movements to make it seem like the joystick was flicked in that direction.

I think the code came first because it was easier to handle? On top of working on this project, I was also learning things about microcontrollers along with CircuitPython. It seems like I was originally planning on using the ATSAMD21E18A-A because it would fit all the inputs as well as having an analogue selector switch (using some resistors and a switch) and four function/macro buttons. Or at least so I thought, because at some point, I realised it took away PA24 and PA25, which were needed for the USB connection to the computer, so I decided to stick the axes of the two joysticks on an I²C ADC (ADS1015) to free up PA24 and PA25. I also had realised at some point that I could use an RTC for the year, month, and day for part of the macro instead of having to manually input it myself, and so I added added it to the I²C line.

I think I started writing the code at this point? (Seriously, there was so much going on that it's hard to remember everything in chronological order.) And with how long the code got, I was uneasy with how large the code was getting and I didn't know how much of the MCU's flash space would be left after all the libraries I needed, so I decided to add an SPI flash chip for expanded storage (it was at this point I hadn't had the Trinket M0 yet). Because of this, I needed four more pins from the MCU that I didn't have, so I opted to "upgrade" to the ATSAMD21G18A-A for more pins. It was likely around this time that I had to look for some other I²C pins because the SPI flash is normally connected to PA08, PA09, PA13, and PA14, and I had PA08 and PA09 planned for the I²C connections. I ended up using PA22 and PA23 for I²C, and then had a couple pins leftover with all the other inputs connected to their own pin. I think I was also just going to use a CircuitPython build for a board that was similar, but after a bit of digging, I became less sure that was a good idea.

I forgot how I ended up with needing a second selection switch, but it seems like I ditched the joystick axes to be able to fit them in? Anyway, I designed the first version of the board after getting some data (button spacing from the Joy Cons), and I think the board sat without traces and airwires for a while before I bucked down and started to do it. I had a bit of problems with placement of the flash and RTC, as well as running the traces for them, but did eventually figure it out. The I²C lines for the RTC was the most annoying because not only did I need to connect the two pins to the MCU, I needed to connect them to separate pull-up resistors as well, but the other problem was getting the trace for the backup battery to the corresponding RTC pin while keeping the trace short and reasonable, and a friend suggested to run the trace in the space between the pull-up resistor pads, which I failed to see (I was getting tired).

Section of the board with the flash, RTC, MCU, part of the backup battery holder (CR2032), and some of the buttons.
If you see the problem with the I²C lines, I'll get to that later.

I think it was sometime after this when I had the Trinket M0, and loading the necessary libraries onto the MCU's flash (the Trinket M0 doesn't have an external flash chip) showed me how much space I had left, which is more than I originally thought. I think I had to strip all the comments and extra whitespace out to really be sure it'd fit? Anyway, since I confirmed that I'd have plenty of space, I could remove the flash chip and shift the pins around a little to have enough analogue pins free for the joystick axes again (even if there's no board space for them).

Sometime I found a guide on building CircuitPython, which includes a link to a guide on adding a new board to CircuitPython, and the latter guide lead me to the configuration files that I'd need to change. Or maybe I came upon it some other way? Regardless, there was the pins.c file with the pin names that board uses to make it easier for the programmer to reference in code, and while I really didn't want to have to make names for it, I did anyway. Later on, I discovered microcontroller.pin that invalidated the need for this.

I forgot to mention, but I think it was around the second board revision (I at least had the Trinket M0) that I found a different version of the input redirect programme for the computer that allowed button assignment, so I was able to give the programme specific buttons for directional pad and allowed me to get rid of the virtual joystick flicking thingy I was doing. I suppose I'll talk about the programme more here. Anyway, the programme (either version) has a window that the user can click on to send the coordinate touched to the 3DS (emulating the 3DS' touchscreen being pressed at that point), but the problem with it was that it wasn't very accurate. The programme allows a specific point to be sent (using L3 or R3 as the trigger) and the point that I sent didn't correspond with what the 3DS says it received (if I sent 205,198, it'd say 208,201). What I tried to do at first for compensation (because I needed the macro to move the mouse and click on the touchscreen emulator) was to get screenshots and scale them down and figure out the translation of points, which kinda worked... Eventually, I found points that corresponded and found that the weird scaling issue is from the horizontal center and below vertical center of the screen. I think at this point I tried poking with the source code (I had to compile the programme anyway) to try to get it to work correctly, but no luck.

I had decided to gather a bunch of points and try to figure out an equation to fix the problem, but I couldn't get anything to work. I think I tried to get an equation from a scatter plot, but I think it kinda worked, but I think I figured it'd be more accurate if I got more points. I then collected literally all the points on the x and y axes to plug into the scatter plot (just one axis though, considering that the scaling is different for both axes), and while it took a bit to get them in, I had something much more accurate (I think I had played around with the dead zones a bit, but don't remember if the final equations included them or not) to work with. After modifying and compiling the code (for the umpteenth time), the coordinates were spot on, except every so often where a pixel would be skipped (it would go from 25 to 27, for example, but be fine after that until the next one), which was good enough because it was much more accurate than it was with the original equation. Because the programme (the one with the button assignment) also allowed the touch emulation window to be resized, it also included some maths to compensate for that, so I had to tinker around with it. I couldn't really get it to work with the window resize, so I ended up just locking the window at a fixed size instead to avoid the hassle.

While the untouched equation for the touchscreen emulation is fine by logic/mathematics, for some reason it just didn't work correctly in Linux, and I kinda did want to try the Windows version (which I think there was a pre-compiled package for), I didn't want to have to reboot to Windows or use Bazett. I might've done a quick test in windows, but I don't remember. I also had tried tinkering with the sent bits before tinkering with the equation and stuff, but that just made the emulation not work most of the time.

It was towards the start of the project that I was planning to have one set of buttons and joysticks and have two microcontrollers so that I could connect to two computers to control two 3DS units with one controller (yeah, it'll do the same thign on both units, but it makes stuff like pokémon trades easier), but after trying to just run the input redirection programme again and just changing the IP address in the second instance (I had applied the mod to my main 3DS at this point), I didn't need to do that. This idea is why I had gotten an ADS1015 breakout from Adafruit, because the Raspberry Pi doesn't have analogue inputs and was also supposed to be the testbed for it. I still did the test, but with connecting the ADS1015 to the Trinket M0 instead, but what I was trying to figure out was if I would be able to connect one joystick axis to two different analogue inputs and get the same values, and it turned out as I expected. (I also had gotten a joystick breakout from Adafruit along with the ADS1015.)

Anyway, another thing I was worried about was how much memory I'd end up needing to run the code and such, but because the Trinket M0 doesn't have enough pins and only has a few pins broken out, I wasn't able to do any testing until I finished my ATSAMD21G18A-A and ATSAMD51J19A-A dev boards. Turns out that there is enough memory to run it if I don't import analogio and add the analogue pins to the programme, but because I had been planning on making another controller that had joysticks, I wanted to keep the MCU the same to make it easier. Obviously the code with the joysticks and stuff ran just fine on the ATSAMD51J19A-A since that MCU has about 6 times the memory than the other, and I went to make another revision to the board using that MCU instead.

Somewhere during this deep dive on these MCUs and CircuitPython (likely between the second and third revisions of the board for this project), I ended up reading about pin muxing (I think for a different concurent project), which I think lead me to save a copy of the IO table from the two MCUs' datasheets before making a spreadsheet with them so that I could sort it by pin or whatever. Before this, I had been relying on the schematics that Adafruit has for their dev boards, but in some cases, it wasn't specific enough. Anyway, I found that for I²C, it was specific pads on the ports that determined which one was SDA and SCL, so while PA22 and PA23 are both I²C pins (on SERCOM 3 or 5), they have to be used specifically. Further in the datasheet gives the specifications that pad 0 is SDA and pad 1 is SCL (there's designations for pad 2 and pad 3, but we're ignoring them for simplicity), and because PA22 is pad 0 and PA23 is pad 1 (again either on SERCOM 3 or 5), PA22 has to be SDA and PA23 has to be SCL. Because I was using the schematic for some Adafruit dev board, it just had PA22 and PA23 labelled as "I²C" without saying which were SDA and SCL, so I had just guessed that it didn't matter or whatever. Turns out that I had the signals swapped in the first and second revisions of the board, but I only ripped the SDA and SCL traces up (and did a pin swap) in the second revsion because that was the more concerning one, even if it wasn't going to be made. I'll be talking more about the pin muxing stuff in a different post for the project that it belongs to.

In my notes, I had the ATSAMD51G and ATSAMD51J as the replacements for the ATSAMD21G if there wasnt' enough memory, but I later found that I wouldn't be able to use the 51G because it's not available in the same package as the 21G along with the fact that it loses one IO pin for a pin that connects to an inductor and such to vary the power input it needs for power savings (if running off of a battery, for example). My only option became the 51J, which was a little larger in size than the 21G, but not much larger (I think the moulded plastic is about 14×14mm compared to 10×10mm?). I wanted to try to keep the routed traces as much as I could, so that I would have a little easier time re-routing, and it made it a bit of a mess while I had to avoid running the design rule check (which would bring up obvious errors). Some inputs did have to shift pins a bit, but otherwise the traces are fairly similar to the second reviison. I also added the connectors for the (Joy Con) joysticks as a placeholder, so that I would be sure I had a path for all the traces (mainly for the left joystick), including the joystick button.

The reasoning for using the Joy Con joysticks for the controller is because of its ease to replace compared to the usual soldered-on variant, and its compact Z-height. While waiting to be able to test the memory consumption of the code, I dug up what I could on the connectors and the joystick measurements, and ended up buying a set of 3rd-party replacement joysticks to be able to measure and confirm the measurements that I found. I had also used it to somewhat mock up an ideal location for the joystick in relation to the directional pad buttons.

One of the design changes between the third revision of the board and the first revision of the board with joysticks was adding a couple more switches to be able to switch the L3 and R3 inputs between the joystick buttons and the discreet buttons (L3 and R3 would be reassigned as the power and home buttons within the input redirection programme) so that I wouldn't have buttons that didn't do anything or were annoying to actuate. The first thing I did was place the left joystick in accordance to my notes (meaning its centre had a negative x coordinate) and then shifting everything over that would need to shift over. I think i calculated the shift for the ABXY buttons to make space for the right joystick, but because some or all of the pads of three of the buttons would extend past the 100mm limiatation of the free version of EAGLE, I had to move them manually by editing the board file (which is literally just a fancy XML file). The next thing I did was move the top of the board and some of the components up to match where the top of the board was in relation to the ABXY buttons. Because of the limits to where I can place the joystick connector and the reachability of the L button, I ended up having to have the L button on the bottom of the board instead of the top. The right joystick was easier to place since there was a bunch of space on top of the board near the MCU (which is under the board).

I think a little later I decided to add an RGB LED for indication, so that I would have an idea of the backup battery state and to know whether or not one of the macros are still running, and I agonised over the placement a bit because of needing to run a trace between it and the MCU and another trace for 5-volt power, but settled near the centre as the best option. Though I already had shifted the pin assignments for the added switches and buttons, I eventually decided to shift the assignments again to make it easier to route the signal trace for the LED so that it wouldn't take such a long path across half the board with having to switch between the top and bottom layers. Luckily, this ended up cleaning the rest of the traces up more.

While doing the inital layout on the first revision of the original board, I was kinda joking around with a friend saying that I could fit a CR2032 and a CR1225 on the board, but I eventually went with the CR2032 because it's more common (I think the CR1225 was actually from the Raspberry Pi clock project where I was considering on making an RTC "backpack" in adition to the display board, which didn't). Anyway, with how much extra board space I gained from the expansion, I mocked up being able to have multiple CR2032 batteries and came up with about 3 or 4 batteries, but with some maths I did back with the first revision of the original board, one CR2032 would last about 9 years, so having it last three or four times that would be a little inane. I also accidentally blocked the path for the backup battery to connect to the RTC because I worried about all the other traces first, but this was fixed with a couple of vias to run part of the trace over the blockage. The trace for the battery check had to take a weird path, since I have the RTC in roughly the same location and the analogue pins being on the opposite side of the MCU.

Battery trace highlighted.

Oh right, after changing MCUs, I forgot I had to change the reset circuit because I was copying the design from some of Adafruit's dev boards (:x), so I had to fiddle with that when I realised it, but I made it work.

While the board is complete and ready to be fabricated, the fact that I only bought enough MCUs for the Griffin Powermate hack and the dev boards and the fact that the silicon shortage had affected suppliers (Mouser, Digi-Key, etc) a little after I bought those MCUs mean that the project is at a standstill until I either wait until there's a stable supply of MCUs or order a small supply of MCUs that will arrive whenever it does. I think this'll be it for now, I don't think there's really much to say about the code and I can't think of anything else about the project to talk about that I haven't already.

21 June 2021

CircuitPython Stuff

I think it was back in 2015 when I happened on MicroPython while seeing if it was possible to run Python on microcontrollers, but I think there were some limitations at the time? It might've just been that I wouldn't've been able to use it on the Teensy boards that I have. Anyway, I pretty much just brushed it aside back then.

Don't remember how or when I originally found CircuitPython, but I had designed a test board with the ATSAMD21E18A-A microcontroller, some tactiles, and a USB mini-B jack; the board ended up sitting for a while since it was kinda meant to answer questions (mainly if I didn't need pullup resistors for the tactiles), but I wasn't interested enough to fully go forth with it.

Earlier this year, I ended up getting a lot more interested in CircuitPython (maybe because I was visiting Adafruit's site more often than not?) because of a custom game controller idea (which will be written about in a separate post), and started the deep dive.

Eventually I learned that I need pins PA30 and PA31 for programming the bootloader (so it was a very good thing that I didn't make that test board because I would've had to solder directly to those pins), that I would need a programmer (SEGGER's J-Link series) and some sort of way to interface the programmer to the project board. I planned on using some probe pins (same ones from the Raspberry Pi clock and controller converter projects, and also some taller ones if I happened to need a little more clearance) on the adapter board between the programmer and the project board, and some circular contact pads on the project board for the four signals I would need to connect to the programmer (3.3 volts, ground, SWCLK, SWDIO).

With at least that, I was able to at least designed the boards for the projects I had while worrying about the other questions later. At this point, I think I was just going to use the generic bootloader and wasn't sure if I was going to have to build a custom version of CircuitPython. When I got to the point of looking into the configuration files for CircuitPython, I did find that I would need to build CircuitPython myself for each project (really each MCU at the very least), and I forgot how I ended up poking at the configuration files for the bootloader, but I found that the generic one wouldn't've worked for my uses because it looks for some sort of status LED.

I had gotten Adafruit's Trinket M0 for answering the question of needing a crystal and for just tinkering with stuff like the DS3231 and ADS1115 (yeah, I know the Trinket has analogue inputs, but the ADS1115 was supposed to be used with the Raspberry Pi for testing... the Trinket ended up being more of a knee-jerk purchase).

I had ordered a whole slew of boards that I designed and ordered a bunch of stuff from Mouser (and a few things from Digi-Key) when I was ready for the first "wave" of projects. The two types of programming adapter boards (the aforementioned probe pin type and a custom one for the project I would be able to complete) were part of this order. The adapter with probe pins was a bit difficult to solder because the annular rings for the probe pins were too small, but I had gotten it soldered enough, and the custom adapter just had wires soldered to the board that would be soldered temporarily to the project board.

Cloning the git repositories and adding the project I was working on at that time (Griffin Powermate hack, more on it in another post), it wasn't too hard to get CircuitPython or the bootloader built, nor was it hard to programme the bootloader. The problem I ran into was that I ended up building an alpha version of CircuitPython 7 (which caused library issues) because I didn't fully read the CircuitPython building guide, so I had to fiddle around a bit before I got it to build CiruitPython 6.2.0, but everythign worked as expected after that.

I think while looking for a set of probe pins (like a header, but with probe pins instead) that would probably make it easy to solder, I found a dimension for the preferred annular ring size and I think I went and changed the vias for the probe pins I had, since I originally had nothing to go off of. (The preferred annular ring dimension was from the same company of the probe pins I have.) After getting the new set of adapter boards (and pins because I didn't want to bother with the old adapter boards at that point), I found that it was much easier to solder (especially the ground pin) and the pins ended up much more stable.

When it came time to do stuff with the dev boards I made (one with the ATSAMD21G18A-A and another with the ATSAMD51J19A-A, these will be in another post), I was ready with having CircuitPython and the bootloader ready for the boards. The problem I ran into was trying to programme the bootloader onto either board using the probe pin adapter, and I tried compressing the pins the entire way onto the pads, but that didn't seem to help, so I defaulted to using the custom adapter for getting the boards programmed.

I know I was running over the order of the contact pads when I thought to compare them to the pins, but I don't remember when it was besides that I already had programmed all the dev boards, and when I brought up the adapter board in EAGLE, I found that I botched the order of two of the pins. I decided to fix the board later, but I figured that the pins probably ended up in that order because I was using the contact pads for the bottom side of the board as reference instead of the top side. I ended up just using a via to run the trace for the 3.3-volt input on the bottom of the board, so that I could leave the SWDIO trace on top of the board and uninterrupted (it was also easier to do than revise the entire board). This time, I ended up salvaging the probe pins from the other adapter boards, which I honestly should've done in the first place because it was quite easy to do, but I still had to buy another set of the shrouded header because it's SMT and I don't have a hot air station. (I tried using the reflow oven at work with the boards upside-down to see if I could desolder them that way, but it didn't do anything, probably because they're too light.)

With the new adapter board revision, I soldered only one of the two boards (I haven't decided on a third pin length) for testing, and went to purposely brick one of my 21G boards by loading the bootloader meant for the 51J onto it via the boot drive that appears after double-clicking the reset button. After setting the programmer up, I ran the bootloader-writing process while holding the boards together with enough force, and it wrote the bootloader just fine. I tried to brick one of my 51J boards, but it wouldn't brick, even with trying to rewrite the bootloader or erase the flash space with the programmer. Both the ATSAMD21 and ATSAMD51 have a sort of write protection that's mentioned in the guide on how to use a programmer to write the bootloader (if you bricked a board or have a new MCU), but because I was following a much easier guide that didn't require me to boot to Windows (or have to pass the dev board through to my Windows VM), it was something that JLinkExe automatically took care of (or whatever). (It makes me kinda wonder how the one that wrote the guide for Linux was able to brick three or four Adafruit ATSAMD51 dev boards if I wasn't able to brick my dev board in a couple different ways.) Anyway, I did get the other adapter board soldered together after the successful test.

CircuitPython has a built-in library called board, which makes it easier to code with because you can have names assigned to a microcontroller pin (for example, "SDA" could be one of the names for pin PA08), but while I thought it was kinda inane to have to set up the pin names for board, I ended up setting the names up for one of the projects I was working on (the custom game controller). I later found microcontroller which has pin built into it, so instead of having to assign "SDA" to PA08 in the config file for building CirucuitPython, I could instead just call the pin directly with microcontroller.pin.PA08 (though I actually import microcontroller.pin as mcp and use mcp.PA08 instead). I went back into the config file and cleared it out since it'd be easier to keep track of the pins and make changes within the code instead of having to use notes to cross-reference everything.

Not really too much to say about CircuitPython itself, since it's really not too much different than Python (though it does have somewhat-reduced functionality), but it does require the programmer to think about available flash space (for large programmes or programmes with assets of some sort) and memory. Flash space can always be expanded with a 1-16MB (I think even 32MB?) flash chip over SPI and changing the specific CircuitPython config file to add the flash chip and the MCU pins that it's connected to, but memory is not expandable as far as I'm aware?

CircuitPython definitely is one of my favourite things that I've come along, since it allows me to really do things that I haven't been able to do with only discreet parts or having to rely on a Raspberry Pi of some sort, and though I'm somewhat limited to the libraries that Adafruit's written for certain devices, I still have a lot more freedom than I used to.

18 June 2021

Cherry MX Spring Modification

A few years ago, I did a spring mod of some Cherry MX switches that made it a little harder to press certain keys, but it was an imperfect solution. I don't remember how I got to looking for a single-spring solution, but I found a bunch that had higher forces than the stock 80cN springs I was using, and it was hard to decide what sort of force to go with without testing them. I went with 210 gram force springs and ordered about 3 dozen of them because I didn't need too many and they weren't all that cheap.

The springs in a bag.

While they look quite long, that was a length I wasn't concerned about since it was really more about the compressed length that was one of the limiting factors. It's slightly larger in diameter than the standard springs, but it still fits fine. This spring makes it a bit harder to reassemble the switch, but not difficult or impossible. Once I had the switch on a board, it was a lot harder to press than I thought (or tried before soldering), but it's blatantly obvious when accidentally hitting it. I think the spring moves a bit weird in the switch because the click doesn't always happen when the switch actuates, so I could've used a red or silent red or whatever instead of the blue stem, oh well.

Hackintosh

With some of the programmes I was writing (the world clocks one being the one I remember best), a friend wanted to be able to use it, but he daily drives Mac OS, so there wasn't an easy way to allow him to use it without compiling the programme. And because i3rd was superceded by Pod, I decided to put i3rd to use by making a hackintosh. I used the 120GB 2.5 inch SSD from the double drive pi (I think that was the Raspberry Pi 3?) as the boot drive since I wasn't going to need a huge amount of space and I didn't want to buy anything if I didn't have to.

I found some reddit where someone did a hackintosh with the same DeskMini that I have but with a generation older CPU and went the same route as them since there was the links to the instructions and stuff. It wasn't too bad from what I remember, but I think there were times that I wasn't sure if the installation was being slow or if it froze.

The one thing I couldn't figure out how to fix was getting more VRAM than 5MB, I tried a lot of things and eventually decided to just live with it. I did get whichever stuff tested/compiled and such, though with one of them, the GUI glitches out when running, which is likely due to the lack of VRAM (this was during testing and it didn't get compiled or shared).

I have plans to try a Mac OS VM with VirtualBox (since it might allow me to have 128MB of VRAM), but I keep forgetting about it. It's uncertain when my friend will shift to Apple Silicon, but it'll be when I'll grab a Mac Mini with the same SoC to not have to worry about using a VM or having VRAM issues.

Qt5

Sometime after making a GUI programme to help a friend run a trivia game (I guess beginning of last year), I started looking into Qt5 because I was having problems with certain Unicode characters causing Tkinter to either crash or screw with displayed text. Might've been other reasons, but it's so long ago that I don't remember.

I learned it by converting my Tkinter programmes, and it was fairly easy to learn since I already had learned a lot of the logic from Tkinter. I think it might've been a couple weeks before I fell in love with Qt5. Anyway, there was a lot of learning and having to go back to programmes I previously converted to add/fix stuff.

Also I think around this time I also learned classes better, which made things easier to do.

It probably was after a couple or so months that I kinda lost steam to convert all the Tkinter programmes (probably because I hardly ever used the last couple I had to left convert) and just moved onto new projects. One of those projects was having a programme that helps me do stuff in EAGLE CAD in the sense that I can plug values into it and it spits out the value I'm looking for. I do know that EAGLE has ULPs, but that's a C-like language and not Python. Anyway, the reason for this is that it was time consuming to draw the parts of the board layout in Fusion 360 to get a value (sometimes having to do this multiple times), and it really is just all mathematics in the end. Also, I'm sticking with EAGLE 7.7.0 btw because it's annoying to have to login for the free version of EAGLE 8+, especially if I'm needing to just look at something real quick.

Anyway, each calculation I made into its own class and then used the main class to place it in the category it belongs to for display. It took a while to figure out how to arrange all the classes without making the window too large, and I ended up finding QScrollArea that really helped keep the width of the programme minimal. After a couple months of working on it (and still not being done), I decided to just leave it for now, since everything within it works and I have around 4000 lines of code. Yeah, I should probably break the classes into their own modules instead, but I don't really feel like working on it regardless.

What it currently looks like.
The precision selection, Ω button, and µ button was added later (the buttons put the corresponding symbols on the clipboard). (Also Qt5 allows for clipboard reading/writing, which is really nice.)

I've also done a world clock sort of programme that utilises QDateTime and QTimeZone, which I think are better than Python's built-in date/time/timezone solution? There's also QTimer which allows a function to be ran after the set timer expires (which is used in said world clock programme to update the time display every second instead of using a while loop and time.sleep(), also you can stop a QTimer with code but not time.sleep()). At this point, I forgot what Tkinter provided that Qt5 doesn't, but I've long adapted to it with a lot of the other things Qt5 provides. It also has its own multi-threading framework, which I think is also easier to use than Python's, especially if I've been using it here and there for things that can really leverage multiple threads.

It was earlier this year when Qt6 was released, and I decided to poke with it a bit since I probably should move to it, but ran into issues on one of the programmes I converted as a test. There were some name changes (QRegExp → QRegularExpression) or ways to access certain items (QMessageBox.Ok → QMessageBox.StandardButtons.Ok), and when I got everything fixed up, it worked... except for the theme. I use a weird mixture of qt5ct and Kvantum, and just couldn't figure out how to stylise/theme Qt6 to match.

Qt5 with the style/theme I have set, Qt6 default style/theme (I'm assuming).

And speaking of name changes, when I was trying to load the Qt6 version of the programme, I ran into more errors because of more name changes (QMessageBox.StandardButtons.Ok → QMessageBox.StandardButton.Ok and Qt.Alignment.AlignCenter → Qt.AlignmentFlag.AlignCenter) (frustration noises here). Anyway I dunno when Qt5 will be end of life, but it probably won't be for at least a few years, and hopefully whenever the time comes, Qt6 will have the maturity that Qt5 currently has.

17 June 2021

Raspberry Pi Clock

I had the idea for a while, but didn't really do too much with it until I had the Raspberry Pi 0 W boards from the Bluetooth SNES controller to SNES converter. One thing I didn't mention in that post was that I was planning to use some probe pins (aka "pogo pins") to connect the board to the GPIO pins, which carried over here. Reason for it is that it frees up space for trace routing, makes it easier to swap the boards as needed, and increases compactness.

Anyway, I think I originally started with using counters to run the seven-segment displays, which would require a pulse every so often over one pin, but would require a few more to set the display faster. I did get a breadboard, a seven-segment display or two, and a couple of the counters to test it out and it did work, but it was really fast since I was using jumper cables. Besides that, this version never really got anywhere else.

I think after the controller converter, I was going to maybe use the SN7407 for driving each segment, but probably didn't have enough GPIO pins? I forgot how I learned about the 74HC595 (probably looking sometehing up?), but after understanding how to use it, it was something better to use, since I only needed three GPIO pins. Before I forget, there seems to be a 74HC595 that has SPI communication, but I'm pretty sure I'm not talking about that one because it doesn't seem to be chainable?

One of my requirements for the clock was to have a button to turn the display on at minimum brightness while the button is held and to have a switch that would turn the display on at full brightness (well, within reason), and from getting a better idea of transistors from a previous project, I used a NPN transistor to connect the ground lines of the displays to ground. The only thing that was unclear was what resistor values I would need for the button and the switch, so I designed a prototype board that was derived from the final board for making it easy to test various resistances.

I also had read how to add an RTC (DS3231) to the Raspberry Pi, and I added it because I had the space and it'd make the clock less reliant on the internet for the correct time. With space still leftover, I also decided to add a temperature sensor (DS18B20, and yes, the DS3231 has its own sensor, but I wanted to leave it alone) using an FFC connector, and a flat flex board that the sensor would be carefully soldered to. It was a little tough to design the flat flex board because I hadn't designed the case at all (though I had ideas of how I wanted it to be), so I had originally desiged it with a right-angle bend, which would end up costing a lot because of the unused space to make a rectangle. Eventually I had a "duh" moment where I could just make a straight board and have it go around the back of the Raspberry Pi to where I wanted the sensor to be (outside of the case and isolated from the inside of the case).

There were also two more switches to be able to flip the display (if the display was upsidedown) and to display the temperature instead or the time. I also had a couple headers for the RTC's backup battery connection and a 5-volt input connection (I was going to use a DC jack so that it's more robust, compactish, and so I can have the cable run along the length of the clock).

I really didn't do anything with it for a while, and then I decided to dig into CircuitPython more, which led me to other solutions. I had made a very basic prototype board to be able to tweak the brightness with the intended transistor, a variable resistor (in the standard voltage divider circuit format), and the button I was intending to use. Powering it from 5 volts made it kinda tough to adjust, but it was doable.

I don't remember if it was this or the controller converter board where I measured the GPIO holes (only a couple) with a pin gauge and found them to be scarily close to the maximum of the probe pin diameter (based on the datasheet), but it wasn't until after I had ordered some pins for something else that I was able to test the pins to the GPIO holes. Suffice to say, they fit right into the hole, so I had to scrap the idea and look for something else. I did find a fairly low-profile header and mating socket, since I was trying to keep the distance between the top of the boards around 6mm (which is the distance I calculated for the controller converter board). Of course, this meant that I had less dead space for resistors and capacitors on the board, so they had to get moved.

Since I was using the SOIC package for the 74HC595, I didn't have a lot of space to move the resistors and caps to while trying to keep the board area underneath the display clear in case I do solder the displays directly to the board instead of using the planned sockets. I went with a smaller but wider package and it gave me plenty of room to stick the caps for each of them between them. For the resistors, I used a bussed resistor for all the inputs that needed to be pulled up to save space, and it allowed me to move the resistor for the temperature sensor underneath the board near the FFC connector (which is on top).

While playing with the Adafruit dev board I had (Trinket M0), I decided to toy around with using its onboard DAC and modded the prototype to be able to control the transistor's base from the DAC (the variable resistor is isolated from the base via the button), and it made it so much easier to control the brightness. I found a DAC (MCP4725) that has a CircuitPython library that I'd be able to use, so I added it to the board and re-routed the button and the switch to the GPIO pins instead. I also tweaked the code after saving a copy.

I found a temperature sensor (MCP9808) that has a CircuitPython library, and even though I don't like its package, I decided to use it while scrapping the case (I had modelled the case when I had the first iteration of the board finished). Doing so freed up some space, and I decided to just use a right-angle connector USB cable to the Raspberry Pi's micro USB port, which meant removing the flip functionality since it won't really sit well on the cable. With this, I dropped the switch count from three to one, and the switch displays the time or temperature and has the display at full brightness with either position. I also just used some vias for the backup battery instead of an actual connector since it'd be a little more compact and I didn't feel like running a long trace to where I have space for the connector to the RTC. I went back to discrete resistors because I only had three things to pull up and had the space for them. I also made another copy of the code and changed it accordingly.

I was also working on an internet-connected microcontroller clock around this time, and for it I had a different GPIO expander that connected via I²C (MCP23017) which I was going to use for another revision of this board, but I ended up dropping this project in favour of the microcontroller version. I was also going to add an accelerometer from that project to this one as well. The microcontroller version ended up being a little easier to work with in the code, but I think the main thing is that the board size is smaller than the one for this project, even if the total parts cost would end up being about the same.

Oh, I learned how to tent vias at or under a certain size sometime around the second revision, which made it easier to not have to worry about putting vias too close to stuff being soldered. And I also didn't think about the permanent dot point segments that I was using for a makeshift colon, so I had a note to fix that within the code (which I might've done, I don't remember) in the second and/or third revision board layout(s).

16 June 2021

Bluetooth SNES Controller to SNES Converter via Raspberry Pi

A long time ago my (half) brother let me use his SNES for numerous years, but some years ago he took it back, and I was left with the games. I eventually saw a review video that Linus Tech Tips did on the Super NT, and after some months, I bought one to be able to play my SNES games again whenever I wanted to. I also bought an 8Bitdo Bluetooth SNES controller with receiver for the SNES, since I kinda wanted the buttons to have the Super Famicom colour scheme, and also because the wired controller I have is a Mad Catz High Performance controller (see below the break) that had a broken directional pad (think of it as a very cheap version of the directional pad on the XBox 360 controller). (Pretty much I was left with a short stick that still allowed use of the directional pad, but it wasn't something to use for anything serious... Also it didn't have any sort of "centre nub" that prevented all four directions being pressed at the same time.)

Anyway, Nintendo released their SNES emulator for Switch Online subscribers (I'm calling it a SNES emulator because that's pretty much what it is and it'll be easier to make the distinction), and I was able to use the 8Bitdo controller fine except for the screenshot or home buttons (which were needed for the emulator menus), but I ended up just using the Joy Cons instead. I forgot when Nintendo launched their Bluetooth SNES controller (again, making distinctions), but I bought a set of four (the max per account) so that I'd have two for the emulator and two for the Super NT. I also bought another 8Bitdo Bluetooth controller receiver for the SNES, which kinda sat after I made sure it worked with the 8Bitdo controller.

When I finally got Nintendo's Bluetooth SNES controllers, they wouldn't pair with the receivers (pretty sure that's what it was), and after checking if there were firmware updates for the receivers, there were none. I decided to take matters into my own hands since I was able to pair the controllers just fine with Pod or my phone, and it shouldn't be that hard to translate the inputs to GPIO outputs. I looked up what ways I had to level shift the 3.3-volt outputs to 5 volts, and besides transistors, I found the SN7407. I also found the CD4021 used in some example SNES/NES controller circuits.

While attempting to design a board, I ran into the issue of where to source the voltage for the SN7407, the pull-up resistors, and the CD4021. I designed an experimentation board that allowed me to switch which 5-volt source to use (either the Raspberry Pi or the SNES Super NT), and which ground to use (only for the ICs). It also had some small tactile buttons so I'd be able to test with a game running and a 5-position tiny XLR connector for connection to the Super NT (more on this later). What I found was to power the CD4021s from the Super NT, and everything else from the Raspberry Pi. I think I modded the board for some reason, but it's escaping me (I think it might've been that the buttons were supposed to connect the SN7407 input pins to ground and not 3.3 volts). With the needed information, I was able to continue on with the final board.

Originally I was going to make one Raspberry Pi handle one player, but I can't remember what lead me to making a board for both players and having one Raspberry Pi handle both players. I also had done some testing with Pod and one of the controllers to poke around with the button values and such.

With all the work I had done (thankfully no other boards besides the experimentation was made), 8Bitdo had finally released the firmware update that included Nintendo's Bluetooth SNES controllers, and I dropped the project since it was no longer needed. Unfortunately I already had bought two Raspberry Pi 0 W boards (from different vendors), but they would end up reserved for another project.

I think it might've waited a while between when Nintendo launched the Bluetooth SNES to starting this project, since the experiment board was designed in June of last year.

Resources I used before I dropped the project:


The reasoning for the 5-position tiny XLR connectors was that the Mad Catz controller had a wire break near the plug, so I had to hack something together to get it working once more (besides taping the wire to the connector in a position that it would work). I used some SNES controller extension cables to make an adapter to connect the Mad Catz controller to the SNES, an XLR to XLR extension (or to be able to plug in the experiment board), and another adapter so that the SNES to SNES extension can still happen if needed.

To fix the directional pad, I grabbed the old directional pad top and bottom from my XBox 360 controller (I reshelled it from white to black), and used the bottom to model the receiver and bottom plate for the Mad Catz controller. I got a stepper drill to increase the hole size in the controller for the new directional pad assembly and drilled it out to the approximate size of the one in the Xbox 360 controller.

I had the receiver 3D printed via FDM process and while it looked okay, it really wasn't. Besides it having a little more flex than I wanted, some of the holes were undersized, so when I did some sort of test fit (can't remember if it was with or without the Mad Catz controller), I had to use a drill to be able to separate the top piece from the receiver, destroying the only spare XBox 360 directional pad top I had. It was a little tough to decide whether to buy a button set that included the piece I needed or to order a couple dozen of the piece I needed, but I eventually went with the latter.

I think I just had the receiver reprinted with MJF (multi-jet fusion), but with it showed that I had a portion too thick because I screwed up the maths — the button press pads and the connecting plate were supposed to be 3mm thick total, not the plate itself. After the fix (and some tweaks to the keying in the plate), I sent it off to be printed (still MJF), and when I got it, it worked as intended. I also did have to find my own screws to join the top to the receiver (no screws was included with the reshelling kit), but with a small box of salvaged screws, I found something that worked (this was confirmed before I had the FDM receiver).

It looks a bit weird because the XBox 360 directional pad top leaves a larger gap in the recess than the original (though the original was long gone before this, so I don't have a proper size comparison), but it's functionally fine otherwise. I didn't realise that I adjusted the angle of the directional pad until now, but from the testing after finishing the project, I didn't notice any issues? Or maybe I'm just seeing things, I dunno.

Normal (probably about 2mm around the directional pad) and mine (about 5mm around the directional pad).


I dunno how they assembled the directional pad, but I do remember it had a 5-6mm diameter plastic shaft connecting the inside piece to the top piece, and because there was a lot of space below the top piece when pressing down, it slowly strained the plastic where the top piece and shaft meet until the top piece broke off. I think I tried to super glue it back on, but it didn't work (also, this was numerous years ago).
Since I found pictures that I had...

Boards, finished board top, finished board bottom, and modded board.