hc05蓝牙android

2013-05-23 09:02:23 itas109 阅读数 33051

STC89C52通过HC-06蓝牙模块与Android手机通信

 

如需转载请标明出处:http://blog.csdn.net/itas109 

QQ技术交流群:129518033

 2017-11-04补充

注意问题:

1.确保上位机波特率为9600

2.确保下位机的波特率为9600,下位机的晶振应该为11.0592MHz

3.本教程中接收指令的是字符1、2、3,不是16进制的0x01、0x02、0x03

4.可能跟上拉电阻有关系,但是不少人反应不需要上拉电阻,时间长了我也不知道了

 

环境
         1、任意单片机开发板即可(当时用的芯片STC90C516RD+),约定为下位机
          2、无线蓝牙串口透传模块 HC-06从机
          3windows xp操作系统
          4、Android智能手机(蓝牙串口通信助手软件),约定为上位机

二、连接的部分电路图(有集成板子的直接插上就可以了,但是要注意若单片机的TXD无上拉能力需要加上拉电阻)

      上拉电阻的阻值有人问我,我当时用的是1KΩ。

 

三、下位机测试程序

(各位大神如果分数比较多可以赞助我一下,就1个积分)

程序地址(整个工程):http://download.csdn.net/detail/itas109/5430553

      

   /***********************************************************

STC90C51RD+与HC-06蓝牙连线:

         蓝牙  STC90C51RD+

         RXD    TXD

         TXD    RXD

其他连线:

         P2.0、P2.1、P2.3分别接一个指示灯

 

!!!!

!!!!注意:若单片机TXD(P3.1)无上拉能力,必须在P3.1端接上拉电阻。本次测试需要接上拉电阻

!!!!

 

手机采用蓝牙串口通信助手

最后已改用Android自编蓝牙通信软件

                                               By itas109

                                               http://blog.csdn.net/itas109

***********************************************************/

#include <reg52.h>

#include <intrins.h>

unsigned char tmp;

unsigned int c=0;

 

sbit led1=P2^0;                      //指示灯0

sbit led2=P2^1;                           //指示灯1

sbit led3=P2^3;                           //指示灯3

 

void init();   //串口初始化

void send(unsigned char a);     //单字节发送函数

void ctrl(); //接收处理函数

 

void main()

{

                   init();

          while(1)

          {         

                  if(RI==1)                     //是否有数据到来

                  {

                   RI = 0;

                  tmp = SBUF;                   //暂存接收到的数据

                                       ctrl();

                                       }      

          }

}

 

 void init()        //串口初始化

 {

                   ES=0;                                                                       //关中断

                     SCON = 0x50;                        // REN=1允许串行接受状态,串口工作模式1,

                                                                                                         //10位UART(1位起始位,8位数据位,1位停止位,无奇偶校验),波特率可变

 

                     TMOD = 0x20;                        //定时器1工作于方式2,8位自动重载模式,用于产生波特率

          TH1=TL1=0xFD;                       //波特率9600 (本次测试采用晶振为11.0592)

          

          PCON &= 0x7f;                       //波特率不倍增

          TR1 = 1;                                                                  //定时器1开始工作,产生波特率

                                                                                                         //发送标志位置0

                     TI=0;                                                                        //接收标志位置0

                     RI=0;

                     

                     //EA=0;

               ES=1;

 

 

          led1=0;                                    //初始化设置3个指示灯全亮

                     led2=0;

                     led3=0;

 }

   

void send(unsigned char a)      //单字节数据发送

{                                                                //注意:若单片机TXD(P3.1)无上拉能力,必须在P3.1端接上拉电阻。本次测试需要接上拉电阻

         TI=0;         

         SBUF=a;

         while(TI==0);

         TI=0;

 

         if(c%2)                                           //发送指示灯标志,每接收一次,此灯亮灭交替

          led3=1;

         else

          led3=0;

         c++;

} 

 

void ctrl()                      //接收处理函数

{

                                     switch(tmp)

                                       {

                                            case '1':

                                                        led1=1;                           //收到字符1,指示灯0灭

                                                        send(tmp);

                                                        break;                                           

                                               case '2':                                         //收到字符2,指示灯1灭

                                                         led2=1;

                                                         send(tmp);

                                                         break;

                                               case '3':                                   //收到字符3,指示灯0、1亮

                                                         led1=0;

                                                         led2=0;

                                                         send(tmp);

                                                         break;

                                               case '4':                                         //收到字符4,指示灯0、1灭

                                                          led1=1;

                                                          led2=1;

                                                          send(tmp);

                                                          break;

                                               case '5':                                         //收到字符5,指示灯3亮

                                                        led3=0;

                                                        send(tmp);

                                                        break;

                                               default:                   //其他,灯全灭

                                                          led1=1;

                                                          led2=1;

                                                          led3=1;

                                                          send(tmp);

                                       }

}


四、上位机软件可以用蓝牙串口通信助手软件

 

 

觉得文章对你有帮助,请扫描二维码捐赠给博主,谢谢!

 

 

如需转载请标明出处:http://blog.csdn.net/itas109 

QQ技术交流群:129518033

 

 

2018-03-19 17:18:50 H2677lucy 阅读数 16256

     代码我忘了在哪找的,只记得是把别人网上的代码稍微整理了一下,之后再加上自己的一些东西就成功了,对,反正就是成功了。很感谢我的一个哥们帮我组建了单片机和蓝牙设备硬件方面的东西,服务器那边也是他写的,灰常感谢...

在上次的博客中写的手机设备向蓝牙设备中发送消息,代码很乱不是很容易整理,这次我会把源码的地址放到文末,大家可以测试一下。

一,Android 手机蓝牙 与 Android 手机蓝牙

    从网上抠下来的那个代码,在一个APP中写了接受和发送,也就是说,两部手机用一个APP就可以通信,     

                                                         

很清楚的看到,两个手机一个APP,在未连接之下点击目标蓝牙会先配对,配对成功之后就可相互发送消息了。

二,Android 手机蓝牙向HC05设备发送数据

在这里之前有一个问题导致APP无法使用,最后看到有博客这样写,我是这样理解的,你的目标设备可以分几类,每一类又有几个专门的UUID,使得匹配,差不多就是这个意思。最后也成功了,也是用这个APK ,那接受的部分就交给我哥们了,啊哈哈哈.

下载地址:https://download.csdn.net/download/h2677lucy/10295861

layout_buletooth_seacher.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/l1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:orientation="vertical">

        <ListView
            android:id="@+id/lvDevices"
            android:layout_width="match_parent"
            android:layout_height="250dp" />

        <TextView
            android:id="@+id/text_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="[CURRENT ORDER]"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/receive_text"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="#EEEEEE"
            android:gravity="center"
            android:textSize="20dp" />
    </LinearLayout>

    <LinearLayout
        android:layout_below="@id/l1"
        android:id="@+id/l2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="3">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:id="@+id/red"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:text="红灯"
                android:textSize="10dp" />

            <Button
                android:id="@+id/green"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="10dp"
                android:layout_weight="1"
                android:text="绿灯"/>
            <Button
                android:id="@+id/blue"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="10dp"
                android:layout_weight="1"
                android:text="蓝灯" />
            <Button
                android:id="@+id/breath"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="10dp"
                android:layout_weight="1"
                android:text="呼吸灯" />
            <Button
                android:id="@+id/close_all_led"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="10dp"
                android:layout_weight="1"
                android:text="关灯" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"

            android:padding="5dp"
            android:layout_height="50dp">

            <TextView
                android:id="@+id/textView"
                android:layout_width="50dp"
                android:layout_height="match_parent"
                android:gravity="center"
                android:background="#31555555"
                android:text="亮度" />

            <SeekBar
                android:id="@+id/seekBar"
                style="@style/AlertDialog.AppCompat.Light"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_gravity="center_vertical"
                android:max="800"
                android:background="#31555555"
                android:progress="50" />
        </LinearLayout>

        <Button
            android:id="@+id/button"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick_Search"
            android:text="搜索" />
    </LinearLayout>
    <LinearLayout
        android:layout_below="@id/l2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >
        <TextView
            android:id="@+id/text_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="[RECEIVE ORDER]"
            android:textSize="18sp" />
        <TextView
            android:id="@+id/msg"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="#EEEEEE"
            android:gravity="center"
            android:textSize="20dp" />
    </LinearLayout>

</RelativeLayout>

BuletoothClientActivity

package com.managesoft.nullchen.buletoothdemo;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

public class BuletoothClientActivity extends Activity implements OnItemClickListener{
    // 获取到蓝牙适配器
    private BluetoothAdapter mBluetoothAdapter;
    // 用来保存搜索到的设备信息
    private List<String> bluetoothDevices = new ArrayList<String>();
    // ListView组件
    private ListView lvDevices;
    // ListView的字符串数组适配器
    private ArrayAdapter<String> arrayAdapter;
    // UUID,蓝牙建立链接需要的
    private final UUID MY_UUID = UUID
            .fromString("00001101-0000-1000-8000-00805F9B34FB");
    // 为其链接创建一个名称
    private final String NAME = "Bluetooth_Socket";
    // 选中发送数据的蓝牙设备,全局变量,否则连接在方法执行完就结束了
    private BluetoothDevice selectDevice;
    // 获取到选中设备的客户端串口,全局变量,否则连接在方法执行完就结束了
    private BluetoothSocket clientSocket;
    // 获取到向设备写的输出流,全局变量,否则连接在方法执行完就结束了
    private OutputStream os;
    // 服务端利用线程不断接受客户端信息
    private AcceptThread thread;
    //定义按钮
    //定义按钮
    private Button close_all_led;
    private Button red1 = null;
    private Button green1 = null;
    private Button blue1 = null;
    private Button breath = null;
    private TextView receive1;
    private SeekBar seekBar;
    private String LED_STATE = "A 红灯亮";
    private TextView re_msg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_buletooth_seacher);
        red1 = (Button)findViewById(R.id.red);
        green1 = (Button)findViewById(R.id.green);
        blue1 = (Button)findViewById(R.id.blue);
        receive1 = (TextView)findViewById(R.id.receive_text);
        close_all_led = (Button)findViewById(R.id.close_all_led);
        breath = (Button)findViewById(R.id.breath);
        seekBar = (SeekBar)findViewById(R.id.seekBar);
        re_msg = (TextView)findViewById(R.id.msg);

        red1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LED_STATE = "R";
                receive1.setText(LED_STATE);
            }
        });
        green1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LED_STATE = "G";
                receive1.setText(LED_STATE);
            }
        });
        blue1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LED_STATE = "B";
                receive1.setText(LED_STATE);
            }
        });
        close_all_led.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LED_STATE = "E";
                receive1.setText(LED_STATE);
            }
        });
        breath.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LED_STATE = "H";
                receive1.setText(LED_STATE);
            }
        });
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                if (b){
                    LED_STATE ="{0:"+i+"}";
                    receive1.setText(LED_STATE);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
        // 获取到蓝牙默认的适配器
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        // 获取到ListView组件
        lvDevices = (ListView) findViewById(R.id.lvDevices);
        // 为listview设置字符换数组适配器
        arrayAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1,
                bluetoothDevices);
        // 为listView绑定适配器
        lvDevices.setAdapter(arrayAdapter);
        // 为listView设置item点击事件侦听
        lvDevices.setOnItemClickListener(this);

        // 用Set集合保持已绑定的设备   将绑定的设备添加到Set集合。
        Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
        if (devices.size() > 0) {
            for (BluetoothDevice bluetoothDevice : devices) {
                // 保存到arrayList集合中
                bluetoothDevices.add(bluetoothDevice.getName() + ":"
                        + bluetoothDevice.getAddress() + "\n");
            }
        }

        // 因为蓝牙搜索到设备和完成搜索都是通过广播来告诉其他应用的
        // 这里注册找到设备和完成搜索广播
        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(receiver, filter);
        filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(receiver, filter);

        // 实例接收客户端传过来的数据线程
        thread = new AcceptThread();
        // 线程开始
        thread.start();
    }

    //搜索蓝牙设备
    public void onClick_Search(View view) {
        setTitle("正在扫描...");
        // 点击搜索周边设备,如果正在搜索,则暂停搜索
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        mBluetoothAdapter.startDiscovery();
    }
    // 注册广播接收者
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            // 获取到广播的action
            String action = intent.getAction();
            // 判断广播是搜索到设备还是搜索完成
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {
                // 找到设备后获取其设备
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 判断这个设备是否是之前已经绑定过了,如果是则不需要添加,在程序初始化的时候已经添加了
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    // 设备没有绑定过,则将其保持到arrayList集合中
                    bluetoothDevices.add(device.getName() + ":"
                            + device.getAddress() + "\n");
                    // 更新字符串数组适配器,将内容显示在listView中
                    arrayAdapter.notifyDataSetChanged();
                }
            } else if (action
                    .equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                setTitle("搜索完成");
            }
        }
    };

    // 点击listView中的设备,传送数据
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
                            long id) {
        // 获取到这个设备的信息
        String s = arrayAdapter.getItem(position);
        // 对其进行分割,获取到这个设备的地址
        String address = s.substring(s.indexOf(":") + 1).trim();
        Log.d("TAG",address);
        // 判断当前是否还是正在搜索周边设备,如果是则暂停搜索
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        // 如果选择设备为空则代表还没有选择设备
        if (selectDevice == null) {
            //通过地址获取到该设备
            selectDevice = mBluetoothAdapter.getRemoteDevice(address);
        }
        // 这里需要try catch一下,以防异常抛出
        try {
            // 判断客户端接口是否为空
            if (clientSocket == null) {
                // 获取到客户端接口
                clientSocket = selectDevice
                        .createRfcommSocketToServiceRecord(MY_UUID);
                // 向服务端发送连接
                clientSocket.connect();
                // 获取到输出流,向外写数据
                os = clientSocket.getOutputStream();

            }
            // 判断是否拿到输出流
            if (os != null) {
                // 需要发送的信息
                //String text = "我传过去了";
                // 以utf-8的格式发送出去
                os.write(LED_STATE.getBytes("UTF-8"));
            }
            // 吐司一下,告诉用户发送成功
            Toast.makeText(this, "发送信息成功,请查收", 0).show();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            // 如果发生异常则告诉用户发送失败
            Toast.makeText(this, "发送信息失败", 0).show();
        }

    }

    // 创建handler,因为我们接收是采用线程来接收的,在线程中无法操作UI,所以需要handler
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            // 通过msg传递过来的信息,吐司一下收到的信息
           // Toast.makeText(BuletoothClientActivity.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
            re_msg.setText((String)msg.obj);
        }
    };

    // 服务端接收信息线程
    private class AcceptThread extends Thread {
        private BluetoothServerSocket serverSocket;// 服务端接口
        private BluetoothSocket socket;// 获取到客户端的接口
        private InputStream is;// 获取到输入流
        private OutputStream os;// 获取到输出流

        public AcceptThread() {
            try {
                // 通过UUID监听请求,然后获取到对应的服务端接口
                serverSocket = mBluetoothAdapter
                        .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void run() {
            try {
                // 接收其客户端的接口
                socket = serverSocket.accept();
                // 获取到输入流
                is = socket.getInputStream();
                // 获取到输出流
                os = socket.getOutputStream();

                // 无线循环来接收数据
                while (true) {
                    // 创建一个128字节的缓冲
                    byte[] buffer = new byte[128];
                    // 每次读取128字节,并保存其读取的角标
                    int count = is.read(buffer);
                    // 创建Message类,向handler发送数据
                    Message msg = new Message();
                    // 发送一个String的数据,让他向上转型为obj类型
                    msg.obj = new String(buffer, 0, count, "utf-8");
                    // 发送数据
                    handler.sendMessage(msg);
                }
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }

        }
    }
}
最后感谢我的好哥们和那个网友。
2017-09-18 10:32:16 weixin_37272286 阅读数 51657

通过本文,可以了解到以下内容:

  • 进入 AT 模式进行蓝牙基本参数设置
  • Arduino 蓝牙控制 LED 电路设计以及代码编写
  • 利用 Andorid 蓝牙串口调试软件测试功能

进入 At 模式进行蓝牙基本参数设置

想要使用 Arduino 的蓝牙模块,首先要对蓝牙模块进行基本参数设置。基本参数设置主要包含:蓝牙名称、模式以及匹配密码等。设置蓝牙模块可以使用 USB-TTL 连接电脑使用串口调试软件进入 AT 模式进行设置,也可以使用 Arduino 连接蓝牙模块进行设置,本文主要介绍后一种方法。

注意事项

在连接蓝牙的时候,要注意线的连接是否正确,检查清楚再进行通电,除此之外,还要主要不要让针脚短路,我在首次使用蓝牙模块的时候,因为没注意接线,导致一个蓝牙模块损坏了,只能发信息不能收信息,所以为了避免不必要的损失,这里要注意。

Arduino HC05 AT模式接线

进入 AT 模式设置蓝牙的接线如下:
Arduino 5V - VCC
Arduino GND - GND
Arduino Pin10 - TXD
Arduino Pin11 - RXD
在通电前检查接线是否正确连接
hc05_at_mode.jpg

Arduino 进入 AT 模式代码

接下来,我们需要为使用 Arduino 设置蓝牙模块 AT 模式编写程序,这个程序是让我们可以通过 Arduino IDE 提供的串口监视器来设置蓝牙模块。详细的 Arduino 代码如下:

#include <SoftwareSerial.h> 

// Pin10为RX,接HC05的TXD
// Pin11为TX,接HC05的RXD
SoftwareSerial BT(10, 11); 
char val;

void setup() {
  Serial.begin(38400); 
  Serial.println("BT is ready!");
  // HC-05默认,38400
  BT.begin(38400);
}

void loop() {
  if (Serial.available()) {
    val = Serial.read();
    BT.print(val);
  }

  if (BT.available()) {
    val = BT.read();
    Serial.print(val);
  }
}

利用 Arduino IDE 串口监视器进行调试

首先,将 Arduino 断电,然后按着蓝牙模块上的黑色按钮,再让 Arduino 通电,如果蓝牙模块指示灯按2秒的频率闪烁,表明蓝牙模块已经正确进入 AT 模式。
打开 Arduino IDE 的串口监视器,选择正确的端口,将输出格式设置为 Both: NL & CR ,波特率设置为 38400 ,可以看到串口监视器中显示 BT is ready! 的信息。
然后,输入 AT ,如果一切正常,串口显示器会显示 OK
接下来,我们即可对蓝牙模块进行设置,常用 AT 命令如下:

AT+ORGL    # 恢复出厂模式
AT+NAME=<Name>    # 设置蓝牙名称
AT+ROLE=0    # 设置蓝牙为从模式
AT+CMODE=1    # 设置蓝牙为任意设备连接模式
AT+PSWD=<Pwd>    # 设置蓝牙匹配密码

正常情况下,命令发送后,会返回 OK ,如果没有返回任何信息,请检查接线是否正确,蓝牙模块是否已经进入 AT 模式,如果上述两点都没有问题,可能是蓝牙模块的问题,可以找蓝牙模块供应商咨询。
设置完毕后,断开电源,再次通电,这是,蓝牙模块指示灯会快速闪烁,这表明蓝牙已经进入正常工作模式。

利用 Andorid 手机连接 Arduino 并控制 LED灯开关

我们完成了对蓝牙模块的设置后,我们将做一个可以通过手机蓝牙连接,控制 Arduino 开关 LED 灯的小实验。

Arduino 电路设计

这里的电路设计比较简单,主要是两部分:
- Arduino 与 HC05 模块连接
- Arduino 与 LED 连接

这里有两点需要注意,Arduino 上的 TXD 应与 HC05 模块上的 RXD 端连接,Arduino 上的 RXD 应与 HC05 模块上的 TXD 连接。
在下面的图例中,我的 LED 是直接连接在 Arduino Pin13 上,而实际电路连接中,因根据连接的 Led 灯的设计考虑是否需要串联电阻。
Hc05_Led.jpg

Arduino 蓝牙控制 LED 灯 程序设计

Arduino 程序代码如下:

void setup()
{
  // 设置波特率为 38400
  Serial.begin(38400);
  pinMode(13, OUTPUT);
}


void loop()
{
  while(Serial.available())
  {
    char c=Serial.read();
      if(c=='1')
      {
        Serial.println("BT is ready!");
        // 返回到手机调试程序上
        Serial.write("Serial--13--high");
        digitalWrite(13, HIGH);
      }
     if(c=='2')
     {
       Serial.write("Serial--13--low");
       digitalWrite(13, LOW);
     }
  }
}

Android 手机端调试

在 Android 端上进行调试,需要下载蓝牙串口调试 APP,可以根据喜好在各大应用商场搜索下载。
下载安装完成 APP 后,我们先打开手机的蓝牙设置,搜索并匹配好我们的蓝牙模块。然后打开 蓝牙串口调试APP ,让 APP 连接上蓝牙模块,然后我们可以在 APP 中输入 1,接下来可以看到 LED 等亮了,并且能在 APP 中看到 Serial--13--high 的返回(有些 APP 返回值可能不是返回在同一行)。我们再在 APP 中输入 2,可以看到 LED 熄灭, APP 中返回 Serial--13--low

小结

在这篇文章中,我们了解了在 Arduino 中使用 HC05 蓝牙模块的两个主要步骤,首先是进入 AT模式 对蓝牙模块进行设置,这里要注意接线的正确性,设置完成后,就可以将蓝牙模块的 TXArduino RX 连接,RXArduino TX 连接,再通过 Arduino 程序中的 Serial 来实现数据的传输与读取。最后,在通过 Android 上的蓝牙串口调试APP,来测试我们的试验是否成功。

参考资料

蓝牙模块HC05
Arduino Bluetooth Basic Tutorial


本文为原创内容,由同一作者于早前发表于简书,Arduino使用HC05蓝牙模块与手机连接

2016-10-22 13:06:55 dengpeng0419 阅读数 13197

很久没有写博客了,计划一直都有,但总是被这样或者那样的事情给耽搁了,在此写下文字监督自己:不论长短,每周至少一篇!本文根据自己的实践总结而来,参考前人博客之余,也自己总结和开发了一些功能,在这里给自己备份也分享给大家。不同之处在于:自动打开并搜索蓝牙、修改蓝牙名字、完整接收蓝牙传输数据、修改蓝牙密码、解除蓝牙绑定。


一、系统框架简介

系统由上、下位机两部分构成,旨在实现移动端app通过蓝牙通信,将app发送过来的数据存储在下位机的存储单元,与此同时,app也可以通过指令查询下位机的参数设置。系统框架图如下:

这里写图片描述

二、软件界面设计

话不多说,先上软件界面:

这里写图片描述

界面主要涉及到按键的监听和Activity的跳转:
①按键监听有四种方法,详情可以参考博客
②Activity的跳转包括:直接跳startActivity转和带返回跳startActivityForResult转,详情可以参考博客

二、蓝牙开发

蓝牙功能 函数名称
(1)开启&关闭蓝牙 enable()/disable()
(2)修改蓝牙名字 changeName()
(3)搜索&显示蓝牙设备 DeviceListActivity
(4)配对&连接蓝牙 connectToServerSocket(address)
(5)数据收发 write/ConnectedThread
(6)修改蓝牙密码 changeMima()
(7)取消蓝牙配对 removeBond

本系统中实现的功能如表所示,这里不打算赘述已经开发成熟的功能,可以参考这篇博客,讲得比较清晰,这里只针对自己在开发中添加的功能做一下记录,开发之前需要提醒的有两点:
①不要忘记蓝牙权限的设置:

<uses-permission android:name="android.permission.BLUETOOTH"/>  
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>  

②让主程序弹出搜索对话框时,不要忘了intent设置;

<activity android:name=".DeviceListActivity" 
android:theme="@android:style/Theme.Dialog" 
android:label="选取连接设备" 
android:configChanges="orientation|keyboardHidden"/>

(1)自动打开并搜索蓝牙

一般的做法都是监听按键(打开蓝牙按键和搜索设备按键),先打开蓝牙,接着搜索蓝牙。但是,这样做无疑使操作更加复杂,在实际生产和用户使用时并不方便,我要做的效果是程序启动的时候自动打开蓝牙并开始搜索设备,于是乎,出问题了,蓝牙虽然打开了,但是不接着搜索设备了,汗。。。解决办法是,打开蓝牙后延时1秒后开始搜索,这样就行了

//得到BluetoothAdapter对象
BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();
mBtAdapter.enable();//打开蓝牙
Timer timer=new Timer();
TimerTask task=new TimerTask(){//定时1k=new TimerTask(){//定时1秒后搜索设备
    @Override
     public void run() {
        Intent serverIntent = new Intent(MainActivity.this, DeviceListActivity.class); //跳转程序设置
        startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);  //设置返回宏定义
    }          
};
timer.schedule(task,1000);

(3)修改蓝牙名字

这一功能有两种实现方式,一种是通过蓝牙传输将名字传输给下位机,用单片机操作蓝牙AT指令直接修改蓝牙名字,这样,以后任何手机搜索该蓝牙设备时,显示的名字都是你所修改的名字;另一种方式是在软件层面实现蓝牙名字的修改,其实是搜索出蓝牙设备的ID,然后将其对应到你想修改的名字,并将这一映射存在本地,那么,以后这部手机下次搜索到该设备时,就会显示你所修改的名字,而不影响其他手机对该设备的显示。这种方法各有特色,就看你的需求了。这里就讲讲第二种方式的实现:

步骤一:搜索蓝牙设备,获取并修改名字

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // TODO Auto-generated method stub
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode) {
    case REQUEST_CONNECT_DEVICE:     //连接结果,由DeviceListActivity设置返回
        if (resultCode == Activity.RESULT_OK) { //选择蓝牙
            String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
            device = mBluetoothAdapter.getRemoteDevice(address);// 得到蓝牙设备句柄
            connectToServerSocket(address);
            setting_thread=true;
            ConnectedThread thread=new ConnectedThread();
            thread.start();
        }
        break;
        default:break;
    }
}

在从DeviceListActivity返回的数据中,其实已经包含蓝牙设备的句柄,我们可以通过device.getName()获取当前连接的蓝牙模块的名字,然后将设备的名字或者其唯一标识符(例如,98:D3:34:90:8A:BC)与新修改的名字做映射保存在本地。
步骤二:存储设置到本地,下次搜索蓝牙时做映射显示
使用SharedPreferences存储数据,SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置比如窗口状态,一般在Activity中 重载窗口状态onSaveInstanceState保存一般使用SharedPreferences完成,它提供了Android平台常规的Long长 整形、Int整形、String字符串型的保存。

//获取SharedPreferences对象
Context ctx = MainActivity.this;       
SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
//存入数据
Editor editor = sp.edit();
editor.putString("STRING_KEY", "string");
editor.commit();

(4)完整接收蓝牙传输数据

在开发蓝牙数据接收时,发现一个问题:数据有时候会接收不完整。由于项目中的每一位数据返回都十分重要,所以必须保证数据的完整性,要解决这一问题需要清楚的了解如何从InputStream中读取数据,从输入流中读取数据最常用的方法基本上就是如下 3 个 read() 方法:

① read () 方法,这个方法从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1 。
② read (byte[] b,int off,int len) 方法,将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。
③ read (byte[] b) 方法, 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。

对于三种方法的选择,需要看具体项目的需求,如果你能够通过某种方法知道数据的长度,那么建议用第一种方法,每次读取一个字节,保证数据的完整性;如果数据长度不知道,那么就用②或者③,一般③用的比较多,这一方法效率高但是读取数据的长度不确定—-不超过数组b的长度,所以就需要多次读取直到把数据全部读完。

(5)修改蓝牙密码

本项目中用的是HC-05蓝牙模块,该模块有接收蓝牙指令模式(即通信模式),和AT指令集模式,一般默认状态为蓝牙通信模式,要想修改蓝牙连接密码,需要将蓝牙模块切换到AT指令集模式,具体过程见下面的流程图:
这里写图片描述

(6)解除蓝牙绑定(配对)

在真实场景中,要想在程序修改完密码后,之前连接的蓝牙设备都无法与该蓝牙模块建立连接,需要解除该模块与手机的绑定,即取消配对。要想解除所有的配对,当然是需要下位机控制蓝牙模块进入AT指令集模式,解除所有绑定,而与此同时,当前修改密码的手机也需要在软件层面解除绑定(配对),这样交互性更强。

用过Android系统设置(Setting)的人都知道蓝牙搜索之后可以建立配对和解除配对,但是这两项功能的函数没有在SDK中给出,本项目利用Java的反射机制去调用removeBond解除绑定,从而取消本机与该蓝牙模块的配对。具体实现程序如下:当程序接收到下位机传来的修改密码成功的指令后,程序会弹出一个强提示,当用户点击确认按钮之后,程序解除绑定并退出程序。

if(modify_flag){//修改密码成功
      AlertDialog.Builder builder = new Builder(MainActivity.this);
      builder.setTitle("密码修改成功");
      builder.setMessage("点击确定按钮重新启动!");
      builder.setCancelable(false);
      builder.setPositiveButton("确认", new OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
           try {//接触绑定
               Method m = device.getClass().getMethod("removeBond", (Class[]) null);
               m.invoke(device, (Object[]) null);
           } catch (Exception e) {
                Log.e("erro", e.getMessage());
           }
           setting_thread=false;
           dialog.dismiss();
           mBluetoothAdapter.disable();//关闭蓝牙
           finish();
           System.exit(0);//销毁程序
           }
      });
      builder.setCancelable(false);
      builder.create().show();
}else {
     Toast.makeText(MainActivity.this, "set failed!", Toast.LENGTH_SHORT).show();
}
2017-07-21 23:51:08 wei348144881 阅读数 36851

调模块总是让人很头疼,有时候遇到一些其实很简单的问题也找不到原因,着实难受。这里写一点我在配置HC05遇到的一些常见的问题和对一些问题的或对或错的理解(首次写东西,不会组织语言。。。。凑合吧)。

1.接口容易出错

受网络端口同名端相连的固定思维影响,新手可能认为蓝牙的串口的接口RX对主机的RX,TX对主机的TX,其实这样完全错误,其实正好相反。不只是蓝牙,串口都是这样接的。

2.AT指令码问题

串口助手调试时发送指令码有十六进制和文本模式,一般使用文本模式【除非你闲的没事非用十六进制hex模式】。与HC06的发送直接AT指令不同,HC05文本模式下通信调试指令是发送AT+换行(输入AT敲一下回车便可)。其他的指令都要在最后加换行。

 

3.蓝牙串口APP中搜索不到设备

有的人在手机上下载了蓝牙串口APP但是打开连接搜索不到设备。原因可能是你的手机并没有与蓝牙配对。这里我们应该先通过手机蓝牙与搜索到蓝牙模块设备与蓝牙进行配对(下边左图),然后在串口APP搜索中就能找到蓝牙设备并进行连接(右图),楼主蓝牙不在身边,能说明白就行。。。。

 

4. 如何进入AT指令

HC05进入AT指令有两种方式(手册上有,这里只是强调一些容易忽视或者手册说的不太明白的东西),一种是固定波特率38400进入AT指令(楼主认为是为了避免AT指令被修改后用户不知道通信波特率而造成不必要的麻烦)。设置方法为:先按住按键,然后给蓝牙上电,略等一秒或者更长的时间,松开按键,此时可见看到2s闪烁间隔方式闪烁,说明已经进入AT指令设置。便可以在串口助手上通过发送AT指令对蓝牙进行相关配置,注意设置波特率为38400。

另一种是上电后按一下按键,便进入AT指令设置(指示灯闪烁频率依旧没什么变化),该设置的波特率是可变的,所以要事先知道蓝牙已经设置的波特率才能操作(模块一般默认波特率为9600)。

5.HC05与HC06从模块配对通信示例。

首先通过AT指令把HC05配置为主模块,配置好最好查询一下蓝牙当前状态,确定模块当前状态是主模块状态;然后配置两个模块的配对密码为相同的密码即可。上电后主模块会自动搜寻附近的蓝牙从机并配对连接,如果配对密码相同就可自动完成配对。所以上电等待配对即可,配对完成后就可以理解为两个蓝牙相接的的串口连接在了一起。下图是通过两个串口调试的结果(能看得清吗?),可以看到这两个接了已经配对的蓝牙的串口可以进行串口通信了。这也就意味着,如果把上位机换成单片机,就可以实现两个单片机的短距离通信。