Push yourself because, no one else is going to do it for you😊 . . DM any of your fitnes queries. . #biceps #instagram #hypertrophy #hypertrophytraining #active #activepureness #activitytracker #activityboard #board #shell #skinglow #skincare #eyeshadow #eye #instagood #insta #instalike #instaart #instacool #instatrain (at The Wellness Club Gym N Spa) https://www.instagram.com/p/B7EGkYylQGb/?igshid=iq35swp4u0t9
It has been almost a year since I started the activity board project. So it is time to wrap it up. As said in the last activity board blog post, the only thing left to do was to finish the interface ...
But before I work on the interface, there was one hardware part I needed to add: SOUND!
The Raspberry Pi has a headphone jack which allows you to connect a headphone. But because I wanted a bit more volume, I needed to incorporate an amplifier. Luckily Adafruit produces a very nice speaker bonnet with integrated amplifier.
I wanted to connect some sleek looking enclosed speakers. But since the AliExpress reseller had some issues shipping them, I took the easy route and connected an old satelite speaker form a home cinema set. I must say it sounds nice and warm.
And with the temporaty speaker in place (which will obviously will be the permanent solution), the hardware part is done.
Installing the Speaker Bonnet took some time. But luckily Adafruit has a lot of awesome documentation available. One issue I couldn't solve in the configuration, is the occasional plopping sound whenever a sound starts or stops. I used a simple hack, by continuously playing a soundclip with sounds above the 20khz (This means I can't hear this sound, and might scare away some stray neighbour cats as an added bonus).
And then we finally arrive at the last item on our to do list: Enzo's Interface. To make the system modular and future proof, I added an interface selector which allows me to switch between multiple interfaces. This way I can add new interfaces when he grows older. The interface selector is controlled with the rotary encoder and activated with a secret key combination.
For Enzo's interface I opted to recreate a mobile interface which allows him to 'contact' all of his TV friends.
By rotating the rotary encoder he can select the friend he wants to contact, and by pressing the illuminated arcade buttons, he can 'call', 'message' or 'facetime' the selected friend.
When one of the illuminated buttons is pressed, a small sound clip of the corresponding cartoon is played.
The only thing left to do, is to hand over the controls to Enzo. Enjoy little buddy! I really hope you love it as much as I love you.
PS. If you want to take a peek at the software, check out the repository on GitHub.
Read all posts in this series:
Part 1: Enzo’s Control Room
Part 2:Â Building the Box
Part 3:Â Fire up the Lasers!
Part 4:Â Spray Away!
Part 5:Â Push the button!
Part 6:Â Assembling the panel
Part 7:Â The dial on the board goes round and round
Part 8:Â Take control!
Part 9: It’s all about the code!
Part 10:Â Bake me some Pi!
Part 11:Â The Final Touch
Now that most of the firmware is all set an done, it's time to start working on the final part of the Activity Board: the Raspberry Pi display interface.
The activity board contains a simple 800x480 pixel HDMI display, so after simply connecting an Raspberry Pi I was quickly able to get my favorite Pi application running. The MagicMirror² framework!
Of course, this wasn't the type of information I want to display on the screen. But since the MagicMirror² is extremely robust and easy to set up, it is the perfect package to run the application on the activity board's display.
As a small test (and mainly as a placeholder) I quikly trew together a small configuration which looked cool. In the mean time I could start working on something more advanced.
Of course, I wanted to control the interface with all the different input options from the activity board, so I had to connect the Arduino and Raspberry Pi together. The simple wire displayed above was enough to let the two core components communicate over RS232 (Serial).
To read out the info the data the Arduino sends, I use the Node Serialport package. It took some head scratching to get it up and running, since I had to recompile it to work with Electron, but after that, it was pretty easy to set up the communication.
A MagicMirror's Node Helper sends the received serial data to a specially crafted MagicMirror² module.
The image above is captured over VNC. Isn't it awesome I can make a remote desktop connection to one of my kid's toys?!
The futuristic looking debug app allows me to test the communication. And is a perfect place holder to keep my son busy for a while ...
The module leverages the power of Vue.js to quickly develop the interface. It took some fiddling to get a Vue.js working as a MagicMirror module, but is does show the flexibility of the MagicMirror² core. If you're interested in how it's all tied together, check out the module's repository.
And with this interface, the technology of the activity board is fully tested. The only thing left is the development of an addictive interface which keeps my son busy while I'm tinkering behind my desk. Perfect parenting!
Read all posts in this series:
Part 1: Enzo’s Control Room
Part 2: Building the Box
Part 3: Fire up the Lasers!
Part 4: Spray Away!
Part 5: Push the button!
Part 6: Assembling the panel
Part 7: The dial on the board goes round and round
Part 8: Take control!
Part 9: It’s all about the code!
Part 10: Bake me some Pi!
Part 11: The Final Touch
With the activity board controller finally in place and tested. It's time to throw together the actual firmware. Let's fire up VSCode!
VSCode has been my code editor of choice for quite a while now. Microsoft did a great job in developing a light weight but super powerful code editor. And with the advent of a PlatformIO VSCode extension, this makes for THE perfect Arduino IDE.
That being said, It's time to start working on the final firmware. Or actually: the final firmware for now. Because the Activity Board will probably be a project which will receive some (software) updates over time.
All the board's functionality will be seperated into a bunch of controllers. There is no particular reason why I called them controllers, It just sounds like I know what I'm doing. For now, the code consists of the following 5 controller classes:
InputController: Responsible for reading all the switch states by communicating with the MCP23017 over I2C.
SevenSegmentController: Controls the 7-segment display by communicating with the MAX7219 seven segment display.
NeopixelController: Controls all the WS2812B RGB-LEDs using the FastLED library.
LedController: Controls all the regular LEDs (incorporated in some of the buttons) using the Arduino GPIO pins.
CommunicationController: Sends JSON commands (like the buttons state updates) to a Raspberry Pi using the ArduinoJson library.
All of the controller classes have a setup() method which is called in the main.cpp setup routine, and most of the controllers have an update() method which is being called during the main run loop.
All of the update() methods are non blocking, to make sure the Activity Board stays responsive. Any necessary delays are implemented by using the elapsedMillis library. But every so often, I just simply count the update ticks to check if I need to do something.
if (tick++ % 100 == 0) { // do something every 100th cycle. }
Most of the controllers are pretty straight forward, and are just there as an easy to use wrapper for the respective libraries. The only controller that is a bit more exotic, is the InputController. To be honest, this controller gave me some headaches.
Don't interrupt me!
The MCP23017 I2C IO expander is capable of firing interrupts whenever one of the inputs changes. Because of this, I connected the two interrupt outputs of the MCP23017 to the Arduino interrupt pins (Pin 2 & 3). It turned out I only needed to connect one, since the MCP23017 can mirror the interrupt signal on both pins. Luckily this was just resulted in a redundant connection, and didn't caused any issues.
Unfortunately there was a bigger problem which I didn't forsee. While the MCP23017 is capable of triggering the Arduino's interrupt pin(s), I'm not able to read out the pin states in the interrupt service routines, since I2C uses interrupts itself, which aren't available in the interrupt service routines.
This means I can set a flag to request an update in the main loop, but I can never act on any input change in the service routine itself. Now for most of the inputs this is absolutely no problem, but for the rotary encoder I really need to check the state for both pin A and B. Now, if these two pins were both connected to the two different MCP23017 registers, I could have solved this with the two Arduino Interrupt pins. Or better yet. If I would have just connected the Rotary encoder directly to the Arduino's interrupt pins, it would have been even easier. But of course ... I didn't.
So after a lot of grumbling, I decided to give up on the interrupts for the rotary encoder (for now), and simply read out the MCP23017 data every run loop. I might mean the encoder wouldn't react as expected, but I could always make some hardware modifications later.
And with taking this easy route, reading the MCP23017 state was pretty straight forward, using Mizraith's fork of the Adafruit MCP23017 library:
// Initialize the library. Adafruit_MCP23017 mcp; // Configure the MCP23017. mcp.begin(); // Use default address 0. mcp.setGPIOABMode(0xFFFF); // All ports input. mcp.setGPIOABPullUp(0xFFFF); // All ports pull up. // Read out the 16 bits. unsigned int newState = mcp.readGPIOAB();
And then it turned out I spent way to much time in overthinking it. Since non of my other controllers is blocking the main run loop, fetching the current state up the buttons every loop is easily fast enough to handle any rotary encoder input. Once again, it turns out KISS is the best approach: Keep It Simple, Stupid!
And with that issue out the way, it was a matter of hooking up all the controllers in my main.cpp file. Whenever an input change, execute an action for that specific input.
This setup really enables me to easily add more actions to any of the buttons. Now and in the future.
And by sending any input change as a json object over the serial port, I can continue using the inputs in my future Raspberry Pi implementation.
For now, it just resulted in one awesome looking activity board with a lot of light effects!
Enjoy the show!
Now, if you are interested in all the fine detail of the code, you can check out the full source code on GitHub. Of course it's fully supplied with unit and integration tests (NOPE!). And it's fully and well documented (NOPE!). Check it it out in the ActivityBoardController repository!
Read all posts in this series:
Part 1: Enzo’s Control Room
Part 2: Building the Box
Part 3: Fire up the Lasers!
Part 4: Spray Away!
Part 5: Push the button!
Part 6: Assembling the panel
Part 7: The dial on the board goes round and round
Part 8: Take control!
Part 9: It’s all about the code!
Part 10: Bake me some Pi!
Part 11: The Final Touch
Trying to debug some minor issues in my ActivityBoard project. The Rigol really is a big help! A great way to check the interrupts I’m trying to handle.
After I finished the front panel, and getting an approval of Enzo, the next step in the process of the activity board is finishing the casing. A nice reason for some evenings in the shed.
My bad carpentry is easily hidden with some layers of paint. Since I'm a pretty lazy guy, this involves a bunch of spray cans.
To pretend like I know what I'm doing, I start off with a primer.
After the primer you should sand and waterproof it, but just like any other impatient maker we skip this step. Let's continue with unleashing the inner goth, and putting the black spray paint to work.
A great suggestion for any inexperienced spray painter: always use some gloves and a mask. Do not wait until the third layer of paint before you start using these. *cough, cough*
The inside looks pretty bad-ass. The outside could have used a bit of extra primer, but don't worry, in the end the outside of the case will not be visible.
Of course the LED-strip needs to be hidden, as well as the end face of the wood. Time to put my new belt- and disc sander to work!
To give the activity board an industrial look, I combine the black paint and transparent front panel with some 2 cm width aluminum L-profiles. The disk sander helps me to get the miters perfectly flush.
Of course, the first miter is the easiest, the other 3 took a lot of sweat, swearing and ... aluminum, to get it right.
I'll be honest with you: it took me 3 evenings to get it the way I wanted. I will not disclose how many pieces of aluminum were wasted in this process.
The combination of the black background, the brushed aluminum and the LEDs give a pretty bad ass result. So after some nights of hard labor, the casing had the desired robust industrial look.
But to give it a final touch, I decided to put some extra effort in the black background. It needed a little technical touch.
So, like most of us do, I had an old motherboard freshly cleaned waiting for me in the dishwasher. It's always handy to have a dust and grease free motherboard at hand ...
Especially when you want to cover that motherboard with a few layers of back paint.
The freshly painted motherboard will be mounted on the inner back plate using some 1 cm standoffs.
It will give the inside an technical appearance and helps me to hide the wires that will to to all the buttons and LEDs.
And with that finishing touch, It's time to light it up on it's final destination.
Now, the next step would have been mounting all the buttons, LEDs en display's. But after the picture above, you won't be surprised that I'll first be spending a few hours to clean that front panel. A transparent acrylic front panel, a dusty shed and greasy fingers don't go well together ...
Read all posts in this series:
Part 1: Enzo’s Control Room
Part 2: Building the Box
Part 3: Fire up the Lasers!
Part 4: Spray Away!
Part 5: Push the button!
Part 6: Assembling the panel
Part 7: The dial on the board goes round and round
Part 8: Take control!
Part 9: It’s all about the code!
Part 10: Bake me some Pi!
Part 11: The Final Touch