String separation from serial command

Hi all - quick overview of what I want to do. I have a cabin that will have LED strips in places(front, rear, tv, hifi, etc) that I want to be independently controlled via c#. I thought I would start simple and make a program to change the brightness of the LED. That worked, as did working out changing the brightness with a trackbar.

Now, because I want to send a string from the pc to the arduino with an area to change and a value to change to, I have the pc sending;

myport.write("Front:" + trackbar.Value);

which to me would send (for example) Front:255 to the arduino

now my question is what is the best way to separate that command to tell the arduino to analogWrite the value to the front LEDs (which is controlled by pin 11 at the moment)

I was thinking of using strtok, hence the ":" but I am a novice and couldnt get it to work...and i was having trouble getting a string to char...and then I got lost...

Anyways, any ideas on how to go about this as I am a tad stumped :frowning:

seanhellen:
I was thinking of using strtok, hence the ":" but I am a novice and couldnt get it to work...and i was having trouble getting a string to char...

Post a complete minimal sketch that demonstrates your best attempt. Explain exactly what you mean by "couldnt get it to work...and i was having trouble getting a string to char...".

Have a look at the parse example in Serial Input Basics - it illustrates the use of strtok() and other things. The example assumes the separator is a comma, but you can change that easily if you want to.

As you are writing your own C# program my recommendation would be to send all the data in every message even if it has not changed - something like this <frontVal, rearVal, tvVal, hifiVal> so that the position of the number within the message defines its purpose. That will make your Arduino code much simpler than trying to distinguish "Front" and "Rear" etc.

If you do wish to send identifiers then it will be much simpler on the Arduino side if you just use single characters such as 'F' and 'R' - for example <F,255>

I am a great believer in using the PC program to do as much of the hard work as possible to make life easy for the Arduino.

...R

pert:
Post a complete minimal sketch that demonstrates your best attempt. Explain exactly what you mean by "couldnt get it to work...and i was having trouble getting a string to char...".

Hi, sorry, forgot to post code...this is how I got it working at its simplest...send a value, analogWrite the value to a pin;

c# code

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            serialPort1.Open();
        }

        private void trackBar1_MouseUp(object sender, MouseEventArgs e)
        {
            serialPort1.Write(trackBar1.Value.ToString());
        }
    }

Arduino code

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

void loop ()
{
  while(Serial.available() > 0)
  {
    String val = Serial.readString();
    analogWrite(11, val.toInt()); 
  }
}

Robin2:
Have a look at the parse example in Serial Input Basics - it illustrates the use of strtok() and other things. The example assumes the separator is a comma, but you can change that easily if you want to.

As you are writing your own C# program my recommendation would be to send all the data in every message even if it has not changed - something like this <frontVal, rearVal, tvVal, hifiVal> so that the position of the number within the message defines its purpose. That will make your Arduino code much simpler than trying to distinguish "Front" and "Rear" etc.

If you do wish to send identifiers then it will be much simpler on the Arduino side if you just use single characters such as 'F' and 'R' - for example <F,255>

I am a great believer in using the PC program to do as much of the hard work as possible to make life easy for the Arduino.

...R

Thanks for the reply...I hadn't thought of making a message with all values in it. I will have a look at the post you've linked to...then begs the question, how will I tell the arduino what each value means, but I'm sure that will prob be answered in the link you gave me :slight_smile:

As it stands at the moment, this is what I have...

c# code

namespace Cabin_Lights
{
    public partial class Form1 : Form
    {
        SerialPort myport = new SerialPort();
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports) PortSelect.Items.Add(port);
        }

        private void Connect_Click(object sender, EventArgs e)
        {
            if(PortSelect.Text != "")
            {
                myport.BaudRate = 9600;
                myport.PortName = PortSelect.Text;
                myport.Open();
                if (myport.IsOpen)
                {
                    Disconnect.Enabled = true;
                    Connect.Enabled = false;
                }
            }
            else MessageBox.Show("Please Select A Port To Connect To", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void Disconnect_Click(object sender, EventArgs e)
        {
            if (myport.IsOpen)
            {
                myport.Close();
                Disconnect.Enabled = false;
                Connect.Enabled = true;
            }
        }

        private void FrontBright_MouseUp(object sender, MouseEventArgs e)
        {
            myport.Write("FrontBright:" + FrontBright.Value);
        }
    }
}

Arduino code

String command;

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

void loop()
{
  if(Serial.available())
  {
    char incom = Serial.read();

    if(incom == '\n')
    {
      parsecom(command);
      command = "";
    }
    else command += incom;
  }

}

void parsecom(String cmd)
{
  String Part1, Part2;

  Part1 = cmd.substring(0, cmd.indexOf(":"));
  Part2 = cmd.substring(cmd.indexOf(":") + 1);

  if(Part1.equalsIgnoreCase("frontbright"))
  {
    analogWrite(11, Part2.toInt());
  }
}

EDIT
The arduino code is now updated...As it stands, using the arduino code above and the serial monitor window, it does exactly what I want it to do...but not with the c# program. I think this is due to the program writing a string, not a char. can i convert it or will I have to use the write command with the char[], offset and count args...in which case, I dont know how to use that... :slight_smile: Maybe something like this (tried but didn't work...slide and let go of the slider, but led stays off)

private void FrontBright_MouseUp(object sender, MouseEventArgs e)
        {
            byte[] buf = System.Text.Encoding.UTF8.GetBytes("FrontBright:" + FrontBright.Value);
            myport.Write(buf, 0, buf.Length);
        }

I think I forgot to mention that I want each section to (eventually) have brightness, fade & flash effects too, so was thinking of "FrontBright", "FrontFade", "FrontFlash" with a value after them.

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0.

...R

Robin2:
It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0.

...R

Ok, will look into this, thanks. And kudos to you for your serial link you posted earlier, came in very handy.

As it turns out, I have now managed to get it working by sending a string from the pc and doing this in the arduino

void loop()
{
  if(Serial.available())
  {
    String incom = Serial.readString();
    parsecom(incom);
  }
}

So I got theere in the end. thanks for the help and the links :slight_smile:

seanhellen:
So I got theere in the end. thanks for the help and the links :slight_smile:

Against all my recommendations ... ah well :slight_smile:

Glad you have it working.

...R

:smiley: im not too sure what to swap the String to as the link you gave me tells me about searching/copying/etc strings.

seanhellen:
:smiley: im not too sure what to swap the String to as the link you gave me tells me about searching/copying/etc strings.

The strings in the link are not at all the same thing as the String class. The capital S is very significant.

...R

re-reading your post, do you recommend using char arrays instead of strings? I tried char arrays before and couldn't get them working...but then that was prob me doing something stupid :smiley:

From memory, I had something like

 char[] buf = "frontbright:" + FrontBright.Value;
port.write(buf, 0, 20);

and then on the arduino

char *cmd = Serial.read();

but the char[] buf line doesn't like what I have put into it and after a bit of playing I gave up.

See robin's examples. Next adjust the sender side to match what the Arduino expects. Your arduino side of things as posted above will indeed not work :wink:

do you recommend using char arrays instead of strings?

A string is a NULL terminated array of chars. Char arrays and strings are not different things.

Char arrays and Strings are, and everyone with more than 2 gray cells recommends NOT using Strings.

Ok, so change them to char arrays...ill get onto it...Im assuming that also means Ill have to change how the c# sends its message too. I might just take my chances...

I have come across another problem now though so will sort that and then get onto changing the Strings over. Although its gonna mean re-doing a few functions as now I can't use substrings anymore.

Just got it sort of working and its getting redone already :stuck_out_tongue_closed_eyes:

seanhellen:
Ill have to change how the c# sends its message too.

Not necessarily. You can do anything with cstrings that you can do with Strings - though they are not as convenient to work with.

However, if you have the choice my recommendation is to use the technique in the 3rd example in Serial Input Basics as it will be the most reliable.

...R

Robin2:
Not necessarily. You can do anything with cstrings that you can do with Strings - though they are not as convenient to work with.

However, if you have the choice my recommendation is to use the technique in the 3rd example in Serial Input Basics as it will be the most reliable.

...R

Ok, thanks for the advice, I have decided I will do it that way once I have got this working...just because I like to have a working version of something before I optimise it. A quick one though - I have gone back to basics for now, got the brightness working, so thats fine. Now I want to get the flash mode going. Now, I have managed to get it going, but the problem is stopping it. Here's the arduino code so far...

void loop()
{
  if(Serial.available())
  {
    String incom = Serial.readString();
    parsecom(incom);
    if(Part1.indexOf("bright") > 0)
    {
      Brightness(Part2.toInt());
    }
    else if(Part1.indexOf("flash") > 0)
    {
      while(Part1.indexOf("flash") > 0) Flash();
    }
  }  
}

void Brightness(int bright)
{
  if(Part1.equalsIgnoreCase("allbright"))
    {
      analogWrite(FLED, bright);
      analogWrite(RLED, bright);
      analogWrite(SLED, bright);
      analogWrite(AMPLED, bright);
    }
    else if(Part1.equalsIgnoreCase("frontbright")) analogWrite(FLED, bright);
    else if(Part1.equalsIgnoreCase("rearbright")) analogWrite(RLED, bright);
    else if(Part1.equalsIgnoreCase("speakbright")) analogWrite(SLED, bright);
    else if(Part1.equalsIgnoreCase("ampbright")) analogWrite(AMPLED, bright);
}

void Flash()
{
  if(Part1.equalsIgnoreCase("allflash"))
  {
      analogWrite(FLED, 255);
      analogWrite(RLED, 255);
      analogWrite(SLED, 255);
      analogWrite(AMPLED, 255);
      delay(500);
      analogWrite(FLED, 0);
      analogWrite(RLED, 0);
      analogWrite(SLED, 0);
      analogWrite(AMPLED, 0);
      delay(500);
    }
}

I have put the while loop in there as it would flash once and that was it. Now to me, this means that when the command from c# has "flash" in it, it should do the flash function until the command says something else...but when I try to turn the brightness up/down (the c# sends "allbright:255" for example), the flashing continues...I am probably missing something really obvious here, but I can't figure out why :smiley:

With this line of code

while(Part1.indexOf("flash") > 0) Flash();

how is the value of Part1 ever going to change?

If you want a responsive program don't use WHILE because it blocks until it completes (and in your case it can never complete). Instead of WHILE use IF and allow loop() to do the repetition - that's what it is for.

By the way it will be much easier to convert the code to use cstrings now, while it is short and simple, rather than later when it grows and becomes more complex. And it is when it becomes more complex and becomes usable for an extended period that the String class may bite you.

...R

Ah, I didn't think about the fact that the while loop will keep going (duh) I was thinking it would go round and round, but still keep an eye on the incom String above it...now you've said that, yep its obvious :smiley: thought it would be - thanks.

Yep, my intention is to sort out this flash, then the fade and then I'm done...then i can go about cstringing everything :smiley: I stripped out a few other functions (flash rates/fade rates/random effects) to make this more simple and maybe when I can be bothered, Ill add them back in but probably not.

Thanks for your help :sunglasses: