Arduino串口控制彩灯

按照RGB彩灯–彩虹颜色渐变的电路连接好后,用下面代码实现。

// Project 10 - Serial controlled mood lamp

char buffer[18];
int red, green, blue;

int RedPin = 11;
int GreenPin = 10;
int BluePin = 9;

void setup()
{
	Serial.begin(9600);
	while (Serial.available())
	Serial.read();
	pinMode(RedPin, OUTPUT);
	pinMode(GreenPin, OUTPUT);
	pinMode(BluePin, OUTPUT);
}

void loop()
{
	if (Serial.available() > 0) {
		int index = 0;
		delay(100); // 等待缓冲区填满
		int numChar = Serial.available();
		if (numChar>15) {
			numChar = 15;
		}
		while (numChar--) {
			buffer[index++] = Serial.read();
		}
		splitString(buffer);
	}
}

void splitString(char* data) {
	Serial.print("Data entered: ");
	Serial.println(data);
	char* parameter;
	parameter = strtok(data, " ,");   
	while (parameter != NULL) {
		setLED(parameter);
		parameter = strtok(NULL, " ,");  
	}

	// 清空缓冲区的数据
	for (int x = 0; x<16; x++) {
		buffer[x] = '\0';
	}
        Serial.flush();
}

void setLED(char* data) {
	if ((data[0] == 'r') || (data[0] == 'R')) {
		int Ans = strtol(data + 1, NULL, 10);
		Ans = constrain(Ans, 0, 255);
		analogWrite(RedPin,255- Ans);
		Serial.print("Red is set to: ");
		Serial.println(Ans);
	}
	if ((data[0] == 'g') || (data[0] == 'G')) {
		int Ans = strtol(data + 1, NULL, 10);
		Ans = constrain(Ans, 0, 255);
		analogWrite(GreenPin, 255-Ans);
		Serial.print("Green is set to: ");
		Serial.println(Ans);
	}
	if ((data[0] == 'b') || (data[0] == 'B')) {
		int Ans = strtol(data + 1, NULL, 10);
		Ans = constrain(Ans, 0, 255);
		analogWrite(BluePin, 255-Ans);
		Serial.print("Blue is set to: ");
		Serial.println(Ans);
	}
}

代码回顾

首先我们先设置缓冲区数据为18,是为了防止数据溢出造成异常。

char buffer[18];

然后我们使用了Serial.begin()函数,告诉Arduino开始串口通信。括号里面的数字(这里是9600)是波特率(每秒钟的信号或者脉冲数)。

void setup()
{
	Serial.begin(9600);
	while (Serial.available())
	Serial.read();
	pinMode(RedPin, OUTPUT);
	pinMode(GreenPin, OUTPUT);
	pinMode(BluePin, OUTPUT);
}

Serial.flush()命令清空串口中残存的字符,因此串口是空的,为输入输出做好准备。

if (Serial.available() > 0) {
		int index = 0;
		delay(100); // 等待缓冲区填满
		int numChar = Serial.available();
		if (numChar>15) {
			numChar = 15;//确保数据不会溢出
		}
		while (numChar--) {
			buffer[index++] = Serial.read();
		}
		splitString(buffer);
	}

主循环函数中Serial.available()函数是为了获取从串口读取有效的字节数(字符)。

if语句里面的delay(100)是为了让缓冲区填满。如果不延时,在接受全部数据之前函数可能就已经执行了,并且开始处理所收到的字符串,因为串口通信相对于复位代码的运行速度是很慢的。

等待100毫秒使送来的数据将缓冲区填满,之后声明整型变量numChar,来记录字符串中的字符数。因此,如果在串口监视器中输入的字符是:

r255,g255,b255

numChar的值将是15,而不是14。因为在文本的每行末尾都有一个看不见的字符,叫做NULL字符。它告诉Arduino什么时候到达文本行的末端。

while (numChar--) {
			buffer[index++] = Serial.read();
		}

将读取的字符串存储到buffer[]数组中。接下来调用了splitString()函数。

void splitString(char* data) {
	Serial.print("Data entered: ");
	Serial.println(data);
	char* parameter;
	parameter = strtok(data, " ,");   
	while (parameter != NULL) {
		setLED(parameter);
		parameter = strtok(NULL, " ,");  
	}

	// 清空缓冲区的数据
	for (int x = 0; x<16; x++) {
		buffer[x] = '\0';
	}
	while (Serial.available())
		Serial.read();
}

这个函数不返回数据,所以它的数据类型设置为void。给这个函数传递一个名为data的字符型参数,但在C++程序设计语言里面,不允许给函数传送一个字符型数组,可通过使用指针染过这个限制。指针在C语言里面是一个高级的概念,想要了解更多,就参考C语言编程的书。

	Serial.print("Data entered: ");
	Serial.println(data);

这两条语句是为了输出接受来的buffer[]数组中的字符。而Serial.println()函数与Serial.print()函数之间的差别就是换行。多个ln就代表了输出后换行。

        char* parameter;
	parameter = strtok(data, " ,");   
	while (parameter != NULL) {
		setLED(parameter);
		parameter = strtok(NULL, " ,");
}

接下来就是整个代码中最重要的部分,也是精华所在。先创建了一个新的变量parameter:

char* parameter;

定义了一个指针型变量parameter,指向的是buffer[]内容的地址。之后使用strtok函数。strtok这个函数是取string和token这两个单词的词头命名的。string是字符串,而token表明这个函数的目的是分割这个字符串。将要分割的字符串作为第一个参数,分割符作为第二个参数,因此有以下语句:

parameter = strtok(data, " ,");

函数把字符串在逗号处分开,假设你输入的字符串为:r255,g255,b255,那么执行这个语句之后parameter的值将是:r255。然后进入while语句,条件是parameter非空(也就是还没有达到字符串在的尾部):

while (parameter != NULL) {
		setLED(parameter);
		parameter = strtok(NULL, " ,");
}

这里面调用了setLED函数(后面再分析)。设置parameter为下一个空格或逗号之前的字符串,通过传递一个NULL参数给strtok实现,语句如下:

parameter = strtok(NULL, " ,");

这个语句告诉strtok函数从逗号处断开字符串。

接下来清除了缓冲区的buffer[]里面的内容。语句如下:

	for (int x = 0; x<16; x++) {
		buffer[x] = '\0';
	}
        Serial.flush();

接下来我们来讨论setLED函数,函数中包含了3个if语句,用来判断各个颜色的值。里面的函数就解释一点strtol函数。

int Ans = strtol(data + 1, NULL, 10);

这个函数将r,g,b字母之后的数字转换成一个十进制的数字。data+1(开始位)是将data[0]后一位即data[1]之后的数开始,NULL(末尾)。然后用constrain函数确保数字在0-255之间。

0 Comments
Leave a Reply