Microcontrollers: G8R, A Code Walkthrough!
Hey, everyone! This video is long overdue, and I apologize for the delay. I’ve been away tending to family obligations abroad and unfortunately, I managed to kill my laptop while away. My laptop has now been fixed but all the material I had already prepared was lost so I am re-recording this for you all. I’m going to do things a little bit differently since the first time around it was more than I could chew. So this time, we’ll focus ONLY on the software, and I will create a separate video on each of the mode’s functionality. OK, without further ado, let’s dive right in.
A quick recap!
So if you’ve been following along you’ll know that G8R is a microcontroller-based module which focuses on Gate and Trigger events. It has support for MIDI I/O, 8 Gate outputs, 4 CV or Gate inputs, and some buttons and an encoder for user interactions. If you missed the previous video just check the links in the description as I go into details on the hardware bits there.
Design decisions!
For the most part, G8R is a rewrite of a previous project of mine by the same name. The original work was done on a Teensy using the Arduino IDE. For this version, I wanted to make the project capable of much more so the first thing I did was ditch the Arduino IDE in favor of PlatformIO. If you are curious about the reasons why, just watch the first Microcontrollers series video as I go into detail on that topic there. The next big thing is that I wanted to utilize more advanced programming paradigms so I decided to create objects for the various parts of the program which led to the layout of the code. Do keep in mind that while I had these thoughts in mind, the code did evolve somewhat organically and several refactors needed to happen to get to the current state.
Walkthrough
I feel the best way to understand an Arduino sketch is to start with the main.cpp. In here you’ll find definitions for the various hardware bits, declarations for objects used throughout the sketch, and most importantly the main loop(). So let’s break things down a bit. When it comes to the hardware, we will not be accessing the pins themselves. In fact, we won’t interact with the superclasses using the pins. Instead, I’ve created a series of objects for accessing the hardware. Here are a few highlights to help clarify:
Gates: This object is used to manage the 8 outputs of the module.
LEDController: The LEDs object is used for the 8 LEDs which are associated with the 8 Gates, however, we don’t use this object directly. Instead, we’ll use LEDController, which is a convenience object for controlling the LEDs, as well as the 9th LED named TEMPO_LED.
Encoder: This object is responsible for all encoder functions, this includes rotation as well as button pushes.
ResetButton: This is the “LED Button” also known as the reset button or tempo LED. This object is used to handle the button pushes and gestures for that button only. It works similar to the Encoder Button in order to keep things simple.
InputHandler: This object provides a means for accessing the 4 CV/Gate inputs on the module.
EurorackClock: This object is probably the most complicated in the sketch. It probably should be refactored and simplified as it does quite a bit. First, it provides us with an internal clock. It also provides us with functionality for setting a tempo, setting clock divisions, handling external clock sources, etc. It works in conjunction with the MIDI Clock too.
midi: This object is used to handle MIDI. We use the FortySevenEffects library here and this is an instance of that.
ModeSelector: Is a singleton whose responsibility is to manage the currently running application. It works by utilizing the “State Pattern”. In the setup function, we’ll pass a series of “mode” objects which are our apps, then at runtime, we’ll simply update the currentMode pointer to the running application. More on this later.
StateManager: This object is responsible for saving and retrieving application state data to and from the EEPROM.
Modes: There are currently 4 modes: Divisions, MIDILearn, Inverse, Logic. Let’s talk about modes next…
Mode Objects
The implementation of modes could be improved, however, it works so maybe something for the future. 😉 The key thing to know about the mode is each mode has setup() and teardown() methods. These methods run exactly one time whenever we change modes. We use these methods to enable and disable functionality that may interfere with other running modes. These are things like, setting up the MIDI callback functions, resetting the LEDs etc.
In addition to the setup() and teardown() functions, each Mode class also needs to implement an update() method which will be called on every loop() cycle. Here is where the various handlers are called, things like handleEncoderRotation(), handleButtonPress() etc. Each Mode will implement the things relevant to that mode.
If you are looking to create a new mode, the best thing to do is start with a mode close to what you have in mind then remove unneeded pieces and work your way back that way. It’s not ideal but it has worked for me so far.
setup() and loop()
Not much to say about setup(), it’s your typical setup() approach as per the Arduino framework. Here you’ll do things needed at the sketch like initializing pins for read or write, configuring MIDI, initializing objects etc.
The loop() has been kept small on purpose. In fact, the only thing we are doing is running update() and other than that modeSelector is just listening for long encoder button presses. When one of those is encountered modeSelector will go into isInModeSelection() and a couple of things will happen: 1) stateManager will save to EEPROM, 2) encoder direction will be used to select a new mode. When a new mode has been selected long pressing will: 1) Set the new mode, 2) run teardown() on the previousMode, 3) run setup() on the newMode, 4) Save to EEPROM once more, note that we also read from EEPROM on setup().
Final Thoughts
I know I’ve just barely lifted the covers here. We haven’t gone too deep into the code. There are quite a bit of objects being used and we didn’t really go into each of them. In addition to making generous use of inline comments I’ve also configured Doxygen on the repo and created a GitHub Action that updates it on a successful merge to the main branch. The output is then converted to a PDF document where you’ll find Doxygen generated documentation for this project. The file can be found in /docs/G8R.pdf.
I hope this has been useful, I will create some demo videos next. Lastly, I have some extra boards so if you plan on making these why not purchase them from me, this way you’ll be supporting the channel and ensuring more projects to come!
Until next time! Stay creative and Happy coding! See U L8R, G8R!
Resources
- Code GitHub Repo: https://github.com/juanlittledevil/g8r_32
- Hardware Repo: Comming Soon - need to fix a couple of things first ;)
- YouTube Channel: https://www.youtube.com/@scruffycatstudios