I am doing a modding project: Controlling my desktop PC's RGB led strips and fans from my phone. I already tried to control leds and fans locally and done successfully (With help of a fan hub). I plan to use HC-06 bluetooth transreceiver for this.
There's only one obstable remaining: receiving multiple commands. I did a similar project before. Controlling an RGB led connected to Arduino and this was the code I used for it:
const int redpin = 3;
const int greenpin = 4;
const int bluepin = 5;
String readString;
void setup() {
Serial.begin(9600);
pinMode(redpin, OUTPUT);
pinMode(bluepin, OUTPUT);
pinMode(greenpin, OUTPUT);
}
void loop() {
while (Serial.available()) {
delay(3);
char c = Serial.read();
readString += c;
}
if (readString.length() >0) {
Serial.println(readString);
if (readString == "blue")
{
analogWrite(bluepin, 0);
analogWrite(redpin, 255);
analogWrite(greenpin, 255);
}
if (readString == "red")
{
analogWrite(bluepin, 255);
analogWrite(redpin, 0);
analogWrite(greenpin, 255);
}
if (readString == "green")
{
analogWrite(bluepin, 255);
analogWrite(redpin, 255);
analogWrite(greenpin, 0);
}
But it only allows me to receive single information at a time. As I said before I'd like to control both fan speed and led color. I'm planning to make a slider on the app for fan speed (valued between 0-255) and the app should send some data like this:
blue
255
or
blue.255
But I do not know how to seperate these two values from each other. Could you please help me?
Thanks in advance.
You could get the whole string (e.g. "blue.255") and compare the substring up to the dot with the keywords red, blue, green as you do now. The rest of the string after the dot could be converted to an integer.
But why use three colour keyword? You could parse a format like 255.0.0.255 where the first three number are the RGB levels and the last one the fan.
Or you could plan ahead and adopt a format like keyword.value, e.g.:
led.blue
fan.255
so that you could add more controls in the future.[/code]
tuxduino:
You could get the whole string (e.g. "blue.255") and compare the substring up to the dot with the keywords red, blue, green as you do now. The rest of the string after the dot could be converted to an integer.
But why use three colour keyword? You could parse a format like 255.0.0.255 where the first three number are the RGB levels and the last one the fan.
Or you could plan ahead and adopt a format like keyword.value, e.g.:
led.blue
fan.255
so that you could add more controls in the future.[/code]
Yes, you are right. I should use the RGB number values instead.
I'll never claim to be an expert at reading serial nor the arduino; however, I'll give you a jump start in offering a tid bit of digestible info I wish I knew when I was starting out. As you progress, you'll pick up better methods.
That said, we'll start out with a small refinement by creating a function called 'Parse'; the ',' is read and discarded. Next, we'll pass our character string to this function. Once we start the function, we'll use 'switch case' to determine which data entry was passed. Once processed, we increase 'member' for the next data member. Since a comma doesn't exist for the last data member, we'll use the '>' to exit to the Parse function.
Condensed example:
char c;
int member;
int fanr;
int pumpr;
String data;
char start = '<';
char End = '>';
char delimiter = ',';
void loop() {
member = 0;
data = "";
//incoming serial looks like this <fandata,pumpdata,etc>
while (Serial.available() > 0) {
c = Serial.read(); //gets one byte from serial buffer
if (c == delimiter or c == End) {
Parse(data);
}else{
if (c != start){data += c;} //build string
}
}
}
void Parse(String charString) {
switch (member) {
case 0: // member = 0 so case = fan
fan = String(charString);
int fanr = round(fan.toInt());
analogWrite(6, fanr);
Serial.println(fanr);
member++;
c = '\0'; // clear
break;
case 1: //member = 1 so case = pump
pump = String(charString);
int pumpr = round(pump.toInt());
analogWrite(5, pumpr);
Serial.println(pumpr);
member++;
break;
// etc, etc
}
}
As far as I know there's no "switch case ()" on Arduino but "switch ()". So I fixed it.
I also get "fan" was not declared in this scope error when tried to run your code. Declaring it as int or String gave me errors so I do not know how to fix this.
Good catch; bad habit on my part.
As for the build errors, I only displayed a snippet to offer an alternative method. There are also errors caused by "int pumpr = round(pump.toInt());" in each case.
Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.
The technique in the 3rd example will be most reliable. Send your data like this <255, 123, 117, 23>
And 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.
Robin2:
Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.
The technique in the 3rd example will be most reliable. Send your data like this <255, 123, 117, 23>
And 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
Hi,
I managed to do a successful and correct reading with your method but now I can't save the values in the receivedChars array to my variables.
This is the code I use. I assumed the values are stored in the array like this and that's how I should access them:
red = receivedChars[0];
green = receivedChars[1];
blue = receivedChars[2];
fan = receivedChars[3];
pump = receivedChars[4];
analogWrite(11,red);
analogWrite(10,green);
analogWrite(9,blue);
analogWrite(6,fan);
analogWrite(5,pump);
But all I end up with are random numbers. I guess these are random and caused because I could not access to the array properly.
That is the sort of thing I expected to see. Alas, you are completely misinterpreting the concept when you use
red = receivedChars[0];
green = receivedChars[1];
// etc
What you have posted 61, 84, 99, 117, 130 represents the characters (not the byte values) in the positions 0 to 19 in receivedChars - if I have counted correctly. Note that 61 comprises two characters and 117 comprises 3 characters.
For example receivedChars[0] will have the value 54 which is the Ascii code for '6' and receivedChars[2] will have the code for a comma (I think it is 44).
You need to use the concepts in my parse example to split the groups of characters using the commas as separator and then use the atoi() function to convert each group (for example "117") into an int.
Since you are parsing all integers, your life is easier using sscanf() like this:
char* data = "Data:255,0,255,125,125;";
struct Vars{
int red;
int green;
int blue;
int pump;
int fan;
};
Vars myVars;
void setup()
{
Serial.begin(9600);
delay(100);
Serial.println(data);
sscanf(data, "Data:%d,%d,%d,%d,%d;", &myVars.red, &myVars.green, &myVars.blue, &myVars.pump, &myVars.fan);
Serial.println(myVars.red);
Serial.println(myVars.green);
Serial.println(myVars.blue);
Serial.println(myVars.pump);
Serial.println(myVars.fan);
}
void loop() {
// put your main code here, to run repeatedly:
}