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.

Home Alarm System – Code

The software for this system implements three states: System Clear, Deactivated, and Alarm Triggered. The code loops while the system is active waiting for the light break sensor to be open indicating that an intruder has broken in. At this point, the code snippet below compares a passcode input by a user to a predefined passcode and acts accordingly. The LCD tells you whether or not the passcode that was entered is correct.

Temperature Controlled Box – Source Code


#include <asf.h>
#include <stdio.h>

static volatile int16_t last_temperature;
#define INPUT_PIN              ADCCH_POS_PIN1
#define OUTPUT_STR_SIZE        40
struct pwm_config mypwm[4];
int32_t destemp;
#define MY_LED0     IOPORT_CREATE_PIN(PORTA, 16)
#define MY_fan                  IOPORT_CREATE_PIN(PORTA,19)
#define MY_LED1     IOPORT_CREATE_PIN(PORTA, 17)
static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
{
#ifdef CONF_BOARD_OLED_UG_2832HSWEG04
gfx_mono_draw_filled_rect(0,0,128,32,GFX_PIXEL_CLR);
#endif
int32_t temperature;
char out_str[OUTPUT_STR_SIZE];

/* Compute current temperature in Celsius, based on linearization
* of the temperature sensor adc data.
*/
ioport_init();

ioport_set_pin_dir(MY_LED0, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(MY_LED1, IOPORT_DIR_OUTPUT);
if (result > 697) {
temperature = (int8_t)((-0.0295 * result) + 40.5);
} if (result > 420) {
temperature = (int8_t)((-0.0474 * result) + 53.3);
} else {
temperature = (int8_t)((-0.0777 * result) + 65.1);
}

last_temperature = temperature;

// Write temperature to display
last_temperature = last_temperature * 1.8 + 32;

if (last_temperature-2>destemp)
{

snprintf(out_str, OUTPUT_STR_SIZE, "Temperature:%1d F AC  ", last_temperature);
gpio_set_pin_low(LED0);
gpio_set_pin_high(LED1);
ioport_set_pin_level(MY_LED0, 3.3);
ioport_set_pin_level(MY_fan, 5);
ioport_set_pin_level(MY_LED1, 0);

}
else if (last_temperature+2<destemp)
{

snprintf(out_str, OUTPUT_STR_SIZE, "Temperature:%1d F Heat", last_temperature);
gpio_set_pin_low(LED1);
gpio_set_pin_high(LED0);
ioport_set_pin_level(MY_LED0, 0);
ioport_set_pin_level(MY_fan, 0);
ioport_set_pin_level(MY_LED1, 3.3);
}
else// if(last_temperature==destemp || (last_temperature-2>=destemp && last_temperature+2<=destemp))
{

snprintf(out_str, OUTPUT_STR_SIZE, "Temperature:%1d F off ", last_temperature);
gpio_set_pin_high(LED0);
gpio_set_pin_high(LED1);
ioport_set_pin_level(MY_LED0, 0);
ioport_set_pin_level(MY_fan, 0);
ioport_set_pin_level(MY_LED1, 0);

}
//            snprintf(out_str, OUTPUT_STR_SIZE, "Temperature: %2d F", last_temperature);
gfx_mono_draw_string(out_str, 0, 0, &sysfont);

// Start next conversion.
adc_start_conversion(adc, ch_mask);
}

static void adc_handler2(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
{
#ifdef CONF_BOARD_OLED_UG_2832HSWEG04
gfx_mono_draw_filled_rect(0,0,128,32,GFX_PIXEL_CLR);
#endif
char out_str[OUTPUT_STR_SIZE];
if(result>= 65000){
result = 64;
}
result = (int)(result/22);
if(result < 64){
result = 64;
}
destemp=result;
snprintf(out_str, OUTPUT_STR_SIZE, "Desired Temp: %1d F   ", result);
//result = (int)result/1.2;
//gfx_mono_draw_filled_rect(0,17,result,7,GFX_PIXEL_SET);
//gfx_mono_draw_filled_rect(result,17,128-result,7,GFX_PIXEL_CLR);

gfx_mono_draw_string(out_str, 0, 20, &sysfont);
adc_start_conversion(adc, ch_mask);
}

int main(void)
{
struct adc_config         adc_conf;
struct adc_config adc_conf2;
struct adc_channel_config adcch_conf;
struct adc_channel_config adcch_conf2;

board_init();
sysclk_init();
sleepmgr_init();
irq_initialize_vectors();
cpu_irq_enable();
gfx_mono_init();

// Enable backlight if display type is not OLED
#ifndef CONF_BOARD_OLED_UG_2832HSWEG04
ioport_set_pin_high(LCD_BACKLIGHT_ENABLE_PIN);
#endif

// Initialize configuration structures.
adc_read_configuration(&ADCA, &adc_conf);
adcch_read_configuration(&ADCA, ADC_CH0, &adcch_conf);

adc_read_configuration(&ADCB, &adc_conf2);
adcch_read_configuration(&ADCB, ADC_CH1, &adcch_conf2);

/* Configure the ADC module:
* - unsigned, 12-bit results
* - VCC voltage reference
* - 200 kHz maximum clock rate
* - manual conversion triggering
* - temperature sensor enabled
* - callback function
*/
adc_set_conversion_parameters(&adc_conf, ADC_SIGN_ON, ADC_RES_12,
ADC_REF_VCC);
adc_set_clock_rate(&adc_conf, 200000UL);
adc_set_conversion_trigger(&adc_conf, ADC_TRIG_MANUAL, 1, 0);
adc_enable_internal_input(&adc_conf, ADC_INT_TEMPSENSE);

adc_set_conversion_parameters(&adc_conf2, ADC_SIGN_ON, ADC_RES_12,
ADC_REF_VCC);
adc_set_clock_rate(&adc_conf2, 200000UL);
adc_set_conversion_trigger(&adc_conf2, ADC_TRIG_MANUAL, 1, 0);
adc_enable_internal_input(&adc_conf2, ADC_INT_TEMPSENSE);

adc_write_configuration(&ADCA, &adc_conf);
adc_set_callback(&ADCA, &adc_handler);

adc_write_configuration(&ADCB, &adc_conf2);
adc_set_callback(&ADCB, &adc_handler2);

/* Configure ADC channel 0:
* - single-ended measurement from temperature sensor
* - interrupt flag set on completed conversion
* - interrupts disabled
*/
adcch_set_input(&adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_NONE,
1);
adcch_set_interrupt_mode(&adcch_conf, ADCCH_MODE_COMPLETE);
adcch_enable_interrupt(&adcch_conf);

adcch_write_configuration(&ADCA, ADC_CH0, &adcch_conf);

// Enable the ADC and start the first conversion.
adc_enable(&ADCA);
adc_start_conversion(&ADCA, ADC_CH0);

adcch_set_input(&adcch_conf2, ADCCH_POS_PIN1, ADCCH_NEG_NONE,
1);
adcch_set_interrupt_mode(&adcch_conf2, ADCCH_MODE_COMPLETE);
adcch_enable_interrupt(&adcch_conf2);

adcch_write_configuration(&ADCB, ADC_CH1, &adcch_conf);

// Enable the ADC and start the first conversion.
adc_enable(&ADCB);
adc_start_conversion(&ADCB, ADC_CH1);

do {
// Sleep until ADC interrupt triggers.
sleepmgr_enter_sleep();
} while (1);
}

Harry Potter Sorting Hat – Code Overview

Once all of the sensors were integrated into a single circuit, all of the separate test code for the sensors (among other things) needed to be written into a single cohesive program. The sensors and piezo buzzers were controlled by the Arduino microcontroller. As such, much of the code was written in the Arduino IDE.

Serial communication was first established between the Arduino and A3BU boards. The A3BU code would initialize and set itself up using the USART framework. The A3BU code had a continuous loop where it would wait to receive a byte (character) on its RxD pin on J1. If the Arduino sent a ‘G’, the A3BU would then write “Gryffindor” to its LCD screen using the GFX Monochrome library. If it received an ‘S’, it would write “Slytherin,” and etc. There were 4 characters for each of the 4 houses.

In the Arduino code, it first entered a while loop which would iterate until the IR distance sensor detected the hat was placed on a person’s head. Afterwards, the color sensor was triggered. It initialized itself and read in the RGB values (luminance was calculated based on these). It passes the calculated luminance value to a function, which returned a single character corresponding to the house (‘G’ = Gryffindor, ‘S’ = Slytherin, ‘R’ = Ravenclaw, ‘H’ = Hufflepuff). This character was sent to the A3BU board using the Serial.print() function. At this point, it would call a function to trigger the piezo buzzers for play the Harry Potter tune to indicate that the mighty hat had sorted the wearer into a house. The wearer would then take the hat off and read their house on the A3BU’s LCD screen (as well as enjoy that sweet tune played by the piezo buzzers).

A3BU Xplained Code: 

#include <asf.h>
#include <conf_usart_example.h>
#include <delay.h>
#include <gfx_mono.h>
#include <sysfont.h>

int main (void)
{
       board_init();
       sysclk_init();
       gfx_mono_init();

       // USART options.
       static usart_rs232_options_t USART_SERIAL_OPTIONS = {
              .baudrate = USART_SERIAL_EXAMPLE_BAUDRATE,
              .charlength = USART_SERIAL_CHAR_LENGTH,
              .paritytype = USART_SERIAL_PARITY,
              .stopbits = USART_SERIAL_STOP_BIT
       };

       // Initialize USART driver in RS232 mode
       usart_init_rs232(USART_SERIAL_EXAMPLE, &USART_SERIAL_OPTIONS);

       gpio_toggle_pin(NHD_C12832A1Z_BACKLIGHT);

     
       uint8_t received_byte;
       char character;

       while(true)
       {
              received_byte = usart_getchar(USART_SERIAL_EXAMPLE);
              character = (int)received_byte;

              gfx_mono_init();

              if(character == 'G')
              {
                     gfx_mono_draw_string("Gryffindor!", 0, 0, &sysfont);
              }else if(character == 'S')
              {
                     gfx_mono_draw_string("Slytherin!", 0, 0, &sysfont);
              }else if(character == 'H')
              {
                     gfx_mono_draw_string("Hufflepuff!", 0, 0, &sysfont);
              }else if(character == 'R')
              {
                     gfx_mono_draw_string("Ravenclaw!", 0, 0, &sysfont);
              }else
              {
                     gfx_mono_draw_string("Hat is confused...", 0, 0, &sysfont);
              }
       }
}

Sample Arduino Uno Code:

void setup() //setup is called automatically
{
    Serial.begin(9600); //Initialize baud rate to 9600
    pinMode(1, OUTPUT); //set TxD pin to output for A3BU communication
}

void loop() //Arduino calls this function automatically continuously (main function)
{
    //Until IR sensor detects the hat is put on, do not go further
    distanceReading = analogRead(2);
    while( distanceReading < 350 ) //350 is the distance from IR sensor to base of boxhat
    {
         distanceReading = analogRead(2); //read value form analog pin 2
    };
    Serial.println("Hat is put on...distance: " + distanceReading);

    
    Serial.println("Activating color sensor...");
    uint16_t luxValue = readColorSensor();    

    char houseCode = determineHouse(luxValue); 
    Serial.print(houseCode); //send the house code to the A3BU

    if(houseCode == 'G' || houseCode == 'S' || houseCode == 'H' || houseCode =='R')
    {
        playHPTune();
    }

    delay(5000);
}
uint16_t readColorSensor()
{
    if (tcs.begin()) 
    {
      Serial.println("Found sensor");
    } else 
    {
      Serial.println("No TCS34725 found ... check your connections");
      while (1);
    }     
    // Now we're ready to get readings!
    delay(1000);

    uint16_t r, g, b, c, colorTemp, lux;

    
    tcs.getRawData(&r, &g, &b, &c);
    colorTemp = tcs.calculateColorTemperature(r, g, b);
    lux = tcs.calculateLux(r, g, b);

    return lux;
}
 

Music Keypad – Code Snippet

One of the most difficult parts of our projects was figuring out how to initialize our keypad and retrieve inputs to the A3BU.  In the code below, we initialize the 4 ADC pins to receive inputs from the 4 buttons on the keypad.  Then we have to set each of the inputs to -1 initially.  The code stated that the light on the microcontroller, LED0, will turn on when button one is pushed via an if statement. We also added a string of code to display on the LCD screen to show what button was pressed. At first, the LCD screen seemed to act a bit intermittent, and after speaking with the professor, a 10k resistor was added. By adding the resistor, we were able to have button one work successfully.  We started to implement button two. We had the issue of something not working properly. Both buttons were wired exactly the same and the code was also identical. After a bit of troubleshooting, the code was changed from  “if (ioport_get_pin_level(COL1) > 0)” to “if (ioport_get_pin_level(COL1) == 1)”. By setting a more definite condition, we were able to clear that issue up. We were able to get button one and two to work successfully. We, then, added button three and four. They were tested and passed with no errors.  When not pressed, the keypad button in question is set to 0: off. When pressed, it is set to 1: on.

Code to initialize our inputs from the numeric keypad. COL1 is for button 1, COL2 for button 2, COL3 for button 3, and COL4 for button 4.
Code to get the input from the keypad back to the board

Once we programmed our A3BU to receive inputs from the keypad, we had to create the desired output.  To create our short jingles, we used pulse width modulation to vary the frequencies and duty cycles.  The desired notes corresponded with a specific frequency, and we chose 85 as the optimum duty cycle to get the clearest tone.  By adding in delays, we were able to control the length of the note and the length of the pauses between the notes.

Code snippet from part of the song “Jingle Bells”

“Jingle Bells”

Defect Detector – Code

The most interesting part of the code for our project was, without a doubt, the code used to filter out false positives read by the ADC. There were two main methods we utilized to do this:

  • Get a good threshold value
// in main

int baseline_reading[25];
for (int i = 0; i < 25; i++) {
    _delay_ms(100);
    baseline_reading[i] = last_result;
}
THRESHOLD = get_threshold(baseline_reading, 25);

CUTOFF = THRESHOLD + OFFSET;

/* … */

int get_threshold(int readings[], int read_count)
{
    float xbar = 0.0, s = 0.0;
    for (int i = 0; i < read_count; i++) {
        xbar += readings[i];
    }
    xbar /= read_count;
    for (int i = 0; i < read_count; i++) {
        s += squared(readings[i] - xbar);
    }
    s /= read_count - 1;
    s = sqrt(s);
    return (int) (xbar + 0.75 * s);
}

The first method employed is simply setting a good threshold that the readings must surpass in order for the program to act upon them. To obtain this threshold a number of readings are taken at the start of the program. These preliminary readings are then used to get a good baseline for the threshold by finding the arithmetic mean of them and adding a fraction of a standard deviation. The precise number of standard deviations we added was arrived at through trial and error.

  • Ensure that the reading is consistent
do {
     // busy loops 200 ms at a time until a sufficiently large weight is placed on the sensor
     int streak = 0;
     do {
         _delay_ms(100);
         if (last_result < THRESHOLD)
             streak = 0;
         else
             streak++;
     } while (streak < STREAK_THRESHOLD);
     streak = 0;
     // sorts the object appropriately
     sort_object(&mypwm, last_result, CUTOFF);
} while (1);

The other method employed is to ensure that the readings we get from the ADC are consistently significant. To do this, instead of calling the sort routine whenever a result is significant, we only call the sort routine when each of the last N results have been significant. This helps to filter out fleeting spikes of voltage read by the ADC whether they are from simple noise or from the spike that results when an object is dropped onto the sensor rather than placed on top of it.

Audio Visualizer — Code (Fourier Transform)

Having previously used Fourier Series and Transforms in our signals and systems, and differential equations courses, we knew that we could find frequency data from a time domain signal. The challenge really came in trying to determine the best way to implement our understanding. On paper and by hand, Fourier Series and Transforms can be pretty time and calculation intensive. Also, because we’d be performing our calculations on a microcontroller, we had to shift our focus to Discrete Fourier Transforms, and then because of our near real time goal and limited processing power, we had to consider the algorithmic Fast Fourier Transform (FFT). With all this in mind, we knew that someone else had probably already solved this problem. After some research about implementation of FFT on Arduino (https://www.norwegiancreations.com/2017/08/what-is-fft-and-how-can-you-implement-it-on-an-arduino/) and some quick Google searches for AVR FFT, we stumbled upon an efficient, flexible, and open-source FFT written in Assembly (http://elm-chan.org/works/akilcd/report_e.html). Fortunately, the open-source code by ©ChaN was easily integrated into our project with Atmel Studio. Furthermore, the source files were well commented and were added to our project with great ease. Using the guidance of the comments in the Assembly FFT and the basic outline of the Arduino implementation, we were able to successfully sample and transform a signal into the frequency domain in near realtime.

Welcome to UofL code

As we are learned the code in lab 4 for ADC and PWM signal. I change the internal temperature sensor to the internal light sensor to control the frequency of the PWM signal, “adcch_set_input(&adcch_conf, ADCCH_POS_PIN0, ADCCH_NEG_NONE, 1); “.  For the night LED will flash cause it have low frequency. and for the day you can see the solid light as the high frequency.  Then we are set the while loop to display the pattern we want.  below is the code PWM signal send out from the 7 pin of A3BU. Each pin connect to 110 ohm resistor then connect to the LED circuit.  Here is a link to the full code we are use for our project. (we modified the code from the ADC internal temperature of A3BU in new example) . 

https://drive.google.com/file/d/1cT-WLmUR-ehIqZsQ6bTmqbt9dZRz8n-2/view?usp=sharing

 

Anagram Solver Code Post and Schematic (Interesting Aspect) – Team SilverCharm

Our final project was the Anagram Solver we have many interesting aspects to our project. Some of these high level aspects include:

  • Controlling the LCD on the A3BU
  • Asking the USER for Input
  • Flashing LED’s for correct/incorrect answers
  • Flashing LED for inputs
  • Generating results based on C programming

Although we have so many interesting features, one of our best features is the crazy light show! After exiting the program, the program flashes both the blue and red LED’s and the LCD backlight in a spectacular fashion. It is truly an amazing aspect of our project!

Below is our schematic made from our board.

Cyber-Hand: Unreal Engine 4 Coding

The best way to display the bend graphically was to show a 3D-representation of a hand doing the same action.  The first way to do this that came to mind would be to make a small “game” simulation in Unreal Engine 4.

The most important parts of this simulation are the UpdateFingers function, which takes input from the A3BU, and the Animation Blending, which takes the input and displays the appropriate graphical representation.

UpdateFingers:

The UpdateFingers function reads a line from the serial stream of the a3bu as a string of the form “100,100,100,100,100” which corresponds as follows “thumb%,index%,middle%,ring%,pinkie%”.  It then takes this string, parses it into 5 discrete values, and passes those values to local percentage variables.  Those percentage variables were then used for the Animation Blending.

Processing for Animation Blending:

The processing step takes the percentage variables and scales them to the total time of each animation.  These time values are then saved in new time variables.

Animation Blending:

Animation Blending takes the time variables and sets the close animation for each finger to the corresponding point in time.  These animations are then blended into the final pose.