- Home /
Arduino LDR Sensor to Play Audio Clip At A Certain Level
I am trying to get information from a simple LDR light sensitive Resistor to trigger an audio clip in Unity 3D. I can get the clip to play but it overlaps and repeats the audio. What I want it to do is activate an audio clip from a random array of audio clips. How can I do this? This is the code I have.
using UnityEngine;
using System.Collections;
using System.IO.Ports;
using System.Threading;
public class Sending : MonoBehaviour {
//Random Clips
public AudioClip[] voices;
public GameObject worldLight;
public static Sending sending;
//public static SerialPort sp = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One);
public static SerialPort sp = new SerialPort("/dev/cu.wchusbserial1420", 115200);
public string message2;
void Awake () {
if (sending == null) {
DontDestroyOnLoad (gameObject);
sending = this;
} else if (sending != this) {
Destroy (gameObject);
}
}
float timePassed = 0.0f;
// Use this for initialization
void Start () {
OpenConnection();
}
// Update is called once per frame
void Update () {
//timePassed+=Time.deltaTime;
//if(timePassed>=0.2f){
//print("BytesToRead" +sp.BytesToRead);
message2 = sp.ReadLine();
print(message2);
// timePassed = 0.0f;
//}
if (message2!="") {
print("called");
lightMeUp(message2);
}
}
public void OpenConnection()
{
if (sp != null)
{
if (sp.IsOpen)
{
sp.Close();
print("Closing port, because it was already open!");
}
else
{
sp.Open(); // opens the connection
sp.ReadTimeout = 16; // sets the timeout value before reporting error
print("Port Opened!");
// message = "Port Opened!";
}
}
else
{
if (sp.IsOpen)
{
print("Port is already open");
}
else
{
print("Port == null");
}
}
}
void OnApplicationQuit()
{
sp.Close();
}
public static void sendYellow(){
sp.Write("y");
}
public static void sendGreen(){
sp.Write("g");
//sp.Write("\n");
}
public static void sendRed(){
sp.Write("r");
}
public static void sendBlue(){
sp.Write("b");
}
public static void sendBlue2(){
sp.Write("1");
}
public static void sendPulse1(){
sp.Write("p");
}
public static void sendPulse2(){
sp.Write("q");
}
void lightMeUp(string message){
print (message);
float fl = float.Parse (message) / 100.0f;
fl = 1.0f - fl;
//Do Something
GetComponent<AudioSource> ().PlayOneShot (voices [Random.Range (0, voices.Length)]);
}
}
Answer by Thomas-Mountainborn · Nov 23, 2015 at 08:05 PM
The fact that it's overlapping is undoubtedly because your Arduino is repeatedly sending out the message that the light sensor threshold has been breached. Since Update() is called every frame, and therefore lightMeUp(), and your Arduino sends out its message every frame as well as long as the sensor value is valid, a sound will be triggered every frame, resulting in a cacophonous mess worthy of any middle school music class.
You need a "flag", a boolean field in your MonoBehaviour, that is set to true when a sound is played, and set to false when the sound finishes playing. Then, in lightMeUp(), you only allow a new sound to play if the boolean is set to false.
Thanks $$anonymous$$, I'm not really great at booleans can you help me with how I would do that? I did try altering the data on the Arduino code so that it read something like this:
void loop()
{
//Light Sensor
if((lightInt - analogRead(lightPin)) > 50 || (lightInt - analogRead(lightPin)) < -50){
lightInt = analogRead(lightPin);
Serial.println(lightInt);
}
That way I thought perhaps it might not send information as much. Basically I'm looking for a way to have the voice clips activate when it's around dusk or just getting dark out.
Booleans are real easy.
//Random Clips
public AudioClip[] voices;
...
//_isPlayingSound is true when a sound is currently playing - just as the name suggests.
private bool _isPlayingSound;
...
void light$$anonymous$$eUp(string message)
{
//If there is a sound currently playing, return immediately so we don't play another sound simultaneously.
//if(_isPlayingSound) is shorthand for if(_isPlayingSound == true), in case you where wondering.
if (_isPlayingSound)
return;
print(message);
float fl = float.Parse(message) / 100.0f;
fl = 1.0f - fl;
//Pick a random sound.
AudioClip randomClip = voices[UnityEngine.Random.Range(0, voices.Length)];
//Play that sound.
GetComponent<AudioSource>().PlayOneShot(randomClip);
//Flag that we are currently playing a sound.
_isPlayingSound = true;
//Start a coroutine that will set the flag to false once the sound stops playing, so we can start playing a new sound.
StartCoroutine(RunResetIsPlayingSoundFlag(randomClip.length));
}
private IEnumerator RunResetIsPlayingSoundFlag(float soundDuration)
{
//Wait until the sound finishes playing.
yield return new WaitForSeconds(soundDuration);
//Disable the _isPlayingSound flag, so we can play a new sound since this one is over.
_isPlayingSound = false;
}
Ah ok, thanks $$anonymous$$, That fixed the overlapping and repeating audio issue but I'm not sure how to make it so that it only plays when the light level is either near dark or near light?
I also tried doing something like this but I did not get the results I was looking for: void light$$anonymous$$eUp(string message){ print (message); float fl = float.Parse (message) / 100.0f; fl = 1.0f - fl;
//worldLight.GetComponent<Light>().intensity = fl;
//Do Something
GetComponent<AudioSource> ().PlayOneShot (voices [Random.Range (0, voices.Length)]);
if (GetComponent<AudioSource> ().isPlaying) {
GetComponent<AudioSource> ().Stop ();
} else {
GetComponent<AudioSource> ().Play ();
}
}
Answer by KnightRiderGuy · Nov 23, 2015 at 09:54 PM
lol ok I changed my arduino code to this and now the audio repeat over and over again randomly, ??
int lightPin = 0; //Define Pin For Photoresistor
//int lightInt = 0;
int lightLevel = analogRead(0);
int threshold = 250;
int range = 1000;
const byte rLed = 12; //Sets Pin number LED is conneted too
const byte yLed = 11;
const byte gLed = 10;
const byte bLed = 9;
const byte bLed2 = 8;
char myChar; //changed the name of this variable because they are not all colurs now
const byte pulsePins[] = {13, 7}; //pins for a pulse output
char pulseTriggers[] = {'p', 'q'};
const int NUMBER_OF_PULSE_PINS = sizeof(pulsePins);
unsigned long pulseStarts[NUMBER_OF_PULSE_PINS];
unsigned long pulseLength = 500;
void setup()
{
//Serial.begin (9600);
Serial.begin (115200);
Serial.setTimeout(13); //Added today Sun Nov 22
pinMode(rLed, OUTPUT);
pinMode(yLed, OUTPUT);
pinMode(gLed, OUTPUT);
pinMode(bLed, OUTPUT);
pinMode(bLed2, OUTPUT);
digitalWrite(rLed, LOW);
digitalWrite(yLed, LOW);
digitalWrite(gLed, LOW);
digitalWrite(bLed, LOW);
digitalWrite(bLed2, LOW);
for (int p = 0; p < NUMBER_OF_PULSE_PINS; p++)
{
pinMode(pulsePins[p], OUTPUT);
digitalWrite(pulsePins[p], LOW);
}
}
void loop()
{
//Light Sensor
/*if((lightInt - analogRead(lightPin)) > 50 || (lightInt - analogRead(lightPin)) < -50){
lightInt = analogRead(lightPin);
Serial.println(lightInt);
}*/
if(lightLevel > threshold - range && lightLevel < threshold + range)
Serial.println(lightLevel);
if (Serial.available()) //if serial data is available
{
int lf = 10;
myChar = Serial.read(); //read one character from serial
if (myChar == 'r') //if it is an r
{
digitalWrite(rLed, !digitalRead(rLed)); //change the state of the r LED
}
if (myChar == 'b')
{
digitalWrite(bLed, !digitalRead(bLed));
}
if (myChar == 'y')
{
digitalWrite(yLed, !digitalRead(yLed));
}
if (myChar == 'g')
{
digitalWrite(gLed, !digitalRead(gLed));
}
if (myChar == '1')
{
digitalWrite(bLed2, !digitalRead(bLed2));
}
for (int p = 0; p < NUMBER_OF_PULSE_PINS; p++)
{
if (myChar == pulseTriggers[p])
{
pulseStarts[p] = millis(); //save the time of receipt
digitalWrite(pulsePins[p], HIGH);
}
}
}
//the following code runs each time through loop()
for (int p = 0; p < NUMBER_OF_PULSE_PINS; p++)
{
if (millis() - pulseStarts[p] >= pulseLength) //has the pin been HIGH long enough ?
{
digitalWrite(pulsePins[p], LOW); //take the pulse pin LOW
}
}
}
Well yeah, you set the "range" variable to 1000. The analogRead() value can only be between 0 and 1023. " if(lightLevel > threshold - range && lightLevel < threshold + range)" will therefore always be true.
$$anonymous$$ake the range value smaller - somewhere around 50, maybe. Also, you need to analogRead() in loop(), or else you will only read the value once on startup, and never again afterwards.
Sorry for my stupidity but not really sure I follow.? I think I am also missing something on the unity side as I have the same code in that read as a string?
int lightPin = 0; //Define Pin For Photoresistor
int threshold = 250;
//The range value should not be very high, because analogRead() can only return values between 0 and 1023. It is necessary because if you were to use a single value (i.e. lightLevel == threshold), it would be impossible that you stay on that value for a sustained period of time. Using the "range" variable, you check if the lightLevel is between 200 and 300, for instance.
int range = 50;
...
void loop()
{
//Read the current light level.
int lightLevel = analogRead(lightPin);
//If the light level is within our desired range, send it to Unity.
if (lightLevel > threshold - range && lightLevel < threshold + range)
Serial.println(lightLevel);
....
}
...
Your answer
Follow this Question
Related Questions
Populate an array of GameObjects with collision? 1 Answer
Triggering candle lights on with Colliders 1 Answer
Triggering a specific sound in an Array - syntax help likely... 1 Answer
Make an array/list of objects in enemy sight trigger 1 Answer
How to add colliders to an array, and pick the oldest one? 1 Answer