怎样通过蓝牙将ArduinoUno连接到Android

电子说

1.3w人已加入

描述

第1步:电路

Android

组件:

1 x Arduino Uno

3 x 220欧姆电阻器

1 x绿色LED

1 x黄色LED

1 x红色LED

1 x TMP36温度传感器

1 x JY-MCU蓝牙从模块(请参阅介绍)

1 x面包板

电线

连接:

步骤1):将Arduino的地线和5V连接到面包板。

步骤2):将LED放在面包板上,并将其阴极接地。将它们的阳极分别通过220欧姆电阻连接到数字引脚:黄色到引脚3,绿色到引脚4,红色到引脚5。

步骤3):将TMP36传感器放在面包板上,将其引脚连接到5V,接地和A0 Arduino引脚。

步骤4):将提供的电缆的一侧连接到JY-MCU蓝牙模块,另一侧连接到面包板;连接如下:

VCC 《-》 5V

GND 《-》 GND TXD 《-》引脚0(Rx)

RXD 《-》引脚1(Tx)

该草图还将使用数字引脚13上的Arduino内置LED。

由于TXD转到Rx,而RXD转到,蓝牙模块的连接可能会有些混乱。 Tx:这是一个解释。发送和接收是针对每个设备的,因此,蓝牙模块的TXD引脚发出的传输必须由Arduino在Rx引脚0上接收;同样,从Arduino Tx引脚1发出的传输也必须到达其RXD引脚上的JY-MCU蓝牙模块。

警告:蓝牙模块可能会干扰PC与Arduino的通信:对电路板进行编程时,请断开VCC。 (在我的测试中,情况并非如此,但请确保它不会造成任何伤害)。

步骤2:Arduino代码-简介

Arduino监听命令以点亮某些LED或显示其状态。另外,定时器中断使它可以通过TMP36传感器检查温度:如果温度大于阈值,则LED点亮;否则,指示灯熄灭。每n秒(其中n是通过应用设置的参数)会向该应用发送状态报告。简单的命令结构使应用程序可以将参数和值发送到Arduino,反之亦然。

程序中定义的命令结构为:

CMD RED | GREEN | YELLOW =开|关

CMD TMAX | SECONDS =值

CMD SECONDS =值

CMD状态

状态消息结构为:

状态红色|绿色|黄色| TMAX | SECONDS | TEMP | THIGH = value

Arduino将以完整状态回答STATUS命令,而在中断时它将报告较短的版本。

示例:

CMD RED = ON将红色LED点亮

CMD GREEN = OFF将绿色LED熄灭

您可以通过在Arduino IDE的串行监视器中发出命令并查看响应来测试草图:确保在底部的下拉选项中选择“回车”。

您可以从附件中下载草图代码。下一步将对此进行详细说明。

步骤3:Arduino代码-详细信息

上一步中所述的命令和消息结构

//串行参数:COM11 9600 8 N 1

// r或 n结束命令行

//蓝牙处于针脚0和1,速度为9600

//命令结构//CMD RED | GREEN | YELLOW = ON | OFF

//CMD TMAX | SECONDS = value

//CMD SECONDS = value

//CMD STATUS

//状态消息结构

//STATUS RED | GREEN | YELLOW | TMIN | TMAX | SECONDS | TEMP | THIGH = value

温度控制所需变量的初始化

float maxTemp = 30.0;//当温度》 maxTemp

int maxTempSensor =(int)((maxTemp/100 + .5)* 204.8);

浮点温度= 0.0;

以后可以更改maxTemp,但是程序需要使用默认值开始。 maxTempSensor是将maxTemp转换为Arduino ADC转换器提供的0-1023范围;温度比较将通过我们希望尽快执行的中断例程执行:直接比较整数Pin输出值而不是浮点温度更为有效。我们仍然要报告温度,程序会将其以相同的名称存储在变量中。

如果您不熟悉温度转换公式,可以在这里查看。

maxSeconds也可以使用命令进行更改,但再次需要默认值

int maxSeconds = 10;//每maxSeconds

Pin常量声明发送状态消息

const int ledPin = 13;//温度指示灯

const int tempPin = A0;//T36温度传感器模拟输入引脚

const int led1Pin = 3;//黄色

const int led2Pin = 4;//Green

const int led3Pin = 5;//红色

中断例程中使用并从其外部访问的变量

volatile int tempVal;

volatile int seconds = 0;

volatile boolean tempHigh = false;

volatile boolean statusReport = false;

Volatile是一个特殊的关键字,可防止编译器执行某些优化:在中断内修改的所有变量例程,并且必须在例程之外进行访问,必须将其声明为volatile,以表明其值可以随时更改,并确保在需要时从内存中读取最新的正确值。

命令字符串变量(稍后将对此进行说明)

字符串inputString =“”;

String command =“”;

字符串值=“”;

布尔stringComplete = false;

setup()函数

void setup(){

//开始串行连接

Serial.begin(9600);

Serial.print(“ Max T:”);

Serial.print(maxTemp);

Serial.print(“ Sensor:”);

Serial.println(maxTempSensor);

inputString.reserve(50);

command.reserve(50) ;

value.reserve(50);

pinMode(ledPin,OUTPUT);

digitalWrite(ledPin,LOW);

pinMode(led1Pin,OUTPUT);

pinMode(led2Pin,OUTPUT) ;

pinMode(led3Pin,OUTPUT);

digitalWrite(led1Pin,LOW);

digitalWrite(led2Pin,LOW);

digitalWrite(led3Pin,LOW);

字符串的reserve方法分配作为参数提供的字节数。

需要以下代码来初始化计时器中断并将其设置为每秒触发一次,这是Arduino可以完成的最慢的时间。有关详细信息,请参见此处。

cli();//禁用全局中断

//初始化Timer1以中断@ 1000毫秒

TCCR1A = 0;//将整个TCCR1A寄存器设置为0

TCCR1B = 0;//与TCCR1B

相同//将比较匹配寄存器设置为所需的计时器计数:

OCR1A = 15624;//打开CTC模式:

TCCR1B | =(1 《//为1024个预分频器设置CS10和CS12位:

TCCR1B | =(1 《 TCCR1B | =(1 《//启用计时器比较中断:

TIMSK1 | =(1 《 sei();//启用全局中断

}

定时器中断例程:我们无法更改其名称,但内容是完全可定制的。/p》

ISR(TIMER1_COMPA_vect)

{

tempVal = AnalogRead(tempPin);

if(tempVal》 maxTempSensor){

digitalWrite(ledPin,HIGH);

tempHigh = true;

}

else {

digitalWrite(ledPin,LOW);

tempHigh = false;

}

温度值-或如上文所述,从传感器读取其0-1023整数表示形式,并将其与阈值进行比较:当上面的内置LED点亮并且tempHigh设置为true时,否则关闭LED并 tempHigh设置为false。

如果(秒++》 = maxSeconds){

statusReport = true;

秒= 0;

}

}

请记住,每秒触发一次中断,但是我们希望报告系统状态的频率降低:秒变量在每次迭代时递增,直到达到s报告到期时的值;稍后将在主循环中通过检查statusReport标志来完成此操作。通常,永远不要执行太慢的操作,例如从中断例程中将数据写入串行。

loop()函数在接收到指令后便会解释并执行命令,然后如果计时器发出标志,则会报告状态打断。为了从串行缓冲区读取字符串,loop()依赖于将在最后定义的serialEvent()函数:该例程在每次loop()运行之间运行。它没有得到广泛的记录,并且可能不适用于所有的Arduino模型。无论如何,将其内容嵌套在主循环中并不难(请参见本步骤的结尾)。

void loop(){

int intValue = 0;

if(stringComplete){ boolean stringOK = false;

if(inputString.startsWith(“ CMD”)){

inputString = inputString.substring(4);

首先,我们检查接收到的字符串是否以“ CMD”开头:如果是这样,我们可以丢弃前四个字符,否则稍后会出现错误。

int pos = inputString.indexOf(‘=’);

if(pos》 -1){

command = inputString.substring(0,pos);

value = inputString。 substring(pos + 1,inputString.length()-1);//提取最多 n个被排除的命令

有两种类型的命令:设置值的命令,在其中找到分隔空格的“ =”值对,以及该命令是单个指令(STATUS)。如果pos处出现“ =”,则字符串将分为命令(左部分)和值(右部分),并将“ =”插入中间,并在行尾添加末尾字符。

如果(command.equals(“ RED”)){//RED = ON | OFF

value.equals(“ ON”)? digitalWrite(led3Pin,HIGH):digitalWrite(led3Pin,LOW);

stringOK = true;

}

否则if(command.equals(“ GREEN”)){//GREEN = ON | OFF

value.equals(“ ON”)吗? digitalWrite(led2Pin,HIGH):digitalWrite(led2Pin,LOW);

stringOK = true;

}

否则if(command.equals(“ YELLOW”)){//YELLOW = ON | OFF

value.equals(“ ON”)吗? digitalWrite(led1Pin,HIGH):digitalWrite(led1Pin,LOW);

stringOK = true;

}

我们检查并执行LED命令;请注意,代码仅检查值ON:如果您写入GREEN = ASD,它将被解释为GREEN = OFF。它不是完美的,但是它使事情变得简单得多。每次识别并执行命令时都会设置stringOK = true,以便以后标记错误的命令。

否则,如果(command.equals(“ TMAX”)){//TMAX =值

intValue = value.toInt();

如果(intValue》 0){

maxTemp = (float)intValue;

maxTempSensor =(int)((maxTemp/100 + .5)* 204.8);

stringOK = true;

}

}

否则,如果(command.equals(“ SECONDS”)){//SECONDS = value

intValue = value.toInt();

如果(intValue》 0){

maxSeconds = intValue;

stringOK = true;

}

}

当值应为数字时,我们需要将其转换并测试它确实是一个数字。对于MaxTemp,我们还按照变量定义部分中的说明计算传感器值

}//pos》 -1

else if(inputString.startsWith(“ STATUS”)){

Serial.print(“ STATUS RED =”);

Serial.println(digitalRead(led3Pin ));

Serial.print(“ STATUS GREEN =”);

Serial.println(digitalRead(led2Pin));

Serial.print(“ STATUS YELLOW =”);

Serial.println(digitalRead(led1Pin));

Serial.print(“ STATUS TMAX =“);

Serial.println(maxTemp);

Serial.print(” STATUS SECONDS =“);

Serial.println(maxSeconds);

Serial.print(“ STATUS TEMP =”);

Serial.println(temperature);

Serial.print(“ STATUS THIGH =”);

Serial.println(tempHigh);

stringOK = true ;

}//inputString.starts With(“ STATUS”)

如果命令是STATUS,则程序仅将所有信息输出到串行。

}//inputString.startsWith (“ CMD”)

stringOK吗? Serial.println(“ Command Executed”):Serial.println(“ Invalid Command”);

指示是否已收到有效或无效命令。

//清除字符串以进行下一次迭代

inputString =“”;

stringComplete = false;

}//stringComplete

下一次命令迭代的变量内务处理。

if(statusReport){

temperature =(tempVal * 0.0048828125-.5)* 100;

Serial.print(“ STATUS TEMP =”);

Serial.println(temperature);

Serial.print(“ STATUS THIGH =”);

Serial.println(tempHigh);

statusReport = false;

}

}

如果中断例程已引发statusReport标志,则将打印一些信息请注意,此时要计算当前温度值:因此,如果在statusReport时间间隔之间发出STATUS命令,则会得到旧的温度值。

如前所述,只要新数据进入硬件串行RX,就会发生serialEvent()。该例程在每次loop()运行之间运行,因此在循环内部使用delay可以延迟响应。可能有多个字节的数据。

无效的serialEvent(){

而(Serial.available()){

//获得新的字节:

char inChar =(char)Serial.read();

//将其添加到inputString:

inputString + = inChar;

//如果传入字符是换行符或回车符,则设置标志

//,因此主循环可以对此做一些事情:

if(inChar ==‘ n’|| inChar ==‘ r’){

stringComplete = true;

}

}

}

从串行读取每个字节并将其添加到输入字符串,直到遇到“ n”或“ r”表示字符串结尾:在这种情况下,设置由loop()检查的stringComplete标志。同时使用回车符 r和换行符 n确保代码能够从各种输入(包括Arduino IDE串行监视器以外的其他串行终端)检测字符串结尾。

关于蓝牙和串行的注意事项

在许多示例中,包括JY-MCU卖方的示例,您可以找到连接在不同Arduino数字引脚(例如10和11)上的蓝牙模块,以及通过SoftwareSerial库访问。根据我的测试结果,当该模块仅用于发送信息时,SoftwareSerial可以完美运行,但是在接收命令时Arduino Uno不够快。我没有尝试降低SoftwareSerial连接的速度(在示例中通常设置为2400bps),因为MIT AppInventor应用似乎不支持蓝牙连接速度设置。

使用SoftwareSerial,serialEvent()不会工作:需要重命名它(例如mySerialEvent())并在loop()的开头显式调用它。

步骤4:App Inventor代码-简介

在使用Android应用之前,您需要将Bluetooth模块与智能手机配对。

给Arduino开发板通电,打开在Android手机上的蓝牙上并搜索附近的蓝牙设备:JY-MCU模块将显示为HC-06,配对密码为1234。

蓝牙Arduino连接的关键组件应用程序是蓝牙客户端,而Arduino板将充当服务器:这意味着应用程序将始终启动连接。给Arduino开发板供电时,蓝牙模块红色LED开始闪烁;按下应用程序的“连接到设备”按钮,然后从列表中选择模块:红色LED指示灯将变为稳定,连接状态将变为“已连接”。

同样,这与蓝牙无关Master/Slave,但仍可能令人困惑:您的Bluetooth模块需要为Slave,但它(或Arduino草图)在客户端中将充当Server-与Android应用程序进行服务器通信。

您可以直接从Android Play商店安装Bluetooth Arduino Connection App,或者您可以通过下载附件将完整的应用程序代码导入到MIT App Inventor项目中。

蓝牙Arduino连接应用程序是使用MIT App Inventor 2开发的;以下步骤将提供详细说明。

步骤5:App Inventor代码-详细信息

Android

Android

Android

Arduino蓝牙连接应用程序的关键组件是:

用于蓝牙配对设备的ListPicker(ListPicker1)

3个按钮,每个按钮控制相应的配色板LED(RedLedBtn,GreenLedBtn,YellowLedBtn)

发送状态命令(GetStatusBtn)的按钮

带有关联按钮的按钮文本框以设置状态报告间隔(SecondsBtn和SecondsTxBx)

巨大的多行状态标签,显示从Arduino板(状态)接收的信息

上一步(BluetoothClient1)

一个时钟组件,该时钟组件在客户端连接时每秒触发一次中断(Clock1)

以下是基于以下内容的应用代码说明:上面的图片。

图1

Variabl当打开应用程序屏幕时,将设置并初始化LED的状态和间隔。

图2

连接ListPicker对象ListPicker1使用两种方法:

已准备好可用(配对)的蓝牙设备的列表,并显示给用户

当用户选择设备时,将调用Bluetooth Client对象的Connect方法为了开始连接:如果成功,它将显示在适当的标签中,并激活Clock中断,以便可以接收来自设备的消息。

图3

在这里,我们演示如何向Arduino板发送命令。

按下GetStatusBtn时,将调用Bluetooth Client对象的SendText方法并执行文本命令发出:注意,在“ CMD STATUS”字符串的末尾添加了“ n”,以便Arduino草图中的serialEvent()函数能够知道消息何时结束。

代码打开或关闭LED稍微复杂一点:

我们使用相应的变量来跟踪其当前状态:如果打开,我们想将其关闭,反之亦然;因此,首先,我们将变量更改为布尔值

,然后使用新状态更新按钮标签

最后,调用BluetoothClient1.SendText来传输命令。

其他命令的代码非常相似,因此未显示。

图4

每次Clock1计时器触发,该例程被执行:它等效于Arduino的serialEvent();。如果BluetoothClient1接收到字节,则将它们复制到状态标签中。请注意,Bluetooth客户端对象具有一种返回所接收消息长度的方法。

步骤6:结论

在此指导中,我演示了一个通过蓝牙连接Arduino开发板和Android智能手机的有用方法。通信是双向的,因此开发板不仅向应用程序报告其状态,而且还从应用程序接收命令。

此外,一个简单的扩展程序允许将Arduino的命令发送给应用程序。电话:例如:按下板上的按钮即可拍照或发送短信。

Arduino草图可以作为远程命令处理的基础,它使用中断来执行一些操作-检查温度并打开LED警报-并发送状态心跳信号:该技术不仅可以通过蓝牙通信应用,还可以通过以太网等其他方式应用。

MIT App Inventor应用程序使用中断同样:它等效于Arduino的loop()+ serialEvent()函数的重复,并且类似地用于接收消息。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分