Pong Video Game Hardware

Each controller consisted of one XplainedMini board and a sliding potentiometer mounted in a wooden casing. The sliding potentiometer was a PS45L-R02BR10K model which varied resistance from 0-10kΩ, with a sliding range of 45mm. It was chosen because the range of resistance was broad enough to produce many values, allowing the virtual paddle to move in “real time”. Another reason this specific potentiometer was used was its visual appeal and compatibility with a breadboard. The ability to easily insert and remove the device streamlined the design process, where any shortcomings could be easily corrected. A 10kΩ resistor was connected, in series, to the potentiometer’s voltage pin. This extra resistor was used to vary the voltage; without it, the voltage would have been a constant +5V and the virtual paddles would not move.

Pong Video Game Overview

This project served as a chance to use topics learned in previous labs, to create a fun video game, playable by all ages. Using serial communication, a pong game was constructed. The software to build it used a combination of Python, C, and Assembler languages, and was and programmed in Windows and run on Windows and the ATMega boards, with an intent to port to a Raspberry Pi 3 model B. The hardware for the controllers was implemented using two XplainedMini boards, each with a sliding potentiometer mounted on a breadboard. These components were all set in a wooden casing. At runtime, the game displayed on a PC and players could maneuver their paddles using the potentiometer to pass a “ball” back and forth. 

Since this game was meant to be played by anyone, it was imperative to create user friendly graphics and controllers. For the software, this goal was a bit of a challenge. The frame and data transfer rates had to be adjusted to make the sliding motion of the potentiometer match the movement of the paddle. For the hardware, this meant making a handheld, sturdy controller that was easy to assemble and disassemble.

Pong Video Game Software

When the game was played, the ball appeared to hit the floor and ceiling; however, it did not actually collide with either. Instead, the ball’s range of motion was limited to bounce the ball from the top to the bottom. This was a y-axis range of 88-502, and when the ball reached either limit it reversed its direction. In order to make the ball collide with the floor and ceiling, both would have needed to be sprites (moving images), then pygame would have to check for a collision in each frame. This process would have been similar to the collision of the ball with the paddle, but it was simpler to limit the ball’s vertical range of motion.

Even though the ball did not actually collide with the bounds of the game, it did collide with the paddles. The function that checked for a collision was “spritecollide”, which was included in the pygame library. When a collision occurred, an empty list “block_hit_list” was filled with whichever object the ball collided with when a collision was detected. The parameters of this function included a list of both players, the reference to the ball object, and “False” which told the program to not delete the player object that the ball hit.

Temperature-Controlled Fan: ADC-Reading Code

I’ve got something super exciting to share: our temperature sensor code! The code retrieves the current ADC value by calling the function ADC_Get() , then interprets that data based on the temperature sensor documented output voltage.  ‘t’ is a global float variable. ‘fanSpeed’ is a global int value. Possible applications: converting float and integer types to character strings for output over serial connections or LCD output.

void Get_Temp(void) 
{
 
 int k = 0; //used to offset output, in case of negative temperatures
 float celcius; // Anders C.
 float fahrenheit; // Daniel Gabriel F.
 float kelvin; // William Thompson, 1st Barron K. 
 float v = 0.0; //voltage
 char temp[17]; //string for output to LCD
 
 for (int i = 0; i< 16; i++)
 {
      temp[i] = 32; //pad output string with spaces
 }
 temp[16]='\0'; //null terminator for c-style string
 
 ADC_Get();
 Acc = (((int)HADC) * 0x100 + (int)(LADC));
 //temperature sensor rated output range: 
 //  100mV to 1750mV (-40 to 125°C)
 if (Acc > 20 && Acc < 359) 
 {
      // Convert ADC output to fraction of Vcc. (5V)
      v = (Acc/1023.0)*(5.0); 
      // per specifications 10mV / °C (100° = 1 Volt, 750mV @ 25°C)
      celcius = (v - 0.5)*100.0; 
      kelvin = 273.15 + celcius; // celcius to kelvin
      fahrenheit = celcius * 9.0/5.0 + 32.0; // celcius to fahrenheit
      t = fahrenheit;
 
      if(t<0)
      {
           t*=-1;
           k = 1; //offset display by 1, to make way for minus sign
           temp[0] = '-';
      }
      else
      {
           k = 0;
           temp[6] = 32;
      }
 
      int p = (int)(t*100.0); // float * 100, then cast to type int.
 
      //Note: 48 is the ASCII offset of '0' (49 = '1', 50 = '2', etc.)
      temp[(5+k)] = p % 10 + 48; //pull off hundredths decimal place. 
      p /= 10;
      temp[(4+k)] = p % 10 + 48; //pull off tenths decimal place
      p /= 10;
      temp[(3+k)] = '.';
      temp[(2+k)] = p%10 + 48;
      p/=10;
      if (p>=0) {
           temp[1+k] =p%10 +48;
           p/=10;
      }
      else
           temp[1+k]='0';
      if (p>=0){
           temp[0+k] = p +48;
      }
      else
           temp[0+k] = '0';

      if (t > targetTemp) // max operating temperature has been exceeded
      {
           //display fan output speed
           temp[13]= fanSpeed / 10 % 10 +48;
           temp[14]= fanSpeed % 10 + 48;
           char msgStrFan[5] = {"Fan=%"};
           for (int i = 9; i < 16; i++)
           {
                temp[i] = msgStrFan[i];
                if(i == 12) i = 15; //skip index 13,14
                temp[i]= msgStrFan[i];
           }
      }
      if (fanSpeed > 99) //fanspeed has reached maximum
      {
           //for a little fun, after the fan has reached
           //maximum speed, we output text to the display
           //until the fan speed reaches <60% load
 
           iZombie = 1; //locks output until fan speed < 60%
           char msgStr1[16] = {"He's dead, Jim."};
           for (int i = 0; i < 16; i++)
           {
                temp[i] = msgStr1[i];
           }
      }
      if (iZombie == 1)
      {
           if (fanSpeed >80)
           {
                char msgStr1[16] = {"He's dead, Jim."};
                for (int i = 0; i < 16; i++)
                {
                     temp[i] = msgStr1[i];
                }
 
           } else if (fanSpeed >60)
                {
                     char msgStr2[16] = {"... could he be?"};
                     for (int i = 0; i < 16; i++)
                          {
                               temp[i] = msgStr2[i];
                          }
 
                } else if (fanSpeed >0)
                     {
                          char msgStr3[16] = {"He. Is. ALIVE !!"};
                          for (int i = 0; i < 16; i++)
                          {
                               temp[i] = msgStr3[i];
                          }
                          iZombie = 0;
                     }
           }
      LCD_Puts(temp); //output character string to LCD
      Wait(); // This function just limits the refresh rate of the LCD
      Wait(); // If the refresh rate is too fast, the characters are dim
  } 
}

Plant Watering System: Schematics

Drum roll please……….

We would like to present our brand new, super deluxe, autonomous plant watering system! You can see in the photo below how all of the components are wired together.

If that looks a little jumbled to you, you might have better luck looking at our schematic instead:

As an overview, the soil moisture sensor is placed into the potting soil of said plant. The program mandates in a loop that the sensor takes 30 consecutive samples  – these values are averaged, and then is sent to the water pumping sections of the code, where the 328P determines whether the user-set threshold is above or below the taken soil values. If the soil moisture is below this threshold, the controller will send a high signal from pin D6 (anode), a low signal from D7 (cathode) , and a high Signal from D5 (enable) to the motor driver. The motor driver receives these signals in conjunction with an external 9-12V power supply (to physically power the pump) and then the water pumps to the plant!

 

Plant Watering System: Code

Hey there Embedded Fans!

Progress is being made on our plant watering apparatus! As of late, the Unknowns have their code in progress, and we’ve gotten our motor driver to work! In order to do this, we’ve sent a high and low signal to two of the input pins on the driver (from pins D6 and D7), and sent a high signal to the enable pin (from pin D5) ! See our code snippet for super cool and exciting bit shifting operations!

But… don’t listen to me – see for yourself!

Soon our plants will never go thirsty again! :,)

SIMON Sign Language Translator Deep Learning Image Processing Code

Below is one of the deep learning training methods in the Python code running on the Windows machine to analyze and classify images. Please see the comments for a detailed description of the program.

	
def train(self, learning_rate = 0.0001, num_epochs = 1500, minibatch_size = 32, print_cost = True):
"""
Created By: Jacob Taylor Cassady
Last Updated: 2/7/2018
Objective: Implements a three-layer tensorflow neural network: LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX.
Arguments:
learning_rate -- learning rate of the optimization
num_epochs -- number of epochs of the optimization loop
minibatch_size -- size of a minibatch
print_cost -- True to print the cost every 100 epochs		
Returns:
parameters -- parameters learnt by the model. They can then be used to predict.
"""
print("Entering train....")
ops.reset_default_graph()
(n_x, m) = self.train_matrix.shape    # (n_x: input size, m : number of examples in the train set)
n_y = self.train_targets.shape[0]		  # n_y : output size.
costs = []							  # Used to keep track of varying costs.

# Create placeholders for TensorFlow graph of shape (n_x, n_y)
print("Creating placeholders for TensorFlow graph...")
X, Y = self.create_placeholders(n_x, n_y)
print("Complete.\n")

# Initialize Parameters
print("Initailizing parameters for TensorFlow graph...")
parameters = self.initialize_parameters()
print("Complete.\n")

# Build the forward propagation in the TensorFlow Graph
print("Building the forward propagation in the TensorFlow Graph...")
Z3 = self.forward_propagation(X, parameters)
print("Complete.\n")

# Add the cost function to the Tensorflow Graph
print("Adding cost function to the TensorFlow Graph")
cost = self.compute_cost(Z3, Y)
print("Complete.\n")

# Define the TensorFlow Optimizer.. We are using an AdamOptimizer.
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

# Initialize all the variables with our newly made TensorFlow Graph
init = tf.global_variables_initializer()

# Use the TensorFlow Graph to train the parameters.
with tf.Session() as session:
# Run the initialization
session.run(init)

# Perform Training
for epoch in range(num_epochs):
	epoch_cost = 0.								# Defines a cost related to the current epoch
	num_minibatches = int(m / minibatch_size)	# Calculates the number of minibatches in the trainset given a minibatch size
	minibatches = random_mini_batches(self.train_matrix, self.train_targets, minibatch_size)

	for minibatch in minibatches:
		# Retrieve train_matrix and train_targets from minibatch
		mini_matrix, mini_targets = minibatch

		# Run the session to execute the "optimizer" and the "cost",
		_, minibatch_cost = session.run([optimizer, cost], feed_dict={X:mini_matrix, Y:mini_targets})

		# Sum epoch cost
		epoch_cost += minibatch_cost / num_minibatches

	# Done training.  Print the cost of every 100 epochs
	if print_cost == True and epoch % 100 == 0:
		print("Cost after epoch %i: %f" % (epoch, epoch_cost))
	# Keep track of the cost of every 5 epochs for plotting later
	if print_cost == True and epoch % 5 == 0:
		costs.append(epoch_cost)

# Plot the costs for analysis
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iteration ( per 5 )')
plt.title('Learning rate = ' + str(learning_rate))
if print_cost == True:
	#plt.show()
	pass

# Save the parameters as a varaible for prediction and evaluation of fit to test set.
parameters = session.run(parameters)

# Develop TensorFlow prediction standards for testing accuracy  of test and train sets
correct_prediction = tf.equal(tf.argmax(Z3), tf.argmax(Y))

# Develop accuracy identifier using TensorFlow
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))

# Display accuracy of train and test predictions.
print("Train Accuracy: ", accuracy.eval({X: self.train_matrix, Y: self.train_targets}))
print("Test Accuracy: ", accuracy.eval({X: self.test_matrix, Y: self.test_targets}))

# Return parameters for prediction against the model.
self.parameters = parameters

train_accuracy = accuracy.eval({X: self.train_matrix, Y: self.train_targets})
test_accuracy = accuracy.eval({X: self.test_matrix, Y: self.test_targets})

accuracies = {"train_accuracy": train_accuracy,
              "test_accuracy" : test_accuracy}

# Return parameters for prediction against the model.
return parameters, accuracies

Spinning LED Display – Schematics

To create our spinning LED display we needed to create a way for the board to communicate with each LED individually. It was important to use an odd number of LEDs so that we could create letters and numbers with a center arm, like E, H, and A. We decided to use 7 LEDs because this number creates a nice height for each letter, and makes the display readable at a distance. The LED display was constructed by creating a ground bus on one side of the breadboard and connecting the cathode of each LED to it. The anode of each LED was then connected to a 100Ω resistor, which in turn connected to a wire which led to the input pin that would control the LED. (refer to schematic below) In this way we were able to send information to each individual LED.

We also needed a simple way to turn the motor on and off, we decided on a slide switch to meet that need. This was created by connecting the positive wire from the motor to ground and the negative end to the center pin of the slide switch. The leftmost pin of this switch was connected to ground while the rightmost pin was connected to 5V. For a bit of fun we created an on/off LED, which was accomplished by running a resistor from the center pin of the switch to the anode of the LED and the cathode was connect to ground. We would advise caution not supply the ground and 5V required to run this circuit from your microcontroller as this practice permanently disabled one of our boards. Rather use some other source of power and ground, like the large breadboards supplied in the lab.

 

 

 

Spinning LED Display – Supplemental Application Code

Since our project had to be able to display various characters (A-Z, a-z, 0-9, , ., ?, !), it was obvious that individually defining each character through its five frames would be time consuming. Thus, a supplementary application was created in Unity to provide the team with a easy-to-use interface that allowed us to “draw” each character and receive a binary/hexadecimal equivalent that would be stored in the code.

‘Bit’ Class:

This object was attached to each of the buttons in the scene. It kept track of whether a button has been enabled or disabled and dynamically updates the color of the button depending on its state (this allows the user to draw the image).

// ‘Bit’ Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Bit : MonoBehaviour
{
    private bool isSet;
    // Use this for initialization
    void Start()
    {
    }
    // Update is called once per frame
    void Update()
    {
           if (isSet)
           {
                  this.GetComponent<Image>().color = Color.black;
           }
           else
           {
                  this.GetComponent<Image>().color = Color.white;
           }
    }
    // Set the bit in memory
    public void Set()
    {
           if (isSet)
           {
                  isSet = false;
           }
           else
           {
                  isSet = true;
           }
    }
    // Get the value of the bit
    public int getBit()
    {
           if (isSet)
           {
                  return 1;
           }
           else
           {
                  return 0;
           }
    }
}

‘Exit’ Class:

This object, once attached to a button, closes the application when pressed.

// ‘Exit’ Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Exit : MonoBehaviour
{
    // Exit the program
    public void exit()
    {
           Application.Quit();
    }
}

‘TextHandler’ Class:

This object continuously communicated with each of the ‘bits’ on the screen and translated them into binary and hexadecimal before updating the text boxes present on the application.

// ‘TextHandler’ Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TextHandler : MonoBehaviour
{
    //Binary Text Holders
    public Text b0;
    public Text b1;
    public Text b2;
    public Text b3;
    public Text b4;
    //Hex Text Holders
    public Text h0;
    public Text h1;
    public Text h2;
    public Text h3;
    public Text h4;
    //Binary Holder
    public List<Bit> bits;
    // Use this for initialization
    void Start()
    {
    }
    // Update is called once per frame
    void Update()
    {
           b0.text = bits[30].getBit() + "" + bits[25].getBit() + "" + bits[20].getBit() + "" + bits[15].getBit() + "" + bits[10].getBit() + "" + bits[5].getBit() + "" + bits[0].getBit();
           b1.text = bits[31].getBit() + "" + bits[26].getBit() + "" + bits[21].getBit() + "" + bits[16].getBit() + "" + bits[11].getBit() + "" + bits[6].getBit() + "" + bits[1].getBit();
           b2.text = bits[32].getBit() + "" + bits[27].getBit() + "" + bits[22].getBit() + "" + bits[17].getBit() + "" + bits[12].getBit() + "" + bits[7].getBit() + "" + bits[2].getBit();
           b3.text = bits[33].getBit() + "" + bits[28].getBit() + "" + bits[23].getBit() + "" + bits[18].getBit() + "" + bits[13].getBit() + "" + bits[8].getBit() + "" + bits[3].getBit();
           b4.text = bits[34].getBit() + "" + bits[29].getBit() + "" + bits[24].getBit() + "" + bits[19].getBit() + "" + bits[14].getBit() + "" + bits[9].getBit() + "" + bits[4].getBit();
           h0.text = binToHex(b0.text);
           h1.text = binToHex(b1.text);
           h2.text = binToHex(b2.text);
           h3.text = binToHex(b3.text);
           h4.text = binToHex(b4.text);
    }
    // Convert the binary value to hex
    string binToHex(string binary)
    {
           int total = 0;
           // Find the decimal equivalence
           for (int i = 0; i < 7; i++)
           {
                  total += int.Parse(binary.Substring(i, 1)) * (int)   (Mathf.Pow(2, 6 - i));
           }
           // Convert it to hex
           int firstValue = total / 16;
           int secondValue = total % 16;
           return getHexChar(firstValue) + getHexChar(secondValue);
    }
    // Return the correct hex character
    string getHexChar(int d)
    {
           const string hex = "0123456789ABCDEF";
           return hex.Substring(d, 1);
    }
}

The code is located here (https://github.com/ahuynh15/IconToBinary) and can be compiled and built into a standalone application.