physicalcomputingwithj
physicalcomputingwithj
Physical Computing with J
14 posts
Don't wanna be here? Send us removal request.
physicalcomputingwithj · 4 years ago
Text
Experiment #1 : Photoresistor to Binary Decoder
Experiment conducted 2021/02/23
For my first experiment, I wired the Arduino to read the analog output of a potential divider circuit with a photoresistor in it (a.k.a. a photocell) as a 10-bit value. It then decoded the 4 most significant bits in code and displayed those 4 bits on LEDs.
youtube
Can't see the video? Watch it on YouTube!
Components Used
This experiment uses the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
1 x Photoresistor
1 x 5KΩ resistor
4 x LEDs
4 x 220Ω resistors
Step 1: Wiring The Photoresistor
I first wired the photoresistor as shown below, using the 5KΩ resistor.
A red wire takes a 5V voltage from the Arduino into the circuit. The voltage passes through the 5KΩ resistor, which then goes through the photoresistor. The black wires then carry the voltage through to the ground pin of the Arduino. The two resistors wired in this way creates a potential divider circuit. I've deliberately used two black wires to ground the signal, one connecting the potential divider to the negative rail, and one connecting the negative rail to the Arduino. This will make it easy to ground the LEDs later by connecting them to the negative rail.
The resistance of the first resistor is static, however the resistance of the photoresistor changes depending on the light level. The more light, the less resistance there is. This means that the more light there is, the lower the signal from the potential divider will be.
The orange wire is used to carry the signal from the potential divider into the Arduino. We connect to pin A0, as the analog pins connect to analog to digital converters. These allow us to read the analog singal from the circuit as a 10-bit, arbitrary unit, numerical value. If we used a digital pin instead, it would only be able to read if the value was high or low.
Step 2: Decoding The Signal in Code
By reading the signal carried by the orange wire, we can now read how much light the photoresistor is detecting. The more light, the smaller the signal will be.
We can program our setup() function to read this analog signal into the Arduino. In the meantime, we will also set up pins 10-13 as digital outputs, which we will use to output the 4 most significant bits of the signal to the LEDs.
void setup() { pinMode(A0, INPUT); // Read in the 10-bit "analog" signal from pin A0 pinMode(10, OUTPUT); // Setup pins 10-13 as output pins pinMode(11, OUTPUT); pinMode(12, OUTPUT); pinMode(13, OUTPUT); Serial.begin(9600); // Setup outputting to the serial output. For our purposes, "9600" is just a magic number }
Next we will create a function that takes that signal as input, and outputs the value of a particular bit as a boolean. Here x is the value being decoded and k is which bit we are decoding (0-indexed). (Though it might be slightly erroneous to make this it's own function, it makes the loop code more readable, and the compiler will likely inline the function for us anyway.)
bool decode(int x, int k) { return (x & ( 1 << k )) != 0; }
We now have everything we need to create our loop function. Here we store the signal from the potential divider in variable s, output that value to the serial port, and then one by one decode each bit and update our digital outputs accordingly.
void loop() { int s = analogRead(A0); // Read the singal on pin A0 Serial.println(s); // Output that signal to the serial output digitalWrite(10, decode(s, 6) ? HIGH : LOW); // Decode the 7th bit digitalWrite(11, decode(s, 7) ? HIGH : LOW); digitalWrite(12, decode(s, 8) ? HIGH : LOW); digitalWrite(13, decode(s, 9) ? HIGH : LOW); // Decode the 10th bit delay(10); }
You can see the complete code on GitHub..
Step 3: Displaying The Decoded Signal
Now that our code is decoding the signal and outputting the 4 bits, we want to display that using our four LEDs. Each LED has it's own 220Ω resistor. They are wired in parallel and each in the same way, except that each connects to a different pin on the Arduino. As we want the left-most LED to display the most significant bit (the 10th bit), we will connect it to pin 13.
Depending on what colour and make LEDs you use, you may need to tweak what strength of the LED resistors to achieve the desired brightness
There you have it!
This was a fun first experiment into working with electronic components, which went smoothly enough I'm a little suspicious. Though, I did have to experiment with using different strengths of resistor to achieve the desired results with the LEDs and potential divider. Through experimentation in TinkerCAD, I also came to realise the amount of light received by the photoresistor and the output of the potential divider were not linearly correlated. As the amount of light increased, the signal would first decrease slowly, then start decreasing faster and faster. For this experiment that wasn't a problem, but it did mean it was much easier to deliberately create binary numbers 0-3 than 4-7 using the LEDs. This could be resolved in code by converting the non-linear input of the potential divider into a linear one.
1 note · View note
physicalcomputingwithj · 4 years ago
Text
Experiment #2 : The Icarus Servo
Experiment conducted 2021/03/02
In this experiment I actually tried a series of things. First I tried controlling the brightness of an LED with a potentiometer. When that didn't work, I replaced the LED with a servo motor instead. At the end, I replaced the potentiometer with a photoresistor to the servo. I attached it to the arm of the servo and attempted to create a program that would make the servo move the resistor towards the brightest light reading it could find. I like to call it the Icarus Servo!
Part 1: Potentiometer Controlled LED
How the Potentiometer Works
The potentiometer has three pins - two terminal pins and one wiper pin. The terminal pins connect to the live and ground wires. Internally, these terminals are connected to each other along a track. A wiper connects to the middle of the track, where its position along the track rotates with the potentiometer's dial. This essentially makes the two sides of the track variable resistors, and the whole track a potential divider circuit - whose signal can be read from the wiper pin. (Fun fact: Because of how the potentiometer works, it doesn't technically matter which way around you connect the terminals!) If you're curious about potentiometers, Jeff Feddersen has a clear and concise explanation on their website.
For this experiment, we can read the signal from the wiper pin into the Arduino, and then use code to output a voltage to the LED. More voltage means more light!
Components Used
This experiment uses the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
1 x Potentiometer
1 x LED
1 x 220Ω resistor
The Wiring
The Code
void setup() { pinMode(7, OUTPUT); // LED ouput Serial.begin(9600); } void loop() { int val = analogRead(A0); // Potentiometer input int out = map(val, 0, 1023, 0, 255); // Converts the input range of the potentiometer (0-1023) to the output range of the analog pin (0-255) analogWrite(7, out); // Write to the LED output Serial.println(out); delay(10); }
You can see all the code for this experiment on GitHub.
The code here takes the signal from the potentiometer via pin A0, and outputs the signal to the LED on pin 7. The potentiometer input is a 10-bit value (in the range 0-1023), but the output pin is only an 8-bit output (in the range 0-255). For this reason, we use the map function to convert the value from the input range into the output range, so that we can use the input to control the LED.
There you have it! But not really...
I set it up in TinkerCAD and it worked a treat! Then I set it up my physical kit, and it didn't work.
The LED only switched between full brightness and completely off, with not even the smallest zone of "inbetween". After double checking the wiring, plotting potentiometer readings in the IDE's serial plotter, and switching out the LED for every colour in the kit, I discovered that the LEDs provided in this kit don't fade!
So let's connect it to a servo instead. Much more fun!
Side note: It took me a moment to realise you need to assemble the potentiometer yourself. The two parts you need are in the small box with eight smaller compartments, specifically they're in the top compartment second from the right.
Part 2: Setting Up the Servo
How the Servo Works
A servo motor is a particular kind of motor that, though only able to rotate in a 180°, uses a potential divider circuit to know it's position at all times. Using the Arduino we can send a target state to motor, and then it will compare the target state of the potential divider to it's actual state to determine how to drive the underlying DC motor. GreatScott! on YouTube has a great video that does more in-depth on how servos work, and even how you can modify them to have more degrees of rotation.
Components Used
This experiment uses the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
1 x Potentiometer
1 x Servo Motor (SG90)
The Wiring
I removed the LED and wired up the servo instead. The brown wire connects to ground, the red to live, and the orange to our control signal from the Arduino. I used pin 9.
The Code
I first ran example code that comes with the Ardiuno IDE to test the servo was working. You can load the example by going to File > Examples > Servo > Sweep. My wiring worked! I then wrote a program to use input from the potentiometer instead.
#include Servo servo; void setup() { servo.attach(9); // Servo output Serial.begin(9600); } void loop() { int val = analogRead(A0); // Potentiometer input int out = map(val, 0, 1023, 0, 180); servo.write(out); // Write to the servo output Serial.println(val); delay(15); }
You can see all the code for this experiment on GitHub.
Like in our LED example, I use the map method to translate the value from the input range to the output range, but this time the output range is 0-180, the rotations in degrees our servo can handle. The Ardiuno kindly abstracts away converting that value into the electrical signal the servo actually wants.
There you have it! Sort of...
youtube
Can't see the video? Watch it on YouTube!
Judging by the plot of the potentiometer signal, it seems that as the potentiometer and other components wobble about, it sends a somewhat volatile signal, hence the weird movement from the servo. Blu-tacking the potentiometer to the breadboard helped to minimise that. It worked well enough for my purposes, and I was curious to try what I've now dubbed the Icarus servo!
Part 3: The Icarus Servo
Components Used
This experiment uses the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
1 x Photoresistor
1 x 5KΩ resistor
1 x Servo Motor (SG90)
The Wiring
I replaced the potentiometer with a photoresistor, in the same setup I used in my first experiment. To begin with, I just set it up on the breadboard, as to test that it was working. I wired the output of the potential divider to pin A0 so I could reuse the code I was currently using.
The photoresistor was not giving a huge range of values, certainly not the full 0-1023 range theoretically possible. Replacing the 10KΩ resistor I was originally using with a 5KΩ resistor helped increased the range. After that however, I just used the serial plotter to see what range of inputs the sensor was giving me, and updated my map function call to work in this range instead.
int out = map(val, 10, 360, 0, 180);
To make my Icarus servo, I wanted to attach the photoresistor to one of the servo motor arm attachments. To ensure that the photoresistor wouldn't swing into the side of the servo, I used code to set the servo to 0 degrees, then attached the servo arm such that it would swing around the "outisde" of the servo i.e. keep clear of the blue box. I then slotted the photoresistor into the servo arm and connected it to the breadboard using plug-to-socket jumper wires.
The Code
I wanted my servo to move the arm areas of light. My simple approach to this was to create a hill climbing algorithm. The program stores a current position in variable, and then continually rotates slightly to either side of this position to see if either of them produce a higher light reading than the current position. If they do, the current position is updated to the new one.
#include Servo myservo; int pos = 0; void setup() { myservo.attach(9); // Servo output Serial.begin(9600); } void loop() { int highestLightLevel = 0; int newpos = pos; for (int i = -1; i <= 1; i++) { // Iterates i through the values -1, 0, 1 - corresponding to "left", "centre", and "right" // Calcuate next position to take a light reading from int testpos = pos + i * 5; if (testpos < 0) testpos = 0; if (testpos > 180) testpos = 180; // Move to that position myservo.write(testpos); delay(200); // The the servo time to reach it's target before taking a light reading // Take a light reading int light = analogRead(A0); // Photoresistor input // If that reading is the highest so far, save it and the tested position to refer to later if (light > highestLightLevel) { highestLightLevel = light; newpos = testpos; } } // Update the "current" position of the servo to the one that produced the highest light reading pos = newpos; Serial.println(highestLightLevel); }
You can see all the code for this experiment on GitHub.
There you have it! For real this time!
youtube
Can't see the video? Watch it on YouTube!
While recording the video I had my phone torch on, which I used to encourage the servo to move in particular directions. Sometimes the jumper wires would push against it, but otherwise it worked reasonably well! Working on this experiment in particular, I was surprised to see just how prevalent potential divider circuits are in electronics.
On reflection, I think part of why the movement of the servo is so janky is that each time the code pulls a signal from the photoresistor, it only takes one reading, meaning ambient variations in reading can cause the servo to move in different directions essentially at random. Taking an average reading over a few milliseconds would help prevent against this. It was definitely good insight onto how to best use the photoresistor.
The code could be also further improved by, instead of hard coding the input range from the photoresistor, dynamically setting it during runtime depending on what signals the motor receives.
0 notes
physicalcomputingwithj · 4 years ago
Text
Experiment #3 : Stepper Motors
Experiment conducted 2021/03/09
In this experiment I made an attempt at setting up the 28BYJ-48 stepper motor. Though I couldn't get it to work in this experiment, I was able to connect a working version in my final project.
How The Stepper Motor Works
Stepper motors come in bi-polar and uni-polar variants. The 28BYJ-48 motor is uni-polar. Compared to bi-polar stepper motors, uni-polar motors have a more complicated internal arrangement but with the tradeoff that they are easier to drive.
Inside the motor there are two metal coils surrounding a rotating magnet, where each coil is associated with two sets of metal teeth that also surround the magnet. Running current through each coil in one direction or the other creates an electromagnetic field, where the direction of the field affects how the teeth are magnetised. By controlling the flow of current through the coils in a particular way, it is possible to rotate the magnet by repeatedly attracting it to one set of teeth after another. This is how the stepper motor is able to rotate with precise control over the speed, length, and direction of the rotation, as opposed to a DC motor which rotates more freely. Stepper motors don't have absolute positioning like a servo does, though limit switches can be used to determine when the motor has rotated a particular point.
The Arduino itself cannot supply sufficient power to the stepper motor. Instead, a stronger power supply is fed into the driver board, and the Arduino controls transistors on the board, which when enabled allow the stronger power supply to pass into the motor. Though this isn't literally what happens, the effect is that the four outputs from the Arduino are amplified by the driver board. Of the five wires connecting to the motor, one is the live wire, which connects to the centre of both motors. Each coil then has one wire attached at either end, which makes up the remaining four wires. By changing which of these four wires are connected to ground, it is possible to control the flow of current through each coil. The red LEDs on the driver board show which control wires are currently live. In order for the driver board to work when connected to the Arduino, the Arduino and the driver board should share a common ground.
If you're looking for complete information on the motor Bret Stateham has a great video on YouTube explaining how it operates.
Components Used
This experiment uses the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
1 x Stepper Motor (28BYJ-48)
1 x Stepper Motor Driver Board (ULN2003)
1 x Power supply module
Wiring the Stepper Motor
Below is a diagram showing how I wired the stepper motor. n.b. TinkerCAD doesn't include a stepper motor and driver board in it's available components, so I've recreated the driver board using a mini breadboard. Additionally, the power supply is different to that provided in the kit.
In essence, the four control pins on the driver board (from top to bottom, as pictured below) connect to pins 11 through 8 on the Arduino (in that order). You could use other pins, these were just those used by the example programs I was trying to run. The live connection from the power supply is run through the breadboard and then into the driver board. Though the breadboard could be made redundant in this example, using it made it easy to connect all the grounds connections of the Arduino, the motor, and the power supply, as to have a shared connection.
When attaching the power supply supplied in the kit, be sure to connect it such that the plus and minus icons denoted on the output pins connect into the correct rails on the bread board. (I initially got this wrong.) With this power supply you can provide power either by using a wall socket plug or by using a battery, as pictured below. Make sure also the jumper on the power supply unit is set to output 5V - as the stepper motor requires this.
Unfortunately, I couldn't get this working and I was never able to identify why. I even tried replacing the chip on the driver board with what I thought were spare chips provided in the kit. In actuality however, these are entirely different chips, and not interchangeable at all. Later on in my final project, I was able to get the stepper motor working, and so my suspicion is that there was an issue with the code I was trying to run - particularly as I ran into such issues during my final project.
0 notes
physicalcomputingwithj · 4 years ago
Text
Experiment #4 : Look Emitting Diodes
Experiment conducted 2021/03/16
I decided to have a look at the components provided in the kit and see which might be fun in combination. I saw the joystick and 8x8 led matrix display, and thought it might be fun to make something that moves around on the screen depending on the joystick input. As I made the screen display a face you can make look in different directions, I've dubbed them the "Look Emitting Diodes"!
Components Used
This experiment uses the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
1 x 8x8 LED Dot Matrix Display (MAX7219)
1 x Joystick
Part 1: Wiring the Display
The Wiring
I first connected the 8x8 matrix display as shown. n.b. TinkerCAD doesn't have an LED matrix or a joystick, so I created both in MS Paint.
To be able to control the display from the Arduino, I installed the LedControl library by Eberhard Fahle V1.0.6. I ran the example code from lesson 15 of the Elegoo starter kit PDF to verify that the display was working!
youtube
Can't see the video? Watch it on YouTube!
The Code
To see how I could write my own output to the display, I wrote a program that displayed the static outline of my eyeball, with intention to add the moving pupil next.
At the start of the program, we include the LedControl library and initialise an interface to our display.
#include "LedControl.h" // LedControl library by Eberhard Fahle V1.0.6. LedControl display = LedControl(12, 10, 11, 1); // Connect to DataIn, CS, and CLK respectively
Next, I create a constant array of bytes, which I essentially treat as an 8x8 2D array of bits. In C++, putting a B before a sequence of ones and zeros creates a byte literal, meaning the language will interpret the numbers as binary, and not base 10.
// An array of bytes, where each byte corresponds to a column, and each bit to an LED // The array is constant as it stores the static parts of the eyeball const byte eyeball[8] = { B00111100, B01000010, B10000001, B10000001, B10000001, B10000001, B01000010, B00111100, };
In the setup function, we prepare the display for showing our output. Most notably, we have to wake up the display from power saving mode.
void setup() { display.shutdown(0, false); // This "wakes-up" the display, which is in power saving mode by default display.setIntensity(0, 8); display.clearDisplay(0); }
In the loop function then, we literate over each byte in the array, and write it to the corresponding column in the display. Thankfully, the library handles the complexity of this for us!
void loop() { for (int col = 0; col < 8; col++) { display.setColumn(0, col, eyeball[col]); } delay(1000); // These delays are just in place for testing display.clearDisplay(0); delay(1000); }
You can view the complete code on GitHub.
Part 2: Completing the Eyeball
The Wiring
Next I wired the joystick, which was pretty straightforward. I didn't wire up the switch pin and I wasn't using it for this experiment, though I suppose I could have rigged it up to trigger a blink animation. Throughout the experiment I kept switching which wire I treated as the "x" input and which as the "y" as I kept holding the joystick at different angles.
The Code
To be able to read in from the joystick I updated my setup function.
void setup() { Serial.begin(9600); pinMode(A0, INPUT); // Read in the 10-bit analog signal from pin A0 (x signal) pinMode(A1, INPUT); // Read in the 10-bit analog signal from pin A1 (y signal) display.shutdown(0, false); // This "wakes-up" the display, which is in power saving mode by default display.setIntensity(0, 8); display.clearDisplay(0); }
The real magic of drawing the moving pupil then I do in the loop function, predominately through the use of bit operators.
void loop() { int x = 1023 - analogRead(A1); // Doing 1023 minus the singal inverts it, which I did to ensure the pupil's vertical movement is not inverted from that of the joystick's int y = analogRead(A0); // Downscale the input range to the width/height of the matrix // Bitshifting the 10 bit input 7 bits to the right causes it to be 3 bits, i.e. 0-7 x = x >> 7; y = y >> 7; // Draw the eyeball byte pupil = 3 << x; // 3 is 11 in binary. These bits are then shifted as far as they need to go for (int i = 0; i < 8; i++) { byte col = eyeball[i]; if (i == y || i == y - 1) { col = col | pupil; // | is the bitwise inclusive OR operator. Each bit of it's operator is the result of ORing the corresponding bits in it's inputs // In essence, this combines the outline of the eyeball with the pupil } display.setColumn(0, i, col); } display.clearDisplay(0); // Clear the display }
With the exception of some odd behaviour around the edges, it worked well!
You can view the complete code on GitHub.
youtube
Can't see the video? Watch it on YouTube!
Part 3: A New Face in Town
Now the experiment was working, I decided a single eyeball was a little creepy, and wanted to replace it with a face that had two eyes. This worked in largely the same way as before, but I added some additional logic to make the eyes move closer together as the user moves up against the edge of the display. As each eye is just a line, I do this by clamping the x position of each eye to be within certain bounds. Though, this code got a little messy as I tried to find ways to let the user reach the edge of the display without having to push the joystick absolutely as far as it could go.
When writing my new code, I also realised that I didn't need to clear the display on each loop. This is because when the library writes each byte, it still writes the zeros, essentially clearing anything that was there before.
This is the loop function of the new code.
void loop() { int x = analogRead(A1); int y = 1023 - analogRead(A0); // Doing 1023 minus the singal inverts it, which I did to ensure the pupil's movement is not inverted from that of the joystick's // Map the positions to the range of the display, and also clamp the positions x = map(x, 0, 1023, -3, 3 + 1); y = map(y, 0, 1023, -7, 1 + 1); x = min(x, 3); y = min(y, 1); // Determine the position of the eyes int top = 6 + y; int left = 2 + x; // (the x axis is 0 at the right side of the matrix from my perspective) int right = 5 + x; // Clamp the positions right = constrain(right, 2, 7); // These are likely muddled up! left = constrain(left, 0, 5); // Draw the eyes for (int i = 0; i < 8; i++) { byte row = 0; if (i == left || i == right) { row = B111 << top - 1; } display.setRow(0, i, row); // Now we are drawing rows, not columns, as that made the most sense for the new vertical lines for eyes } }
You can view the complete code on GitHub.
Hello World!
youtube
Can't see the video? Watch it on YouTube!
I think the end result is pretty charming! It was definitely an interesting application of binary bit manipulations and helped me practise my skills in using them. Though, the final code could definitely be un-muddled and improved. I definitely learned more about what to be thinking about when interpreting input signals from electronics in non-continuous ways - dealing with nuances such as ensuring the absolute maximum value of the input signal does not have it's own discrete output value.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #1 : Brainstorming
The Idea
Charles Babbage's mechanical computer and Alan Turing's a-machine (better known as the Turing Machine) both live rent-free in my imagination, which leads to some esoteric ideas and trains of thought! For my project I want to create a primitive Turing Machine - where the machine itself is operated by the Arduino, which then reads and writes to a mechanical memory.
Fig 1: I did try to have a more reasonable idea before jumping into working on an overengineered Turning Machine. But, this idea was easily the most captivating, both to me and to the people I showed it to. Though, I did also consider doing a project with my new favourite infrared camera, the Nintendo Wiimote.
Though I am confident I can program the machine, how the mechanical memory will work and how I will construct it are both full of unknowns. Therefore, the first thing I did was think through every component the machine will need, and brainstorm how each may work. Running experiments is the only true way to determine what will work and what won't, but I still wanted to take time to explore options on paper as to help inform my thinking later on in the process (and if I'm lucky eliminate some dud ideas early on).
Brainstorming the Mechanical Memory
Modern computers use Random Access Memory, which allows all the memory cells to be read from and written to at the same speed, regardless of it's physical position. In the spirit of the hypothetical machine Turning set out however, I want to construct a Sequential Access Memory.
For this project, the memory needs to be able to do three things. 1. Move the memory cells. (as this will probably be easier than moving the read and write electronics to the desired memory cells) 2. Read the symbol at the current cell. 3. Write at least two symbols to the current cell.
Fig 2: A series of sketches of how the various read/write mechanisms of the machine could work.
Moving Memory Cells
Creating a mechanical memory comes with a set of challenges. To be able to read and write to memory, it seems likely that each memory cell will have to have moving parts (making marks on the strip would be both difficult to read, and difficult to "undo" to make way for new marks). This could be a problem, as each moving part is a potential point of failure. Additionally, a long and potentially rigid strip, though possible, could be difficult to move around and/or to fit in a confined space.
The first problem I don't think can be fully eliminated, but I tackle in the next section. The second problem can be resolved by making the memory a loop, rather than a linear strip. In practise, this could be the circumference of a disk, or possibly a ring. A stepper motor will be ideal for rotating a disc around it's centre, making moving the memory cells precisely much easier. The only drawback of this is there will be wasted space in the middle of the disk, but that is more than acceptable for this project.
Reading & Writing
As long as the memory can read and write at least two "symbols" to each memory cell, the machine will can be Turning complete. The hypothetical a-machine could write, erase, and detect arbitrary symbols to a strip of paper. The prospect of leaving the binary realm and having more than two symbols is exciting, however for practical purposes I will use two symbols. Specifically, the write head will move a physical object into one position or another, and the read head will detect the absence or presence of that object in one of the positions.
Looking at the approaches I brainstormed, it appears there is something of a trade-off between the complexity of a memory cell and the complexity of the read and write mechanisms. The less complex you make one, the more complex becomes the other.
However, there is a quite simple solution which I will experiment with first. In it, each memory cell is a folded piece of cardboard, held down by some thread, that is able to flip back and forth. Flip it one way, and that's your first symbol, flip it the other way, and that's your other symbol, henceforth, true and false. A motor can write to the memory cell by flipping it in the desired direction. A photo resistor can read the memory cell, by seeing if it blocking light to a particular location - which it will do when in the "true" position, and won't when it is in the "false" position.
Experiments!
With a potential version of the machine mapped out, I now need to experiment with each component to see if it'll work as intended. My immediate next steps are to test:
If I can rotate the memory loop
The mechanics of the write motor
The mechanics of the read sensor
If all the parts work together
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #2 : Ring Based Memory
Experiment conducted 2021/05/04
youtube
Can't see the video? Watch it on YouTube!
My first experiment was to see if I could create a ring version of the loop memory, able to spin around a centre. I wanted to see if I could create a ring instead of a disc as if I could, then it would be possible to put the read and write heads in the same position, with one positioned inside the ring, and one on the outside (so long as the motor drove the loop from the outside). The weight reduction, though not required, felt like it could be a nice bonus. As is evident from the video however, it was not successful.
Making The Ring
I made the ring out of metal wire, malleable enough to be bent into shape by force, but strong enough to retain it's shape otherwise.
To complete the loop, I wrapped the two ends around each other, and compressed the ends together using plyers. When that proved too difficult, I laid the ring on a chopping board, and hammered the join instead. I had to hit it on four different sides to make the join completely rigid. Shown below are the before and after. Though the join doesn't look particularly compacted in the second image, it was sturdy enough to keep the ring from wobbling.
The wheels shown in the video were taken from a dresser. My intention was to attach the wheels to the side of a cylinder - in this case the sides of a plastic sweets tub - and have the ring ride, not along the wheels themselves, but along the flat surface in between them.
The Issues
As seen in the video, the ring doesn't roll around the wheels. However, the issues run deeper than that. Part of the reason it doesn't roll is it was difficult to get the ring in a circular enough shape just by hand, i.e. without proper tools or a correctly shaped object to wrap it around. Less evident from the video however is that the sides of the tub are actually at a slight angle, ultimately meaning the flat area of the wheel is not perpendicular to gravity. Furthermore, the region was too wide to reasonably keep the ring on one track.
It's become clear from this experiment that to find a way to make this kind of ring roll clearly, let alone also be side driven by a motor and able to hold memory cells, isn't really feasible for this project. My next experiment is to see if I can get a working memory cell and writing mechanism, but after that I will experiment with a disc version of the memory loop.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #3 : Bit Writing
Experiment conducted 2021/05/04
In this experiment I tried creating the memory cell (or "bit") I described in my brainstorming post, as well as a write mechanism to switch it's state. In the video below, I show the working experiment and explain some of the details as to why it works.
youtube
Can't see the video? Watch it on YouTube!
The Code
Here is the code for the simple test program I was running on the Arduino. Make sure you have the Servo library installed. You can manage your libraries from the Arduino by going to Tools > Manage Libraries from the toolbar.
#include <Servo.h> Servo myservo; void setup() { myservo.attach(9); // Change 9 to whichever pin you connect the servo to Serial.begin(9600); } void loop() { myservo.write(0); delay(3000); myservo.write(180); // The servo I'm using has 180 deg of rotation, so writing 180 will make it swing as far as it will go delay(3000); }
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #4 : Disc Based Memory
Experiment conducted 2021/05/11
In this experiment created a spinning disc using a plastic sweet tub lid, and tested to see how well I could spin it around using a stepper motor. In the video below, I show the working experiment and explain some of the details as to why it works. (In the video, I also mistakenly refer to the stepper motor as a servo motor!)
youtube
Can't see the video? Watch it on YouTube!
The Code & Wiring
The program I used was the stepper_oneRevolution program from the Arduino IDE examples (File > Examples > Stepper > stepper_oneRevolution). I modified the stepsPerRevolution variable to 2048 and the speed parameter in myStepper.setSpeed to 15.
The driver board is used so that the Arduino can more easily control the stepper motor. The stepper motor itself is wired into the driver board, and the driver board into the Arduino. The order of the wires from the Arduino to the driver board is important. In this case, I have wired them to work with the example program.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #5 : Bit Reading
Experiment conducted 2021/05/11
This time I set out to test how I could read which state my cardboard memory cells are in. I tested how well a photoresistor could be used to detect if the cardboard was blocking light to it. In the video below I demonstrate that the photo resistor could do this quite successfully, but also explain the different factors that contribute to how well it will or won't work on the final machine. After this initial experiment, I also ran additional tests with different coloured additional light sources and different lengths of cardboard, to see what affect that had on the photoresistor's outputs.
youtube
Can't see the video? Watch it on YouTube!
For this experiment I reused the code and wiring from my first photoresistor decoder experiment, modified to the needs of this experiment.
Experimenting with Additional Light Sources
After my initial experiment, I tested putting different colours of LED above the photoresistor (but blockable by the cardboard) to see how that affected the photoresistor's ability to detect the cardboard. I also experimented with different widths of cardboard flaps to see what affect that had.
Results
Each table shows the output from the photoresistor for each combination of additional light source and width of cardboard. Less light equates to a larger output from the photoresistor.
Round 1
Additional light source No cardboard 2.1cm cardboard 6.1cm cardboard Green LED 374 610 (+236) 687 (+313) Red LED 386 616 (+230) 694 (+308) Blue LED 448 619 (+171) 707 (+259) Yellow LED 459 616 (+157) 686 (+227) None 544 617 (+73) 701 (+157)
Ranking 2.1cm: Green, Red, Blue, Yellow, None Ranking 6.1cm: Green, Red, Blue, Yellow, None
Round 2
Additional light source No cardboard 2.1cm cardboard 6.1cm cardboard Green LED 405 627 (+222) 699 (+294) Red LED 392 632 (+240) 704 (+312) Blue LED 401 614 (+213) 693 (+292) Yellow LED 492 632 (+140) 702 (+210) None 564 627 (+63) 698 (+134)
Ranking 6.1cm: Red, Green, Blue, Yellow, None Ranking 2.1cm: Red, Green, Blue, Yellow, None
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #6 : Perils of the Disc Memory
Chronologically, this post takes place alongside the following posts, where I assemble the whole machine. However, creating a version of the disc memory that wasn't full of issues proved to be a saga in and of itself, so I separated it out into it's own post.
From Disc One to Disc Three
Originally, I had intended to use the plastic lid from my original experiment (disc number one) on the final machine. However, the distorted shape and warble of that lid meant that the write motor wasn't consistently able to hit the top of each bit - as some bits ended up centimetres lower down than others. In response to this I decided to make a new lid, and also took the opportunity to make it bigger, in hopes of being able to fit more bits on it that way.
I made lid number two by drawing around the base of a bin onto a sheet cardboard. Though this disc turned out well, I struggled to pin point the centre of it, and ultimately missed.
Rather than continually trial and error for a suitable centre on disc two, I moved onto disc three. To make it, I considered pinning a piece of string to a decided centre and attaching a pencil to the other end, as a way of drawing a circle with a known centre. However, I realised I could remove human error if I instead printed a computer generated template, stuck that onto the cardboard, then cut around it.
I printed a template I made in PowerPoint onto four pieces of A4 (each with lots of overlap with it's neighbours), taped them to one another in their correct position, then taped the edges of the paper to the cardboard. I also put a pin through the centre of the template, which served both to mark the centre on the cardboard, and to hold the paper in place once I started cutting away the edges of the paper.
The scissors I used while cutting where able to cut the cardboard fine, however struggled cutting the cardboard and the paper at the same time. This caused the cardboard to fray and made it more difficult to cut. To resolve this, I used a Stanley knife to cut around the circumference of the template circle, allowing me to then cut away the remaining cardboard. As my kitchen chopping board wasn't particularly big, I did this on a pizza board to prevent damaging the desk with the Stanley knife.
Attaching the Disc to the Stepper Motor
This disc initially worked really well. Using a single blade of a small pair of scissors, I was able to create a small hole that fit tightly onto the axle of the stepper motor. However, far quicker than I expected the movement of the stepper motor enlarged the hole and caused it to gradually loose it's grip. This soon meant that whenever the stepper motor stopped spinning, the momentum of the disc would cause it to overshoot, which it could do in either direction depending on whether the motor had been going clockwise or anti-clockwise. Though I kept the cardboard bits fairly large, this margin of error still made it difficult to consistently position the bits such that the write motor could hit them.
My solution to this was to attach a much more rigid piece of wood to the underside of the disc at it's centre, which then attaches to the stepper motor axle. To attach the disc to the wood, the disc is sandwiched between two metal washers with a screw through them that is drilled into the wood. I didn't have the tools to machine a hole in the bottom of the wood that could tight-fit onto the axle, so to attach the wood to the axle, I drilled out a hole in the wood and inserted a piece of plastic into it, which also has a hole drilled out. I was able to push the axle into the hole in the plastic, and the whole thing became a tight-fit.
This was successful for quite a while! However, after an amount of time it became more and more frequent that the axle would "slip" within the plastic, causing the motor to stutter and the disc to become out of sync.
Alternative Attachments
I experimented with different replacements for plastic that could maintain a tight-fit without causing issues for the motor. This included blu-tack, cable insulation, and padding in the form of cut felt and blanket. I even tried ditching the piece of wood and replacing it with a binder clip. However, all of these solutions were prone to one or more of following:
The amount of material was imbalanced on either side of the axle, causing the disc to slant.
The momentum of the spinning disc was to strong for the material to be able to prevent the disc overshooting it's target
Material getting caught in the gears inside the motor (I think may have occurred)
The axle slipping inside the material, creating friction the motor couldn't handle and causing the disc to become out of sync.
In the final machine, I used a combination of cable insulation and cut blanket. This works very well when it does work, and has lasted longer than anything else I tried. However, it is still prone to the friction problem, and will at times cause the disc to become out of sync.
Evaluation
While I do think that access to more precise tools would have elevated some of these problems, with hindsight I also think there are a couple underlying problems. The first is the weight of the disc. By making the disc bigger, and as a result of using a heavier kind of cardboard, the disc gains more momentum when spinning, which makes keeping it stable much trickier. The second problem is by attaching a length of wood to the disc the axle of the motor essentially becomes longer, making it harder to balance the disc and keep it flat.
Given another go at it, I think a solution based around gear attachments would work much better. The disc would be affixed onto it's own axle, and then driven by a stepper motor with a factory made gear attachment of the appropriate model. As well as making the machine more reliable, this would open up the opportunity to use gear ratios to have more fine grained control over how the stepper motor drives the disk.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #7 : Assembly of the Components
Now that I had each part of the machine working in isolation (save for the perils of the memory disc), it was time to put them all together! A complete technical breakdown of the machine is included at the end of the blog, but this post outlines the particular challenges I faced.
Testing the Hardware
My first step was to connect all of the components to the Arduino and appropriate power supply. Save for the handful of challenges outlined below, the components all worked as expected! I initially tested the components using a simple program that would iterate through each component and operate it. (I even reused my decode function from my photoresistor decoder to test the LEDs.)
#include <Stepper.h> #include <Servo.h> // Stepper motor const int stepperStepsPerRevolution = 2048; const int stepperRPM = 15; Stepper myStepper(stepperStepsPerRevolution, 8, 10, 9, 11); // Servo motor const uint8_t writeMotorPin = 6; Servo myServo; // Pins const uint8_t photoresistorPin = A5; const uint8_t maintenanceLedPin = 4; const uint8_t runningLedPin = 3; const uint8_t signalLedPin = 2; // Program bool decode(int x, int k) { return (x & (1 << k)) != 0; } void setup() { Serial.begin(9600); myStepper.setSpeed(stepperRPM); myServo.attach(writeMotorPin); pinMode(photoresistorPin, INPUT); pinMode(maintenanceLedPin, OUTPUT); pinMode(runningLedPin, OUTPUT); pinMode(signalLedPin, OUTPUT); } void loop() { // Stepper Motor if (true) { Serial.println("Stepper clockwise"); myStepper.step(stepperStepsPerRevolution); delay(1000); Serial.println("Stepper counterclockwise"); myStepper.step(-stepperStepsPerRevolution); delay(1000); } // Servo motor if (true) { Serial.println("Servo 180"); myServo.write(180); delay(1000); Serial.println("Servo 0"); myServo.write(0); delay(1000); Serial.println("Servo 90"); myServo.write(90); delay(1000); } // LEDs if (true) { Serial.println("LEDS"); for (int i = 0; i < pow(2, 3); i++) { Serial.println(i); digitalWrite(maintenanceLedPin, decode(i, 0) ? HIGH : LOW); digitalWrite(runningLedPin, decode(i, 1) ? HIGH : LOW); digitalWrite(signalLedPin, decode(i, 2) ? HIGH : LOW); delay(500); } } // Photoresitor if (true) { Serial.println("Photoresistor"); for (int i = 0; i < 10; i++) { Serial.println(analogRead(photoresistorPin)); delay(10); } } }
Hardware Challenges
Distributing Power
All the components can be powered by the 5V provided by the Arduino except for the stepper motor, which needs 5V from a stronger power supply. To be able to wire all of my components through the breadboard, one live rail is connected to the power supply, and the other to the Arduino. Both of the ground rails are connected directly to the Arduino to provide a common ground. To ensure the power supply does not provide signal to the "Arduino rail", you need to remove the jumper from the appropriate side of the power supply. When connecting the power supply to the breadboard, be sure also to check that the positive and negative indicators correctly align with the rails they are plugged into. If they don't you need to connect the supply to the other side of the breadboard. (Technically, you would instead treat the ground rail as the live rail and vice-versa, but that seems like a relatively sure-fire way to create confusion.)
Stepper Motor Pins
When creating my test program to drive the stepper motor, it initially didn't work. The stepper motor would make noise, and the red LEDs on the driver board would turn on and off, but it wouldn't rotate. I ran the simple example program in the Arduino IDE to test if the problem was in my code, and sure enough the motor worked when I ran the example program. I eventually found that when you initialise the stepper motor interface, the pins are not meant to be consecutively ordered.
Correct code
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
My incorrect code
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11); // Note that the middle two pins are wrong
Servo Motor and Infrared Receiver Library Incompatibility
After attaching all of my core components, I attached the Infrared receiver that would allow me to use the remote control on the machine (see below). When I did however, the receiver would rapidly pulse red as if receiving signals and the servo motor would make noise without turning - as if it were receiving malformed control signals. After an extensive debugging session, I found I could - without touching any of the wiring - run simple example programs that used only one of the two components without any issues. However, any program that used both would cause the issues to occur.
With a corrected worded internet search, I found a forum post from someone with the same issue. I discovered that the servo motor and IR receiver libraries I was using have compatibility issues with one another. Though initially I tried looking for other libraries I could use, my ultimate solution was to detach the servo motor whenever it is not in use.
Consequently, this is what the current version of the Hardware::write method looks like
void Hardware::write(bool value) { writeMotor.attach(writeMotorPin); msg(value ? "TRUE" : "FALSE"); if (value) { writeMotor.write(0); delay(1000); writeMotor.write(180); delay(1000); } else { writeMotor.write(180); delay(1000); writeMotor.write(0); delay(1000); } writeMotor.detach(); }
Implementing Maintenance Mode
After all the components were fully working, I implemented what I call "maintenance mode". This is a feature of the final program which I used for the assembly, debugging, and calibration of the machine, as well as when something goes wrong while it is running. Using the Infrared receiver and remote control provided in the kit, I can send commands to the program telling it to do certain things. The final program lets you:
Turn maintenance mode on and off
Start or pause the turning machine
Rotate the stepper motor (which drives the disc) clockwise or anti-clockwise
Change how much the stepper motor would rotate by when given the above instruction
Write true/false to the current bit
Rotate the servo motor (the write motor) to a 90 degree angle (useful for positing bits onto the disc in the right place)
Output to the serial monitor a reading from the photoresistor (useful for calibrating the light threshold)
Output to the serial monitor if the read head is reading true or false.
As well as outputting to the serial monitor, I use three LEDs on the breadboard to help understand what state the machine is in. * Blue LED - Turns on whenever maintenance mode is active. * Green LED - Turns on whenever the turning machine is running. * Yellow LED - Blips whenever an input is received from the IR receiver.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #8 : Assembly of the Machine
The entire machine is built on a large cardboard box, which I did simply so I could easily move the machine as one unit. Each component is attached to the box by some combination of blu-tack, tape, carboard, and metal wire. Besides continual failure of the memory loop, there were only a few other challenges I faced while physically putting the machine together.
Attaching the Write Motor
The write motor needs to be suspended above the memory disc in such a way that the area around the disc is left clear for memory bits to pass through without hitting anything. Initially I suspended the motor in the air with a single piece of wire, as shown in the video below.
youtube
Can't see the video? Watch it on YouTube!
This worked reasonably well, and had the advantage that is was easy to adjust the position of the motor by bending the wire. However, because I had taped the wire to the side of box, it was able to rotate back and forth, meaning I couldn't move the cables connecting to the motor out of the way of the memory loop without also moving the motor. Without a better way to keep the wire stable, my solution was to attach another piece of cardboard to the box and drive the wire through it at two different points. This allowed the wire to stay in a fixed position, while still being moved about. I could have drove the wire into the box itself, but by using a separate piece of cardboard instead I could ensure I wouldn't make holes in the box that might cause problems for future me.
Protecting the Photoresistor
The challenges I faced with the memory disc had knock on effects on the read head. In testing, I had discovered that the photoresistor worked better if it was positioned as close as possible to where a flipped cardboard bit would land when blocking it. However, when the disc warbled, this would cause flipped bits to strike the photoresistor from the side. This in turn caused the stepper motor to get stuck, and the photoresistor to get bent out of place.
My solution to this was to place a stack of post-it notes next to the photoresistor that prevent the disc from leaning too low. Sometimes this will create enough friction on the disk to prevent the stepper motor from turning, which in turn causes the memory disc to go out of sync. Overall though, this solution solves more problems then it creates.
Inconsistent Ability to Write to Bits
During my initial test of how well I could write to the cardboard bits, I left the motor running for a while to see if eventually the bit would stop working. In that experiment it kept going, and seemed to hold up really well. However, in that test the bit was sat on a rigid, flat, surface. Now that the bit was on a wobbly disk, problems consistently being able to write to the bit became quite frequent.
My solution to this was to every time the machine attempts to write to a bit, have the machine then re-read the bit to test if writing was successful. While it is very much a hack, it works quite well on the final machine. I call it the failure-to-write failsafe.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #9 : The Lean, Mean, Turing Machine!
Having assembled the full machine (and tampered with the memory to motor attachment a bit) I was able to get the machine fully operational! In the two videos below, the Turning Machine is running a program that increments the binary number in memory by one. Though, the Arduino program is capable of running any valid Turing Machine program is it given (in the format of a C data structure). The first video is a more polished demo - but unfortunately the machine wasn't able to count very far, so I also included the second video. It's not as good a demo, but in that video it counts up to four! (It's also a good demonstration of the failure-to-write failsafe.)
You can see the complete code for the final machine on GitHub.
youtube
Can't see the video? Watch it on YouTube!
youtube
Can't see the video? Watch it on YouTube!
Project Evaluation
Overall I am really happy with how the project turned out! Though the poor thing is a bit janky, it is a successful proof of concept. Though not practical at face value, I could envision a working version of the machine being used to help teach computing principles in a more hands-on way. And beyond any practical purpose, it is a surprising enguaging machine to watch in action when it works.
What was learned
From the start of my initial experiments to the end of my final project, I've been surpised by how many components are - at the tinker's level - essentially "plug-and-play". Obviously this is in great part due to the huge number of libraries and tutorials that have been made by other people, but the ability to - for example - plug in an infrared receiver and pretty quickly start sending instructions to other hardware is very compelling. Once you start doing more complicated things though, it is definately important to grow a foundational understanding of electonrics. I've learned that often, when making a component work, the devil's in the details. Be that knowing how much power a component needs to drive, how different libraries interact, or the importance of a shared ground!
As I noted in an early post, I also thought it's been interesting to see how frquently potential divier circuits are used in electronics. Though they can be sometimes abstracted away by the plug-and-play nature of the environment, it's been interesting to play attention to some of the funamental features of electronics that reappear often.
Opportunities for Development
I think the most apparent way in which the project could be developed further is through more sophisticed manufacture of the physical components. The electronics and code of the machine work flawlessly, however persistent issues with the disc memory and the memory cells mean that accumulation of error quickly brings the machine into critial error (sometimes even just one error throws everything off!). In principle, the issues of rotating the disc could be resolved by replacing the stepper motor with a DC motor and putting limit switches at the position of each memory cell so the program can tell when it has successfully rotated to a cell. In practise though, I can also imagine each indiviual limit switch becoming a potential point of failure in and of itself; the best part is no part, as they say. And besides that, the issues writing to the bits are not so easy to resolve. The precision offered by factory made parts or custom parts manufactured with proper tools (be that more traditional workshop tools, 3D printing, or anything else), would make the machine much more reliable.
The key benefit of making the machine more reliable is it could be made to operate on far more bits - which is another way the project could be developed further. A larger memory would allow for more sophisticed programs to be run. Though a mechanical computer such as this will never get anything close to even some of the easiest digital machines in terms of scale and speed, with enough memory some interseting (if trivial) programs could still be executed. There is an underlying limitation of the current design which - if addressed - could make it much easier to add more memory to the machine. Currently, the size of the memory and the hardware of the machine are couppled to one anthoer. In other words, if you wanted to make the memory disc bigger (or memory cells smaller), you would have to adjust the machine's hardware to be able to accomdate for this. If you consider Turing's hypothetical machine however, becuse it moves over a strip of paper, the paper can be of any size, and the machine will remain the same. If the size of the memory and hardware of the machine could be decouppled, running much larger programs becomes *more* feasable. If a sufficiently large memory could be achieved, you could even adapt the Turning machine to read it's program from memory, instead of it being stored on the Arduino.
0 notes
physicalcomputingwithj · 4 years ago
Text
Final Project #10 : Technical Breakdown
Alternative title: So you want to build a Turning Machine?
This post breaks down the electrical components and wiring of the final machine I made, and also gives a high level overview how I made the Arduino program.
Components
For this project I used the Arduino UNO R3 Project Starter Kit.
1 x UNO R3 Controller Board (the Arduino)
1 x Breadboard
Breadboard jumper wires
3 x LEDs (of any colour, but preferably each a different colour)
1 x Red or Green LED
4 x 220Ω resistor
1 x Photoresistor
1 x 5KΩ resistor
1 x Stepper Motor (28BYJ-48)
1 x Stepper Motor Driver Board (ULN2003)
1 x Power supply module
1 x Servo Motor (SG90)
1 x IR receiver module
1 x IR remote
Wiring
The code
You can see the complete code on GitHub.
The code is separated into two core classes, AMachine and Hardware. This separation of concerns made it much easier for me to work on the project. Had the two functions been conflated, it would have been trickier to tell whether errors were due to issues with my hardware code or due to my turning machine implementation.
As these classes handle almost all of the program's complexity, the main .ino filb. is actually quite straightforward.
#include "hardware.h" #include "amachine.h" #include "example-program.h" // Variables AMachine amachine; Hardware hardware; // Setup - Is run once by the Arduino void setup() { amachine.program = incrementFourBit(); amachine.hardware = &hardware; hardware.amachine = &amachine; hardware.setup(); } // Loop - Is run repeatedly by the Arduino void loop() { hardware.loop(); if (!hardware.maintenanceMode) amachine.step(); }
The A-machine Class
The a-machine class emulates the Turning machine, and I implemented in such a way to try and be as abstract as possible. For this reason, it has no direct control over the electronics. Instead, it has a pointer to a Hardware object, which is uses as an interface to the hardware. The declaration for the class summarises it quite well.
class AMachine { public: size_t state = 0; bool running = false; Program program; Hardware* hardware; void step(); void halt(String msg); };
It's attributes store the state of the machine, whether or not is it currently running, and what program it is executing (more on that below). The class only has two methods, step and halt. The step method is the heart of the machine, and is run every time the Arduino loops. Even without comments, it is pretty easy to understand.
void AMachine::step() { if (!running) { // If the machine isn't running, don't step return; } if (state >= program.stateCount) { // If the program doesn't contain instructions for the state we are in, halt the machine halt("Invalid state"); return; } bool bit = hardware->read(); // Read the current bit, and select which instruction to execute accordingly Instruction ins = bit ? program.states[state].onTrue : program.states[state].onFalse; if (bit != ins.write) { // ins.write dicates *what* should be written to the bit (not *if* we should write or not) hardware->write(ins.write); } hardware->move(ins.move); // move as many bits as the instruction dicates, where positive numbers go one way, and negative numbers the other state = ins.state; // Advance the machine to the next state if (ins.halt) { // Halt the machine if the program tells us to halt("Ended successfully"); return; } }
The halt method could be replaced with running = false; state = 0; in every instance, but as it was also helpful to output debug information to the serial monitor while testing, I turned it into it's own utility method.
void AMachine::halt(String msg) { String m("Machine stopped in state #"); m += state; m += " ("; m += msg; m += ")"; hardware->msg(m); running = false; state = 0; }
The Hardware Class
The Hardware class handles all of the machine's electronics and serial communication. It's primary methods are read, write, move, and msg, as they are all used by the AMachine to interface with the hardware. It also has setup and loop methods, called by their respective Arduino functions, that are responsible for initialising the hardware, updating the status LEDs, and responding to input from the IR receiver. All additional methods are essentially utility methods used in service of the others.
Of note, having learned by lesson from a previous experiment, is that the read method will take an average reading from the photoresistor over a short time period, to help mitigate the effects of noise in the signal.
You can see the code for this class on GitHub in the hardware.h and hardware.info files.
Keeping the AMachine and Hardware classes separate worked out well for me when implementing the failure-to-write failsafe. As this is distinctly a hardware problem, I was able to update the Hardware::write() method to keep checking if the bit had been written to, without having to alter or complicate the Turning machine implementation at all.
Hardware::write() in hardware.ino
void Hardware::write(bool value) { msg(String("Writing ") + (value ? "TRUE" : "FALSE")); writeMotor.attach(writeMotorPin); bool realValue = !value; while (realValue != value) { writeMotor.write(writeMotor.read() == 0 ? 180 : 0); delay(800); realValue = read(); } writeMotor.detach(); }
Writing in AMachine::step() in amachine.ino
if (bit != ins.write) { hardware->write(ins.write); }
Another nice benefit of separating the hardware code from the turning machine code, is that the hardware code could be completely reworked for a different set-up, and the AMachine class would still be able to operate it so long as it had the primary methods required. For this project I didn't implement a C++ interface as it wasn't needed, but had I have done it would have looked something like this.
class HardwareInterface { public: bool maintenanceMode // Program methods virtual void setup() = 0; virtual void loop() = 0; // A Machine methods virtual bool read() = 0; virtual void write(bool value) = 0; virtual void move(int amt) = 0; virtual void msg(String msg) = 0; };
With only some minor tweaks the rest of the program would work with any class that implemented this interface. (You could even implement a virtual hardware if you wanted to!)
The Program Structure
The final piece of the puzzle is how we represent programs that the Turning machine can execute, which in the sprit of separation of concerns is separate from the Turning Machine implementation itself. (It also just makes it a lot easier to swap out which program the Turning Machine is running.) Like the Turning machine's step method, the code is reasonably self explanatory. As it's a simple data structure, it fits entirely in a header file, no implementation file required!
struct Instruction { // The series of actions the A-Machine should take on a given step bool write; // Wheter it should write true or false to the current bit int move; // How many bits along it should move. Positive values move one way, negative values move the other way, zero means don't move. int state; // Which number state in the program the A-Machine should switch to. bool halt; // If true, the A-Machine will stop running }; struct State { Instruction onTrue; // The instruction that should be run when the current bit is true Instruction onFalse; // The instruction that should be run when the current bit is false }; struct Program { State* states; // An array of states int stateCount; };
It wasn't really necessary for this project to be able to serialise and read back Program data structures, so the example program I ran in the demos is just hard coded in `example-program.cppb..
This is the program it generates. It is able to increment a 4-bit number by one, and then return to the memory cell where it started.
State If Write Move Next State Halt 0 TRUE FALSE 1 1 FALSE FALSE TRUE 0 0 TRUE 1 TRUE FALSE 1 2 FALSE FALSE TRUE -1 0 TRUE 2 TRUE FALSE 1 3 FALSE FALSE TRUE -2 0 TRUE 3 TRUE FALSE 1 4 FALSE FALSE TRUE -3 0 TRUE
n.b. if you didn't want it to be able to return to where it started, you only actually need one state. This program can increment a binary number of any size.
State If Write Move Next State Halt 0 TRUE FALSE 1 0 FALSE FALSE TRUE 0 0 TRUE
0 notes