手机蓝牙控制开关

先讲一下整体思路哈!手机肯定不能直接控制台灯的,需要一个中间物来协调,在这里我用的是51单片机(如果大家不知道也没关系,下面我还会说的)。接下来就是具体怎么控制的,其实原理挺简单的。1.手机通过蓝牙来与单片机通信,因而单片机需要外接一个蓝牙模块(我用的是hc-05 主从一体 蓝牙模块)。大家千万不要被外接给吓到了,外接模块一点都不难的,就只要去淘宝卖相应的模块然后用杜邦线(不知道的可以把它当做导线来理解)和51单片机连起来就好了。到这里手机已经可以和51单片机通信了,也就是说手机可以给单片机发送“开灯”和“关灯”的消息了。2.接下来就要解决当51单片机接收到”开灯“和”关灯“的消息后,该怎么控制台灯实际的开关?这个时候我们就需要一个继电器(也是单片机的外接模块),关于继电器我们可以看下面的图片。继电器一共有三个输出端口(常开端 公共端 常闭端)。事先申明,我们可以通过单片机控制继电器的公共端是和常开端

连通,还是和常闭端连通。现在我们只需要剪断台灯的一根电线,将电线的两头分别和继电器的常开端和公共端连接起来即可。通过单片机控制继电器的公共端和常开端连接时台灯打开,反之台灯关闭。大致原理就是这样,我们来梳理一下整个流程。首先手机通过蓝牙和单片机的蓝牙模块建立通信,当手机发送一个打开台灯信号时,单片机收到相应的信号并控制继电器的公共端指向常开端,台灯亮起。

next~就是具体实现了。一共分为两大部分,分别是Android端和单片机端,先从Android端开始说起。

一.Android端:

Android端其实就是一个简单的蓝牙通信,Android端只需要通过蓝牙向单片机的蓝牙模块发送开关对应的消息即可(我是用0xff表示打开台灯,0x00关闭台灯)。先来看看工程的总体结构以及软件界面。

其中BluetoothTool类是一个蓝牙工具类,里面有关于蓝牙的连接以及发送接收数据功能。IUpdateUI是一个接口,用来在 BlurtoothTool中更新主界面的设备列表以及log。MainAty就是主界面的Activity。activity_main.xml和layout_lv_devices_item.xml不用多说了吧,就是一些界面有关的。关于Android端的解析以贴代码为主,因为代码中我都有详细的解释,比较重要的我会在博客中用文字再次解释的。

1.MainAty:

获取蓝牙适配器,可以通过蓝牙适配器获取蓝牙设备的信息。

/**

  • 获得默认的蓝牙适配器
    */
    private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

如果手机没打开蓝牙,则界面跳转到打开蓝牙界面。

@Override
protected void onStart() {
super.onStart();

/** 判断蓝牙是否可用,不可用时请求打开*/
if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivity(intent);
}

}

通过蓝牙适配器获取之前匹配过的蓝牙设备信息(如单片机的蓝牙设备),所以第一次使用的时候,先用手机自带的蓝牙匹配成功一次哈!蓝牙设备中一个比较重要的信息就是设备地址-如98:D3:33:80:83:05就是一个蓝牙设备地址,唯一标示。

/** 获取以前匹配过的蓝牙设备*/
Set devices = null;
if (mBluetoothAdapter != null)
devices = mBluetoothAdapter.getBondedDevices();
else
Toast.makeText(MainAty.this, "该设备不支持蓝牙功能 ", Toast.LENGTH_SHORT).show();

    if (devices != null && devices.size() > 0) {
        data.clear();
        for (BluetoothDevice device : devices) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("lv_left_icon", R.drawable.lv_left_icon);
            map.put("lv_address", device.getAddress());
            map.put("lv_right_icon", R.drawable.lv_right_white);
            data.add(map);
        }
    } else {
        HashMap<String, Object> map = new HashMap<>();
        map.put("lv_left_icon", R.drawable.lv_left_icon);
        map.put("lv_address", "没有已经匹配的设备");
        map.put("lv_right_icon", R.drawable.lv_right_white);
        data.add(map);
        mTextView.append("没有已经匹配的设备" + "\r\n");
    }

    simpleAdapter.notifyDataSetChanged();

连接指定的蓝牙:通过调用BluetoothTool连接蓝牙,我们传入了设备的地址"(String) data.get(0).get(“lv_address”)"以及连接类型 BluetoothTool.ServiceOrClient.CLIENT(这里我们是以客户端的形式连接,也就是单片机的蓝牙当作客户端 )。之后设置了BluetoothTool的更新UI接口,并在MainAty中具体实现。
builder.setPositiveButton(“连接”, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mBluetoothTool = new BluetoothTool((String) data.get(0).get(“lv_address”),
BluetoothTool.ServiceOrClient.CLIENT);
mBluetoothTool.SetOnIUpdateUI(new IUpdateUI() {
@Override
public void updateListViewDevices() {
for (int i = 0; i < data.size(); i++) {
if (i == index) {
data.get(i).put(“lv_right_icon”, R.drawable.checked);
continue;
}
data.get(i).put(“lv_right_icon”, R.drawable.lv_right_white);
}

            simpleAdapter.notifyDataSetChanged();
        }

        @Override
        public void updateLog(String msg) {
            mTextView.append("\r\n" + msg);
        }
    });

}

});

可以看到打开台灯按钮的点击事件,只是调用了BluetoothTool的发送功能向单片机蓝牙发送了一个ff消息(具体发送时将ff转化成16进制0xff,因而在单片机端我们会收到一个0xff的数据)。关闭事件同上。

@Override
public void onClick(View view) {
int id = view.getId();

switch (id) {
    case R.id.id_btn_open:
        if (mBluetoothTool != null) {
            mBluetoothTool.sendData("ff");
        } else
            Toast.makeText(MainAty.this, "蓝牙未连接...", Toast.LENGTH_SHORT).show();
        break;
    case R.id.id_btn_close:
        if (mBluetoothTool != null) {
            mBluetoothTool.sendData("00");
        } else
            Toast.makeText(MainAty.this, "蓝牙未连接...", Toast.LENGTH_SHORT).show();
        break;
    default:
        break;
}

}

2.BluetoothTool

传入的蓝牙设备地址(一般是单片机端蓝牙的地址)
/**

  • 蓝牙设备地址
    */
    private String mBluetoothAddress = null;

通过传入的蓝牙地址获取相应的蓝牙设备。

/**

  • 蓝牙设备
    */
    private BluetoothDevice mDevice = null;

mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mBluetoothAddress);

这里我们都是以客户端的形式连接的。(也就是单片机上的蓝牙是客户端)。

/**

  • 枚举 表示是客户端还是服务端
    */
    public static enum ServiceOrClient {
    NONE, SERVICE, CLIENT
    }
    private ServiceOrClient mServiceOrClient = ServiceOrClient.NONE;

Handler 用来更新UI

private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_LISTVIEW:
if (iUpdateUI != null)
iUpdateUI.updateListViewDevices();
break;
case MSG_UPDATE_LOG:
if(iUpdateUI!=null)
iUpdateUI.updateLog(msg.obj + “”);
break;
}
}
};

单片机的蓝牙与手机端的蓝牙通信的socket,说明一下蓝牙通信其实也是基于socket通信的。

/**

  • 蓝牙客户端socket
    */
    private BluetoothSocket mClientSocket = null;

以客户端身份连接的线程,我们来看看具体实现。

/**

  • 客户端线程
    */
    private ClientThread mClientThread = null;

通过蓝牙设备获取相应的socket,之后单片机的蓝牙和手机的蓝牙通信都是通过这个socket。其中在蓝牙中,每个服务和服务属性都唯一地由"全球唯一标识符" (UUID)来校验。而且这个UUID的值必须是00001101-0000-1000-8000-00805F9B34FB,这个是android的API上面说明的,用于普通蓝牙适配器和android手机蓝牙模块连接的。获取之后通过socket的connect进行连接,连接成功之后开启读取数据的线程。

/**

  • 客户端线程
    /
    private class ClientThread extends Thread {
    @Override
    public void run() {
    super.run();
    try {
    /
    * 客户端通过服务端的UUID与之连接*/
    mClientSocket = mDevice.createRfcommSocketToServiceRecord(
    UUID.fromString(“00001101-0000-1000-8000-00805F9B34FB”));

         Message msg = Message.obtain(null, MSG_UPDATE_LOG);
         msg.obj = "正在连接。。。";
         mHandler.sendMessage(msg);
    
         /** 连接*/
         mClientSocket.connect();
    
    
         msg = Message.obtain(null, MSG_UPDATE_LOG);
         msg.obj = "连接成功";
         mHandler.sendMessage(msg);
    
    
         msg = Message.obtain(null, MSG_UPDATE_LISTVIEW);
         mHandler.sendMessage(msg);
    
         /** 接收数据*/
         mReadThread = new ReadThread();
         mReadThread.start();
    
     } catch (IOException e) {
         e.printStackTrace();
    
         Message msg = Message.obtain(null, MSG_UPDATE_LOG);
         msg.obj = "连接失败";
         mHandler.sendMessage(msg);
     }
    

    }
    }

可以看到线程一直在查看有没有数据,如果有的话就接受,并根据接收到的数据进行相应的显示。有一点要先说一下,就是如果手机成功发送了一个开灯命令给单片机,单片机收到之后成功控制继电器将台灯打开之后,单片机会回发一个消息0xff给手机。因此手机端只要收到0xff这个消息,就知道台灯打开成功了手机就可以显示台灯成功开启。

/**

  • 读取数据线程
    */
    private class ReadThread extends Thread {
    @Override
    public void run() {
    super.run();

     byte[] buffer = new byte[1024];
     int bytes;
     InputStream in = null;
    
     try {
         in = mClientSocket.getInputStream();
    
         while (true) {
             if ((bytes = in.read(buffer)) > 0) {
                 byte[] buf_data = new byte[bytes];
                 for (int i = 0; i < bytes; i++) {
                     buf_data[i] = buffer[i];
    
    
                     int j = buffer[i];
                     j = buffer[i] & 0xff;
                     String str = Integer.toHexString(j);
                     if ("ff".equals(str)) {
                         Message msg = Message.obtain(null, MSG_UPDATE_LOG);
                         msg.obj = "台灯打开";
                         mHandler.sendMessage(msg);
                     } else if ("0".equals(str)) {//注意不能用00,因为0x00实际的值是0
    
                         Message msg = Message.obtain(null, MSG_UPDATE_LOG);
                         msg.obj = "台灯关闭";
                         mHandler.sendMessage(msg);
                     } else {
                        Message msg = Message.obtain(null, MSG_UPDATE_LOG);
                         msg.obj = "err...";
                         mHandler.sendMessage(msg);
                     }
    
                 }
             }
         }
    
     } catch (IOException e) {
         e.printStackTrace();
    
         Message msg = Message.obtain(null, MSG_UPDATE_LOG);
         msg.obj = "连接数据失败";
         mHandler.sendMessage(msg);
     } finally {
         if (in != null)
             try {
                 in.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
     }
    

    }
    }

向单片机发送数据,通过socket获取相应的输出流,然后将要发送的字符串转化成16进制发送。

/**

  • 发送数据

  • @param str
    */
    public void sendData(String str) {
    if (mClientSocket == null) {
    Log.d(TAG, “sendData: 没有连接。。。”);

     Message msg = Message.obtain(null,
  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个基于STM32F103最小系统的蓝牙控制LED灯的完整代码: ```c #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_usart.h" GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; void GPIO_Configuration(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); } void USART_Configuration(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } void LED_Configuration(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } void delay(uint32_t n) { uint32_t i; for (i = 0; i < n; i++); } void USART_SendChar(char ch) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, ch); } char USART_ReceiveChar(void) { while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); return USART_ReceiveData(USART1); } int main(void) { char ch; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); GPIO_Configuration(); USART_Configuration(); LED_Configuration(); while (1) { ch = USART_ReceiveChar(); if (ch == '1') { GPIO_SetBits(GPIOC, GPIO_Pin_13); USART_SendChar('L'); } else if (ch == '0') { GPIO_ResetBits(GPIOC, GPIO_Pin_13); USART_SendChar('H'); } else { USART_SendChar('E'); } } } ``` 上述代码使用了USART1与蓝牙模块通信,接收蓝牙模块发送的数据后根据数据内容控制LED灯的开关。当接收到字符'1'时,LED灯亮;当接收到字符'0'时,LED灯灭;其他字符则返回'E'表示错误。你可以通过手机蓝牙发送字符'1'或'0',来控制LED灯的开关
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

和星星在一起

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值