Ben's Double Transducer
Kyla's Double Transducer
Ben's Double Transducer Detail: Protoboard of Buzzer and Mic
Kyla's Double Transducer Detail: Breadboard with Button, Motor Driver and Photoresistor
Kyla's Double Transducer Detail: LCD Display
Ben's Double Transducer Video
Kyla's Double Transducer Video
The system senses the brightness of a light and then a buzzer makes a sound. The buzzer gets louder the brighter the light is. Then a mic detects the volume of the buzzer and causes a motor to slide out a pole depending how loud the sound is. The louder the buzzer gets, the further the pole moves. But if you press the button, the buzzer doesn't make any sound at all. Instead, the pole just moves depending how bright the light is.
The overall process of this project was definitely a challenge, but also a valuable learning experience. After learning a few basics, we immediately had to apply all our hardware and software knowledge in a more nuanced way through the double transducer. This made it so we had to learn a lot along the way. Even so, this is often the best way to learn things, and working hands-on proved to be much more efficient than simply learning conceptually.
One major challenge we had was getting the linear actuator to displace a specified amount because the only way to communicate with it was to send high or low signals to different pins. This made it so the linear actuator could only extend and retract, giving us no control over how much it extends or retracts. To get around this, we calculated the time the linear actuator took to extend fully. Then, we calculated the change in values, and let the linear actuator extend or retract a corresponding amount of time to represent changes in position.
In retrospect, the one adjustment would maybe be reconsidering the middle step. We chose to use sound because using a buzzer and mic seemed simple at face value. However, it turned out to be a little more difficult than we expected. For instance, the buzzer essentially only has two settings: on and off. To achieve the gradations the rubric specified, we had to import a library and play around with the code it gave us to achieve different volume levels. The mic also ended up being a hard piece to work with. We weren't able to get the parts until the week it was due, and so we had slightly less time to figure out how it worked. In the end, we faced an issue where the mic val didn't seem to be responding directly to the buzzer sound. It would've been nice to have some forewarning how tricky the mic can be work with, but either way it was a valuable lesson.
As for technical growth, we definitely learned more about how information is processed in Arduino. For instance, the data types in C like unsigned ints and longs. Also, the millis event counter was a new type of data structure. For the hardware, soldering was a new experience for both of us. Even though it was a little tricky, it was fun to try. We also had to get creative with out methods of mapping output values from each step so that it would make more sense displayed on the LCD.
Block Diagram
Electrical Schematic
/*
Arduino code basic structure sketch
Demonstrates how to structure an Arduino sketch so that the loop contains four basic elements:
1) read inputs (gather information from the world)
2) compute and make decisions based on those inputs
3) drive outputs (effect different actuators in the world)
4) report information back to the user
This sketch takes input light, and plays a buzzer noise with an intensity relative to the light's brightness.
A mic picks up the sound and moves a linear actuator in or out based on the volume of the sound produced.
A button in the middle will skip the buzzer/mic, and will move the linear actuator based on the light's brightness.
*/
#include <toneAC.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
// PIN SETUP
LiquidCrystal_I2C screen(0x27, 16, 2);
const int BUTTON_PIN = 7;
// LIGHT BRIGHTNESS
const int PHOTO_PIN = A0;
// SOUND
const int MIC_PIN = A3;
const int BUZZER_PIN = 8;
// LINEAR POSITION
const int MOTOR_SIGNAL_A = 11;
const int MOTOR_SIGNAL_B = 5;
// OTHER CONSTANTS
const int BUZZER_FREQ = 250;
const unsigned long QUARTER_SECOND_WAIT = 500;
const int TIME_TO_FULL = 2500;
unsigned long next_time_to_run_lcd = 0;
unsigned long next_time_to_run_motor = 0;
// LINEAR POSITION
int old_motor_pos = 0;
bool is_motor_stop = false;
// Places the labels in the correct position with 0s for the values
void setup_screen() {
screen.init();
screen.backlight();
screen.home();
screen.print("i:00");
screen.setCursor(6, 0);
screen.print("m:00");
screen.setCursor(8, 1);
screen.print("00");
screen.setCursor(12, 1);
screen.print("o:00");
}
// Sets up the pins, screen and resets the linear actuator
void setup() {
// LIGHT BRIGHTNESS
pinMode(PHOTO_PIN, INPUT);
// SOUND
pinMode(MIC_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
// LINEAR POSITION
pinMode(MOTOR_SIGNAL_A, OUTPUT);
pinMode(MOTOR_SIGNAL_B, OUTPUT);
// Timing
next_time_to_run_lcd = millis() + QUARTER_SECOND_WAIT;
next_time_to_run_motor = millis();
setup_screen();
Serial.begin(9600);
// Reset Linear Actuator to fully retracted
digitalWrite(MOTOR_SIGNAL_A, HIGH);
digitalWrite(MOTOR_SIGNAL_B, LOW);
delay(3000);
}
// Updates the screen with new input and output values
void update_screen(long mapped_photo_val, long mapped_buzzer_val, long mapped_mic_val, long mapped_motor_val) {
// PHOTORESISTOR
screen.setCursor(2, 0);
screen.print(mapped_photo_val);
// BUZZER
screen.setCursor(8, 0);
screen.print(mapped_buzzer_val);
// MIC
screen.setCursor(8, 1);
screen.print(mapped_mic_val);
// LINEAR ACTUATOR
screen.setCursor(14, 1);
screen.print(mapped_motor_val);
}
void loop() {
// Step 1: Gather inputs
int photo_val = analogRead(PHOTO_PIN); // from photoresistor
long mapped_photo_val = map(photo_val, 0, 1023, 0, 99);
int mic_val = analogRead(MIC_PIN); // from mic
long mapped_mic_val = map(mic_val, 0, 1023, 0, 99);
int button_val = digitalRead(BUTTON_PIN); // from button
// Step 2: Make decision/Computations
int buzzer_val = 0;
int new_motor_pos = 0;
int change_motor_pos = 0;
if (button_val == LOW) { // Button is not pressed
buzzer_val = mapped_photo_val / 10; // because buzzer only has 10 sound settings, we need to scale the input into 10 discrete values
new_motor_pos = mapped_mic_val;
} else { // Button is pressed
new_motor_pos = mapped_photo_val;
}
change_motor_pos = new_motor_pos - old_motor_pos;
int motor_a_val;
int motor_b_val;
if (change_motor_pos < 0) { // Retracts if the change in input value is negative
motor_a_val = HIGH;
motor_b_val = LOW;;
} else if (change_motor_pos > 0) { // Extends if the change in input value is negative
motor_a_val = LOW;
motor_b_val = HIGH;
} else { // Stays still otherwise
motor_a_val = LOW;
motor_b_val = LOW;
}
// Step 3: Output
toneAC(BUZZER_FREQ, buzzer_val); // plays a sound with volume based on buzzer_val
if (millis() >= next_time_to_run_motor) {
if (!is_motor_stop) { // Moves the motor into the desired position based on mic/photoresistor
digitalWrite(MOTOR_SIGNAL_A, motor_a_val);
digitalWrite(MOTOR_SIGNAL_B, motor_b_val);
is_motor_stop = true;
next_time_to_run_motor = millis() + TIME_TO_FULL / 100 * abs(change_motor_pos);
} else { // Stops the motor when it reaches the desired position for TIME_TO_FULL ms to ensure it stays at the correct position
digitalWrite(MOTOR_SIGNAL_A, LOW);
digitalWrite(MOTOR_SIGNAL_B, LOW);
is_motor_stop = false;
next_time_to_run_motor = millis() + TIME_TO_FULL;
old_motor_pos = new_motor_pos;
}
}
// Step 4: Display
if (millis() >= next_time_to_run_lcd) {
update_screen(mapped_photo_val, buzzer_val * 10, mapped_mic_val, new_motor_pos); // buzzer_val * 10 to scale from 0-9 to multiples of 10 up to 90
next_time_to_run_lcd = millis() + QUARTER_SECOND_WAIT;
}
delay(100);
}