Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇

Linkit 系列博文:

联发科Linkit 7688 (一) 上手及在Mac下搭建OpenWrt交叉编译环境,C语言编译Hello,World

联发科Linkit 7688 (二)GPIO基本操作与C语言编程

联发科Linkit 7688 DUO(三): 通过 Arduino 控制外设和传感器

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇

Linkit 7688 DUO(六) 加入MQTT物联网协议



前一篇讲了 Linkit 7688DUO开发板接上一些典型Arduino传感器的作法。

本篇,我们要为开发板接上更多的Arduino的传感器和模块了。


一、 前篇概要:


Linkit 7688 DUO开发板上有两个处理器芯片。

 一片是  Linkit 7688, 主处理器

 一片是  ATmega32U4,  这是Arduino的处理芯片,提供Arduino编程接口,用于控制传感器外设等

两个处理器通过内部串口相连。


Linkit 7688 DUO开发板上, 从ATmega32U4管脚接出的众多管脚,其中:D0-D13 为数字IO口, A0-A5为模拟IO口, S0-S3为串口设备SPI管脚

前篇,我们练习了在开发板上接入LED、开关、继电器、蜂鸣器等, 并制定了一个两个处理器串口通讯的消息协议。


在开发中, 要写两个程序:

     1, 写一个Arduino程序, 写入ATmega32U4中。Arduino程序通过串口接收主处理器Linkit 7688送来的消息, 执行相应动作。

     2, 写一个主程序,写入Linkit7688中。 主程序通过串口向 ATmega32U4 发送消息


二、温度湿度传感器 (Temperature & Humidity Sensor), 见下图

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇_第1张图片


模块有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 图中标注"S”的管脚接 信号(数字I/O), 中间的管脚接5V
这种是DHT11传感器, 是便宜货,能粗略地测温度、湿度 
用在根杜邦线把模块接到开发板上,其中“-”脚接GND,  "S"脚接D3, 中间管脚接5V

要在Arduino中使用DHT传感器,首先要安装 DHT库。
新建一个名为“ DHT”的文件夹, 下载 dht.cpp 和 dht.h 两个文件,放入DHT文件夹  ,然后再将DHT文件夹复制到ardunio的库文件夹。
在MAC OS的操作:右键点击Arduino IDE的图标出菜单,选菜单项“显示包内容”, 进入 “Contents/Java/libraries"(这个文件夹就是ardunio的库文件夹了)

重启Arduino IDE,  则可在 "项目“-> include library 中打开DHT库了。

编一个Arduino程序如下:采用消息协议,当收到消息,则测量温度、湿度,并发送结果消息到主控板。 

注意:程序要用到 dht库(请确保已装好)。

注意:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#include <dht.h>

#define COMMAND_TEMPERATURE 't'
#define ACK_TEMPERATURE 'u'

dht DHT;  // dht object

int pinDHT = 3; //pin connects to DHT Temperature/Humidity sensor

void setup() {
  MessageInit(57600); //init message
  pinMode(pinDHT, INPUT); //set pinDHT INPUT
}


void getTemperature() {
  //Serial.println("in");
  int ret = DHT.read11(pinDHT);
  switch (ret)  
  {  
    case 0:  // ok
       {
         unsigned char buf[3];         
         buf[0] = (int)DHT.temperature; //integer part of temperature
         buf[1] = (int)((DHT.temperature - (int)DHT.temperature)*10); //one digit frac
         buf[2] = (int)DHT.humidity; //integer part of humidity
         MessageSend( ACK_TEMPERATURE, buf, 3 ); //send ACK_TEMPERATURE message back
         return;
       }  
    case -1:   break; //checksum error
    case -2:   break; //Time out error
    default:   break; //unknown error 
  } 
  MessageNAK( COMMAND_TEMPERATURE ); //send NAK message back
}

void loop() {
   if ( MessageReceive() ) {
     if ( MessageCommand() == COMMAND_TEMPERATURE ) {
       getTemperature();
     }
   } 
}


Arduino程序等待 COMMAND_TEMPERATURE消息, 调用getTemperature()函数 ,该函数中调用 DHT.read11()读取传感器数据。如果读取数据成功,返回 ACK_TEMPERATURE消息,消息内容三个字节,第一个字节是温度的整数位、第二个字节是温度的小数第1位。第三字节是湿度.


编一个C语言主程序 temperature_test.c,采用消息协议,向Arduino发送COMMAND_TEMPERATURE消息送到,等待Arduino返回 ACK_TEMPERATURE消息, 读取其中的温度和湿度值打印出来。

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include <memory.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_TEMPERATURE 't'
#define ACK_TEMPERATURE 'u'


//wait ACK_TEMPERATURE message from Arduino
int wait_ack_message(message_t * msg) {
	int times, ret;

	times = 0;
	while ( (ret = message_receive(msg)) != 1 && times++ < 30) {
		usleep(30*1000);
	}

	if ( ret == 1 ) {
		if ( message_command(msg) == ACK_TEMPERATURE ) {
			char temperature = (char)message_data(msg, 0);
			printf("Temperature: %d.%d, Humidity:%d\n", temperature,
					message_data(msg, 1), message_data(msg, 2));
			fflush(stdout);
			return 1;
		}
	}

	return 0;
}


int main(int argc, char **argv) {
	int fd;   //file descriptor of serial port
	message_t msg; //message object


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		message_send( &msg, COMMAND_TEMPERATURE, NULL, 0); //send COMMAND_TEMPERATURE
		wait_ack_message( &msg ); //wait for ACK_TEMPERATURE

		serial_close(fd);
	}

	return 0;
}



主程序 发送 COMMAND_TEMPERATURE 消息,  等待 ACK_TEMPERATURE 返回消息, 从返回消息的内容中第一、二字节得到温度值,第三字节得到湿度值。


将C语言程序交叉编译后生成temperature_test,用scp命令上传到开发板

SSH登入 Linkit 7688 DUO开发板,运行 temperature_test


运行结果, 得到了当前温度和湿度:

Temperature: 25.0, Humidity:87



三、倾斜开关、水银开关(TILT SWITCH), 见下图

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇_第2张图片

倾斜开关其实就是一种开关,开关上有一个玻璃管子,管子里有一个水银球。当开关倾斜,水银i球就会滚动。当开关倾斜到一定程度,水银球就会落到管子底部,把管子底部的两根电极接通。如果反向倾斜,则水银球离开管子底部,管子底部的两根电极将不通。因此,也称为水银开关。

倾斜开关可以用于监测货物有否翻倒等。


倾斜开关模块有三个管脚,其中  (图中左侧)标注‘-’的管脚接地(GND), (图中右侧)标注"S”的管脚接接信号(数字I/O)   中间的管脚接 5V
用三根杜邦线把模块接到开发板上,其中“-”脚接GND,   “S"脚接 D3口,  


按键抖动现象:当开关触点断开、闭合时,由于水银球的弹性作用,开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。在一段时间内会处于时断时续状态。这种现象在机械开关中也存在。这种现象叫按键抖动(key jitter)。 对于机械开关,一般不稳定期在20ms,  水银开关不稳定期长达1秒以上。

按键抖动的处理:在软件中,要对按键抖动进行消除处理,否则软件会一个短时间内出现频繁开关的不正常现象。处理方法时,在第一次检测到开关状态变化时,不要立即触发。等待一小段时间后,再次检测开关状态,如果两次或多次检测开关状态均为稳定值,则触发开关事件。 这个一小段时间片在机械开关中常为10-20ms。 在水银开关中,我把它定为间隔100ms, 且连续检测4次均为稳定值时,即开关状态变化.

编一个Arduino程序,采用上例中的通信协议,监控开关状态,如有状态变化,通过串口发送消息到到主控板, 编译上传到ATmegaU32

Arduino程序等待 COMMAND_TEMPERATURE消息, 调用getTemperature() , 其中调用 DHT.read11()读取传感器数据。
如果读取数据成功,返回 ACK_TEMPERATURE消息,消息内容三个字节,第一个字节是温度的整数位、第二个字节是温度的小数第1位。第三字节是湿度。




编一个C语言主程序 temperature_test.c,采用消息协议,向Arduino发送COMMAND_TEMPERATURE消息送到,等待Arduino返回 ACK_TEMPERATURE消息, 读取其中的温度和湿度值打印出来。 交叉编译后生成temperature_test,用scp命令上传到开发板</p><p><pre name="code" class="cpp">#include <stdio.h>

注意:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#define COMMAND_SWITCH 's'

int pinSwitch = 3; //pin 3 connects to switch
int lastValue = 0; // last value of pin
int checkTimes = 0; // check times for key jitter

void setup() {
  MessageInit(57600); //init message < need message_protocol.ino >
  pinMode(pinSwitch, INPUT); //set pinSwitch INPUT
  lastValue = digitalRead(pinSwitch); //get last value
}

void loop() { 
  int value = digitalRead(pinSwitch); //read the switch
  
  if (value != lastValue) { //if value changed 
    //eliminate key jitter: check 4 times 
    checkTimes = 0; 
    delay(100); //delay 100 ms
    while ( value == digitalRead(pinSwitch) && checkTimes++ < 4 )
      checkTimes++;
      
    //if continous 4 times
    if (checkTimes >= 4 ) {
      unsigned char c = value & 0xFF; //change to unsigned char
      MessageSend( COMMAND_SWITCH, &c, 1); //send message
      lastValue = value;
    }  
  }
  
  delay(10); //delay in between reads for stability  
}


编一个C语言主程序 tilt_test.c,采用消息协议,监听串口有否按键消息送到,打印出来, 交叉编译后生成tilt_test,用scp命令上传到开发板

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_SWITCH 's'

int test_switch() {
	int fd;   //file descriptor of serial port
	message_t msg; //message object


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		//loop
		while ( 1 ) {

			if ( 1 == message_receive(&msg) ) {  //if received message
				if ( message_command(&msg) == COMMAND_SWITCH ) {
					printf("switch value = %d\n", message_data(&msg, 0 ) ); //print value
					fflush(stdout);
				}

			}

			usleep(10*1000); //wait 10 milli-seconds
		}

		serial_close(fd);
	}

	return 0;
}

将C语言程序交叉编译后生成tilt_test,用scp命令上传到开发板

SSH登入 Linkit 7688 DUO开发板,运行 tilt_test

用手反复倾斜开关,观察水银球来回滚动,和屏幕输出结果:

switch value = 1

switch value = 0

switch value = 1


水银球接通开关时,switch value = 0.   水银球不接通开关时, switch value = 1


由于水银球在管子中只能在一个方向上滚动,所以只能测一个方面的倾斜度。
如果要测四个方面的倾斜度,则需要用到 另一个传感器   BALL SWITCH (球形开关),如下图:




这个模块的管脚接法与 水银开关(TILT_SWITCH)完全一样,功能也基本相同,只不过它是一个电子开关,支持在四个方向上的倾斜都可以触发开关。
所以, 使用的控制程序也一样,由于它是电子开关,没有水银球抖动的问题。所以在 Arduino程序中的防抖动部分: delay(100);  改为 delay(10)就好了。
请自己试一下吧。 




四、激光发射器(Laser Emit), 见下图

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇_第3张图片

激光发射器可以发射出一串激光,常用于计数(需配合有一个光敏电阻接收器),如生产线上统计通过的物件数量、门闸上统计进门人数等。

激光发射器有一个激光发射头,有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 图中左侧标注"S”的管脚接接信号(数字I/O), 中间的管脚接5V 或 3.3V
用三根杜邦线把模块接到开发板上,其中“-”脚接GND,  “S"脚接D3, 中间的管脚接5V

激光发射器的控制方法与一个LED灯一样的。

编一个Arduino程序,采用消息协议,收到消息后,将D3设置为HIGH 或 LOW, 则激光将发射或关闭。 Arduino程序如下,请编译后上传到ATmegaU32

注意:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#define COMMAND_LASER 'a'

int pinLaser = 3; //pin 3 connects to laser emit module

void setup() {
  MessageInit(57600); //init message < need message_protocol.ino >
  pinMode(pinLaser, OUTPUT); //set pinLaser OUTPUT
}

void loop() { 
  
  if ( MessageReceive() ) { //if receive message
     if ( MessageCommand() == COMMAND_LASER ) {
       int value =  MessageData(0);
       if (value == 1)
         digitalWrite(pinLaser, HIGH); 
       else
         digitalWrite(pinLaser, LOW); 
       MessageACK( COMMAND_LASER );
     }  
  }
}



编一个C语言主程序 test_laser.c,采用消息协议,向Arduino发消息。

注意:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_LASER 'a'

int test_laser() {
	int fd;   //file descriptor of serial port
	message_t msg; //message object
	unsigned char value;

	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		value = 1;
		message_send( &msg, COMMAND_LASER, &value, 1); //send COMMAND_LASER message, value = 1

		sleep(5);//wait 5 seconds

		value = 0;
		message_send( &msg, COMMAND_LASER, &value, 1); //send COMMAND_LASER message, value = 0

		serial_close(fd);
	}

	return 0;
}

交叉编译后生成laser_test,用scp命令上传到开发板

将C语言程序交叉编译后生成laser_test,用scp命令上传到开发板

SSH登入 Linkit 7688 DUO开发板,运行 laser_test

运行结果: 程序一运行,则发射器发出一串激光,5秒后,激光关闭



五、光敏电阻接收器(Photoresistor), 见下图


模块有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 图中左侧标注“S"的管脚接信号(数字I/O), 中间的管脚接电源3.3V
光敏电阻接收器模块的工作方式是: 当光敏电阻有强光射在上面时, “S"管脚输出 LOW. 当没有强光射在光敏电阻上面时, “S"管脚输出 HIGH

用三根杜邦线把模块接到开发板上,其中“-”脚接GND,  标注“S"的管脚接 D4口, 中间管脚接3.3V

这一次,我把激光发射器也同时接上去作为光源, 激光发射器的"S"管脚接D3口。激光发射器的"-"接GND, 中间管脚接3.3V

编一个Arduino程序,采用消息协议,不断读取D4口的状态值,如有状态变化,通过串口发送COMMAND_SWITCH消息到到主控板。这个程序同时也接收消息,如果有COMMAND_LASER消息,则打开或关闭激光。

程序如下,请编译上传到ATmegaU32

#define COMMAND_LASER  'a'
#define COMMAND_SWITCH 's'

int pinLaser = 3; //pin 3 connects to laser emit module
int pinPhotoResistor = 4; //pin 4 connects to photoresistor module
int lastValue = 0;  //last value of photoresistor

void setup() {
  MessageInit(57600); //init message < need message_protocol.ino >
  pinMode(pinLaser, OUTPUT); //set pinLaser OUTPUT
  pinMode(pinPhotoResistor, INPUT); //set pinPhotoResistor INPUT
  lastValue = digitalRead( pinPhotoResistor );
}

void loop() { 
  
  int value = digitalRead( pinPhotoResistor );
  if ( value != lastValue ) // PhotoResistor status change
  {
    unsigned char c = value & 0xFF; //convert to unsigned char
    MessageSend( COMMAND_SWITCH, &c, 1); //send COMMAND_SWITCH to linkit 7688
    lastValue = value;
  }
  
  if ( MessageReceive() ) { //receive message
     if ( MessageCommand() == COMMAND_LASER ) { //if is COMMAND_LASER
       int value =  MessageData(0);
       if (value == 1)
         digitalWrite(pinLaser, HIGH); //turn LASER on
       else
         digitalWrite(pinLaser, LOW); //turn LASER off
       MessageACK( COMMAND_LASER );
       return;
     }       
  }
  
  delay(1);
}


编一个C语言主程序 resistor_test.c,采用消息协议,首先打开激光,然后监测有否COMMAND_SWITCH消息送达,如有,则打印出来。同时,对通过的物体计数。

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

程序如下:

#include <stdio.h>
#include <unistd.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_SWITCH 's'
#define COMMAND_LASER 'a'

int test_resistor() {
	int fd;   //file descriptor of serial port
	message_t msg; //message object
	unsigned char value;
	int counter = 0;


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		value = 1;
	    message_send( &msg, COMMAND_LASER, &value, 1); //turn LASER on

		//loop
		while ( 1 ) {

			if ( 1 == message_receive(&msg) ) {  //if received message
				if ( message_command(&msg) == COMMAND_SWITCH ) { //if is COMMAND_SWITCH
					value = message_data(&msg, 0 ); //get value

					if ( value == 0 )
						counter++;    //increase counter

					//print it out
					printf("switch value = %d, counter = %d\n", value, counter );
					fflush(stdout);
				}

			}

			usleep(10*1000); //wait 10 milli-seconds
		}

		serial_close(fd);
	}

	return 0;
}


将C语言程序交叉编译后生成 resistor_test,用scp命令上传到开发板

SSH登入 Linkit 7688 DUO开发板,运行 resistor_test

运行: 程序一运行,则发射器发出一串激光,将激光对准光敏电阻。

当激光射在光敏电阻上时,光敏电阻的输出值为0.    当光敏电阻收不到激光时,光敏电阻的输出值将变为1.

试着用物体穿过激光,则 激光会被遮挡一次,光敏电阻的输出值先为0,再变成1,再变成0.  此时计数器加1. 并打印在屏幕上。

如此,则可以计数了,比如:可以用于门闸计算进门的人数;也可用于生产线上,对通过的物体计数。

运行结果:

switch value = 0, counter = 1

switch value = 1, counter = 1

switch value = 0, counter = 2

switch value = 1, counter = 2

switch value = 0, counter = 3

switch value = 1, counter = 3

。。。


六、红外线接收(IR RECEIVER), 见下图

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇_第4张图片


模块有三个管脚,其中  (图中左侧)标注‘-’的管脚接地(GND), 图中右侧标注"S”的管脚接信号(数字I/O), 中间的管脚接电源5V
用三根杜邦线把模块接到开发板上,其中“-”脚接GND,  标注"S”的管脚分别接 D5口, 中间的管脚接电源5V


6.1 红外线遥控原理

每个人的家中都有N多个红外线遥控器。

红外线遥控的原理: 遥控器发出某个频率红外线(通常是38K),  这束红外线有时高、有时低,即以类似“10101100..."的编码方式发出。接收器收到这束红外线后,必须先解码,得到一组数字。这个数字就是遥控器发出的命令。

但是,红外线的编码是比较复杂且很不标准的。各个厂家有不同的编码标准,典型的厂商编码标准有SONY、SHARP、NEC、SANYO、SAMSUNG、PANASONIC、LG等等。一般的家电遥控器,都是用专用的遥控器专用IC芯片去实现编码和解码的,而且不同厂商的IC芯片还不一样。 所以遥控器很不通用。


在Arduino中,我们可以使用上图这个红外线接收器模块,利用Arduino的计算能力进行解码,而且不需依赖专用IC芯片,就可以支持各种厂商的编解码,使得各种遥控器都能遥控Arduino。


6.2 IRremote库安装

要在Arduino中使用红外线,首先要安装 IRremote库。 在 https://github.com/shirriff/Arduino-IRremote 下载 整个库文件,改目录名为 “IRremote" (注意文件名大小写不能错,否则以后编译时会出现错误:  “IRsend" not a named type.
 
然后再将IRremote文件夹复制到arduino的库文件夹。
在MAC OS的操作:右键点击Arduino IDE的图标出菜单,选菜单项“显示包内容”, 进入 “Contents/Java/libraries"(这个文件夹就是ardunio的库文件夹了)

arduino库文件夹中有一个 “RobotIRremote”的子文件夹,这是一个老版本的IRremote. 必须把RobotIRremote文件夹删除,否则以后编译时会出现 ”Multiple IRremote.h"的错误。

好了,现在重启Arduino IDE,  则可在 "项目“-> include library 中打开IRremote库了。

6.3 红外线学习程序

比如:我想用家中的电视机遥控器 遥控Arduino.  那么,首先要写一个红外线学习的Arduino程序,用于读取和记录遥控器的编码,如下:

//------------------------------------------------------------------------------
// Include the IRremote library header
#include <IRremote.h>

//------------------------------------------------------------------------------
int recvPin = 5;  //IR receiver connects to pin 5
IRrecv irrecv(recvPin); //init IRrecv
unsigned char ir_type = 0;  //IR type
unsigned int  key_count = 0; //key count
unsigned long last_key = 0;  // last key value
unsigned int  repeat_count = 0; //repeat key count

//+=============================================================================
// print IR type string
void  printIRType()
{
  switch (ir_type) {
    case NEC:          Serial.print("NEC");           break ;
    case SONY:         Serial.print("SONY");          break ;
    case RC5:          Serial.print("RC5");           break ;
    case RC6:          Serial.print("RC6");           break ;
    case DISH:         Serial.print("DISH");          break ;
    case SHARP:        Serial.print("SHARP");         break ;
    case JVC:          Serial.print("JVC");           break ;
    case SANYO:        Serial.print("SANYO");         break ;
    case MITSUBISHI:   Serial.print("MITSUBISHI");    break ;
    case SAMSUNG:      Serial.print("SAMSUNG");       break ;
    case LG:           Serial.print("LG");            break ;
    case WHYNTER:      Serial.print("WHYNTER");       break ;
    case AIWA_RC_T501: Serial.print("AIWA");          break ;
    case PANASONIC:    Serial.print("PANASONIC");     break ;
    case DENON:        Serial.print("DENON");         break ;
    default:           Serial.print("UNKNOWN");       break ;
  }
}
//+=============================================================================
// Configure the Arduino
//
void  setup ( )
{
  irrecv.enableIRIn();  // Start the IR receiver
  Serial.begin(57600); // Init Serial
  Serial.println("please press keys on IR remote controller");
}

//+=============================================================================
// The repeating section of the code
//
void  loop ( )
{
  decode_results  results;        // Somewhere to store the results
    
  if (irrecv.decode(&results)) // receive an IR code
  { 
    //whether results has valid value
    if ( results.decode_type != UNKNOWN && results.value != 0xFFFFFFFF ) { 
    
      //whether the same key is pressed 
      if (results.value == last_key ) { 
        repeat_count++;
      } else {
        last_key = results.value;
        repeat_count = 0;
      }
      
      if ( repeat_count == 0 ) {
        //print informations
        
        if ( ir_type == 0 ) {
          ir_type = results.decode_type; //save IR type     
          //print type
          Serial.print("unsigned char ir_type = ");
          Serial.print(ir_type);
          Serial.print("; // ");
          printIRType();
          Serial.println("");
          //print ir codes array statement     
          Serial.println("unsigned long ir_codes[] = { ");
        }
        
        //print IR value
        Serial.print("\t0x");
        Serial.print(results.value, HEX);
        Serial.print(",   //KEY ");
        Serial.println(key_count++); //increase key count
        
      } else if ( repeat_count == 3 ) { 
        //repeat 3 times will end current receive task
        Serial.println("};");
        ir_type = 0;
        key_count = 0;
        last_key = 0;
        repeat_count = 0;
      }
    }
    
    irrecv.resume();              // Prepare for the next value
  }
  
  delay(10); //wait a while for stability
}

这个Arduino程序只需用Arduino IDE上传到ATmega32U4即可, 不需要使用到Linkit 7688。

程序的用途是:当遥控器逐个按下时,记录下按键值,显示在Arduino的串口监视器窗口中。如果同一按键重复按二次,则忽略。如果同一按键重复按三次,则重新开始记录。

将程序编译上传到ATmega32U4, 运行,将电视机遥控器对着红外接收模块,逐个按下遥控器的各个按钮,最后一个按键重复按三次,运行结果如下:

unsigned char ir_type = 3; // NEC

unsigned long ir_codes[] = { 

0x20DF08F7,   //KEY 0

0x20DF8877,   //KEY 1

0x20DF48B7,   //KEY 2

0x20DFC837,   //KEY 3

0x20DF28D7,   //KEY 4

0x20DFA857,   //KEY 5

0x20DF6897,   //KEY 6

0x20DFE817,   //KEY 7

0x20DF18E7,   //KEY 8

0x20DF9867,   //KEY 9 (最后一个键,要重复按三次)

};

如此,则得到一段代码, 其中:ir_type是遥控器编码厂商类型,ir_codes[]是一个数组,分别记录了第0 至 n个 按键的值。


6.4 红外线接收程序

编一个Arduino程序,采用消息协议,监控红外线接收状态,则发送COMMAND_IR_RECEIVE消息(内容为键值到主控板,程序如下:

注意:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#include <IRremote.h>

#define COMMAND_IR_RECEIVE 'i'

int recvPin = 5;  //IR receiver connects to pin 5
IRrecv irrecv(recvPin); //init IRrecv

//IR key codes
unsigned char ir_type = 3; // NEC
unsigned long ir_codes[] = { 
	0x20DF08F7,   //KEY 0
	0x20DF8877,   //KEY 1
	0x20DF48B7,   //KEY 2
	0x20DFC837,   //KEY 3
	0x20DF28D7,   //KEY 4
	0x20DFA857,   //KEY 5
	0x20DF6897,   //KEY 6
	0x20DFE817,   //KEY 7
	0x20DF18E7,   //KEY 8
	0x20DF9867   //KEY 9  
};

void setup() {
  MessageInit( 57600 ); // init message < need message_protocol.ino >
  irrecv.enableIRIn();  // Start the IR receiver
}


void loop() {
  unsigned char i;
  decode_results  results;        // Somewhere to store the results  
  
  if (irrecv.decode(&results)) // receive an IR code
  {
    if ( results.decode_type == ir_type && results.value != 0xFFFFFFFF ) {
      //find value in ir_codes array
      for (i = 0; i < sizeof(ir_codes); i++) {
        if (results.value == ir_codes[i]) {
          MessageSend( COMMAND_IR_RECEIVE, &i, 1); //send message, content is key index 
        }
      }
    }
    irrecv.resume();              // Prepare for the next value
  }
  
  delay(10);        // delay in between reads for stability
}

Arduino程序中的 ir_type,  ir_codes 就是从红外线学习程序的运行结果中复制来的, 注意:要把 ir_codes数组最后一个数据后的 逗号删去。

Arduino程序的作用是:  当收到红外线信号,在ir_codes数组中查找按键值。如找到,则向主控板发送一个 COMMAND_IR_RECEIVE消息,消息内容是 按键在数组中的序号
将Arduino程序编译上传到ATmega32U4


编一个C语言主程序 ir_test.c,采用消息协议,监听有否COMMAND_IR_RECEIVE消息,如有则打印其内容出来
注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

/*
 * ir_test.c
 *
 */

#include <stdio.h>
#include <unistd.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_IR_RECEIVE 'i'

int main() {
	int fd;   //file descriptor of serial port
	message_t msg; //message object


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		//loop
		while ( 1 ) {

			if ( 1 == message_receive(&msg) ) {  //if received message
				if ( message_command(&msg) == COMMAND_IR_RECEIVE ) {
					printf("IR key index = %d\n", message_data(&msg, 0 ) ); //print key index
					fflush(stdout);
				}

			}

			usleep(10*1000); //wait 10 milli-seconds
		}
		serial_close(fd);

	} else {
		printf("open serial fail\n");
	}

	return 0;
}

将C语言程序交叉编译后生成 ir_test,用scp命令上传到开发板

SSH登入 Linkit 7688 DUO开发板,运行 ir_test

运行: 程序后,当用遥控器对这开发板按键时,则打印出按键结果, 效果如下:

IR key index = 1

IR key index = 2

IR key index = 3

。。。




六、红外线发射(IR EMISSION), 见下图

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇_第5张图片

模块上有一个红外线发射二极管

模块有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 图中左侧标注"S”的管脚管脚接信号(数字I/O), 中间的管脚接电源5V
用三根杜邦线把模块接到开发板上,其中“-”脚接GND,  标注"S”的管脚接 D13口,  中间的管脚接电源5V
注:IRremote库要求必须使用ATmega32U4的 D13做为红外发射口(需要用到时钟和PWM), 接其它口是不行的。


编一个Arduino程序,采用消息协议,接收COMMAND_IR_SEND消息,根据消息内容中的按键序号,翻译为红外线值,发送红外线。程序如下:

程序中要使用到 IRremote库 和  message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#include <IRremote.h>
#define COMMAND_IR_SEND 'r'

IRsend irsend; //note: for ATmega32U4, IR send module must connects to pin 13

//IR key codes
unsigned char ir_type = 3; // NEC
unsigned long ir_codes[] = { 
	0x20DF08F7,   //KEY 0
	0x20DF8877,   //KEY 1
	0x20DF48B7,   //KEY 2
	0x20DFC837,   //KEY 3
	0x20DF28D7,   //KEY 4
	0x20DFA857,   //KEY 5
	0x20DF6897,   //KEY 6
	0x20DFE817,   //KEY 7
	0x20DF18E7,   //KEY 8
	0x20DF9867    //KEY 9  
};

void setup() {
  MessageInit( 57600 ); // init message < need message_protocol.ino >
}


int sendIR(unsigned char index) {
  if (index >= sizeof(ir_codes))
    return 0;
    
  switch (ir_type) {
    case NEC:           irsend.sendNEC(ir_codes[index], 32); return 1;
    case SONY:          irsend.sendSony(ir_codes[index], 32); return 1;
    case RC5:           irsend.sendRC5(ir_codes[index], 32); return 1;
    case RC6:           irsend.sendRC6(ir_codes[index], 32); return 1;
    case DISH:          irsend.sendDISH(ir_codes[index], 32); return 1;
    case SHARP:         irsend.sendSharp(ir_codes[index], 32); return 1;
    case JVC:           irsend.sendJVC(ir_codes[index], 32, false); return 1;
    case SANYO:         return 0;
    case MITSUBISHI:    return 0;
    case SAMSUNG:       irsend.sendSAMSUNG(ir_codes[index], 32); return 1;
    case LG:            irsend.sendLG(ir_codes[index], 32); return 1;
    case WHYNTER:       irsend.sendWhynter(ir_codes[index], 32); return 1;
    case AIWA_RC_T501:  irsend.sendAiwaRCT501(ir_codes[index]); return 1;
    case PANASONIC:     irsend.sendPanasonic(0, ir_codes[index]); return 1; //?address
    case DENON:         irsend.sendDenon(ir_codes[index], 32); return 1;
    default:           return 0;
  }
  return 0;  
}


void loop() { 
  if ( MessageReceive() ) { //if message received
     if ( MessageCommand() == COMMAND_IR_SEND ) {
       unsigned char index =  MessageData(0); //get index of ir_codes from message data(0)
       if ( sendIR(index) )  //send IR
          MessageACK( COMMAND_IR_SEND ); //if send OK, response ACK message
       else
          MessageNAK( COMMAND_IR_SEND ); //if send fail, response NAK message
     }  
  }
}

Arduino程序的作用是:  当收到COMMAND_IR_SEND消息,在消息内容中取得按键编号,发送红外线。如发送成功,返回ACK消息。如不成功返回NAK消息。
将Arduino程序编译上传到ATmega32U4


编一个C语言主程序 ir_send.c,采用消息协议,向ATmega32U4发送COMMAND_IR_SEND消息,则可以控制它发送红外线

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_IR_SEND 'r'

int main(int argc, char **argv) {
	int fd;   //file descriptor of serial port
	message_t msg; //message object
	unsigned char value;

	if (argc > 1 ) {
		value = atoi(argv[1]);
	} else {
		printf("Usage: ir_send <key>\n");
		return 1;
	}

	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		message_send( &msg, COMMAND_IR_SEND, &value, 1); //send COMMAND_IR_SEND message

		serial_close(fd);
	}

	return 0;
}


C程序的作用是:  读取命令行参数,取得按键编号,向ATmega32U4发送COMMAND_IR_SEND消息
使用方法举例:   ir_send  0    即红外线发送 第0个按键

将C程序交叉编译后生成ir_send,用scp命令上传到开发板

SSH登入 Linkit 7688 DUO开发板,将红外发射头对着电视机,运行ir_send:

ir_send 0

则可以看到,电视机接收到红外线信号,作出了反应。


ir_send 1

则可以看到,电视机再次接收到红外线信号,作出了反应。


呵呵, 可以遥控电视机了。



相关代码可以在我的资源中下载: Linkit7688DUO开发板连接多种Ardunio模块的范例程序





你可能感兴趣的:(c,arduino)