Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Trying Something With Unity & Arduino

Discussion in 'Scripting' started by KnightRiderGuy, Nov 25, 2015.

  1. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    Ok I'm trying to get a little help here with some minor issues I'm having with some scripting between Unity 3D and Arduino.
    Basically what I want to do is receive data from an Arduino Photo resistor or (LDR) and input that information into Unity 3D and be able to trigger audio clips from an array of audio clips something like this:

    Lets say that when the LDR senses light at a level close to dusk (evening/sunset) in the real world that this information is received in Unity 3D and triggers off some audio responses from an array of audio clips.

    Now I have it working to some degree but not exactly like how I mention above. I'll include a video link here: This is a little demo showing how it is currently working. As you will see not quite what I want.


    Here is the Arduino code I am using and it sends the information but I think on my Unity end I'm not reading it in correctly as Its using a string.
    Arduino Code:

    Code (CSharp):
    1. int lightPin = 0; //Define Pin For Photoresistor
    2. int lightInt = 0;
    3. //int lightLevel = analogRead(0);
    4. //int threshold = 250;
    5. //int range = 50;
    6.  
    7. const byte rLed = 12; //Sets Pin number LED is conneted too
    8. const byte yLed = 11;
    9. const byte gLed = 10;
    10. const byte bLed = 9;
    11. const byte bLed2 = 8;
    12. char myChar;         //changed the name of this variable because they are not all colurs now
    13. const byte pulsePins[] = {6, 7};  //pins for a pulse output
    14. char pulseTriggers[] = {'p', 'q'};
    15. const int NUMBER_OF_PULSE_PINS = sizeof(pulsePins);
    16. unsigned long pulseStarts[NUMBER_OF_PULSE_PINS];
    17. unsigned long pulseLength = 500;
    18.  
    19. void setup()
    20. {
    21.   //Serial.begin (9600);
    22.   Serial.begin (115200);
    23.   Serial.setTimeout(13); //Added today Sun Nov 22 ( not sure if this is needed? )
    24.   pinMode(rLed, OUTPUT);
    25.   pinMode(yLed, OUTPUT);
    26.   pinMode(gLed, OUTPUT);
    27.   pinMode(bLed, OUTPUT);
    28.   pinMode(bLed2, OUTPUT);
    29.   digitalWrite(rLed, LOW);
    30.   digitalWrite(yLed, LOW);
    31.   digitalWrite(gLed, LOW);
    32.   digitalWrite(bLed, LOW);
    33.   digitalWrite(bLed2, LOW);
    34.   for (int p = 0; p < NUMBER_OF_PULSE_PINS; p++)
    35.   {
    36.     pinMode(pulsePins[p], OUTPUT);
    37.     digitalWrite(pulsePins[p], LOW);
    38.   }
    39. }
    40.  
    41. void loop()
    42. {
    43.   //Light Sensor
    44.     if((lightInt - analogRead(lightPin)) > 50 || (lightInt - analogRead(lightPin)) < -50){
    45.       lightInt = analogRead(lightPin);
    46.       Serial.println(lightInt);
    47.     }
    48.     //read the current light level.
    49.    /* int lightLevel = analogRead(lightPin);
    50.     //If the light level is within our desired range, send it to Unity.
    51.     if(lightLevel > threshold - range && lightLevel < threshold + range)
    52.     Serial.println(lightLevel);
    53.     */
    54.   if (Serial.available())              //if serial data is available
    55.   {
    56.    
    57.     int lf = 10;
    58.  
    59.     myChar = Serial.read();             //read one character from serial
    60.     if (myChar == 'r')                  //if it is an r
    61.     {
    62.       digitalWrite(rLed, !digitalRead(rLed));  //change the state of the r LED
    63.     }
    64.     if (myChar == 'b')
    65.     {
    66.       digitalWrite(bLed, !digitalRead(bLed));
    67.     }
    68.  
    69.     if (myChar == 'y')
    70.     {
    71.       digitalWrite(yLed, !digitalRead(yLed));
    72.     }
    73.  
    74.     if (myChar == 'g')
    75.     {
    76.       digitalWrite(gLed, !digitalRead(gLed));
    77.     }
    78.  
    79.     if (myChar == '1')
    80.     {
    81.       digitalWrite(bLed2, !digitalRead(bLed2));
    82.     }
    83.  
    84.     for (int p = 0; p < NUMBER_OF_PULSE_PINS; p++)
    85.     {
    86.       if (myChar == pulseTriggers[p])
    87.       {
    88.         pulseStarts[p] = millis();  //save the time of receipt
    89.         digitalWrite(pulsePins[p], HIGH);
    90.       }
    91.     }
    92.  
    93.     for (int q = 0; q < NUMBER_OF_PULSE_PINS; q++)
    94.     {
    95.       if (myChar == pulseTriggers[q])
    96.       {
    97.         pulseStarts[q] = millis();  //save the time of receipt
    98.         digitalWrite(pulsePins[q], HIGH);
    99.       }
    100.     }
    101.   }
    102.  
    103.   //the following code runs each time through loop()
    104.   for (int p = 0; p < NUMBER_OF_PULSE_PINS; p++)
    105.   {
    106.     if (millis() - pulseStarts[p] >= pulseLength)  //has the pin been HIGH long enough ?
    107.     {
    108.       digitalWrite(pulsePins[p], LOW);   //take the pulse pin LOW
    109.     }
    110.   }
    111.  
    112.   for (int q = 0; q < NUMBER_OF_PULSE_PINS; q++)
    113.   {
    114.     if (millis() - pulseStarts[q] >= pulseLength)  //has the pin been HIGH long enough ?
    115.     {
    116.       digitalWrite(pulsePins[q], LOW);   //take the pulse pin LOW
    117.     }
    118.   }
    119. }

    Now here is my Unity Code. I have included the whole code so you can see everything.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO.Ports;
    4. using System.Threading;
    5.  
    6. public class Sending : MonoBehaviour {
    7.  
    8.     //Random Clips
    9.     public AudioClip[] voices;
    10.  
    11.     //_isPlayingSound is true when a sound is currently playing - just as the name suggests.
    12.     private bool _isPlayingSound;
    13.  
    14.     public GameObject worldLight;
    15.     public static Sending sending;
    16.  
    17.     //public static SerialPort sp = new SerialPort("COM4", 9600, Parity.None, 8, StopBits.One);
    18.     public static SerialPort sp = new SerialPort("/dev/cu.wchusbserial1420", 115200);
    19.  
    20.     public string message2;
    21.  
    22.  
    23.  
    24.     void Awake () {
    25.         if (sending == null) {
    26.             DontDestroyOnLoad (gameObject);
    27.             sending = this;
    28.         } else if (sending != this) {
    29.             Destroy (gameObject);
    30.         }
    31.        
    32.     }
    33.  
    34.  
    35.     float timePassed = 0.0f;
    36.     // Use this for initialization
    37.     void Start () {
    38.         OpenConnection();
    39.     }
    40.    
    41.     // Update is called once per frame
    42.     void Update () {
    43.         //timePassed+=Time.deltaTime;
    44.         //if(timePassed>=0.2f){
    45.  
    46.             //print("BytesToRead" +sp.BytesToRead);
    47.             message2 = sp.ReadLine();
    48.             print(message2);
    49.         //    timePassed = 0.0f;
    50.         //}
    51.  
    52.         if (message2!="") {
    53.             print("called");
    54.             lightMeUp(message2);
    55.         }
    56.     }
    57.  
    58.     public void OpenConnection()
    59.     {
    60.        if (sp != null)
    61.        {
    62.          if (sp.IsOpen)
    63.          {
    64.           sp.Close();
    65.           print("Closing port, because it was already open!");
    66.          }
    67.          else
    68.          {
    69.           sp.Open();  // opens the connection
    70.           sp.ReadTimeout = 16;  // sets the timeout value before reporting error
    71.           print("Port Opened!");
    72.         //        message = "Port Opened!";
    73.          }
    74.        }
    75.        else
    76.        {
    77.          if (sp.IsOpen)
    78.          {
    79.           print("Port is already open");
    80.          }
    81.          else
    82.          {
    83.           print("Port == null");
    84.          }
    85.        }
    86.     }
    87.  
    88.     void OnApplicationQuit()
    89.     {
    90.        sp.Close();
    91.     }
    92.  
    93.     public static void sendYellow(){
    94.         sp.Write("y");
    95.     }
    96.  
    97.     public static void sendGreen(){
    98.         sp.Write("g");
    99.         //sp.Write("\n");
    100.     }
    101.    
    102.  
    103.     public static void sendRed(){
    104.         sp.Write("r");
    105.     }
    106.  
    107.  
    108.     public static void sendBlue(){
    109.         sp.Write("b");
    110.     }
    111.  
    112.     public static void sendBlue2(){
    113.         sp.Write("1");
    114.     }
    115.  
    116.     public static void sendPulse1(){
    117.         sp.Write("p");
    118.     }
    119.  
    120.     public static void sendPulse2(){
    121.         sp.Write("q");
    122.     }
    123.  
    124.     void lightMeUp(string message){
    125.         //If there is a sound currently playing, return immediately so we don't play another sound simultaneously.
    126.         //if(_isPlayingSound) is shorthand for if(_isPlayingSound == true), in case you where wondering.
    127.         if (_isPlayingSound)
    128.             return;
    129.         print (message);
    130.         float fl = float.Parse (message) / 100.0f;
    131.         fl = 1.0f - fl;
    132.  
    133.         //Pick a random sound.
    134.         AudioClip randomClip = voices[UnityEngine.Random.Range(0, voices.Length)];
    135.         //Play that sound.
    136.         GetComponent<AudioSource>().PlayOneShot(randomClip);
    137.         //Flag that we are currently playing a sound.
    138.         _isPlayingSound = true;
    139.         //Start a coroutine that will set the flag to false once the sound stops playing, so we can start playing a new sound.
    140.         StartCoroutine(RunResetIsPlayingSoundFlag(randomClip.length));
    141.     }
    142.    
    143.     private IEnumerator RunResetIsPlayingSoundFlag(float soundDuration)
    144.     {
    145.         //Wait until the sound finishes playing.
    146.         yield return new WaitForSeconds(soundDuration);
    147.         //Disable the _isPlayingSound flag, so we can play a new sound since this one is over.
    148.         _isPlayingSound = false;
    149.    
    150.     }  
    151. }
    152.  
    On an additional note I would also like to try and expand this to do something like this also.
    Lets say the light level reaches that of "sunup" morning time that this would then trigger from a different array of random audio clips appropriate for sensing morning time.

    Also that lets say someone kept waving their hand over the sensor repeatedly that it would draw audio responses from another array of random clips that indicated an "Annoyed" response.

    I'm not really the greatest at code so any useful and productive help on this would be greatly apreciated.
     
    code-blep likes this.
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,930
    So you are getting communication from the Arduino into the serial port and it comes out in Unity?

    What seems to be broken then? Have you started by specifying all of the messages that you intend to pass from Arduino to Unity and started working on getting the first one, then the next one, etc.?
     
  3. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    Well the communication is OK, I'm just having more code related issues than anything. I need help with getting it to do what I need it to do.
    Which is too first only play an audio clip from a random array IF the light value is near sunset level.
    Thats step one.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,930
    I would say step one is defining a message protocol whereby the Arduino encodes the light level into a string and sends it to Unity.

    Step two would be making the Arduino send those messages properly.

    Step three would be making Unity receive and display those messages.
     
  5. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    Yes but I'm not sure I'm coding it right, that's why I posted the code.
     
  6. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    OK I'm still not making any headway with this one. just to recap I'm trying to get this so that the Arduino sends light value information into Unity, which it does as a string I think if I'm understanding that part of the code.

    The Unity side is receiving that data as a message string but I'm not making it clear or understanding HOW to make it so that I can get these results:

    1) Have it so that when the light level reaches near darkness (Something happens)
    2) When the light level reaches near daylight (Something happens)
    3) that if the light value was deliberately changed frequently like someone waving their hand in front of the sensor repeatedly (Something Happens)

    Note the "Something happens" will be just random audio clips drawn from 3 different audio clip arrays.
     
  7. KnightRiderGuy

    KnightRiderGuy

    Joined:
    Nov 23, 2014
    Posts:
    515
    I didn't realize my somewhat duplicate question was not really answered here. I did manage to get some more in-depth help on both another posting and the Arduino Forum. Just in case anyone else is struggling with a similar issue of how to get the different data strings coming into Unity from the Arduino to works this is what I ended up needing to do.

    Although this part of the script is not really important except as near as I can tell to display the data coming in to the Inspector slots defined like this:

    Code (CSharp):
    1. //public float TempSensorData;
    2.     public string TempSensorData;
    3.     public string LDRdata;
    4.     public string ButtonsData;
    Next in the Update I had to scrap what I originally had and go with case statements, which is something very new for me to learn. But I'm starting to get the hang of them a little after someone on the Arduino forum took the time to walk me through what they are and how to use them.

    Code (CSharp):
    1. void Update()
    2.     {
    3.         string read = sp.ReadLine();
    4.         if (read == "")
    5.             return;
    6.  
    7.         MessageReceived(read);
    8.     }
    9.  
    10.     void MessageReceived(string message)
    11.     {
    12.         char type = message[0];
    13.         string value = message.Substring(1);
    14.  
    15.         switch (type)
    16.         {
    17.         case 'T':
    18.             //temperature sensor reading
    19.             TemperatureText.text = TempSensorData.ToString ();
    20.             TempSensorData = value;
    21.             break;
    22.  
    23.         case 'L':
    24.             //light sensor reading
    25.             LDRdata = value;
    26.             if(LDRdata == "") return; //if its empty stop right here
    27.  
    28.             // parse the input to a float and normalize it (range 0..1) (we could do this already in the Arduino)
    29.             float input =  1 -  float.Parse (LDRdata) / 100f;
    30.             // set the slider to the value
    31.             float oldValue = slider.value;
    32.             slider.value = input;
    33.  
    34.             // after the slider is updated, we can check for the other things for example play sounds:
    35.             if (source.isPlaying) return; // if we are playing a sound stop here
    36.  
    37.             // else check if we need to play a sound and do it
    38.             if (slider.value > 0.9f && oldValue <= 0.9f) // ---------this has changed
    39.                 source.PlayOneShot (BrightnessAudioClips [Random.Range (0, BrightnessAudioClips.Length)]);
    40.  
    41.             else if (slider.value < 0.15f && oldValue >= 0.15f) //----------this has changed
    42.                 source.PlayOneShot (DarknessAudioClips [Random.Range (0, DarknessAudioClips.Length)]);
    43.             break;
    44.  
    45.         case 'O':
    46.             //deactivate Left Direction Arrow
    47.             SystemGuidanceManagerScript WAOff = FindObjectOfType<SystemGuidanceManagerScript>();
    48.             WAOff.WestArrowOff ();
    49.  
    I didn't include the entire part for the button inputs as they are pretty much "Follow this format" & copy and paste in my old case statements from the original button inputs.
    Now the data is being handled as separate pieces of information coming into Unity from the Arduino.

    I'm extremely happy to say that I got the DS18B20 temperature sensor reading in the values and displaying them on my UI text. I searched all over the internet looking for anyone who had used the temperature sensor in Unity and was not finding anything, I may be the only one who has ever wanted to do this or something??

    Anyways, here is a video of the project working :)