Go Down

Topic: Serial Communication with Unity3D - C# programming w/o plugins (HELP) (Read 9518 times) previous topic - next topic

Rickmc3280

Jun 01, 2014, 03:49 am Last Edit: Jun 01, 2014, 03:56 am by Rickmc3280 Reason: 1
I am trying to create a simple box in Unity 3D, that when I click UP - it sends a message to the Arduino which then increases a variable which changes the PWM of a motor I have connected to it.

No matter what I try I cannot seem to get everything working because of the ASCII codes  and complexity of the Serial communication which I am not familiar yet.

How can I write a simple script in Unity that when I click the button it increases the speed.  I want the output data going into the arduino to say Sp7 which the function can break down into Speed and then 7 which I am going to create cases or an array for.  My largest problem is setting up the Serial commands so that it reads the entire input until \n.  I would also like to make a new exit statement such as /end and when it receives that to execute the speed adjustment command.  Also what coding would be needed on the Arduinos side?

How Can I make the Arduino cypher out the data using both string and int?  Like in the example of Sp7 that way it exexutes the change speed function and changes the speed to 7?

Arduino
Code: [Select]
#include <TimerOne.h>




void setup() {
 // put your setup code here, to run once:
 
 Serial.begin(115200);
 pinMode(9, OUTPUT);
 Timer1.initialize(11230); // Intialize timer1, and set a 89hz frequency
 Serial.println ("PWM Signal has been initialized");
}
 int integerValue=0; //max value is 65535
 char incomingByte;
 int dutyCycle;

void loop() {
if (Serial.available() > 0) {   // something came across serial
   integerValue = 0;         // throw away previous integerValue
   while(1) {            // force into a loop until 'n' is received
     incomingByte = Serial.read();
     if (incomingByte == '\n') break;   // exit the while(1), we're done receiving
     if (incomingByte == -1) continue;  // if no characters are in the buffer read() returns -1
     integerValue *= 10;  // shift left 1 decimal place
     // convert ASCII to integer, add, and shift left 1 decimal place
     integerValue = ((incomingByte - 48) + integerValue);
   }
   dutyCycle=integerValue;
   Serial.println(integerValue);   // Do something with the value
  Timer1.pwm(9,dutyCycle);  // setup pwm on pin 9, with a __ duty cycle defined by int dutyCycle
 }
}


Unity C# Code
Code: [Select]
using UnityEngine;
using System.Collections;

public class GUI_Button : MonoBehaviour {
void Setup () {
{

void update() {
}

void OnGUI () {
// Make a background box
GUI.Box(new Rect(10,10,100,90), "Speed Control");

// Make the first button. If it is pressed, change of speed will occur
if(GUI.Button(new Rect(20,40,80,20), "UP")) {
// put serial command here
}

// Make the second button.
if(GUI.Button(new Rect(20,70,80,20), "DOWN")) {
// put serial command here
}
}
}



Robin2

I don't know anything about Unity3d, but may I offer some comments on your Arduino code? (in no particular order)

You don't need while(1), it just duplicates what loop() does.

Why are you using Timer1 to produce PWM values rather than the built-in analaogWrite() ?

I presume the stuff you generate with Serial.println() is going back to your Unity3d program - is it able to show you the messages? If not it might be more useful to cause the Arduino to flash an LED to give you a notification that things are working.

Can you use the Arduino Serial Monitor to control the Arduino instead of using Unity3d? It's a good idea for a beginner to be able to use the same Arduino program with the Serial Monitor and a PC program as it allows you to be sure the Arduino code works properly.

If you want to send complex comands to the Arduino - such as sp7 - then you need code to receive and store the full command before starting to interpret it. Are all the commands the same length (eg 3 chars)? How does the Arduino know when a command starts and ends? Wrapping the commands within start and end markers is a good idea - for example <sp7>

DON'T be tempted to use the C/C++ String (capital S) functions on an Arduino - it hasn't enough SRAM. Either use chars or strings (small S).

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rickmc3280

#2
Jun 01, 2014, 03:56 pm Last Edit: Jun 01, 2014, 08:32 pm by Rickmc3280 Reason: 1
Hiya Robin,

thank you for the advice, I am making adjustments now, but to answer a few of your questions:

Timer1:  I am using Timer1 to produce a very specific PWM frequency of 89 or 90 hz.  It is the only way I knew how to do it was to specify the frequency in the startup and then adjust the pwm in the loop cycle.  // IT HAS TO BE 89.9 hz to control the secondary board.

my Serial.println(), is only for diagnostics at this point and to make sure everything is getting across correctly, otherwise I have to keep the Arduino attached to an oscilloscope to make sure its putting out the right PWM.

I wish I could use the Serial Monitor in its most basic form, but for all intents and purposes I am using Unity to create a User Interface.  I will also in the future be making a user interface in 3D that allows people to control the Arduino using it instead of the Serial Monitor.

All of the commands will be 4 characters.  for example I have decided on <11.1> based on your idea for the wrapping, and the fact that I need a decimal place with a value up to 12.0.  This singular value will pretty much be the only data for now, although I am thinking of sending symbols like $$ to do specific commands which I think is simpler because you can specify them more easily without having to break apart the buffer.

In order for me to set it up right I guess I should do it all in stages: example

void loop () {

while (Serial.Available() > 0);
dataFromPC = Serial.Read:

**** In this Area I have to setup a way to take the input from Serial and put it into an array which I have failed at thus far.  

I want to be able to make the arduino get the information after the char '<' and before the '>' char, but I do not know how to set that up.  in my mind it goes something like this:

Data From PC = <12.0>

float cypheredData = 12.0
Arduino sees : < only.

but how to remove the less than and greater than symbols is not something I know how to do even after browsing on the forums and the learning and references pages, and that could be because I dont know the correct terminology to use and that I've never programmed before so it is something that I am still learning.



How can I make the arduino continue and put the numbers only into either a float or string?  Having to figure out how to change type is what has set me back the most.  I spent about 5 hours this morning rewriting code and it never worked so I've been running basic LED tutorials like this one

Code: [Select]

int ledPin = 13;

void setup()
{
 Serial.begin(9600);
 pinMode(ledPin, OUTPUT);
}

void loop()
{
   while (Serial.available() == 0);
  int val = Serial.read() - '0';
 
  if (val == 1) {
  Serial.println ("Led is On");
  digitalWrite(ledPin, HIGH);
  }

  else if (val == 0){
  Serial.println ("led is OFF");
  digitalWrite(ledPin, LOW);
  }
 
  else {
    Serial.println("Invald!");
  }
  Serial.flush();
}

 


I want to add something like if val == < then read.SerialBytesUntil (>, ??, ??) but how do I change the data type so that it actually fits correctly into a float?

Robin2

I wrote a couple of demo programs here and here that may give you some ideas. While the PC code in one of them is in Python the concepts apply to most languages.

Quote
I wish I could use the Serial Monitor in its most basic form, but for all intents and purposes I am using Unity to create a User Interface.


I didn't mean to use the Serial Monitor in place of the Unity3d stuff. But, ultimately, your Unity3d program will be sending characters to and receiving characters from the Arduino. If your Unity3d program just uses printable characters then you could just as easily send those characters from the Serial Monitor for test purposes. It would allow you to make sure the Arduino code works properly without the complexity of trying to get Unity3d correct at the same time.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rickmc3280

#4
Jun 01, 2014, 09:43 pm Last Edit: Jun 01, 2014, 09:49 pm by Rickmc3280 Reason: 1
Thanks Robin.  

I just created this code which allows me to put in string information until /n.  I am going to just use the /n terminator until I can get more efficient with the program, but it should work for me for now.  Also going to keep looking at your code there is a lot to learn from it.

Code: [Select]
void setup() {
 
 Serial.begin(9600);
}
void loop()
{  

if (Serial.available() > 0);
String inBuffer = Serial.readStringUntil('\n');
if (inBuffer > 0) {
Serial.println(inBuffer);
}
}


The only problem I have now is that it dumps the buffer after a few seconds which I am sure can be fixed with a delay or handshake.  

On a side note, I've looked at your code for about 2 minutes and have already learned some new things.  Thank you, that is awesome.  (did not know the loop could contain separate functions listed as Name1() Name2() ).  I'm gonna check it out for a while, and I hope it can make my prog more efficient. Thanks again.

Robin2


(did not know the loop could contain separate functions listed as Name1() Name2() ).


I try to put as little code as possible into loop(). I try to name my functions so that the code is self documenting. And I try to keep functions short if I can and also limit them to just one activity so if there is a problem I only have to look at a little bit of code and I can ignore all the rest of the stuff that is irrelevant to that problem.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rickmc3280

#6
Jun 02, 2014, 12:53 am Last Edit: Jun 02, 2014, 01:18 am by Rickmc3280 Reason: 1
So its a very rough sketch and I did not finish entering in all of the numbers yet, but it works.  The great thing is that because it requires the String to be exact it as secure as I want it to be, for now.  

Video: http://youtu.be/mlUHgIuU2wU

Code: [Select]
#include <TimerOne.h>

int dutyCycle = 0;
String inBuffer;


// =========SETUP====================

void setup() {

 
 Serial.begin(115200);
 pinMode(9, OUTPUT);
 Timer1.initialize(11230); // Intialize timer1, and set a 89hz frequency
 Serial.println ("PWM Signal has been initialized");
 Timer1.pwm(9,dutyCycle);  // setup pwm on pin 9, with a __ duty cycle
                          // defined by int dutyCycle
 
}
 
 
 
// ===========LOOP====================

void loop() {
 
 getSerialData();

 if (inBuffer== "00.0"){ dutyCycle = 0;}

}
//===========GET_SERIAL_DATA===========
 
void getSerialData(){  

if (Serial.available() > 0);
String inBuffer = Serial.readStringUntil('\n');
if (inBuffer > 0) {
Serial.println(inBuffer);

 if (inBuffer == "00.0") { dutyCycle = 0; }
 if (inBuffer == "00.1") { dutyCycle = 8; }
 if (inBuffer == "00.2") { dutyCycle = 17; }
 if (inBuffer == "00.3") { dutyCycle = 25; }
 if (inBuffer == "00.4") { dutyCycle = 34; }
 if (inBuffer == "00.5") { dutyCycle = 42; }
 if (inBuffer == "00.6") { dutyCycle = 51; }
 if (inBuffer == "00.7") { dutyCycle = 59; }
 if (inBuffer == "00.8") { dutyCycle = 68; }
 if (inBuffer == "00.9") { dutyCycle = 76; }
 if (inBuffer == "01.0") { dutyCycle = 85; }
 if (inBuffer == "01.1") { dutyCycle = 93; }
 if (inBuffer == "01.2") { dutyCycle = 101; }
 if (inBuffer == "01.3") { dutyCycle = 110; }
 if (inBuffer == "01.4") { dutyCycle = 118; }
 if (inBuffer == "01.5") { dutyCycle = 127; }
 if (inBuffer == "01.6") { dutyCycle = 135; }
 if (inBuffer == "01.7") { dutyCycle = 144; }
 if (inBuffer == "01.8") { dutyCycle = 152; }
 if (inBuffer == "01.9") { dutyCycle = 161; }
 if (inBuffer == "02.0") { dutyCycle = 169; }
 if (inBuffer == "02.1") { dutyCycle = 177; }
 if (inBuffer == "02.2") { dutyCycle =186; }
 if (inBuffer == "02.3") { dutyCycle = 194; }
 if (inBuffer == "02.4") { dutyCycle = 203; }
 if (inBuffer == "02.5") { dutyCycle = 211; }
 if (inBuffer == "02.6") { dutyCycle = 220; }
 if (inBuffer == "02.7") { dutyCycle = 228; }
 if (inBuffer == "02.8") { dutyCycle = 237; }
 if (inBuffer == "02.9") { dutyCycle = 245; }
 if (inBuffer == "03.0") { dutyCycle = 254; }
 if (inBuffer == "03.1") { dutyCycle = 262; }
 if (inBuffer == "03.2") { dutyCycle = 270; }
 if (inBuffer == "03.3") { dutyCycle = 0; }
 if (inBuffer == "03.4") { dutyCycle = 0; }
 if (inBuffer == "03.5") { dutyCycle = 0; }
 if (inBuffer == "03.6") { dutyCycle = 0; }
 if (inBuffer == "03.7") { dutyCycle = 0; }
 if (inBuffer == "03.8") { dutyCycle = 0; }
 if (inBuffer == "03.9") { dutyCycle = 0; }
 if (inBuffer == "04.0") { dutyCycle = 0; }
 if (inBuffer == "04.1") { dutyCycle = 0; }
 if (inBuffer == "04.2") { dutyCycle = 0; }
 if (inBuffer == "04.3") { dutyCycle = 0; }
 if (inBuffer == "04.4") { dutyCycle = 0; }
 if (inBuffer == "04.5") { dutyCycle = 0; }
 if (inBuffer == "04.6") { dutyCycle = 0; }
 if (inBuffer == "04.7") { dutyCycle = 0; }
 if (inBuffer == "04.8") { dutyCycle = 0; }
 if (inBuffer == "04.9") { dutyCycle = 0; }
 if (inBuffer == "05.0") { dutyCycle = 0; }
 if (inBuffer == "05.1") { dutyCycle = 0; }
 if (inBuffer == "05.2") { dutyCycle = 0; }
 if (inBuffer == "05.3") { dutyCycle = 0; }
 if (inBuffer == "05.4") { dutyCycle = 0; }
 if (inBuffer == "05.5") { dutyCycle = 0; }
 if (inBuffer == "05.6") { dutyCycle = 0; }
 if (inBuffer == "05.7") { dutyCycle = 0; }
 if (inBuffer == "05.8") { dutyCycle = 0; }
 if (inBuffer == "05.9") { dutyCycle = 0; }
 if (inBuffer == "06.0") { dutyCycle = 512; }
 if (inBuffer == "06.1") { dutyCycle = 520; }
 if (inBuffer == "06.2") { dutyCycle = 0; }
 if (inBuffer == "06.3") { dutyCycle = 0; }
 if (inBuffer == "06.4") { dutyCycle = 0; }
 if (inBuffer == "06.5") { dutyCycle = 0; }
 if (inBuffer == "06.6") { dutyCycle = 0; }
 if (inBuffer == "06.7") { dutyCycle = 0; }
 if (inBuffer == "06.8") { dutyCycle = 0; }
 if (inBuffer == "06.9") { dutyCycle = 0; }
 if (inBuffer == "07.0") { dutyCycle = 0; }
 if (inBuffer == "07.1") { dutyCycle = 0; }
 if (inBuffer == "07.2") { dutyCycle = 0; }
 if (inBuffer == "07.3") { dutyCycle = 0; }
 if (inBuffer == "07.4") { dutyCycle = 0; }
 if (inBuffer == "07.5") { dutyCycle = 0; }
 if (inBuffer == "07.6") { dutyCycle = 0; }
 if (inBuffer == "07.7") { dutyCycle = 0; }
 if (inBuffer == "07.8") { dutyCycle = 0; }
 if (inBuffer == "07.9") { dutyCycle = 0; }
 if (inBuffer == "08.0") { dutyCycle = 0; }
 if (inBuffer == "08.1") { dutyCycle = 0; }
 if (inBuffer == "08.2") { dutyCycle = 0; }
 if (inBuffer == "08.3") { dutyCycle = 0; }
 if (inBuffer == "08.4") { dutyCycle = 0; }
 if (inBuffer == "08.5") { dutyCycle = 0; }
 if (inBuffer == "08.6") { dutyCycle = 0; }
 if (inBuffer == "08.7") { dutyCycle = 0; }
 if (inBuffer == "08.8") { dutyCycle = 0; }
 if (inBuffer == "08.9") { dutyCycle = 0; }
 if (inBuffer == "09.0") { dutyCycle = 0; }
 if (inBuffer == "09.1") { dutyCycle = 0; }
 if (inBuffer == "09.2") { dutyCycle = 0; }
 if (inBuffer == "09.3") { dutyCycle = 0; }
 if (inBuffer == "09.4") { dutyCycle = 0; }
 if (inBuffer == "09.5") { dutyCycle = 0; }
 if (inBuffer == "09.6") { dutyCycle = 0; }
 if (inBuffer == "09.7") { dutyCycle = 0; }
 if (inBuffer == "09.8") { dutyCycle = 0; }
 if (inBuffer == "09.9") { dutyCycle = 0; }
 if (inBuffer == "10.0") { dutyCycle = 0; }
 if (inBuffer == "10.1") { dutyCycle = 0; }
 if (inBuffer == "10.2") { dutyCycle = 0; }
 if (inBuffer == "10.3") { dutyCycle = 0; }
 if (inBuffer == "10.4") { dutyCycle = 0; }
 if (inBuffer == "10.5") { dutyCycle = 0; }
 if (inBuffer == "10.6") { dutyCycle = 0; }
 if (inBuffer == "10.7") { dutyCycle = 0; }
 if (inBuffer == "10.8") { dutyCycle = 0; }
 if (inBuffer == "10.9") { dutyCycle = 0; }
 if (inBuffer == "11.0") { dutyCycle = 0; }
 if (inBuffer == "11.1") { dutyCycle = 0; }
 if (inBuffer == "11.2") { dutyCycle = 0; }
 if (inBuffer == "11.3") { dutyCycle = 0; }
 if (inBuffer == "11.4") { dutyCycle = 0; }
 if (inBuffer == "11.5") { dutyCycle = 972; }
 if (inBuffer == "11.6") { dutyCycle = 980; }
 if (inBuffer == "11.7") { dutyCycle = 989; }
 if (inBuffer == "11.8") { dutyCycle = 997; }
 if (inBuffer == "11.9") { dutyCycle = 1006; }
 if (inBuffer == "12.0") { dutyCycle = 1014; }

 
 Timer1.pwm(9,dutyCycle);  // setup pwm on pin 9, with a __ duty cycle
                          // defined by int dutyCycle
                         
 Serial.println(dutyCycle);
}}

Robin2

I'm a great believer in doing as much as possible on the PC which is easier to program. So I would get the PC to send the dutyCycle numbers to the Arduino. Think of all the lines like if (inBuffer == "00.0") { dutyCycle = 0; } that you wouldn't need :)

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rickmc3280

#8
Jun 02, 2014, 12:14 pm Last Edit: Jun 02, 2014, 12:16 pm by Rickmc3280 Reason: 1
XD

haha. Yeah in heinsight I should have done that first, but I wanted to make sure I knew how to make the arduino do what I wanted before I moved to the other program.  Its a good project to learn from ay?  Thanks for your help.  Ill keep this thread updated as I continue to learn how to make the SerialComs work in Unity.

For the life of me, I still cant effectively make the datainput from pc go into an array and then an int.  Do you have any advice on that?  Considering that I can use either Java Scripts or C# scripts, the programming isnt that different so I've got a little bit of experience with it now and I'll still need this conversion on the PC side.  The reason being that my floats from the program  have multiple decimal places and my control buttons will preferably be a string to int conversion on PC side only.  I did this workaround mostly because of my multiple fails.

When I tried it,  what I did was try to put the data into an array, which worked, except it was still a string, so I needed to find a way to convert it to intergers, i spent about 2 hours on that before I posed my code and could not get it to work.

I ended up deleting that random line of code :)  I had to test that code somewhere else to see if it would work.  I tried your multiple functions in loop but I could not get them to update correctly (I had a function called speed update, but it wasnt updating the PWM for somereason, so I put them all under the same function).

Robin2


For the life of me, I still cant effectively make the datainput from pc go into an array and then an int.  Do you have any advice on that?  


Can you explain this more clearly - perhaps give an example of what you want to send from the PC.

I don't understand what you mean by "go into an array and then an int".  Do you mean an array of INTs or an array of CHARs?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rickmc3280

An example would be, and this is as far as I know how it works.  When I send numbers through from the pc to arduino, all that the arduino sees depending on the program such as a basic int x = Serial.read.  if it typed in 136 all that it sees is 1 and then it treats the 3 andd 6 as nadditional entry where basically it only sees 1 digit at a time. I need it to see all of them, and the only way I knew how to do it simply (I thought) was to put it in a string and then I learned about arrays.  I tried using a character array to get all the digits until /n for this reason, but it never worked out.

Preferably as you saw in my sketch I want to be able to send numbers and it change the pwm frequency and if it gets more complex ill send a letter with nymbers to call a specific function.

Robin2


if it typed in 136 all that it sees is 1 and then it treats the 3 andd 6 as nadditional entry where basically it only sees 1 digit at a time. I need it to see all of them,


The demos I linked to earlier show how to deal with this.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Rickmc3280

#12
Jun 02, 2014, 11:31 pm Last Edit: Jun 02, 2014, 11:48 pm by Rickmc3280 Reason: 1
Derp.  

I didnt look at the second link.  

Thank you.

Code: [Select]
void readOneFloat() {

    // this function takes the characters from the Serial Monitor and converts them
    //   to a single floating point value using the function "atof()"
   
    // a similar approach can be used to read an integer value if "atoi()" is used

    // first read severalChars into the array inputSeveral
   inputSeveral[0] = 0;
   byte charCount = 0;  
   byte ndx = 0;        
   
   if (Serial.available() > 0) {
     while (Serial.available() > 0) {
       if (ndx > maxChars - 1) {
         ndx = maxChars;
       }
       inputSeveral[ndx] = Serial.read();
       ndx ++;        
       charCount ++;
     }
     if (ndx > maxChars) {
       ndx = maxChars;
     }
     inputSeveral[ndx] = 0;
   }

    // and then convert the string into a floating point number
   
   inputFloat = atof(inputSeveral); // atof gives 0.0 if the characters are not a valid number
   
   Serial.print("InputFloat --- ");
   Serial.println(inputFloat, 4); // the number specifies how many decimal places
}


A question I have right off the bat is when you establish ndx and charCount= 0, and then after the Serial Read you put ndx ++; and charCount++;  Wouldnt that Add 0 to 0, or is there other logic going on and then once you put inputSeveral(ndx) = 0; wouldnt that dump the buffer?

For my program, if you dumped the buffer It would reset the pwm to 0, and I need to leave it at the last entry.  

Robin2

++ increments a variable by 1. It is shorthand for += 1

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

sureshn

Serial communication between applications on your computer (such as Unity3D) is not easy although you can easily control the PWM outputs within Arduino sketch.  Suggestions are to use Arduino application and test your sketch with the serial monitor without interactivity.  You may use our products Arduinotrix Studio (Windows) and Arduinotrix SDK (in C++) to interactively set your PWM output values to Arduino Uno and control your motor with few lines of Arduinotrix SDK code for your sketch.

Go Up