Introduction
OK, so we have our basic Prong game, it’s time to transform it into Pronguino, by adding Arduino paddle controllers, its going to be a simple circuit, but a great start to learn about voltage dividing, serial communications and bitwise operations.
Getting Started
First, we will focus on building the circuit and programming the Arduino UNO, making sure everything compiles and uploads OK.
Recommended
I recommend that you read and run through ‘Project 14 – Tweak the Logo’ in the Arduino Projects Book, as this will give a bit more background on what we are doing here.
Components
All the components that you need listed here are in the Arduino Starter Kit.
Part | Quantity |
---|---|
Arduino UNO Rev 3 | 1 |
USB Cable | 1 |
Breadboard | 1 |
Potentiometer 10k Ohms | 2 |
Jumper Wires | 6 |
Black Stranded Jumper Wire | 1 |
Red Stranded Jumper Wire | 1 |
The Circuit
Assembly
These instruction steps are used to assemble the circuit as shown above, quite straight forward with some added tips…
Step | Instruction |
---|---|
1 | Ensure you have unplugged from power source and at least one end of USB cable is removed from either the UNO / computer. |
2 | Connect the red stranded jumper wire to one of the bus lines on the breadboard marked with + to the 5V pin on the UNO. |
3 | Connect the black stranded jumper wire to the adjacent bus line marked with – to the GND (Ground) pin on the UNO. |
4 | The potentiometers that come with the kit (at time of publishing this), have to straddle the centre of the breadboard, so place one on the left of the board so the side with two legs is facing the bus line where you connected the power. Place the other on the right hand side. Make sure you press them in firmly, so they don’t wobble. |
5 | Connect one of the smaller jumper wires (15mm blue ones seem to do trick), from a board socket aligned with the left leg of potentiometer to the – socket on the bus line where you have connected black wire. Do the same with the other potentiometer. |
6 | Connect another of the smaller wires (the 5mm orange one), from a board socket aligned with the right leg of potentiometer to the + socket on the bus line where you have connected red wire. Do the same with the other potentiometer. |
7 | On the other side of the board where the single legs (wipers) of the potentiometers are, connect a green jumper wire from a socket aligned with the wipers, to the Analog-In A1 pin on the UNO, for the left hand side potentiometer, and A2 for the one on the right. |
The Code
Download
Arduino
We could write this code in a few lines in one file, but as I want to make the code more readable, understandable and extendable, I have broken down into the main file pronguino, along with a library called Controller.
In the main file, similar to Processing, there is setup()
where the serial communications and controllers are initialized, and the loop()
is continuously reading and writing the value from the controllers.
The library is made up of a header file Controller.h which is then implemented in a c++ file Controller.cpp, I will cover the subject of Library Packaging in a later post. I will let the code speak for itself, and take a deeper dive into some areas below.
Plug the Arduino into your PC, upload sketch, and we are ready for the next step
Testing
Note: If you have a mobile phone or similar attached to your computer via USB, you may get some spurious results, bear this in mind if you are getting strange movement from paddles with or without Arduino attached.
So, we have the controllers built, code uploaded, and usually you would update the Processing code as below, and spend the next few hours playing Pronguino, right? or maybe the next few hours debugging two sets of code, and an electronic circuit?
OK, we haven’t got the luxury of a user interface on the UNO, but what we do have while connected to the PC is the Serial Monitor, which is opened from the Arduino IDE .. Tools -> Serial Monitor.
I have added a constant called TESTING
, and when set to false
, on upload of code, you should see something like this, as you turn the potentiometers, which is the data being written to the serial port.
⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮ѡ⸮⸮⸮⸮pppppP@A23#$$# @PP``acD4$#
If you change TESTING
to be true
, upload the new code, the setup()
will call function resultsHeader()
to print some headings, and loop()
will continuously print the data being read from each controller via the function resultsDetail()
, and the data that would be written, you should see something like below under various conditions ..
Both potentiometers turned to far left
controllerValue1 controllerValue2 highNibble lowNibble data 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Both potentiometers turned to far right
controllerValue1 controllerValue2 highNibble lowNibble data 15 15 240 15 255 15 15 240 15 255 15 15 240 15 255
Both potentiometers turning to somewhere in the middle
controllerValue1 controllerValue2 highNibble lowNibble data 11 10 176 10 186 8 10 128 10 138 8 8 128 8 136
Troubleshooting
Hopefully, with the circuit being quite simple, there is not much to go wrong, but the common things to check are as follows ..
Issue | Resolution |
---|---|
Nothing is working | Check Arduino is plugged in securely Check red & black wires on board are secure |
Random data (without change potentiometer) | Check potentiometers are firmly pushed in Check the data wires are securely connected |
Getting 15 instead of 0 as a nibble value, when potentiometer, turned all way to left. | Check the left pin of potentiometer is connected to GND, and right to 5V |
Program Storage Space
Just a quick note to those worried about testing code taking up valuable space on the Arduino, the compiler will not include any unreachable code, so when TESTING = false;
, the functions resultsHeader()
and resultsDetail(...)
are not compiled, check console after compiling …
Sketch uses 1952 bytes (6%) of program storage space. Maximum is 32256 bytes. Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
… in comparison to having TESTING = true;
…
Sketch uses 2424 bytes (7%) of program storage space. Maximum is 32256 bytes. Global variables use 260 bytes (12%) of dynamic memory, leaving 1788 bytes for local variables. Maximum is 2048 bytes.
Processing
Time to add the “uino” to Prong, below is the updated Processing code to include functionality that reads data from the controller and moves the paddles accordingly…
Experiment: The paddle movement seems OK when you just turning the potentiometers at a steady rate, but can sometimes get out of sync, have a tinker with checkController()
in Player class and/or the timing of the outgoing data in Arduino code or incoming data in the Processing code, to see if it can be improved.
What’s New ?
File | Description |
---|---|
Controller | Contains a new class Controller , which encapsulates the values received from Arduino controllers. |
Functions | A new static class Functions , which contains functions that can be called from anywhere in the package, initially used for some bitwise calculations. |
Whats Changed ?
File | Description |
---|---|
pronguino | Imports serial library Initializes serial port in setup() Added serialEvent() |
Constants | Added controller related constants |
Options | Added serial port related options |
Player | Added Controller instantiation to constructor Added setController(int value) methodAdded checkController() method |
Play
Take a break, and play one long (we will look into that soon) game of Pronguino.
Challenge: Currently, when turning controller to left the paddle moves up, and turning right, moves it down. If you prefer the opposite, without changing the circuit, just edit either the Processing or Arduino code.
Potentiometers
In the context of this circuit the potentiometers are being used as voltage dividers.
Terminal 1 (T_1) connected to GND
Terminal 2 (T_2) connected to 5V
Terminal 3 (T_3) connected to A1 or A2
Given that …
Symbol | Description | From | To | Equation | Value |
---|---|---|---|---|---|
V | Voltage Supply | T_1 | T_2 | 5 Volts | |
R | Total Resistance | T_1 | T_2 | R_1 + R_2 | 10k Ohms |
… and turning the potentiometer to various positions, we would get the voltage drop required…
Symbol | Description | From | To | Equation | Left | Middle | Right |
---|---|---|---|---|---|---|---|
R_1 | Resistance | T_2 | T_3 | 10k\Omega | 5k\Omega | 0\Omega | |
R_2 | Resistance | T_1 | T_3 | 0\Omega | 5k\Omega | 10k\Omega | |
V\scriptsize out | Voltage | T_1 | T_3 | {R_2 \over R_1 + R_2}V | 0V | 2.5V | 5V |
.. .which is then measured by analog inputs A1 or A2 and converted by the ADC (Analog-to-Digital Converter) into to a resolution of 10 bits (0000000000 – 1111111111) or 0 – 1023.
Learn: I find it important to get to know your components, and how they work, it helps when things don’t work as planned, learn some of the equations such as Ohms Law and Voltage Division Rule etc.
Bitwise Operations
Reference
Operator | Name | Description | Example | Result |
---|---|---|---|---|
& | AND | If both are 1 | 1 & 1 | 1 |
1 & 0 | 0 | |||
| | OR | If either is 1 | 1 | 0 | 1 |
0 | 0 | 0 | |||
^ | eXclusive OR | if both different | 1 ^ 0 | 1 |
1 ^ 1 | 0 | |||
~ | NOT | Changes to opposite | NOT 0 | 1 |
NOT 1 | 0 | |||
<< | Left Shift | Shifts n bits to left | 00001000 << 2 | 00100000 |
>> | Right Shift | Shifts n bits to right | 01000000 >> 3 | 00001000 |
Term | Meaning | Binary | Hexadecimal | Decimal |
---|---|---|---|---|
Bit | 1 bit | 0 to 1 | 0x0 to 0x1 | 0 to 1 |
Nibble | 4 bits | 0000 to 1111 | 0x0 to 0xF | 0 to 15 |
Byte | 8 bits | 00000000 to 11111111 | 0x00 to 0xFF | 0 to 255 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
bit8 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 |
bit8 | bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 |
---|---|---|---|---|---|---|---|
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Arduino
In the Arduino code we have the following lines in the pronguino loop()
…
// Convert to nibbles
byte highNibble = (byte) controllerValue1 << 4;
byte lowNibble = (byte) controllerValue2;
// Combine the two nibbles to make a byte
byte data = highNibble | lowNibble;
… where we have read the two values (ranging from 0 – 15) from each controller, and rather than sending these values individually, we will combine them into one byte to send…
Given that …
Binary | Hexadecimal | Decimal | |
---|---|---|---|
controllerValue1 | 00001000 | 0x08 | 8 |
controllerValue2 | 00001010 | 0x0A | 10 |
… to get the highNibble
we shift controllerValue1
left 4 bits …
Binary | Hexadecimal | Decimal | |
---|---|---|---|
controllerValue1 | 00001000 | 0x08 | 8 |
<< 4 | |||
highNibble | 10000000 | 0x80 | 128 |
… we are only changing data type of controllerValue2
to get lowNibble
, so to get data
we bitwise OR highNibble
and lowNibble
…
Binary | Hexadecimal | Decimal | |
---|---|---|---|
highNibble | 10000000 | 0x80 | 128 |
| lowNibble | 00001010 | 0x0A | 10 |
data | 10001010 | 0x8A | 138 |
Processing
In the Processing code we have two functions in the Functions file …
/**
* Extracts a high nibble (first four bits) from a byte
*
* @param data byte to extract nibble from
*/
static int getHighNibble(byte data) {
return (int) ((data >> 4) & (byte) 0x0F);
}
/**
* Extracts a low nibble (last four bits) from a byte
*
* @param data byte to extract nibble from
*/
static int getLowNibble(byte data) {
return (int) (data & 0x0F);
}
… when the serialEvent()
function receives the data sent from the Arduino, it needs to be split up to identify the movement of each of the controllers … for controller 1, getHighNibble(byte data)
is called…
First the data
needs shifting to the right 4 bits …
Binary | Hexadecimal | Decimal | |
---|---|---|---|
data | 10001010 | 0x8A | 138 |
>> 4 | |||
result | 00001000 | 0x08 | 8 |
… and then apply a bitwise AND with 0x0F
…
Binary | Hexadecimal | Decimal | |
---|---|---|---|
result | 00001000 | 0x08 | 8 |
& 0x0F | 00001111 | ||
highNibble | 00001000 | 0x08 | 8 |
.. and to get value for controller 2 we call getLowNibble(byte data)
.. and bitwise AND data
with 0x0F
..
Binary | Hexadecimal | Decimal | |
---|---|---|---|
data | 10001010 | 0x8A | 138 |
& 0x0F | 00001111 | ||
lowNibble | 00001010 | 0x0A | 10 |
Fritzing
Fritzing provides a software tool that allows to you draw schematics, mock up your breadboard, upload code to devices, design PCB layouts and also offering service to print PCBs. I have used this to produce the schematic and breadboard mock-up for the “controllers” …
Learn: It is definitely worth getting to grips with circuit design and prototyping with tools such as Fritzing, even for the simplest of circuits, as when they become more complex, you want to be focusing on the circuit and not how to use the tool!
… the cool thing about doing this using Fritzing, is the schematic and breadboard are continuously checking and modifying each other, for example if you started with the breadboard and just add the required components on, without any wires, then go to schematic, connect everything up logically, switching back to the breadboard will show dashed lines from pins to sockets as a guide to where the wires should go (known as a ratsnest) , and will disappear as wires are added…
Note, as the ratsnest is being created from a logic diagram, they will take the most direct route i.e. joining pins from from potentiometer to the other as they are both connected to GND, and as we just don’t wont all the wires going from component to component, in this case we would take a wire from each component to a GND socket on board.
Similarly, if you started with a fully wired up breadboard, and then switched to create schematic, you have to re-arrange the connections and components to create a tidy looking diagram, the connections will highlight red, where no connected as been made, and green when all good, ratsnest guides are also created in this view.
This Fritzing file is included in the download for this post.
Tinkercad
Flipped your LED?, fried your chips? or toasted your breadboard ? … if you haven’t yet and you’re paranoid about forgetting to put that resistor in, then it is worth taking a look at Tinkercad, whilst Fritzing suits my needs for schematic and breadboards, I search around for some free simulation software, and for basic Arduino circuits, this fitted the bill.
It has all the components from Arduino Starter Kit, and more. As you get more daring with your circuits (I have seen posts about weird noises, strange smells and smoke!), you may want to try it out first with TinkerCad, it will show you when something blows. Also useful for measuring resistance / voltage etc as you can add a multi-meter …
There are a couple of limitations that can be worked around …
Limitation | Workaround |
---|---|
As it is web based you cannot communicate with a physical serial port. | If you look at the code below, you will see that TESTING = true; and as you can see a virtual serial monitor, this should be enough to tinker with. |
You cannot add files to code window, i.e. in our case we cannot add Controller.cpp and Controller.h | Again, have a look at the code below, I have implemented the class Controller at the top of the file. |
Code
This TinkerCad code is included in the download for this post.
Conclusion
OK, so we now have the basic Pronguino game being controlled by a couple of potentiometers, I have learnt how to use Fritzing and TinkerCad to help in the design and testing of the circuit. Hopefully provided some hints and tips on coding and testing, remember I am new to Arduino, Processing and electronics, and have presented my findings in such a way that helps me remember and look back on, and hope that it will help you too.