Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Cory Parsnipson
Nov 15, 2015
I’m doing an electronics project. I hope this fits in with all the building DIY projects. I want to make this accessible to people not familiar with electronics or electrical engineering. I like video games and making video game related hardware makes me excited and I hope you get excited reading this too. :)



“Making” a gameboy? What does that mean? Like, you’re making a mock-up for design school or something? Drawing pictures of one you’d like to exist??

No I mean, like, physically making a game console you can hold in your hands and walk around with. Not completely from scratch, of course. I don’t exactly know all the work that goes into something like this, but it’s going to be a lot of electronics work and some software development. There’s a lot of guides out on the internet about making your own game system but for some reason they stop halfway (in my opinion) by skimping on features, polish, or capabilities. I want to make something that I would actually use myself and not just a short lived toy that looks good in a tutorial.

Why are you doing this

Let’s rewind back to March. The year is 20XX. poo poo hit the fan everywhere at the same time and it wasn’t clear whether or not people would be allowed to go outside anymore. Nobody was able to wipe their rear end for 6 weeks. Nintendo Switch sales went through the roof and, because of manufacturing slowdowns, the supply absolutely dried up. This meant it was sold out from every store you could imagine, and the units you could buy online were being sold by scalpers for ridiculous prices. Seeing as how I also wanted to buy a switch, I decided to do the crappy immigrant parent thing and buy a lovely knock-off that wasn’t as good.

Enter: the Raspberry Pi.






If you don’t know, a Raspberry Pi is a cutely named portable computer. It runs a variant of the Linux operating system and people like using them in hobby projects because they’re cheap and the entire system comes in one piece, meaning you only need the peripherals you would normally attach to a regular desktop computer like a keyboard, LCD, and mouse. This computer isn’t very powerful, but you can use them when you need a high end microcontroller for projects like uh…

  • Making drones
  • Automated sprinkler management system
  • Home media server
  • etc

Ok, so what is this project?

I’m stuck in my room without a Nintendo Switch and I just want to play games while lying down on the couch. Without having to look at the TV. Also I want all my games in one box.

I’m a big fan of the Nintendo 3ds and I have one that I play regularly. I think it’s almost perfect except that there’s two screens and it folds. I think my ideal handheld would be the lower half of a Nintendo 3ds with some elements taken from the Nintendo Switch. So that’s what I’m attempting to make.

Here’s a child’s drawing that I made:


I always feel a little crazy when explaining what I’m doing to other people. If it sounds insane and a whole lot of work, that’s because it is. On the other hand, I’ve been pleasantly surprised at the capabilities available to hobbyists in the modern DIY electronics space. Kids these days have all the Raspberry Pi’s and the Arduinos and the widely accessible youtube guides for repairing gaming hardware. When I was in school, you had to search for microcontrollers by name (e.g. STM32F103C8T6) and if you had questions, people would just call you a nerd and laugh. :corsair:

Project Goals

I feel like I’m spoiling things by writing this out, but seeing as I’m unlikely to actually get that far, here goes:

Phase 1 - COMPLETE

Just make something, anything. This is where I’m at now. Take a Raspberry Pi, add an LCD, add speakers, add controller buttons, and add a battery. Then put it all in a cardboard box. Maybe it'll be a pizza box who knows.

Phase 2

Next I’d figure out how to make a plastic casing (3d printing, anyone?) and put all my circuitry on a green circuit board (this is called a PCB for those in the know). This would also involve moving away from the Raspberry Pi 3A+ model shown above and using the more portable Raspberry Pi Compute Module 3.

Create a compact version of a portable raspberry pi somehow, using custom PCB's if necessary. Upgrade the power supply to be able to supply the 5V 3A power envelope and increase battery capacity for acceptable battery life. Also make sure this has undervoltage protection to solve range anxiety issues. Use smaller audio circuitry with louder and more power efficient speakers. Add meta buttons, fix shoulder buttons. Use a cheaper LCD screen that doesn't come in an all-in-one package.

If possible design the RPI attachment to be modular so that the RPi 3A+ can be swapped out for the Compute Module 3 or an RPi4 or some other SBC of similar size.

I’d consider “the Gameboy” to be complete at this point.

Phase 3

Dockify this poo poo, yo! The Raspberry Pi already expects people to hook this up to an external screen like a traditional PC. So I’d really like to copy the Nintendo Switch and make a docking station for my console so I can play it on a big TV or in handheld mode. This might involve moving some components around on the PCB and fiddling with different (USB/HDMI/etc) port configurations to get it to work well with a docking station.

Phase 4

:filez:??? What :filez:? From here on out, this will start to sound like some kind of pipe dream. Modify the previous hardware to expose the 40 GPIO pins. This may require moving the existing connections to the higher pin numbers that are only available via the Compute Module. This would be really cool because then you could interface with the lowest 40 GPIO pins as if you were still carrying around a vanilla Raspberry Pi with you. Then this turns into a hybrid gaming console and regular Raspberry Pi (with buttons attached) if you wanted to continue developing random Raspberry Pi projects on it. Also replace the Retropie image with a regular Raspbian image for more of a linux desktop experience. I don't know if this would work since Raspbian might have too much overhead to play games on. This could probably be worked around with some creative use of scripts.

Some software mods might come in handy to write code using a game controller instead of a keyboard and mouse. I might attempt to use this as a daily dev computer.

Phase 5

I'm thinking about adding a cartridge slot to it. This would just be a hardware wrapper around an SD card (that you already need to insert into the raspberry pi anyway). I got some cool ideas for a 3d printed case that might also use a microcontroller to show box-art on an e-ink display depending on what files you have loaded in the card. There's some logistics to figure out here because the Raspberry Pi only has a limited number of SD card interfaces you can bolt on to it and the wireless module also uses one of these slots.

Phase 6

Since I started the project, the Raspberry Pi foundation has released the next version of hardware creatively dubbed, the Raspberry Pi 4. This is a much, much more powerful computer that’s slightly more expensive than the RPi3 but it is also approaching netbook/tablet levels of compute power. I hear that you can play PSP and playstation games on it. Switching to using an RPi4 would really make it feel like a portable linux computer.

Phase 7+

Swap out the Raspberry Pi for a custom CPU and maybe use an FPGA to create custom hardware accelerators in the hope to support even more powerful software applications. And write custom software to act as an alternative to RetroPie. Pshhh yeah, like this would be some Tony Stark level poo poo.

Cory Parsnipson fucked around with this message at 06:14 on Jan 20, 2022

Adbot
ADBOT LOVES YOU

Cory Parsnipson
Nov 15, 2015
Table of Contents

Part 1: Young, dumb, and full of crazy half baked ideas
  1. The OP

  2. 1.8" TFT Screen

  3. Sound Breadboarding (Part 1, Overview)
  4. Sound Breadboarding (Part 2, About Amplifiers)
  5. Sound Breadboarding (Part 3, I2S and Stereo Decoder)
  6. Sound Breadboarding (Part 4, Protoboard Time)

  7. Input Breadboarding (Part 1, Cory Decides to Use an Arduino)
  8. Input Breadboarding (Part 2, Pushbutton Prototype)
  9. Input Breadboarding (Part 3, Thumbstick Research and Shopping)
  10. Input Breadboarding (Part 4, Thumbstick Pinout and Breakout Board)
  11. Input Breadboarding (Part 5, Thumbstick Software Calibration)

  12. 5" OSOYOO LCD Screen

  13. Input Breadboarding (Part 6, Extending 10 Buttons to 20)
  14. Input Breadboarding (Part 7, Cory Does Charlieplexing)
  15. Input Breadboarding (Part 8, Charlieplexing Implementation With Switches)
  16. Input Breadboarding (Part 9, Shopping for "Real Buttons")
  17. Input Breadboarding (Part 10, Membrane Testing with Conductive Ink)
  18. Input Breadboarding (Part 11, Steve-O Pays a Visit)
  19. Input Breadboarding (Part 12, Re-hooking Thumbstick to Charlieplexed Setup)

  20. Exploring the Inside of My 3DS and Joycons
  21. Ooooh yeah
Part 2: The Great Cardboard Debacle
  1. Cardboard Enclosure (Part 1, VISUALIZATION)
  2. Cardboard Enclosure (Part 2, CAD: Cardboard Aided Design)
  3. Cardboard Enclosure (Part 3, Trouble with Z height of Left Controller Pieces)
  4. Cardboard Enclosure (Part 4, Origin of the Cardboard Backplane)

  5. Device Review (Also I Got A 3D Printer)

  6. Cardboard Enclosure (Part 5, Copper Tape Testing)
  7. Cardboard Enclosure (Part 6, Designing a Charlieplexing Paper Circuit)
  8. Cardboard Enclosure (Part 7, Finalized Charlieplexing Circuit)
  9. Cardboard Enclosure (Part 8, I Shouldn't Have Used Charlieplexing! Motherf*cker!)
  10. Cardboard Enclosure (Part 9, Charlieplexing Post Mortem)
  11. Cardboard Enclosure (Part 10, Key Matrix Research)
  12. Cardboard Enclosure (Part 11, Key Matrix is Now My New Friend)
  13. Cardboard Enclosure (Part 12, Right Controller is Cut Out)
  14. Cardboard Enclosure (Part 13, Controller Cables Made out of Paper)
  15. Cardboard Enclosure (Part 14, Cutting out Shoulder Buttons)

  16. Prusa's Here

  17. Cardboard Enclosure (Part 15, Laid Down Copper Tape on Paper Cable)
  18. Cardboard Enclosure (Part 16, Should I Give Up?)

  19. Building the Prusa (Part 1)
  20. Building the Prusa (Part 2)
  21. Building the Prusa (Part 3)
  22. The First Print
  23. The, uh..., First Print

  24. Cardboard Enclosure (Part 15, PSYCH! Never Gonna Give You Up)
  25. CardBoard Enclosure (Part 16, Clutch cakesmith)
  26. Cardboard Enclosure (Part 17, Sturdy Cable)

  27. Printing Benchys and First Foray into KiCAD

  28. Cardboard Enclosure (Part 18, More Benchys and Another Cable)

  29. There and Back Again: Adventures in 3D Printing
  30. Variable Layer Height

  31. Cardboard Enclosure (Part 19, Testing Metal Dome Switch Membrane with Copper Tape)
  32. Cardboard Enclosure (Part 20, DOUBLE PSYCH! I Gave Up)
Part 3: Up to My Eyeballs in CAD
  1. FreeCAD Modeling (Part 1, Tutorials)
  2. FreeCAD Modeling (Part 2, Useful 3D poo poo)
  3. FreeCAD Modeling (Part 3, Legend of Cory)
  4. FreeCAD Modeling (Part 4, Useless 3D poo poo)

  5. KiCAD (Part 1, Controller Schematic)
  6. KiCAD (Part 2, Why Did They Use Separate PCB's????)
  7. KiCAD (Part 3, Audio Amplifier Schematic)
  8. KiCAD (Part 4, PWR_FLAG)
  9. KiCAD (Part 5, Cory Makes a Footprint :can:)
  10. KiCAD (Part 6, Cory Sings a Stupid, Cringey KiCAD Song)
  11. KiCAD (Part 7, Laying Out Footprints)

  12. Ahhh, Whoops...

  13. KiCAD (Part 8, Starting to Make a Metal Dome Switch Membrane Footprint)

  14. Cory Discovers the Raspberry Pi Fusion

  15. Cory Loses His poo poo Over Making a PCB (Part 1)
  16. Cory Loses His poo poo Over Making a PCB (Part 2)
Part 4: Third Time's the Charm
  1. PLA Enclosure (Part 1, Putting the Printer to Use)
  2. PLA Enclosure (Part 2, Thumbstick holder v1)
  3. PLA Enclosure (Part 3, Thumbstick holder v2)
  4. PLA Enclosure (Part 4, Shoulder Buttons)
  5. PLA Enclosure (Part 5, Fused Controller Frame)
  6. PLA Enclosure (Part 6, Left Controller Pieces in Place)
  7. PLA Enclosure (Part 7, Turning on Ironing for Shoulder Buttons)
  8. PLA Enclosure (Part 8, Enter: the Chassis)
  9. PLA Enclosure (Part 9, ABXY Button Jig)

  10. Help, My Baby Boy is Addicted to CAD!

  11. PLA Enclosure (Part 10, Both Controllers Mounted)

  12. Fun Ideas with Cakesmith

  13. PLA Enclosure (Part 11, Wiring Left Side)
  14. PLA Enclosure (Part 12, Switched to Higher Gauge)
  15. PLA Enclosure (Part 13, Weird Behavior Seen During Wiring)
  16. PLA Enclosure (Part 14, Booting up Linux With Half a Controller)
  17. PLA Enclosure (Part 15, Wiring Explained)
  18. PLA Enclosure (Part 16, USB Connector on Arduino Broke Off)
  19. PLA Enclosure (Part 17, Reinforced and Rebuilt)
  20. PLA Enclosure (Part 18, The Future of Gaming)
  21. PLA Enclosure (Part 19, Chassis Version 2)

  22. Need to Revisit Power Dissipation in Thumbstick Module
  23. Should be Okay I Guess?? Also Links to Batteries

  24. PLA Enclosure (Part 20, Chassis V2 and Second Tier Fit Check)
  25. PLA Enclosure (Part 21, Add Sound Back In)
  26. PLA Enclosure (Part 22, Sound's Working Again)

  27. Batteries, USB hubs, BitBuilt Projects, oh my!
  28. Batteries (Part 2, Looking Far and Wide)
  29. Batteries (Part 3, 18650's Arrive. Messing around with Godot Engine)

  30. USB Hub PCB Design Experiment (Part 1, Be the Leaf)
  31. USB Hub PCB Design Experiment (Part 2, Draft 1)

  32. Batteries (Part 4, Batteries are Kicking My rear end)
  33. Batteries (Part 5, SPICY ELECTRONICS)
  34. Batteries (Part 6, Step down or Step Up?)
  35. Batteries (Part 7, Power Up Attempt 1)
  36. Batteries (Part 8, MPS Boost Converter and OSHPark Arrived)

  37. USB Hub PCB Design Experiment (Part 3, JLCPCB Boards Arrived)

  38. Batteries (Part 9, Buck/Boost Converter Arrives, Power Up Attempt 2)
  39. Batteries (Part 10, Liftoff)

  40. Soft Latching Power Button (Part 1, Interesting Discovery and Preliminary Research)
  41. Soft Latching Power Button (Part 2, Answer Some Questions)

  42. Batteries (Part 11, Troubleshooting Reboot Loop)
  43. Batteries (Part 12, Taking an S2 Engine into Itself)
  44. Batteries (Part 13, The Absolute State of this Prototype)
  45. Batteries (Part 14, Interfacing with the BQ27441 Fuel Gauge)

  46. Soft Latching Power Button (Part 3, Integration Troubles)
  47. Soft Latching Power Button (Part 4, More Integration Troubles)
  48. Soft Latching Power Button (Part 5, Even More Integration Troubles)
  49. Soft Latching Power Button (Part 6, SparkFun can Kiss My rear end)
  50. Soft Latching Power Button (Part 7, Investigation)

  51. The Secret Life of Engineers

  52. Batteries (Part 15, Interrupt Setup and Handling)
  53. Batteries (Part 16, More Interrupt Setup and Handling)

  54. Status Overlay (Part 1, Who is DispmanX???)
  55. Status Overlay (Part 2, HUD overlay progress)
  56. Status Overlay (Part 3, HUD show and hide plus installation scripts)
  57. Status Overlay (Part 4, Low and Critical Battery Actions)

  58. Soft Latching Power Button (Part 8, Alternative Design Exploration)
  59. Soft Latching Power Button (Part 9, babyeatingpsychopath Elaborates)
  60. Soft Latching Power Button (Part 10, Built myself into a corner :()

  61. PLA Enclosure (Part 23, Second tier holder v2 Design)
  62. PLA Enclosure (Part 24, Power Switch holder)

  63. Batteries (Part 17, Power Supply Installation 1)
  64. Batteries (Part 18, Power Supply Installation 2)
  65. Batteries (Part 19, Ta daaaaaah!)

  66. Bug hunting (Part 1, YOU UNDERESTIMATE MY POWER)
  67. Bug hunting (Part 2, No it Must Be the Hardware that is Faulty)

  68. Play Log (Part 1, Metroid Zero Mission/Buttons/Battery Data)

  69. Bug hunting (Part 3, merging babyeatingpsychopath's PR)

  70. PLA Enclosure (Part 25, Starting Exterior Design)
  71. PLA Enclosure (Part 26, Left Controller Front Plate Mockup)
  72. PLA Enclosure (Part 27, Test Fitting First Left Controller Facade)
  73. PLA Enclosure (Part 28, Test Printing Small Text)
  74. PLA Enclosure (Part 29, Text Swatches and Left Controller Facade Second Iteration)
  75. PLA Enclosure (Part 30, Front Plate Modelled)

  76. Incident Report

  77. Play Log (Part 2, Metroid Zero Mission/Midnight Piss/Arduino Brownout)

  78. PLA Enclosure (Part 31, Fan Replaced and Front Plate Printed)
  79. PLA Enclosure (Part 32, Beginnings of a 3D Printer Shrine)
  80. PLA Enclosure (Part 33, Modeling HDMI Port, Starting Back Plate Modeling)
  81. PLA Enclosure (Part 34, HDMI Port Take 2)
  82. PLA Enclosure (Part 35, Connector Holes Test Fit)

  83. Buy a Screwdriver???
  84. PLA Enclosure (Part 36, Beginning Back Plate Modeling)
  85. PLA Enclosure (Part 37, Concrete Paver for 3D Printer Shine)
  86. PLA Enclosure (Part 38, The Last Test Plate)
  87. PLA Enclosure (Part 39, Back Plate Modeling Done)

  88. Phase 1 Complete
  89. Phase 1 Postmortem

  90. Play Log (Part 3, Pokemon Diamond and Pearl)

  91. Installing Godot Engine player on RetroPie

Proper Uppercut's Project TOC

  1. I've decided to go a slightly different direction with it
  2. Audio setup
  3. Formfactor planning
  4. Printing an enclosure (Part 1)
  5. Printing an enclosure (Part 2)
  6. Printing an enclosure (Part 3)

Phase 2
Part 5: A New Journey
  1. The Legend of Cory: The Hundred Year Handheld
  2. Phase 2 Quest List

  3. 3D Printer Tasks - Acrylic Doors
  4. Shrine Wheels - Planning

  5. Gaming has Changed...

  6. PCB Buttons Test, USB Hub and Shrine Wheels preparation

  7. USB Hub Assembly and Testing (Part 1)
  8. USB Hub Assembly and Testing (Part 2)
  9. USB Hub Assembly and Testing (Part 3)

  10. PCB Buttons Test (Part 2)
  11. PCB Buttons Test (Part 3)

  12. Speakers 2.0 (Part 1) - Research and Planning
  13. Speakers 2.0 (Part 2) - Adafruit Class D Amp Testing
  14. Speakers 2.0 (Part 3) - Speakerdome 2022
  15. Speakers 2.0 (Part 4) - End of Speakerdome
  16. Speakers 2.0 (Part 5) - Custom Class D Amp PCB
  17. Speakers 2.0 (Part 6) - Conclusion

  18. Shrine Wheels - Initial CAD Mockup

  19. LCD Selection - Initial Research and Planning

  20. Shrine Wheels - Locking mechanism ideation

  21. LCD Selection - Alibaba: Special Victims Unit

Documents

TBD

Source Files

Phase 1 github - https://github.com/CoryParsnipson/rpi-proto-1

This repo contains models for 3d printed parts to hold the controller and screen together. CAD models made with FreeCAD and STL files are also in the same directories. There is also a directory containing an Arduino sketch for polling controller buttons via a key matrix.

References
  1. Raspberry Pi Fusion by TodorSauce/Ashen - One of many prior Raspberry Pi portable projects. I really like what this guy did. Very inspirational.

Cory Parsnipson fucked around with this message at 08:02 on Mar 8, 2023

Cory Parsnipson
Nov 15, 2015
As of the time of this writing, I’m actually quite a ways into Phase 1 of my project right now, looking into how to obtain/make custom controller buttons. I have a small screen, speakers, and some buttons hooked up right now which I will eventually write about in more detail. My updates will probably get slower and less organized as I catch up.

Install Retropie

Ok, so after you get a Raspberry Pi, the first step should be to install the operating system on it. Since I am focusing on games, I went to the Retropie website to download an installer. Retropie maintains their own distro of Raspbian (which itself is a fork of Debian Linux) that is stripped down to a minimal set of packages for performance and storage capacity reasons.

The next step is to get stoned and play Super Mario 64 for three weeks and then wake up from a hazy stupor and remember that you’re supposed to be doing something.

Adding on a small LCD screen

Ok so this is where the idea of the project started taking shape. Before I got this working, I was sort of content with feeling some nostalgia playing some games on my TV with a spare PS4 controller and then moving on. But when I got the LCD screen working and seeing everything in action, the idea of actually making a whole portable device seemed possible.

Here’s the screen in action:

https://i.imgur.com/oJUVFd1.mp4

This is a tiny screen. It’s hard to tell in the pictures, but this is a tiiiiny-rear end screen. It’s smaller than the original gameboy screen, but probably about the same size as an old nokia phone or a tomagotchi. The LCD is surprisingly sharp and bright. I bought it a really long time ago and it sat in my closet. You can get this at Adafruit except right now it’s out of stock. It’s a bit overpriced, tbh, at 20 bux and has an unnecessary SD card reader bolted on the back.



Setting up the LCD is actually a bit aggravating because it’s not fully supported by the Raspberry Pi and there was a lot of guesswork involved. As you might guess, the screen is one of the more complicated components.

Prerequisites

I had to enable the SPI interface on the Raspberry Pi to talk to the LCD. Adafruit is a great resource and supports all of their products with great documentation.



The connection is shown in the picture above on the left (the copper circles with white silk screen labels). Serial Peripheral Interface (SPI) is a communication protocol that lets you send data packets across electronic circuits using relatively few (4 or 5) wires. The interface uses a clock signal (SCLK), read and write signals (MOSI and MISO), and a chip select (this signal can be called a lot of things like SS, CE, or D/C in the picture above).

*NOTE: so it’s a little confusing because the adafruit display has 3 chip selects: TFT_CS, CARD_CS, and D/C. The D/C signal is the chip select used in the SPI interface to talk to the LCD. In other places, this signal is called “Slave Select”. The TFT_CS signal can be used to enable/disable the LCD screen ,and the CARD_CS signal can be used to tell the Adafruit screen to use data from the SD card reader instead of the SPI interface.

To enable the SPI interface on Raspberry Pi, you can run sudo raspi-config after SSHing into it, and going to “Interfacing Options” > “SPI” and say “yes” when asked if you want to enable this. Then reboot. Alternatively, you can ssh into the Pi and browse to /boot/config.txt, do a sudo vi config.txt and make sure the line dtparam=spi=on is uncommented.

Hooking up the LCD to the Raspberry Pi

The Raspberry Pi exposes a set of 40 general purpose input/output (GPIO) pins. These provide ways for developers to interface the RPi with different hardware in addition to other ports like USB, HDMI, or the audio jack.



Notice that there’s two pins called MOSI and MISO (GPIO 9 and GPIO 10). The documentation shows that these two pins are part of the Raspberry Pi’s SPI interface. These pins need to be hooked up to the MOSI and MISO pins of the LCD. (Actually, only MOSI needs to be hooked up because we only write to the LCD screen, never read) Most of the pins from the LCD interface will have a Raspberry Pi counterpart. I needed to also hook up VCC, GND, RESET, D/C, TFT_CS, and LITE (this is the LCD’s backlight).


Here’s the Raspberry Pi hooked up to the LCD screen using a breadboard and some easy insert wires. That’s all that’s needed for the hardware hookup. The next part is to get the software working.

Installing the LCD driver

A long time ago, a dude named Notro made LCD drivers for a whole bunch of devices. They were incorporated into the Raspbian kernel a while back so luckily there's no need to download anything.

His driver repo is kind of out of date, but still really useful. Anyway, everything in his repo boils down to this. Just run this command:

code:
sudo modprobe fbtft_device name=adafruit18_green txbuflen=32768 debug=7 rotate=270
This is a linux command to load a linux driver (which is already built into the kernel) for a TFT screen loader that supports a range of specific LCD displays that Notro decided to code drivers for. This is where it gets aggravating because of the guesswork. So he has a list of supported screens here, except that doesn’t really tell you what value you should put down for “fbtft_device name”. I tried “adafruit18” initially following other examples I found online, but what I saw was this:



If the driver works, you’ll see the screen light up but appear dark. If it DOESN’T work (like if you put in the wrong device name), it will look like a blank white screen. This is because the sequence of initialization commands the driver is sending to the screen is incorrect. I learned this information from a forum post here.

I spent a week staring at a blank screen trying to figure out what was going wrong.

The fbtft driver has 4 different names for the 1.8” adafruit display, but it’s not really documented anywhere. The names correspond to different manufacturing suppliers for the LCD panels (this determines which init sequence you need to use), which are marked for the end-users with colored sticker tabs attached to the screen. There's red tabs and green tabs and also screens that don’t have any color on them sometimes referred to as “black” tabs. And occasionally the stickers are incorrect. It’s a whole poo poo show.

Anyway, as you can see above, mine has a green tab so I tried using “adafruit18_green” as the device name on a whim, I think inspired by some other people on the raspberry pi forum suggesting this. I can’t find the forum post anymore, which is a shame. I saw a dark screen after trying this, which was promising. I wrote some random garbage to the screen’s framebuffer and saw colorful static, indicating that it was working!



This looks really stupid in real life, but when something like this happens, I end up standing up on my desk and pumping my fists. You’d think I did something amazing like growing potatoes out of my own poo poo to avoid starving while stranded on Mars.

Ok, so I’m 90% of the way there. The last thing to do is to figure out how to get the video game graphics output to show up on the LCD screen. Unfortunately, the Adafruit 1.8” LCD screen is not officially supported by Raspbian or Raspberry Pi.

Someone made a hack called fbcp that you can download that will kind of work. This is a script someone wrote to copy the contents of framebuffer 0 (going to the HDMI) into framebuffer 1 (going to the LCD screen). The HDMI output is high definition, 60fps video that has low CPU consumption because it’s handled by the really powerful GPU. Once that is set up, the LCD works:



The picture is downscaled from 1920x1080… So good luck reading anything on it..

As a bonus, you can exit to the console and have linux on a tiny 1.8” screen. Good luck using it…



To get the console, browse to the shutdown menu and then run con2fbmap 1 1. That command redirects the console graphical output from framebuffer 0 to framebuffer 1.

Some complaints I have is that the framerate of the small LCD is about 20fps because fbcp does a manual copy of the graphics card buffer to the screen since this LCD isn’t “officially” supported. This is too slow for games and also cuts into the CPU and power consumption of the device. Also you’ll need to turn on the LCD manually every time you start the Raspberry Pi with the modprobe command and then restart fbcp. The right way to do this is to create what’s called a dts overlay so that the device is recognized automatically. That’s a bit too much work for me right now because I’m planning on replacing this LCD with a bigger one and the replacement is going to be a very different screen.

StupidSexyMothman
Aug 9, 2010

This is a fascinating project & I can't wait to read more!

Slugworth
Feb 18, 2001

If two grown men can't make a pervert happy for a few minutes in order to watch a film about zombies, then maybe we should all just move to Iran!
Also very into this. I mean, I don't get a lot of it, but it's still fascinating.

Cory Parsnipson
Nov 15, 2015
Thank you! The sentiment means a lot. :D

Also, I encourage asking questions if you're curious. The electronics stuff isn't super complicated like math or science, but it does require a lot of background information which makes it pretty inaccessible, imo. I've been thinking about maybe separating the nitty gritty details into their own sections within my posts to make it more digestible. One of the things that's in the back of my mind a lot is how to make people think of electronics the way they think of programming; ubiquitous and needing only a computer and the internet to get started. I hope that my posts will be both entertaining to onlookers and useful to people hoping to replicate what I did.

Rockman Reserve
Oct 2, 2007

"Carbons? Purge? What are you talking about?!"

As someone with a stack of unused RPis and at least one working 3d printer at any given time I'm watching this with bated breath. Bookmarked.

Forseti
May 26, 2001
To the lovenasium!

Cory Parsnipson posted:

Some complaints I have is that the framerate of the small LCD is about 20fps because fbcp does a manual copy of the graphics card buffer to the screen since this LCD isn’t “officially” supported. This is too slow for games and also cuts into the CPU and power consumption of the device. Also you’ll need to turn on the LCD manually every time you start the Raspberry Pi with the modprobe command and then restart fbcp. The right way to do this is to create what’s called a dts overlay so that the device is recognized automatically. That’s a bit too much work for me right now because I’m planning on replacing this LCD with a bigger one and the replacement is going to be a very different screen.

That's probably an SPI limitation, raw video data gets huge fast. Some dude made a library that optimizes it a bit by updating the parts that change rather than sending the full screen every time. Dunno if the screen you'll be using next is still SPI but if it is you might want to look into this: https://github.com/juj/fbcp-ili9341

The device tree isn't the most intuitive or well documented thing IMO but you'll probably have to mess with it at some point in your project. Basically ARM boards don't have a standardized way of querying unknown hardware to see what driver and system resources a piece of hardware needs to work. They used to hard code it into specific header files for every board back in the day but that got unwieldy fast so they came up with the device tree as a standardized way to describe what hardware is there and how it's connected.

Good luck, cool project! If you haven't actually bought the display yet, https://waveshare.com and https://buydisplay.com are good vendors for displays.

Cory Parsnipson
Nov 15, 2015

Forseti posted:

That's probably an SPI limitation, raw video data gets huge fast. Some dude made a library that optimizes it a bit by updating the parts that change rather than sending the full screen every time. Dunno if the screen you'll be using next is still SPI but if it is you might want to look into this: https://github.com/juj/fbcp-ili9341

The device tree isn't the most intuitive or well documented thing IMO but you'll probably have to mess with it at some point in your project. Basically ARM boards don't have a standardized way of querying unknown hardware to see what driver and system resources a piece of hardware needs to work. They used to hard code it into specific header files for every board back in the day but that got unwieldy fast so they came up with the device tree as a standardized way to describe what hardware is there and how it's connected.

Good luck, cool project! If you haven't actually bought the display yet, https://waveshare.com and https://buydisplay.com are good vendors for displays.

Thanks for the tips! You sound like you know what you're doing. I might need some help down the line!

I didn't know about the SPI mod, that's really cool. I might experiment with that in the future (I have some other project ideas that might be a good fit for the 1.8" screen). However for this project, I'm invested in getting another screen. I have a 5" LCD from osoyoo sitting in a box that allegedly interfaces with the RPi with a DSI cable. It seems like a more preferable way to go. Also, thanks for the vendor leads. I bought a compute module and dev board from waveshare. These two sites will also come in handy much later down the line because this dang 5" LCD was 50 whole bucks! And it comes on its own (bulky) PCB. Probably some time in Phase 2, I want to go back and see if I can learn how to put together a display driver for a "raw" LCD screen because getting the parts separately would be much cheaper. It sounds like a whole debacle though.

I'm writing a post about what I did to add speakers. Coming soon!

Forseti
May 26, 2001
To the lovenasium!
I know enough to fake it anyway :D

Definitely a lot of knowledgeable folks around these parts though who can point you down the right path though. As you are discovering there is a lot of difference between various displays! The basic problem with SPI is just that it's not all that fast. Not sure what the color depth is on that display but even 256 color would require 128px * 160p * 1 byte * 60fps =~ 1.2MB/s in raw data even for that small screen. 320px*240px*2byte*60 and you're looking at =~ 9.2MB/s for a 320x240 display with 16 bit color. There's definitely some protocol overhead to account for, especially if the driver isn't doing a bulk transfer of all that data. Depending on how much noise those jumper wires/breadboard are introducing you might get clock stretching slowing down the bus to see through the noise. Or the driver could be just bit banging SPI over GPIO if there's not an actual hardware SPI controller being used, which would be a lot slower (and eat a good bit of your CPU resources).

That driver I linked to above is taking advantage of the LCD's controller chip having its own memory and just sending the the parts that changed from the last frame to greatly reduce the amount of data being sent. It probably needs tweaking to work with each specific driver chip but it looks like support for a lot of the usual suspects is already there.

DSI is definitely much faster and will have hardware controllers to reduce the overhead on it.

Honestly, if your display with the PCB has an HDMI port and you can fit it alright, it's not a bad way to go. HDMI will always have first class support in Raspbian since it's by far the most common way people are hooking up a display, so it should always work really well. At the very least it's a good development platform and a good way to remove variables and isolate the cause of any issues.

Edit: Ooops, re: clock stretching it's actually i2c that has that built into the protocol where the device you're talking to can slow down the bus speed in the face of noise. The driver still might just set a slowish SPI baud rate by default or bump it down if it gets errors though.

Forseti fucked around with this message at 17:51 on Nov 12, 2020

Cory Parsnipson
Nov 15, 2015
SOUND

I’m going to talk about sound now and it’s going to take a few weeks to get through it all. I think sound is one of those things that’s kind of like black magic because it requires you to know a whole bunch of jargon and put a couple pieces together before things start working. Luckily, a whole bunch of people like listening to things that sound good, so there’s a lot of designs that come “premade” on the internet that you can copy down without having to understand a whole lot.

First things first, here’s speakers added to the whole rig (at various points during the design):

https://i.imgur.com/fARdPIU.mp4

I guess sound doesn't work with embedding. Link here: https://imgur.com/fARdPIU

https://i.imgur.com/Rz6HTX7.mp4

Sound: https://imgur.com/Rz6HTX7

https://i.imgur.com/2ffKWxz.mp4

Sound: https://imgur.com/2ffKWxz

Sorry about the poo poo camera work, I don’t know how to film properly… I was trying to get close up against the speakers. My phone doesn’t seem to pick up the sound well, but the speakers are actually pretty loud. I’d say as loud or slightly louder than GameBoy Advance speakers.

Note that the first video is missing a Stereo Decoder/DAC (blue circuit board with 3.5mm jack on it) and you might be able to tell that the audio quality is much noisier than the other two (or not, I can’t really tell anymore).

Here’s some Stuff That Might Come in Handy To Know About Sound

There’s 4 “functional” blocks that every speaker system needs:
  1. A power source
  2. An audio signal source
  3. Amplifiers
  4. Speakers (some way to generate noise)



The meat of the design is in the amplifier section. This takes a “weak” audio signal and power source and makes it into a stronger signal. This is because usually the audio source is a computer or portable electronics device and speakers need a lot of power to translate the signal into mechanical vibrations. The difficult part for us is that the amplifier specs must be compatible with the speakers and be driven with the right power source or else you’ll end up exploding something. We don’t want that to happen.

There’s some variations on this formula. For example, if you want headphones instead of speakers, you can probably go without the amplifiers because headphones don’t need that much power.

Speakers

Friendly PSA: They sell tiny widdle speakers for tiny widdle electronics devices! I bought these PUI Audio speakers. They’re about the size of a dime and they’re rated for 8 ohms, which is something important to remember when searching for amplifier designs. The speakers have two terminals, the minus terminal should be hooked up to ground, and the positive terminal should be hooked up to an (amplified) audio signal.

I honestly have no idea how someone on the outside comes to know all this stuff, but when in doubt, go buy parts on digi-key (or other places like mouser, waveshare, or Buy Display). That’s how I get a lot of parts and the rest comes from reading blogs, datasheets, or guides from places like Adafruit.

Power Source and Audio Signal

So for us, the power source and the source of the audio signal will both come from the raspberry pi. Convenient!



Amplifier

The diagram above shows the first design I tried to make. It uses the PWM output of the Raspberry Pi directly into an amplifier. The amplifier design I chose uses an LM386 Op amp that I found off the internet. I will be going into more detail about this design in the next post, though I feel like some people might be wondering, like, “what?? How did you specifically choose this design??” What happened here is that the site I linked describes some very textbook standard amplifier circuits that everyone seems to know about. To get an idea of what this design is, let me just tell you this. It’s babby’s first audio amplifier. If this amplifier was a flavor, it would be pumpkin spice. It’s basic.



That’s great, though, because I’ve never done this before. We just need some recognizable sound coming out of this thing for the first pass. All other amplifier designs are more complicated and either build on this foundational circuit or use some crazy gimmick for special scenarios.

ALSO another note, I could probably have bought an amplifier circuit premade off Adafruit but I decided to breadboard it myself using discrete components. I might have made things more complicated than they should have. But a lot of things off the Adafruit website are overpriced, imo, and everything I use off Adafruit is another thing I’ll need to break down and integrate into my PCB later on. They charge a premium for convenience.

SNEAK PEEK

The second and current design I’m using incorporates an additional step (“2a”) where I broke down and bought a stereo decoder from Adafruit.



This makes the sound quality clearer, frees up the PWM port on the Raspberry Pi for more important things, and also makes it easier to integrate the sound control with Alsa mixer for linux. I will also be talking more about this in a couple of posts.

Rockman Reserve
Oct 2, 2007

"Carbons? Purge? What are you talking about?!"

If I'm a dumbass that spends hours figuring out the most basic of circuits and stuff, is DSI going to be the way to go for a display on something like this? I kind of imagine I want to have as many GPIO headers open as possible for buttons and whatnot, right?

e: also this is going to be a hell of a fun project to try to source parts for when it seems like Adafruit is out of stock of 95% of their total inventory :psyduck:

Rockman Reserve fucked around with this message at 18:46 on Nov 14, 2020

Cory Parsnipson
Nov 15, 2015

food court bailiff posted:

If I'm a dumbass that spends hours figuring out the most basic of circuits and stuff, is DSI going to be the way to go for a display on something like this? I kind of imagine I want to have as many GPIO headers open as possible for buttons and whatnot, right?

e: also this is going to be a hell of a fun project to try to source parts for when it seems like Adafruit is out of stock of 95% of their total inventory :psyduck:

The easiest is to use a display that connects to the raspberry pi using an HDMI cable,. You can just plug and play in that case, no coding or software configuration necessary. I think DSI would be the next best thing because of the reason you mentioned, but it's weird because for some reason the Raspberry Pi designers don't like exposing this port to the users, so only their official display and maybe like 2 or 3 other displays can use it. It has something to do with the display adapter being closed source or something...

Here's the DSI display that I'm planning to switch to: https://www.amazon.com/OSOYOO-Capacitive-Connector-Resolution-Raspberry/dp/B07KKB5YS9

Here's an alternative display from Pimoroni I was considering: https://shop.pimoroni.com/products/hyperpixel-4?variant=12569485443155
^^ this one looks super cool. It's an IPS screen that's 4" (I consider this the perfect size) and has a touch screen on it. The only problem is that it uses all 40 GPIO pins.

Here's some displays that have an HDMI port (I don't recommend these specifically, just showing some examples):
https://www.adafruit.com/product/1928
https://www.buydisplay.com/4-3-inch-raspberry-pi-touch-screen-tft-lcd-display-hdmi-with-driver-board

Displays are expensive af :psyduck:

Forseti
May 26, 2001
To the lovenasium!
If you can, try to figure out what displays are used in those cheap emulator hand helds on aliexpress (or any other cheap and popular thing on aliexpress that has an LCD) and you can probably find those displays cheap on eBay/aliexpress/various retailers. For example, displays based on the ILI9341 are really cheap on eBay and it has a DRM driver in mainline Linux. Caveat is that it uses SPI for its connection which is slow, but that optimized driver I mentioned a few posts ago manages to get pretty good results from it still.

I didn't know that about DSI on the RPi that really sucks. I would have guessed that it would be the ideal way since it's intended for the purpose, but doesn't really matter if you can't get a good display for it at a good price :(

Personally, I'd probably look into something like this 4.3" 480x272 Display at $18 with a carrier board. You'd need to use the DPI Interface on the RPi which will use up a lot of pins, but if you can still find a way to connect everything else you use, the parallel interface is much faster than SPI and you shouldn't have any frame rate issues. It's also plumbed right into the GPU on the SoC so it should have less overhead. 480x272 is the same resolution as the PSP I'm pretty sure, and I'm pretty sure can show things like the SNES without any scaling, but not 100% on that so double check me there. I haven't poked into the datasheet but if it lets you use RGB565 that'll save you some pins, and that would still be 65k colors.

SPI clock on the RPi appears to be limited to 3.6MHz if you're running from 5V and 1MHz if you're running at 3.3V. That gives you a best case bit rate of 3.6 Mbit/s without overclocking it (and not accounting for any protocol overhead), which won't go far for raw video data. USB 1.0 Full Speed data rate is 12 Mbit/s as a comparison. In other words, you pretty much need to use that optimized driver that sends the delta per frame to get reasonable performance from SPI and you'd still be limited probably to 320x240.

mediaphage
Mar 22, 2007

Excuse me, pardon me, sheer perfection coming through
i've bee following, i hope you keep posting!

out of curiosity, have you see the analogue pocket

Cory Parsnipson
Nov 15, 2015

Forseti posted:

Personally, I'd probably look into something like this 4.3" 480x272 Display at $18 with a carrier board. You'd need to use the DPI Interface on the RPi which will use up a lot of pins, but if you can still find a way to connect everything else you use, the parallel interface is much faster than SPI and you shouldn't have any frame rate issues. It's also plumbed right into the GPU on the SoC so it should have less overhead. 480x272 is the same resolution as the PSP I'm pretty sure, and I'm pretty sure can show things like the SNES without any scaling, but not 100% on that so double check me there. I haven't poked into the datasheet but if it lets you use RGB565 that'll save you some pins, and that would still be 65k colors.

Wow, that's really awesome, this is pretty much exactly what I'm looking for. Well, maybe except with a capacitive touch screen but one thing at a time. To be honest, I'm not ready to go fiddling around with the screen just yet. Not until the end of Phase 1, especially since the DPI interface takes up so many pins. I need to figure out how many GPIO pins I need for the battery, controller buttons, and "glue logic" stuff (like brightness, volume, power button, etc).

EDIT: actually, my controller parts don't look like they'll be coming in until after Thanksgiving so maybe now is a good time to look at screens again.

mediaphage posted:

analogue pocket

Thanks! Ah, yeah I remember hearing about this. Those guys are hardcore :stare:

Analog is known for implementing old game consoles completely in FPGA hardware. This is what I was referring to in the OP by "Tony stark level poo poo". I studied computer architecture in college and I always thought it would be fun to try see how much juice you could get out of a custom portable core/GPU. It's just uh... a lot of work and really hard though. :blush:

By the way, my only critique is that the Gameboy/Gameboy pocket form factor seems outdated and uncomfortable to me. I hope that's not sacrilege since their product is massively polished and clearly shows their nostalgia for the original gameboy. Team GBA ftw though :colbert:

Cory Parsnipson fucked around with this message at 07:59 on Nov 15, 2020

Cory Parsnipson
Nov 15, 2015
An LM386 Amplifier Circuit

In the 70’s and 80’s, Integrated Circuits (ICs) were made by a few select companies like Fairchild Semiconductors and Texas Instruments. When the good ole’ engineers at these companies rolled out new ICs, they’d name them with cryptic letters and numbers. Since this was before we had the internet and video games and readily-available porn, people would hungrily follow news about new chips being released and gobble up datasheets to figure out what other primitive electronic device they could build with them. It was pretty rad.

This brings me to this a specific IC called “LM386”, an op amp* designed for audio applications. It’s a low power amplifier that is commonly used in guitar pedals, radios, etc. Ha! Classic LM386! In fact, this amplifier is so well known it spawned some popular spinoffs like LM386N-1, LM386N-3, LM386N-4, and--who could forget-- the LM386MMX-1, haha oh man. Good times. If you ask somebody how to make a sound amplifier, they’ll just tell you to use an LM386.



I remembered this years after the fact and hit google with a “LM386 amplifier circuit” out of the blue.


https://www.circuitbasics.com/build-a-great-sounding-audio-amplifier-with-bass-boost-from-the-lm386/

I used the one called “A Great Sounding LM386 Audio Amplifier”. The sections I wrote below talk about how and what operational amplifiers are, but like I said before, if you know what you’re looking for, you can ignore all that and just find a design and copy it down.

What to look for

Know your speakers. These tiny speakers have a power rating of 700 milliwatts with a max rating of 1 watt. So as long as the amplifier doesn’t output more power than that, we’re good.

The LM386 datasheet says that, if I use the LM386N-1, the output power is typically around 325 milliwatts. And that’s if I use a 6V input power but I’m going to be using the 5V on the raspberry pi (so average power output will probably be 5/6ths of 325 milliwatts). Now the only thing to worry about is if these speakers will be loud enough. But I will tell you after the fact that I built the whole thing and found that they indeed are loud enough. :smug:

Speaker Breadboarding

Here’s me using a function generator I bought off ebay to test the left speaker in the middle of putting it together:



Here’s a close up when I have both amplifiers built and hooked up to the raspberry pi:



And once again, I over complicated this for myself. I could have searched “audio amplifier” and bought something off Adafruit, sparing you this long post about audio circuitry.

Software Setup: Using PWM audio output

Once the circuit is made, we’re not done yet! We gotta hook up the raspberry pi audio to the speakers. The Raspberry Pi 3 has a 3.5mm audio jack you can get sound out of straight from the box. This is great, but unfortunately, I’ll probably end up ignoring this and using something else. If you want audio signal into a custom circuit, you can go through the GPIO. Once again, the gpio pinout:



There are 4 pins here that you can tell the raspberry pi to generate Pulse-Width Modulation (PWM) signals from. And the pins are grouped into pairs. GPIO 18 and GPIO 19 are one pair of PWM outputs and the other pair is GPIO 12 and GPIO 13. You can set an alternative mode where the PWM signal is used to create an audio signal and is fed by the raspberry pi sound system. This alternate mode can be enabled using the WiringPi utility that comes installed with Raspbian:

code:
gpio mode 26 alt0
gpio mode 22 alt0
These lines of code need to be entered upon every reboot. Or you can change the boot config to make it permanent:

code:
dtoverlay=pwm-2chan,pin=12,fun=2,pin2=13,fun=2
Unfortunately, this doesn’t work, because the dtoverlay doesn’t allow specifying two pwm channels in its parameters. So you’d need a custom dtoverlay to change this. But if someone did, this is the line you’d enter to make it happen.

So in the picture above with all the orange annotations on it, you can see the two wires I used to connect GPIO 18 and GPIO 19 to the amplifiers. The wire labeled “L-Channel Audio” goes from GPIO 18 to the left LM386 amp input and the wire labeled “R-Channel Audio” goes from GPIO 19 to the right amp.

As with the LCD screen, I have gripes about PWM audio:
  1. The PWM audio isn’t very high quality. It’s prone to noise and interference from other signals. And the PWM generator clock isn’t very fine grained so the frequencies it generates may not be correct. This can translate to pops and clicks in the audio signal.
  2. Coupled with the dtoverlay issue I mentioned above, PWM is a pain to use. Either all 4 pins must be in audio mode or general purpose mode. So if I need to use a PWM signal for some purpose other than audio, I’m hosed.
The good news is PWM audio was never the plan, so instead I will add in a stereo decoder. This requires a different software approach, so instead of PWM, I throw all that away and use something called I2S instead. More on that soon.

I will probably end up iterating quite a few times on every part of the design. I hope that doesn’t end up too confusing.

*Operational Amplifiers (a.k.a. “Op Amps”)

Unless you’re an audio geek, or an electrical engineer, or my grandpa who likes fiddling with old transistor radios, you probably don’t care what an “op amp” is. I just thought I’d mention it because otherwise the speaker design would seem like a magic box that generates sound. It is pretty close to being a magic box that generates sound, though.

An op amp’s schematic symbol is this:


THIS IS ALL YOU NEED TO KNOW ABOUT OP AMPS: THEY TAKE IN SMALL SIGNAL AND PUSH OUT BIG SIGNAL.

Like this:


It’s hard to tell from my scribbles, but all the bumps and grooves of the waveforms are the same but the amplitudes (i.e. vertical range) are different. This is where the term “amplifier” applies. In layman’s terms, if you put in a sound wave, it comes out sounding the same but louder and more powerful. Note in this context, amplitude is volume.

WARNING: Science

Skip this section if you don’t care to learn more about op amps.

Judging from the symbol, you’d think they just take the input signal and push it through the triangle and pop out a bigger signal somehow, right? WRONG. First of all, they invert the signal if you use the minus terminal. Secondly, and more weirdly, the input resistance is supposed to be infinite. Meaning the current of the input signal never flows through the op amp at all. Instead the signal goes into a few transistors that are set up to recreate the signal they receive in the gate terminals by drawing from the power rails (top and bottom wires). It’s like the output is “air-gapped” from the input. Op amps are freaky.

If you’d like to get into the mathematics of it, there’s some guides online, but it won’t make much sense until the second year of electronics classes… I’d recommend a school textbook, tbh, there’s useful context in a book.

Cory Parsnipson fucked around with this message at 21:08 on Nov 15, 2020

mediaphage
Mar 22, 2007

Excuse me, pardon me, sheer perfection coming through

Cory Parsnipson posted:


Thanks! Ah, yeah I remember hearing about this. Those guys are hardcore :stare:

Analog is known for implementing old game consoles completely in FPGA hardware. This is what I was referring to in the OP by "Tony stark level poo poo". I studied computer architecture in college and I always thought it would be fun to try see how much juice you could get out of a custom portable core/GPU. It's just uh... a lot of work and really hard though. :blush:

By the way, my only critique is that the Gameboy/Gameboy pocket form factor seems outdated and uncomfortable to me. I hope that's not sacrilege since their product is massively polished and clearly shows their nostalgia for the original gameboy. Team GBA ftw though :colbert:

I agree it’s cool and polished and I never want one. In that vein, though, panics play date is a similar form factor with the exception of the attractive crank and I’m super stoked for that

Forseti
May 26, 2001
To the lovenasium!
Has Analogue's portable actually been shipped to anyone or tested by blogs or anything? I'm curious what the battery life is like because in general, FPGAs are considerably more power hungry than general purpose CPUs. Very cool though and has the advantage of being a high quality build by people who know what they're doing.

Re LM386, nice work! I think you may know this but the LM386 at this point is ancient and a good learning tool and quick and dirty fix when you want to test something on a bench, but there are much better options out there now. Also, the breadboard is great for prototyping but isn't really a good test for signal to noise ratio and distortion. You essentially have antennas hanging all over the place there and the breadboard connections have very high parasitic capacitance. The antennas will introduce noise and the capacitance will smear out your nice sharp digital transitions for a signal like PWM. Not sure if it makes a huge difference at audio frequencies, but I wouldn't be surprised if you could hear the difference easily.

Not sure what you're thinking, but I'd get one of the new fangled class D amplifier ICs made by e.g. Texas Instruments. They'll have all the filtering and such inside and just by the nature of being tiny should be more resistant to noise. They're also WAY more efficient than the LM386 and should help your battery life quite a bit and also be able to go considerably louder.

They make a ton of them and other manufacturers besides TI make them too. I've messed with the cheapo modules you can easily find on eBay and they work quite well, but they want an actual audio signal as input. Not sure if you can find them as easily as modules, but from a quick search, there are definitely chips that can take an I2S signal as input (the one that came up that I looked at is the TAS5760L), which means you wouldn't have to go all analog engineer to keep your signal from getting noisy. If you just put the amplifier physically near the speaker it'll probably be pretty low noise.

Hell, these things are so popular you can buy them from Parts Express (though I wouldn't because they're the same low buck Chinese boards you find on eBay but marked up).

Cory Parsnipson
Nov 15, 2015

mediaphage posted:

I agree it’s cool and polished and I never want one.

Huh, really? How come? I'm curious to know what the deal breaker is. Hardware emulation is supposed to be the tits.

Forseti posted:

Also, the breadboard is great for prototyping but isn't really a good test for signal to noise ratio and distortion. You essentially have antennas hanging all over the place there and the breadboard connections have very high parasitic capacitance. The antennas will introduce noise and the capacitance will smear out your nice sharp digital transitions for a signal like PWM. Not sure if it makes a huge difference at audio frequencies, but I wouldn't be surprised if you could hear the difference easily.

Haha yeah I forgot to mention that. The sound isn't bad, it's got a little background static. When you add in the DAC, the difference is quite noticeable.

Forseti posted:

Not sure what you're thinking, but I'd get one of the new fangled class D amplifier ICs made by e.g. Texas Instruments. They'll have all the filtering and such inside and just by the nature of being tiny should be more resistant to noise. They're also WAY more efficient than the LM386 and should help your battery life quite a bit and also be able to go considerably louder.

Exactly, that's the plan. I was going to see how far the LM386 took me and then try to do some power analysis to see how much of a difference the class D amp would make. I was also putting it off because the class D amp looks way more complicated...

By "actual audio signal" do you mean an analog signal?

Forseti
May 26, 2001
To the lovenasium!

Cory Parsnipson posted:

By "actual audio signal" do you mean an analog signal?

Yep, apologies for being imprecise there, but that's what I mean. The modules I've personally used have all used analog audio signals, L/R/Common for input. The premade modules are super easy to use, the chip itself might be more complicated to work into a design though. A company like TI will tell you everything you need to know though and will have reference designs (which is what the cheap eBay modules are executions of).

At the very least you can use a module pretty much like you'd use an LM386 and just get better efficiency, although I'm not sure how much impact that will have in terms of battery life at the relatively low powers you need.

mediaphage
Mar 22, 2007

Excuse me, pardon me, sheer perfection coming through

Cory Parsnipson posted:

Huh, really? How come? I'm curious to know what the deal breaker is. Hardware emulation is supposed to be the tits.

I can appreciate all the hard work and effort that went into its design and construction, but to be entirely honest I really don't care about hardware emulation for my own needs. All the stuff I want to play will work fine in software emulation, for the most part. I bet it feels nice in the hand, though.

Cory Parsnipson
Nov 15, 2015
Adding a “Stereo Decoder” between the Amplifier and Audio Source

This is going to be a light update. It's kind of boring, imo. It'll get a little better later and after this I'll delve into the controller and buttons, which I think is way more interesting but that could be me being biased since I'm still learning about it.

Up to this point, my speaker setup looks like this:



I added a new block in between the amplifier and the Raspberry Pi:



Here you can see that the change is very minimal. The new block is the blue circuit board in the top right. All that is required is to rewire the input going into the amplifiers (short orange wires) and the audio signal coming from the Raspberry Pi. I was able to quite literally just drop the block into the old design for a quick upgrade.

The “Stereo Decoder” block is something that I bought from Adafruit. It’s really fancy and quite easy to use, but might be hard to integrate later. Despite being called “Stereo Decoder” this thing actually does two very important things.
  1. Stereo Decoding - As in “mono” and “stereo”. The signal coming from the Raspberry Pi will be in stereo, meaning that the datastream will have data for both left and right speakers and this circuit splits (a.k.a. “decodes”) the single datastream into two wires. This is necessary because we’re going to have one speaker/amplifier unit per side and the amplifiers take one wire expecting a single data stream. This wasn’t needed when we were using PWM because it already came in two wires since the Raspberry Pi was already kind enough to split it for us. The I2S output format does not. More on this later.

  2. Digital-to-Analog Converting - Yes, this baby is also a DAC. And it’s a nice one too. I mean not, like, a professional Dr. Dre beats type DAC, but it’s $7 and has a great signal to noise ratio.

Digital-to-Analog Converters (DACs)

The name itself refers to converting a digital signal that switches between two discrete values (0 and 1, or 0V and 5V, etc) into an analog signal that can assume a continuous range of values (e.g. any voltage between 0V and 5V). People use these things mostly for converting audio from digital signal to actual sounds you can hear. (You can think of sounds as made up of a large combination of sinusoidal waves, and sinusoidal waves cannot be expressed using only 2 integers).

Maybe this illustration I found online would help:


Most computers and pieces of technology will need a DAC analogous to the way computers and pieces of technology need a graphics card. Your laptop/desktop probably has a DAC that feeds the 3.5mm jack coming out of it. If it plays sound and it doesn’t sound terrible, it probably has a DAC.

Ok, but what did we gain by doing this?

The sound is slightly better and I don’t have to fiddle with the lovely software options that we’d be stuck with using PWM. This is a “real” sound setup in the respect that we now have a digital audio signal being properly converted to an analog signal, sent to an amplifier, and then played out of a speaker.

Contrary to what I said in the previous section, the Raspberry Pi does not come with a DAC on it’s motherboard. This is presumably to keep the cost/size down and because lots of people wouldn’t need high quality sound when they’re making their light switch dimmers or sprinkler systems or whatnot. The 3.5mm jack coming out of the Raspberry Pi is directly connected to a CPU pin and also driven by software PWM.

PWM vs DAC

Why have both? They’re really different tools that have some overlap. The advantage of PWM is that it only requires one GPIO pin and can be implemented in software, so you don’t need any fancy add-ons. But the PWM signal requires extra CPU cycles to generate the signal and it’s dependent on the clock frequency. This means we can only really express a limited range of analog signals and coupled with the fact that PWM can only assume 256 different values, we end up with not a very high quality analog signal, we may end up with some clicks and hissing in the output due to the low resolution of the PWM signals. This is ok for things like a light bulb dimmer, but for audio, cutting the corners on quality can be noticeable. If we bring in a DAC, and choose a nicer one relative to your PWM generator specs, we can reduce the signal to noise ratio and output an audio signal with much less background noise. (Thanks babyeatingpsychopath)

Software setup for I2S audio output

I2S is yet another communication protocol (like SPI) for transferring data, except this is specially designed for transmitting digital audio signals. ‘Nuff said.

The GPIO pin connections needed for I2S are here:


Adafruit makes this really easy with their installation script. Literally all you need to do is to download and run the script from github and then it configures the I2S interface for you. Once you finish running the command, you need to reboot and then playing audio will automatically come out of the speakers.

One caveat is that there is a hack where they loop a silent audio file in the background to prevent popping when you start and stop playing audio files. This requires some CPU time but according to them, it’s negligible on the Raspberry Pi 3. I currently don’t need this because I’m using Retropie, which never releases the I2S interface but if I switch to Raspbian, it’s something I’d need to consider turning on.

mediaphage posted:

I can appreciate all the hard work and effort that went into its design and construction, but to be entirely honest I really don't care about hardware emulation for my own needs. All the stuff I want to play will work fine in software emulation, for the most part. I bet it feels nice in the hand, though.

Trying to understand more specifically. Do you mean like the specialized hardware makes it too expensive/overkill for playing older games? Or the fact that you still need to lug around cartridges or old peripherals? I think hardware emulation theoretically should not lag at all, except in the instances where the original system itself lags. This is important for me, because when there's lag it really kills the mood imo.

Cory Parsnipson fucked around with this message at 23:00 on Nov 30, 2020

babyeatingpsychopath
Oct 28, 2000
Forum Veteran

Neat project.

I accidentally bought some I2S microphones a little bit ago. Those are nontrivial to get working on tiny micros, as I2S is a pretty speedy protocol, all things considered.

Because I like to be pedantic, going from 8-bit to 16-bit on your digital audio doesn't do anything for the fidelity, it just lowers the noise floor. If you're using the same sample rate, then you get all your information in the same number of samples, but you get more quantization noise with a lower bit depth. CD players use a 1-bit DAC running at 16x sampling frequency, so it "acts like" a 16-bit (really 14-bit because of math) DAC.

Can't wait until you get into your buttons and switches. Charlieplexing and diodes, aplenty, right?

mediaphage
Mar 22, 2007

Excuse me, pardon me, sheer perfection coming through

Cory Parsnipson posted:

Trying to understand more specifically. Do you mean like the specialized hardware makes it too expensive/overkill for playing older games? Or the fact that you still need to lug around cartridges or old peripherals? I think hardware emulation theoretically should not lag at all, except in the instances where the original system itself lags. This is important for me, because when there's lag it really kills the mood imo.

Mostly the latter. I'm not the most organized person on the best of days, so i'm really mostly only interested in something that works with a minimum of physical units.

With that said I suppose I could always get a flash cart...or for that matter their "unofficial" jailbreak, if released, would allow for it. I'm not necessarily trying to push piracy I just can't handle having a bag of things to haul around.

Cory Parsnipson
Nov 15, 2015

babyeatingpsychopath posted:

Because I like to be pedantic, going from 8-bit to 16-bit on your digital audio doesn't do anything for the fidelity, it just lowers the noise floor. If you're using the same sample rate, then you get all your information in the same number of samples, but you get more quantization noise with a lower bit depth. CD players use a 1-bit DAC running at 16x sampling frequency, so it "acts like" a 16-bit (really 14-bit because of math) DAC.

Oh gently caress, I'm spreading misinformation. :saddowns:

Just so I get this clear, do you mean that the fidelity isn't affected because the audio signal is already in digital form so the quality is the same no matter what speaker I use? And that the 16-bit output will have less quantization noise, and this does translate to slightly better perceived sound? I realized that I never looked up the definition of fidelity and the best I could find is "accuracy with respect to the original recorded sound". Is there a more technical definition? I don't remember what I learned in school. :sweatdrop:

babyeatingpsychopath posted:

Can't wait until you get into your buttons and switches. Charlieplexing and diodes, aplenty, right?

I never got why they called it "charlieplexing" and not just "key matrix" but I think so? I've been using the buttons directly hooked up to GPIO, but now that I need like 16 of them, I need to switch to the matrix'd approach. I'm using this page as a reference. The key matrix and charlieplexing look basically the same to me, but I also never done charlieplexing before so I'm not sure if they're the same thing or if I'm missing some nuances.

mediaphage posted:

Mostly the latter. I'm not the most organized person on the best of days, so i'm really mostly only interested in something that works with a minimum of physical units.

Ah, same. I don't get the appeal of the recent "classic" versions of old consoles. It doesn't sound fun to be playing on a tiny controller like 5 feet away from the TV. Sorry if I poo poo on anyone. I'm a digital-only kind of guy, never really liked having to store PlayStation CD cases or switching out CDs and I even got a bigass SD card in my 3ds so everything is just available.

On the other hand... Sitting on a plane with my backpack full of GBA cartridges was awesome. Especially when pokemon had those freakin sweet colored cartridges. I would have killed to get my hands on Pokemon Sapphire.

Cory Parsnipson fucked around with this message at 04:56 on Nov 22, 2020

Cory Parsnipson
Nov 15, 2015
Moved the speaker circuitry to a protoboard

Here’s one last part related to sound and then we can put this away for a while.

I needed to free up my breadboards so I could mess around with circuitry related to other parts of the project. That’s when I had a great idea! The speakers had been sitting on my desk like this for months and now it’s definitely time to move them to a slightly more permanent layout, like a protoboard, for instance.






This is me starting to move the right amplifier and speaker to the protoboard (the green thing).






Once the right amplifier was fully moved over, I temporarily hooked up the power, ground, and audio input signals to it for debugging. It took a few hours going over the connections and fixing mistakes, but finally, after about 3-4 hours it started working again.



A protoboard is different from a breadboard because you need to solder everything down, so it’s pretty permanent. Some protoboards have their holes connected to each other in a similar way breadboards do, but others (like one I’m using) don’t, so you have to wire everything together by yourself. You can see that the underside of it is really messy.



Here’s both speakers added to the protoboard. The left amplifier speaker went a lot faster than the right one. I spent a weekend doing about 5 - 6 hours of soldering and it was pretty fun. I haven’t had to solder anything in a really long time.

The top of it is really cool looking though. I bet I could really freak someone out if I waved it around in public.






Here’s the whole thing wired up and hooked up to the Raspberry Pi. The sound works as it did before. I got rid of one breadboard, but I still need the one to put the stereo decoder and LCD on.

You might have noticed before that the third sound sample I posted above shows the protoboard instead of the breadboard version. This is why.

So with that, we’re basically ready to put this in a shoebox. This is version 1 of the sound system.

Loose ends
  • Replace the LM386 amplifier with a Class D amplifier for better battery life. I also need to remember to measure the current draw during typical usage to estimate the power consumption. I’m too lazy to do that right now...

  • Add in capability of detecting if headphones are plugged in and automatically mute the speakers (but not the headphones) if so. Unmute the speakers when the headphones are unplugged. This may require me to swap out the 3.5mm jack with one specially designed to have inputs for detecting if something is plugged in. Also note that we may need to come up with a script to store “profiles” to remember to unmute/mute or have different volume levels for separate situations such as “handheld mode”, “headphones plugged in”, “headphones plugged in while docked”, “no headphones while docked”, etc. I’m basically describing how laptops and smartphones handle this stuff, and I really like that, so let’s copy those things.

  • Take a look at the Adafruit schematic for the stereo decoder and then put that, the amplifiers, and speakers all on a single PCB (see Phase 2).

  • I can also probably choose a different speaker that has a better physical profile, smaller power requirements, and a more suitable frequency response. The ones I have go from 1kHz to 20kHz. Human hearing is about 20 Hz to 20kHz, but I don’t need the whole range. I can probably save some money by finding ones that stop around 15kHz or use that leeway to get speakers that have a lower cutoff than 1kHz. (Although, the ones I have now sound fine, so it might not be worth optimizing for the lower frequencies)

Next

Whew! We have two main functional areas left--buttons and batteries. I‘m going to get really deep into figuring out what people do to make custom controllers and it’ll involve a lot of hardware. I don’t do hardware trinkets a lot so this was a huge learning experience for me.

On Amplifier Classes

I actually learned this while researching amplifiers for this project, but amplifiers are organized into different classes by letter and there’s like Class A all the way to Class G/H or something.

This page explains things in more detail and gets somewhat technical (but not too technical). I may be oversimplifying, but the gist is that all these different types of classes try to solve the issue of being power efficient vs having high quality audio output.

Class A amplifiers are the first and oldest amplifier design. It’s simple, all the circuitry in the amplifier is always on and drawing full power. It is also the best sounding one, because there’s no concessions for power efficiency or anything else. Class B amplifiers tried to be more power efficient at the cost of sound quality. Using transistors in a different layout, what the Class B amplifier does is try to turn off parts of the circuit when they are not needed. This takes advantage of the sinusoidal qualities of audio waveforms.

Then there’s class AB amplifiers, which is what this LM386 amp falls under (I think), that uses a combination of class A and class B characteristics to compromise between sound quality and power efficiency. It’s less efficient than class B but doesn’t have some of its problems in reproducing sound.

There’s a class C amplifier. This is not a good design for audio amps because it has “nonlinear gain”. These classes are for amplifiers in general and not just audio amplifiers. Class C amps are typically used for radio frequency (RF) transmitters. Think, ham radio, or card readers, I guess.

And then there’s the infamous Class D amp. This is completely different from the previous amplifiers. It was invented by a really smart old guy in the 1950s. This amp tries to output an analog, sinusoidal signal using the PWM technique. The PWM waveform this amp generates is fed into a low pass filter to smooth it out into a close approximation of the analog sound signal. This amp is low power and small size, which makes it great for portable electronics and factory standard sound systems for cars. Only recently have these amplifiers become feasible because of the lower cost and quality of components that allow companies to build class D amps with sound quality approaching class AB amplifiers.

There’s more classes being created to this very day. Class F, G, and I basically are improvements upon the AB amplifier design, trying to hyper optimize certain aspects of the design. There are also class S and T amplifiers that work like the class D amp, also trying to improve upon the basic methodology using different or more modern mechanisms.

Yeah, so there. That’s the amplifier landscape I think.

StupidSexyMothman
Aug 9, 2010

this thread is what i wish my electronics engineering classes had been like.

I wish I knew how to solder, but I know if I learned the skill I would just end up with a pile of unused soldering tools and an even larger pile of stuff that I could build/should fix when I get around to setting up the soldering station.

Cory Parsnipson
Nov 15, 2015

oldskool posted:

this thread is what i wish my electronics engineering classes had been like.

Wow, that's an amazing compliment! My school was super theoretical. We basically only drew circuits on paper and did ginormous math equations, it loving sucked. I mean, there's a ton of knowledge that they shoved into me, but I wish the same thing as you--having a portion of hands-on projects and cool things to build would have been nice.

oldskool posted:

I wish I knew how to solder, but I know if I learned the skill I would just end up with a pile of unused soldering tools and an even larger pile of stuff that I could build/should fix when I get around to setting up the soldering station.

I think a lot of EE people feel the same despair at some point and for me, it made me want to go into computer science for a while. Because all you need is a computer and the internet to get started. For modern consumer electronics there's a ton of things done in the name of "improvement" that make it really unfriendly for hobbyists and I think that's a huge shame. First of all there's surface mount components, and now everything is starting to be incorporated on silicon wafers or NAND chips with really microscopic connection form factors. If your electronics breakage is inside the chip, unfortunately there's nothing you can really do to fix it unless you want to invest in a heat gun and a microscope. I wouldn't worry too much about not being able to fix everything, but every once in a while you'll be surprised. In the electronics thread, some dude just fixed his friend's treadmill and saved, like, $1000. I think being able to indulge in an impromptu electronics project and occasionally fixing something is more than enough to justify having these items sitting around in the garage 99% of the time.

You can get soldering irons for pretty cheap and it doesn't take up a lot of space. And I'd say, you maybe only need 5 things:
  1. Soldering iron, temperature controlled is preferred and a sponge holder is a bonus; no need to get a really expensive one
  2. Solder wick; I love this thing and also rely on it to do certain kinds of surface mount soldering
  3. Solder
  4. Solder flux - marker form is convenient and will work for most things
  5. Wire cutters
Holding clamps and a solder vacuum are optional, but really nice to have. Gonna get me some helpin' hands some day.

All my worldly possessions can fit in an 8x10 room and I have two cardboard moving boxes full of tools and components that I lugged around for 5 years without touching them at all. But now I'm ready to use them and I'm glad I didn't throw any of it away. I've been borrowing my roommate's coffee table and setting everything up/tearing it down every time for some guerilla electronics work. You don't have to build your own electronics workshop to get started (although having one would be super sweet).

You'll pick it up fast! I find soldering to be somewhat therapeutic, much like sewing or knitting. There are far too few opportunities to solder in your own projects.

I really encourage you to jump in if you ever get the itch. Things are so much better now! There's Adafruit and Arduino and a lot of communities where you can get information from. This was much harder before. I recommend making a list of ideas and writing in it every time you come up with one. A small percentage of these ideas will be simple enough to achieve, and that's really something! This could be a combination of ideas that you have while lying in bed or when you have some sort of pain (e.g. cell phone charger dock???? door opener?? PISS CUP???) and ideas you get from other peoples' projects like on Hackaday or something. A good starting point could be copying others' work and then improvising once you get the hang of things.

MINI UPDATE FROM THE FUTURE:

Happy Holidays, everyone! Like most people, I managed to drop some big bucks and snagged myself a brand-new, fancy pants PlayStation 5! I bought a used set of joycons. :downsbravo:



I'm trying to figure out how the shoulder buttons work and I gave up trying to figure it out using just pictures from the internet.

Forseti posted:

They're even passing on the cheap manufacture aspect of surface mount these days, they're happy to stuff the board for you and it's actually not even that much money if you stick to the list of parts that they keep stocked. You can order prototype boards for literally a couple bucks and have the option to get the solder stencil so you can squeegee on the paste, plop the parts down, and then pop it onto a hot plate to solder everything as long as you keep everything mostly on one side.

The PCB fabs will make boards stupidly cheap for smaller board sizes too and you start to realize "oh yeah, this is why everyone uses surface mount" when you can get 250 pieces of a gumstick sized PCB for $30 at your door.

Embrace it man! I think it's a golden age for electronics hobbyists :)

Huh, wow, that's enticing. Maybe I will get into surface mount stuff. :colbert:

Cory Parsnipson fucked around with this message at 21:13 on Nov 26, 2020

Forseti
May 26, 2001
To the lovenasium!
Also China's got your back homie. It does kinda suck that you can't get everything in a nice breadboard friendly package anymore but on the other hand there are things like the ESP8266/ESP32 that are unbelievably capable for next to nothing and they put tons of stuff on breakout boards that you can get on eBay for next to nothing now that kind of fill in the gap for no DIP packages of the actual ICs. Something like the "blue pill" stm32f103 board absolutely crushes the giant 40-pin PIC's we used to make robots in high school and is like $2-3 ea on eBay, less than the PICs cost.

They're even passing on the cheap manufacture aspect of surface mount these days, they're happy to stuff the board for you and it's actually not even that much money if you stick to the list of parts that they keep stocked. You can order prototype boards for literally a couple bucks and have the option to get the solder stencil so you can squeegee on the paste, plop the parts down, and then pop it onto a hot plate to solder everything as long as you keep everything mostly on one side.

The PCB fabs will make boards stupidly cheap for smaller board sizes too and you start to realize "oh yeah, this is why everyone uses surface mount" when you can get 250 pieces of a gumstick sized PCB for $30 at your door.

Embrace it man! I think it's a golden age for electronics hobbyists :)

babyeatingpsychopath
Oct 28, 2000
Forum Veteran

Cory Parsnipson posted:

Oh gently caress, I'm spreading misinformation. :saddowns:

Just so I get this clear, do you mean that the fidelity isn't affected because the audio signal is already in digital form so the quality is the same no matter what speaker I use? And that the 16-bit output will have less quantization noise, and this does translate to slightly better perceived sound? I realized that I never looked up the definition of fidelity and the best I could find is "accuracy with respect to the original recorded sound". Is there a more technical definition? I don't remember what I learned in school. :sweatdrop:

The audio is converted by the DAC into a band-limited signal. Everything above samplefreq/2 cannot be represented (as per Nyquist). Therefore, if your sample rate is the same, you get precisely one waveform of your sample points, whether or they're 8 bits or 16 bits or whatever. The 16-bit signal is "closer" to the original, but there isn't any more frequency information in it. Because an arbitrary analog signal falls in a random spot between sampling points, the average difference at a single sample point when summed across all the points is white noise. If you only have 8 bits of depth, then you're looking at a larger signal-to-noise ratio, as each point has a larger distance away it can be.

This is called "quantization noise" and if your digitizer is any good (audio ones are) then this noise is exactly equivalent to tape hiss. 4 bits gives more "hiss" than 8, which is more than 16, etc. So if you're playing on a crappy low-fidelity speaker anyway, your sound and tones (especially if they're small numbers of sine waves added together) will sound the same at 8 bits and 16, but you will probably be able to hear the difference in a voice sample or a drum hit or something that's got a lot of complex frequency stuff in it.

quote:

I never got why they called it "charlieplexing" and not just "key matrix" but I think so? I've been using the buttons directly hooked up to GPIO, but now that I need like 16 of them, I need to switch to the matrix'd approach. I'm using this page as a reference. The key matrix and charlieplexing look basically the same to me, but I also never done charlieplexing before so I'm not sure if they're the same thing or if I'm missing some nuances.

Normal switch matrix takes n pins to drive n2/2 pins, but charlieplexing uses n pins to drive n2-n pins. So a charlieplex 4x4 matrix only needs to use 6 pins, instead of 8. By being clever about which of your inputs are high, which are low, and which are input, you can get all your input with fewer pins, as long as you can sacrifice refresh rate.

Cory Parsnipson
Nov 15, 2015

babyeatingpsychopath posted:

The audio is converted by the DAC into a band-limited signal. Everything above samplefreq/2 cannot be represented (as per Nyquist). Therefore, if your sample rate is the same, you get precisely one waveform of your sample points, whether or they're 8 bits or 16 bits or whatever. The 16-bit signal is "closer" to the original, but there isn't any more frequency information in it. Because an arbitrary analog signal falls in a random spot between sampling points, the average difference at a single sample point when summed across all the points is white noise. If you only have 8 bits of depth, then you're looking at a larger signal-to-noise ratio, as each point has a larger distance away it can be.

This is called "quantization noise" and if your digitizer is any good (audio ones are) then this noise is exactly equivalent to tape hiss. 4 bits gives more "hiss" than 8, which is more than 16, etc. So if you're playing on a crappy low-fidelity speaker anyway, your sound and tones (especially if they're small numbers of sine waves added together) will sound the same at 8 bits and 16, but you will probably be able to hear the difference in a voice sample or a drum hit or something that's got a lot of complex frequency stuff in it.

Got it, thanks! I read that the Adafruit DAC does a lot of filtering right in the breakout board so maybe that's another reason for the noticeable difference. And that's before accounting for any of the effects from the breadboard. Man, I've got a lot of reading to do if I want to optimize the sound system...

babyeatingpsychopath posted:

Normal switch matrix takes n pins to drive n2/2 pins, but charlieplexing uses n pins to drive n2-n pins. So a charlieplex 4x4 matrix only needs to use 6 pins, instead of 8. By being clever about which of your inputs are high, which are low, and which are input, you can get all your input with fewer pins, as long as you can sacrifice refresh rate.

Oh. Yeah before I skimmed the Wiki page but now I've gone back and I see what you mentioned right there. Sounds awesome! I'm about to reconfigure the controller circuit and now I definitely want to try charlieplexing. I think the controller should work just fine as long as the main code loop takes less than 100-200ms to run. That should be more than enough time to run everything, so I guess I'll find out if everything works pretty soon.

Cory Parsnipson
Nov 15, 2015
INPUT

Yay, time for something new. This is all new to me as well, so there’s gonna be a lot of experimenting here. As a bonus, a lot of the work here will be with physical objects and shiny, colorful hardware to keep my tiny monkey brain entertained.

And as I write that, I’m realizing that the rest of this post will be mostly a text dump talking about preliminary research...

The Simple Way (and Other Alternatives)

The most direct method, and probably what most people would start with, is to wire buttons directly to the Raspberry Pi’s GPIO pins. All you need is a tactile push button, a resistor, and a couple wires and you choose a non-reserved GPIO pin and hook them all together. Then, I guess you need some sort of script or something to periodically read the value of the GPIO pin and map it to a keypress.


Like this, but multiplied by about 20

Wiring up buttons seems more complicated on the Raspberry Pi than other microcontrollers like the Arduino because you have the entire Linux operating system stack to route the GPIO data through and that seems really error prone to me. My personal opinion is that I hate having stray bash scripts around because you have to put them somewhere and then figure out how to make them run automatically on certain system events and then also remember to copy them when you reinstall stuff (and then explaining it to other people who are trying to use your script but with a slightly different environment so the script doesn’t work without tweaks that neither of you can make without digging into each other’s computers). So I really don’t like the idea of doing it this way.

Also another thing that I would realize later when someone on the Raspberry Pi forum pointed this out is that the Raspberry Pi doesn’t have any built-in way to get analog input through the GPIO pins. There are no analog-to-digital converters (ADCs)* hooked up to the pins so you can’t just bolt on a thumbstick or two and/or have analog trigger buttons. I feel like this would be the main deal breaker for most because if you have to bring in extra ICs for this you might as well go with another solution I’ll describe below.

*an ADC does the opposite of digital-to-analog converter (DAC). Clever, huh?

IPACs are Game Controller Brains???

I vaguely remember reading somewhere that there was a whole fighting game scene where they make their own “fight sticks” (portable arcade controllers) and there was some kind of chip or part you needed to buy to “make the computer recognize your buttons as a controller”. So yeah that’s what I had to go on for this part. I’m pleased to say that up until this present moment, I’ve learned a lot of terminology and it’s been very enlightening.

Searching some kind of variation on the phrases above, I discovered that the aforementioned device is called an “IPAC” which is what the people over at Ultimarc call their product. What it is, apparently, is a microcontroller with GPIO pins (that you wire up to arcade buttons and joysticks and whatnot) that appears as a keyboard to whatever computer/arcade system you hook it up to.


Here's a beautiful DIY "IPAC fight stick" setup by an anonymous user from reddit

Each button is bound to a keystroke and you just need to configure whatever game you’re playing to have the right key bindings. I’m not going to do this, and I recommend anyone doing the same not to either, but I’m putting this here for complete-ness.

Microcontroller-ception

Forget that arcade stuff for a moment, because I don’t want to spend, like, $35 on an IPAC that isn’t even small enough for what I want to do. NOTE: Ultimarc does sell a “nano” version of the IPAC device but, alas, they don’t support adding the necessary number of buttons I would need. Additional searching for “how to make a game controller”, will bring up a ton of DIY guides about how to make crappy push button controllers using Arduinos and stuff.

:thunk: Hmmm, this is quite a concept. So, I could use an Arduino in place of an IPAC and do the programming myself??? Actually, since I was fresh off browsing the Raspberry Pi website for different RPi modules, I had a Raspberry Pi Zero in mind because of its compact form factor. This led me to some weird thoughts, like, “what the heck, am I really going to buy a second Raspberry Pi to put inside this box with my first Raspberry Pi? I need a second microcontroller to serve the first one??? And they’re both gonna run LINUX??”


Obligitory internet meme

Yes, actually, but not with a second Raspberry Pi because then that would be pretty nuts. We don’t need a second linux machine inside the handheld, that would be major overkill. BUT a second microcontroller that’s not super powerful and has a small physical profile seems like the perfect tool for the job. Am I overcomplicating things? Ummm….. probably. I do this all the time. But on the other hand, do you have a minute to talk about our lord and savior, Arduino???

Arduino Pro Micro

I decided to go with the Arduino because it’s easy and has a really nice ecosystem. This will pay off for me in the software department later. The Arduino companySparkFun* sells something called an Arduino Pro Micro that is really small and has exactly what I need. 1 USB port for interfacing with other devices, about 9 digital IOs, and 9 (9!!!) analog pins. Even better, the Pro Micro is a popular choice for hobbyists making keyboards and stuff so you can get them really cheap if you’re willing to buy from China.

*So it turns out the Arduino Pro Micro is not an official Arduino product, but instead originally designed by SparkFun. Arduino sells another board called the Arduino Micro, which is larger than the Pro Micro but does similar stuff.



I bought these from Amazon, which makes them about $5 a pop, but you can find them on AliExpress for $1-3. You need to be a little careful because sometimes you’ll get a counterfeit that doesn’t work properly and then you’ll be wondering why your Arduino is acting weird. BTW, you don’t have to use an Arduino. You might be able to find a cheaper or more capable microcontroller, like the ESP8266/ESP32 Forseti mentioned.

Here’s the best part about using a separate microcontroller: you know how before I said the direct buttons and IPAC method map to keyboard presses? We can do even better than that.

The HID protocol and HID Devices

This is really important to the whole project. Human Interface Device (HID) is a protocol defined in the 90’s and co-developed by a bunch of huge tech companies to attempt to standardize device drivers. I bet this is the reason why digging around on the internet for drivers is no longer as much of a pain in the rear end as it used to be.

So what does this have to do with us? Almost every kind of modern peripheral that hooks into the computer via USB now adheres to this HID standard. Mouse, keyboard, microphones, game controllers, you name it. And that’s the key to getting the Arduino to work.

If we figure out a way to turn this Arduino into an HID compliant device we can make it appear as a game controller to the host computer. This way, it will work in an operating system the same way that all other game controllers, like Xbox controllers or Dualshock or third party controllers like 8bitdo, work.

This is extremely nice for the following list of reasons:
  • The raspberry pi and retropie packages support this out of the box. Since they already expect you to pair an external controller and have nice configuration interfaces, we can re-use those because the Arduino will appear like any other controller (except it will be permanently bolted into a PCB inside the case).

  • Automatic multiplayer support because of the previous reason. We get whatever Retropie developers did for free. If you want to play Bomberman or Legend of Zelda with your friends on the couch, there’s nothing stopping you. You can pair additional controllers to a Retropie that has our controller connected without worrying about setting up the scripts to handle different cases.

  • It will make any software developers you tell your project about jizz themselves. The entire controller system connects to the Raspberry Pi through a tiny, well defined interface. Something about encapsulation and decoupling functional blocks in a system design blah blah blah

  • I can test this by plugging it into my laptop and use any diagnostic software to check if the buttons are working instead of exclusively having to go through the Raspberry Pi

  • Unlike the direct GPIO button and IPAC methods, we use HID to make this a genuine gamepad instead of keyboard bindings. If you have a game controller and a keyboard connected, we won’t have to worry about any headaches about binding collisions. It is also easier for random software packages you download to support our buttons out of the box because it’s defined as a gamepad instead of masquerading as a keyboard with 15 buttons. Disclaimer: I’ve never actually used the IPAC or direct GPIO binding methods so they may have workarounds for this but I wouldn’t know of them.

This is one of those moments. Maybe it’s just me. It’s probably really obvious to someone who does this a lot.

HID at a High Level

But how do we “add” HID to the Arduino? The basic gist of HID is that it is a protocol (messaging specification) designed to send data over a USB connection. The protocol sends data using specially defined packets called “reports” that each contain (1) a definition and datatype of all the buttons the device contains and (2) the current state (1/pressed or 0/not pressed or some analog value) of each button. Any compliant HID hosts will understand properly formatted reports.

You can read about the HID in spectacular detail from this amazing blog post. I recommend reading it to become familiar with the concepts, but we actually won’t need to know much about the mechanics of the HID protocol because…

...the Arduino ecosystem already has open source HID libraries! Just slam this into the IDE and follow the included examples in the repo, and we’ll have a working game controller in no time. So that’s what I did and will get to in the next post.

HID and How it Relates to the Previous Methods

In retrospect, the microcontroller in the IPAC probably also contains code to make it appear as an HID keyboard. So we’re just doing the same thing but with more steps using the Arduino. The Adafruit GPIO script doesn’t do this, but roughly “emulates” a keyboard device using bash and/or python scripts. Also note that if you choose a non-Arduino microcontroller, you’ll need to track down an implementation of a compatible HID library or roll your own. And that’ll be a whole thing by itself (you’ll definitely need to read the blog post in this case).

One More Thing

I heard that Microsoft even developed some experimental thing where you can send HID messages over an I2C connection. This would actually be good for us because the Raspberry Pi 3A+ only has 1 USB port and I would rather use it as a general purpose USB port instead of having it be consumed by the gamepad. A cursory search doesn’t turn up anything easy to understand so the effort probably fizzled out because no one needed it. If need be I could just get a USB hub to solve the issue, and figure out something when it comes time to fit it all in a case.

Cory Parsnipson fucked around with this message at 23:54 on Dec 8, 2020

Cory Parsnipson
Nov 15, 2015
Remember when you were little and playing with LegoTM and you got to the part in the instructions where they tell you to put aside the existing piece you’re working on and start making a new piece? I was always like, “oh man how do they know to do that”. Well, now is the day when I get to know to do that. I’m putting aside the Raspberry Pi for a bit to work on the controller. This is because the controller will interface with the Raspberry Pi through the USB port, as mentioned before, so we can work on them as separate items until it’s time to integrate them.

Also note, this information can also be used to create a standalone wired game controller, like those crappy third party ones you see on eBay. I won’t be getting to the part where we mount this stuff on a PCB and make a case for it until later though.

How to Make Your Own Custom Game Controller using an Arduino

The Arduino HID library requires you to pick an Arduino module that is compatible with USB development. Meaning that certain Arduino models let the developers mess around with the USB port to send and receive data inside the program loop. Arduinos that use the ATMega32u4 processors (i.e. the Pro Micro) are compatible. The original Arduino (the Arduino Uno) does *not* come with support for this out of the box, but I read that you can add support for it by replacing the firmware with something called “Hoodloader2”.

Additionally, I also read that there are a couple potential problems with using “Hoodloader2”, but I wouldn’t know anything about that since I started with the Pro Micro. The full compatibility list is in the HID repo README file.

Ok, that’s pretty much all the prep out of the way. Here’s my first breadboard prototype:


In progress shot where I have 2 buttons wired up and 2 more to go


4 buttons wired up and testing the connectivity


Full 10 button prototype that I hosed around with for a while

I used these temporary push button switches that came with my arduino starter pack. You might notice that I have the switches in a pull up configuration. I was unaware/forgot/too dumb to make the connection that Arduinos come with built in pull up resistors for each GPIO pin so I didn’t have to make my own. Not a big deal, but I could have saved a bunch of wires and resistors.

Quick Aside about Pull Up Resistors

I think intuitively, one would normally expect that if you press down the switch, you’ll get a digital one signal on the input and when the switch is open it’s a zero. Unfortunately, because of real life physics, getting this arrangement is less reliable than inverting the logic.

With the former arrangement, if the switch is not pushed down, the wires are disconnected. This does not mean the voltage at the input pin is zero--it is an unknown value. The actual values are influenced by interference in the surrounding environment (electro-magnetic, radio, parasitic, etc), I think. In practice, you’ll probably see that it is zero but this is coincidental and not something you can depend on.

With the pullup arrangement, when the switch is released, the input is connected to VCC (5 volts here) via the pull up resistor. When the switch is pressed down, it connects the input to ground (as well as the pull up resistor, so there is some current flowing). In both scenarios, we have a well defined value at the input pin.

Okay Back To The Controller Hardware Prototype

Here’s a crappy video of me using it to try and play Megaman Battle Network 3 Blue:


https://i.imgur.com/vUmfODW.mp4
Click here if you want sound for some reason

I took this quick video to show somebody something, but then I forgot to take a better video and now I don’t have the controller in this form anymore. I should invest in a tripod… :thunk:

For the full experience, I need about 14 buttons to configure everything. I had to leave out the Select and L shoulder button bindings to juuuust fit everything in 9 buttons so that I could play MMBN3. It’s also very uncomfortable and my fingertips were raw after about 30 minutes of trying to figure out how to walk around haha

HID implementation

We need to do some actual programming for once. After the buttons are wired up like above, we need to loop through all the connected GPIO pins and read the value, and then use the HID library to plug in the values into your HID report.

The NicoHood HID repo comes with examples that show you how to use the HID device presets. The library lets you create a keyboard, mouse, gamepad, and even a “surface dial” thingy (this thing) device. You want to use the right template so the library will generate the correct report format and so the host can figure out how to properly categorize your device.


For example, using the Gamepad HID template will show up as a controller in Windows 10. “Wireless Controller” here is a PS4 controller that I’ve connected previously. Neat huh???

Oh, and there is a way to change “Arduino Leonardo” to a custom value but it requires some manual editing to a config file somewhere. I’ll do that at some point. It’ll be a nice touch.

Breaking Down the Gamepad Code

Unfortunately, there’s no documentation and the wiki page for the Gamepad HID template is blank, but the code is straightforward enough to fill in the blanks with some educated guessing. Peep this file, yo.

The first interesting part is the Gamepad.begin() line. You need to put this in your setup function to initialize the Gamepad template. The Gamepad class/struct is defined as part of the HID library. You would use the corresponding Keyboard or Mouse structs to use those templates.

code:
void setup() {
  pinMode(pinLed, OUTPUT);
  pinMode(pinButton, INPUT_PULLUP);

  // Sends a clean report to the host. This is important on any Arduino type.
  Gamepad.begin();
}
Secondly, the example uses “pinButton” that will loop through some predetermined code to “press” all the Gamepad buttons and move the dPads and thumbsticks programmatically. That might be confusing for a little bit.

code:
Gamepad.press(count);
Gamepad.release(count); // added by me, this is not in the example code
Further down you see that the example counts from 0 to 31 and will eventually press each button individually. This is done through the press() and release() Gamepad member functions. A button is any 2-state (digital) button on a gamepad. This may be things like “ABXY”, Start, Select, shoulder buttons, and even the thumbstick R3 and L3 (or whatever they’re called on a non PlayStation controller). Sometimes, like on the Nintendo Switch and 3ds, the triggers are digital and not analog. So if that’s the case, they would be mapped to buttons as well.

By default the HID library’s Gamepad template has 32 hardcoded buttons. If you don’t need all 32, just don’t connect them. If you need more, you’re gonna have to make a custom Gamepad template. I’m good for now, so I’m not gonna delve into that.

code:
// Move x/y Axis to a new position (16bit)
Gamepad.xAxis(random(0xFFFF));
Gamepad.yAxis(random(0xFFFF));

// Move other thumbstick (added by me, not in example code)
Gamepad.rxAxis(random(0xFFFF));
Gamepad.ryAxis(random(0xFFFF));
From the definition, I can see that there are 2 thumbsticks and you refer to them through different axes. One thumbstick (presumably the “left” one?) has two 16-bit values called xAxis and yAxis, with a third 8-bit value for zAxis. Typical controllers will only use the x and y axes, but if you want something like an accelerometer, you would use the z axis, I guess. The second thumbstick (presumably the “right” one?) has the same values, but labeled rxAxis, ryAxis, and rzAxis. I’ll get to this in the next update.

The dPad is special for some reason. Probably just copying how a lot of companies implemented it. There are 2 dPads for some reason and we will only need one.

code:
// Go through all dPad positions
// values: 0-8 (0==centered)
static uint8_t dpad1 = GAMEPAD_DPAD_CENTERED;
Gamepad.dPad1(dpad1++);
if (dpad1 > GAMEPAD_DPAD_UP_LEFT)
  dpad1 = GAMEPAD_DPAD_CENTERED;
I didn’t use this in my own code by accident… I should have read this more closely. Instead I bound the dPad buttons like all the other buttons. Retropie doesn’t care because you use a utility to define your button mappings anyway, but it would be good to fix this so it’ll be compatible with other things. Like if, for some reason, I want to use my garbage controller with Steam or something.


The configuration wizard in Retropie. You press corresponding buttons (right) to map to Retropie functions (left).

code:
// Functions above only set the values.
// This writes the report to the host.
Gamepad.write();
This is also important. Use this function to send the HID report to the host machine once you’ve made all your updates.

Breaking Down the Code that I Wrote

Here’s my repo with the full example code.

There’s some other stuff in here, but basically I just loop through all the GPIO pins I hooked up and do a digital read and then call press() or release().

code:
for (int idx = 0; idx < NUM_BUTTONS; ++idx) {
    int button = idx + 1;
    int pinIsHigh = digitalRead(idx2Pin[idx]);
    // …
}

if (pinIsHigh) {
  if (!getPressed(button)) {
    setPressed(button, true);
    Gamepad.press(button);
  }
} else {
  setPressed(button, false);
  Gamepad.release(button);
}
Uhhhh... heh :sweatdrop:, I don’t remember why I was keeping track of the button states with getPressed() and setPressed(). I think I was using it to print out diagnostic messages and I forgot to delete it. I’ll clean it up in the next iteration.

The rest of the stuff in there is dealing with the thumbsticks, so you can ignore that for now. This should be everything we need to make the controller act like in the video I posted above.

Testing the Gamepad

In Windows, you can go to the control panel and right click on the Gamepad icon under “Devices” and then click “Gamepad Settings”. This will open a utility that lets you calibrate the gamepad and view which buttons are being pressed.


https://i.imgur.com/w3J6YOH.mp4

For people in Linux, there are a couple utilities. One that I think is nice is called evdev-joystick



This shows analog axes for thumbsticks and underneath is a display for the state of buttons like in the Windows utility. Testing in linux is not as simple as using the utility like in WIndows. More on this in the next update.

Cory Parsnipson fucked around with this message at 22:06 on Dec 16, 2020

Cory Parsnipson
Nov 15, 2015
I Added a Thumbstick to the Arduino Controller

First, the final product:



https://i.imgur.com/2NOOqdS.mp4
You have no idea how good this feels after using the push buttons.

This was both more complicated and simpler than I thought it would be. To be more specific, the hardware was simpler than I thought it was going to be but the software was kind of a pain in the rear end to get right. Working on the thumbsticks turned out to be pretty fun and cool though, because I learned a lot about something I have no idea about.

The first problem is to figure out what everything is called. To this end, the easiest place to start is to google 3DS replacement parts and DIY repair videos. For whatever reason, the part of the thumbstick that contains the electrical and mechanical components is called a “thumbstick module”.


A 3DS thumbstick module. There is supposed to be a grey thumbstick cap that clicks into place on top of this for that recognizable rubber circle look. More on this later.

“Thumbstick module” seems to be a general term for this entire class of parts. For example, here’s a DualShock 4 thumbstick module:


I found a picture with the thumbstick attached on top so you can recognize what it is.

I initially did research into how the circle pad works. It’s a real curiosity, especially since the “slider” type of thumbstick isn’t really in favor anymore. Then I also looked into the Nintendo Switch thumbsticks, after I noticed it seemed really popular with hardware modders for some reason.

How a 3DS Circle Pad Works

When I first used a circle pad on the 3DS I thought it was quite uncomfortable. But it’s really grown on me and I was hoping to include two of these into my RPI shell. I suspect it’s not as popular as a “real” thumbstick, and if anyone reading this has an opinion about 3DS circle pad vs Switch thumbstick I’d be interested in hearing it.

Take a look at this:

Here’s an exploded view of the 3DS circle pad parts.

Yes... there are two separate dust rings and they’re actually completely different parts. I couldn’t really depict this in the illustration, but the outer dust ring actually fits right under the 3DS case and above the lower notched ridge of the thumbstick cap. You can actually see this on the 3DS if you move the thumbstick to one of the extremes. It’s the shiny black plastic film looking thing that is in the gap. This is needed to prevent dirt and grime from getting into the thumbstick column. The inner dust ring sits on top of the thumbstick module and between the bottom of the thumbstick cap. This “washer” is here to make sure the movement between these two parts feels smooth. I did a little searching hoping to see if I could skimp on the dust rings but it looks like that’s not possible as they both have distinct purposes.

Some repair videos and tutorials were really helpful for seeing how everything fits together, especially the super secret technique for fitting the cap through the case hole.

I managed to find really affordable dust rings in large quantities on Aliexpress and it was really hard to find the right keywords for these things, so I’ll link them here:
Outer dust ring
Inner washer ring

I also bought a pair of caps here. I bought from Amazon here, but this was before I resorted to AliExpress, which seems to have better deals. As with anything associated with Nintendo, the parts are really expensive (some going for, like $8 each???) and it’s a pain to buy these because a lot of sellers are making crappy knock-offs.

Thumbstick modules are similarly priced, around $8 - $15, and I’m trying to get the new 3DS parts and not the older models, so you have to be careful there. Expensive, right????

How a Nintendo Switch Thumbstick Works

The Nintendo Switch thumbstick, by comparison, feels like you can buy a single piece and have everything ready to go. It’s clear that Nintendo did a ton of R&D for their crown jewel console and it shows. Not only that but Nintendo Switch replacement parts seem to be cheap and plentiful. I think this is because of how well the Switch is selling and also because they chose to make the design out of inexpensive parts.


This diagram almost feels unnecessary.

Everything is pretty much self explanatory. The gasket does the same thing as the dust ring, but doesn’t seem super necessary.

Buying replacement thumbsticks is muuuch easier. I bought a pack of 4 off Amazon for about $5 each, but on AliExpress you can get some for $2 per. For the whole mechanism! No wonder this is really popular with modders.

I Thought You Were Gonna Use a 3DS Circle Pad???

Yeah that was the plan. I like how the circle pad is designed to be compact and it looks cool, but building a circle pad presented quite a few problems for me.

First of all the most important part is that the circle pad lacks the “click in” button that true analog sticks have. The Nintendo Switch thumbstick and PlayStation thumbsticks both let you press down on the stick and have it click down. This would be a problem if you need to use L3 and R3 in some games. Secondly, the 3DS thumbstick requires so many more parts and not only that but they are more expensive since they are specific to the 3DS. All the parts together can run you up to $20 per thumbstick if you’re not careful! Third, the 3DS thumbstick requires some careful attention to the design of the case. There is a specific size of the through hole that the cap needs to sit inside as well as a constraint on the thickness of the casing so you can fit it in between the ridges of the thumbstick cap. I haven’t even gotten to worrying about the case yet, but this is just making things more complicated for myself.

By comparison, the Switch thumbstick is a cheaper all-in-one package. However, there is one concern and that’s JoyCon drift. I don’t know how it works and what causes it, and neither apparently does anyone else, but from what I’ve read it looks like it could be a problem with the thumbstick design itself. Personally, the ones I bought are just fine, but I’ve barely used them. Just something to watch out for in the future.

Actually I just did another search and now they have “3rd generation” thumbstick replacements that allegedly solve the drifting issue? So I guess go with those. They are slightly more expensive, but probably worth it if the drift is no longer an issue.

That said, I do have all the parts for making two whole 3DS circle pads, but I guess they’ll have to sit in my box for a different project. Or I can try to make a version of the shell with the circle pads just to see what it would be like… :thunk:

Cory Parsnipson
Nov 15, 2015
Connecting the Thumbstick to the Arduino Controller

Ok, let’s go back to the thumbstick module. Now that we’ve figured out how the hardware works, we need to figure out how to interface with this thing.



There’s a small, striped ribbon coming out of the 3DS thumbstick module. The next step is to figure out how to connect this to a breadboard for prototyping. I had no idea what this was called, so I looked up pictures of open 3DS’s and Switches and went on some forums to do some pointing and grunting.


It me.


Point and grunt.

They told me to look into “FPC’s”.

So it turns out this ribbon cable is called a “flat flexible cable” (FFC) and it hooks into a “fine pitch connector” (FPC). The specific type of connector in the Switch picture is a “zero insertion force” (ZIF) connector, meaning that you don’t need to shove the cable into the socket like a NES cartridge, but simply place it gently in and lock it in place with the connector’s hinge. I’ve seen people also call this a “ribbon lock”, which appears to be a more general, colloquial term for it.

This thread has someone who did the hard work of measuring the cable and estimating the specs. It’s important to figure out the right pitch (spacing between wires) of the FPC and ZIF connectors. It turns out the 3DS and Nintendo Switch thumbsticks are 0.5mm pitch. The next important attribute is to figure out how many “positions” you need in the connector. This is just a way of saying how many wires are in the cable. For the Switch, there are 5 positions (see below), and for the 3DS, there are 4 positions. The discrepancy is because the Switch thumbstick can be pressed down and used as a push button. The extra wire is for this signal.

I managed to find the exact part or something extremely close to the Switch connector on digikey. And I also picked up some connectors for the 3DS too.

An invaluable forum post here describes the pinout for the Nintendo Switch thumbstick:


Five wires, nice and simple. “U/D” (up/down) must be the y axis, while “L/R” (left/right) is the x axis. “BTN” is the wire for the click down function I mentioned before.

Since the connector is a surface mount part, I also needed to buy or make a breakout board for this. I bought these off Amazon.



They’ve done the job quite nicely.





You can see the ZIF connector for the Switch thumbstick up close here. The black part is actually a hinge that swings open. You put the Switch FFC into the connector and then close the black hinge on top and if you do it right, the cable is stuck in there.



I added some quick release wires after soldering in the male pin header to the through holes.



Here I mounted the thumbstick and breakout board to a padded manila envelope.



I connected the whole contraption to the proper Arduino pins in my controller. Ok here’s the funny part… The ZIF connector I got actually says the contacts are on the bottom of the connector, so this meant that I needed to flip the cable around. You’ll notice the breakout board is now upside down relative to the thumbstick. This is actually how it is in the real JoyCon as well. This took about a day of debugging while writing the software additions to figure out why no signal was coming through.



I moved onto corrugated cardboard because the manila envelope was too flimsy. This is much better.



And here’s the shot again of the final contraption attached to the Arduino controller. I think I’ve unintentionally re-invented the Wiimote and nunchuck layout. I thought it felt familiar playing games like this.

Next up is going to be a section describing the trials and tribulations of getting the thumbstick software to work. I hope I'm not boring everyone to death...

StupidSexyMothman
Aug 9, 2010

This is a fascinating look into joysticks.

I really like the ribbon cable design for the low-profile thumbsticks but I'm sure a large part of that is my aforementioned inability to solder. It almost feels designed to fail considering how fast the actual stick replacement is once you get the controller disassembled.

Cory Parsnipson
Nov 15, 2015

oldskool posted:

This is a fascinating look into joysticks.

I really like the ribbon cable design for the low-profile thumbsticks but I'm sure a large part of that is my aforementioned inability to solder. It almost feels designed to fail considering how fast the actual stick replacement is once you get the controller disassembled.

That's a good point. I guess you could argue that Nintendo went too cheap on the Switch thumbsticks, especially if they're getting sued twice. The ribbon locks are pretty standard. Inside a 3DS, you'll see the SD card reader, LCD screen, and shoulder buttons are also attached this way. The more detachable components the better for repairs.

=============================

On an unrelated note, this is embarrassing... I was looking at my code again to rewrite it and I've completely confused myself over how the push buttons are working.

code:
int pinIsHigh = digitalRead(idx2Pin[idx]);

if (pinIsHigh) {
  if (!getPressed(button)) {
    setPressed(button, true);
    Gamepad.press(button);
  }
} else {
  setPressed(button, false);
  Gamepad.release(button);
}
This part of the code implies that when I press the buttons, the input receives a high signal and when the buttons aren't pressed, the inputs are low. This is the opposite of how the pull up resistor set up should work. In my circuit, I put the pull up resistors in the wrong place:



The resistors here are connecting the wrong side of the switch, so when the switch is open, the input is being pulled to ground*. I think when the switch is closed, the connection to VCC pulls the input high, which the pull up wasn't able to do because it's not a strong voltage source. (Not super sure about this part) So I guess when it's working here, it's working by accident. :saddowns: I mean if it works the way I'm explaining it, then it's a perfectly fine way of doing things but not what I thought I was building. I'm going over all of this again and rewired the controller to use charlieplexing (update coming soon) and using the built in pull ups properly so hopefully I'll get it right the next time around...

*the switches are confusing. They each have 4 contacts. The contacts on the same side are disconnected when the switch is open. But corresponding contacts on opposite sides are always connected. (e.g. the blue wire and the resistor on the bottom most switch are always connected)

Cory Parsnipson fucked around with this message at 22:39 on Dec 17, 2020

Forseti
May 26, 2001
To the lovenasium!
Yeah, you have pull-down resistors there and they'll work as you described. The two pins that are always connected are common (the wiring inside looks like an H where the middle bar is a switch) so that wiring is a bit confusing because you have to know how the switch looks inside to know that, but is electrically valid. Do you mean you have the pull-up resistors in the MCU turned on as well and they're fighting with the the pull down? That would make a voltage divider so technically when the switch is open it would be somewhere between ground and Vcc but if the resistor values are far apart that might not be apparent. When closed, there's no resistance to Vcc so it'll win or destroy something trying (ie: don't press a button if the pin happens to be set to output mode and low).

Adbot
ADBOT LOVES YOU

Cory Parsnipson
Nov 15, 2015

Forseti posted:

Yeah, you have pull-down resistors there and they'll work as you described. The two pins that are always connected are common (the wiring inside looks like an H where the middle bar is a switch) so that wiring is a bit confusing because you have to know how the switch looks inside to know that, but is electrically valid. Do you mean you have the pull-up resistors in the MCU turned on as well and they're fighting with the the pull down? That would make a voltage divider so technically when the switch is open it would be somewhere between ground and Vcc but if the resistor values are far apart that might not be apparent. When closed, there's no resistance to Vcc so it'll win or destroy something trying (ie: don't press a button if the pin happens to be set to output mode and low).

Indeed, they are pull down resistors. I looked it up, that's good to know. And yes, I did have the pull up resistors in the MCU turned on too. By some cosmic chance, I narrowly avoided breaking my Arduino. Somehow all my mistakes cancelled themselves out and ended up working. Here's the circuit:



The pull up resistor is connected directly to the pull down resistor. When it's open it forms a voltage divider like you said. It says online that the internal pull up resistors are supposed to be around 30k-50k ohms and I was using 1k ohm resistors except for the last two buttons where I used 2k resistors because I ran out. So the voltage divider is around 0.1 or 0.3V and is rounded down to zero by the firmware. When I close the switch, I'm bypassing the pull up resistor so now the full 5 volts goes across the lower resistor.

Modifying the Arduino Code to use the Thumbstick

Ok, time to revisit the controller code and add the handler for the thumbstick. I’ll be referencing the same repository as last time.

Remember this?

Cory Parsnipson posted:

code:
// Move x/y Axis to a new position (16bit)
Gamepad.xAxis(random(0xFFFF));
Gamepad.yAxis(random(0xFFFF));

// Move other thumbstick (added by me, not in example code)
Gamepad.rxAxis(random(0xFFFF));
Gamepad.ryAxis(random(0xFFFF));
From the definition, I can see that there are 2 thumbsticks and you refer to them through different axes. One thumbstick (presumably the “left” one?) has two 16-bit values called xAxis and yAxis, with a third 8-bit value for zAxis. Typical controllers will only use the x and y axes, but if you want something like an accelerometer, you would use the z axis, I guess. The second thumbstick (presumably the “right” one?) has the same values, but labeled rxAxis, ryAxis, and rzAxis. I’ll get to this in the next update.

Now we get to call those functions using the outputs of the Switch thumbstick. The U/D pin goes to y axis and the L/R pin goes into the x axis.



If we look at the pinout of the Pro Micro, pins 18 and 19 are the first two analog inputs. We need to use those and configure them in the software to expect an analog signal. Aside from VCC and GND, I also plugged the BTN signal of the Switch thumbstick arbitrarily into pin 14, a dedicated digital pin. They are defined in the code like this:

code:
const int pinL3 = 14;

const int pinLXAxis = A1;
const int pinLYAxis = A0;


Posting this image a third time… Focusing on the long wires between the thumbstick and the arduino. You can’t identify the exact pins using this picture, but hopefully you can see that in the GPIO diagram, the pins are clustered on the right side of the microcontroller and that’s exactly what I did IRL.

code:
void setup() {
  // ...
  pinMode(pinL3, INPUT);
  digitalWrite(pinL3, HIGH);

  digitalWrite(pinLXAxis, LOW);
  digitalWrite(pinLYAxis, LOW);
}
During setup, I change the L3 pin into an input and write a HIGH value to it (because the BTN signal is high when not pressed and low when pressed). The analog pins are written LOW because we expect them to range between -32767 and 32767 and assume a value of zero when the stick is centered. The Arduino forums say that you don’t need to set the pinMode for analog pins, you just do an analogRead() on them.

Then in the main loop, I treat the L3 button exactly like the other buttons, since the button on the Switch thumbstick will be pulled to ground when the thumbstick is pressed in.

code:
void loop() {
  for (int idx = 0; idx < NUM_BUTTONS; ++idx) {
    int button = idx + 1;
    int pinIsHigh = digitalRead(idx2Pin[idx]);

    if (pinIsHigh) {
      setPressed(button, false);
      Gamepad.release(button);
    } else {
      if (!getPressed(button)) {
        setPressed(button, true);
        Gamepad.press(button);
      }
    }

  //...
}
Finally, I use analogRead() to get the analog value of the thumbstick positions and write the Gamepad’s xAxis and yAxis values after doing a bunch of processing on it. Why so much code? Well, the rest of this post will be dedicated to explaining it.

code:
void loop() {
  // ...

  // analog axes
  int readX = analogRead(pinLXAxis); // store in temporary variables to use in constrain()
  int readY = analogRead(pinLYAxis);

  const int XMIN = 200, XMAX = 880;
  const int YMIN = 125, YMAX = 835;

  // clamp values to observed joystick values
  readX = constrain(readX, XMIN, XMAX);
  readY = constrain(readY, YMIN, YMAX);
    
  Gamepad.xAxis(map(readX, XMIN, XMAX, -32767, 32767)); 
  Gamepad.yAxis(map(readY, YMIN, YMAX, 32767, -32767)); // flip Y axis because I have it oriented upside-down

  Gamepad.write();
}
Testing the Code Using Windows Control Panel

I need to go back to the Windows control panel, and view game controller properties.


https://i.imgur.com/5bb8Fq4.mp4

This time, we need to click the “Settings” tab and then “Calibrate”. This window will let you see the raw values of the analog stick. In this gif, I modified the code above to write the raw values from analogRead() directly to the HID report.



The neutral position for this stick appears to be (509, 509). If I move the thumbstick around, I can observe what range of values the thumbstick will give us. The x axis range is approximately from 237 to 868 and the y axis range is approximately from 134 to 823. This is when the stick is being driven at 5V. I don’t know what a real Switch is calibrated to, but the reverse engineering wiki implies that the thumbstick is driven at 1.8V so the range will be different in that case. These are very random numbers. We want to remap these numbers to less random numbers, -32767 and 32767, which are the min and max you can express with a 16 bit value a.k.a. the size of the axis value storage.

code:
const int XMIN = 200, XMAX = 880;
const int YMIN = 125, YMAX = 835;

// clamp values to observed joystick values
readX = constrain(readX, XMIN, XMAX);
readY = constrain(readY, YMIN, YMAX);
The first step is to clamp the values to the observed min and max. I put in some leeway in case the thumbstick can go a little further. The values are really “jittery” in the windows calibration screen and seem to vary between individual thumbstick units and over time in the same unit as well. This could be a problem which I think is related to how cheaply manufactured the thumbsticks are.

code:
Gamepad.xAxis(map(readX, XMIN, XMAX, -32767, 32767)); 
Gamepad.yAxis(map(readY, YMIN, YMAX, 32767, -32767)); // flip Y axis because I have it oriented upside-down
Arduino comes with a built in map() function that lets you take in a number and transform it to a different interval. Also I flip the y axis because I have the thumbstick oriented upside-down in my cardboard contraption.

Viewing Joystick input on the Raspberry Pi with sdl2-jstest

I wanna note that the previous code is the final product, but before that, I only had the analogRead() lines in the code. At this point, I could see everything working in windows, but when I went to plug the controller into the Raspberry Pi, the analog stick wasn’t doing anything. The configuration wizard refused to map the analog stick movement to any buttons. It was difficult to figure out what was going wrong here because I didn’t know anything about how RetroPie works internally. I looked through some of the relevant configuration files and then annoyed one of the moderators for answers.

I was told to use jstest (and jscal) to try and diagnose the problem. These are joystick utilities that come preinstalled with the RetroPie image.

code:
jstest /dev/input/js0
Using this command, I could see stuff was happening when I moved the joystick:

code:
Driver version is 2.1.0.
Joystick (Arduino LLC Arduino Leonardo) has 10 axes (X, Y, Z, Rx, Ry, Rz, Hat0X, Hat0Y, Hat1X, Hat1Y)
and 32 buttons (Trigger, ThumbBtn, ThumbBtn2, TopBtn, TopBtn2, PinkieBtn, BaseBtn, BaseBtn2, BaseBtn3, BaseBtn4, BaseBtn5, BaseBtn6, ?, ?, ?, BtnDead, (null), (null), ...
Testing ... (interrupt to exit)

Axes:  0:     0  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0:     0  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0:     0  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0:  4599  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0:  8453  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0: 12310  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0: 15203  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0: 19830  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0: 25227  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0: 32455  1:   -15  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Axes:  0: 32455  1:     0  2:     0  3:     0  4:     0  5:     0  6:     0  7:     0  8:     0  9:     0
Initially I'd picked an arbitrary range of 0 to 16384 for the axis values. I dunno why, it just seemed like a good starting point. I read on Wikipedia that old joysticks used whatever range they felt like and then you had to calibrate them to whatever computer you were using. The wiki article also says that modern joysticks seemed to have settled on the full 16-bit range of +/- 32767 (a wise decision imo), so this means that my joystick's full range covered only neutral to half tilt. If RetroPie is expecting the full range, then it makes sense why none of the thumbstick movement registered as anything. I used jscal to recalibrate the joystick to use the full +/- 32767 range which is what you are seeing in the above code snippet. Unfortunately, this did not change my situation at all.

The mods over there told me to use the sdl2-jstest instead of the built in jstest. Not familiar with SDL or how the graphics library stuff works internally, but they said RetroPie uses SDL2 so it’s better to use this program. You have to install it yourself though.

Run jstest /dev/input/js0 and you’ll see something like this:



The numbers here are not the full range. This was where I’d say I had a lot of trouble because I spent a while wondering why the joystick still wasn’t doing anything even after I calibrated it. This is why the mods said to use sdl2-jstest! The other utility and its calibration is not being used by RetroPie. This is when I went back to the Arduino firmware and expanded the range from +/- 16384 to +/- 32767 to avoid having to deal with the calibration headaches. (Seriously, not only would I have had to calibrate it, but then store the calibration output into a file and set up a bash script that would run every time on startup to restore the calibration coefficients. Absolutely avoid this if you can.)



This is what I got afterwards. This is what happens when your initial min and max values are too small. The thumbstick output overflows and wraps around like a Mario Bros level.



Only after I added some leeway and clamped the output to my initial range, was the joystick behaving the way I wanted. And finally, the configuration wizard and RetroPie UI was recognizing my thumbstick inputs. Whew, this part actually took a couple weeks going over my entire setup, looking into RetroPie documentation and config files, and then finally asking for help on the forums. I feel like there were quite a few undocumented gotcha’s where I had to put together unintuitive pieces of information that might have been common knowledge for someone who was familiar with the whole software package. Delving into RetroPie was difficult.

Anyway, it felt super good to have it working. Me using it again for posterity:

https://i.imgur.com/2NOOqdS.mp4

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply