×

使用Android和Arduino的简单蓝牙灯控制器

消耗积分:2 | 格式:zip | 大小:0.29 MB | 2023-02-07

分享资料个

描述

简介

仅使用智能手机即可控制家中任何电器的能力非常有趣。这个项目包括使用一个 Arduino、一个便宜的蓝牙模块和一个继电器来控制,例如,通过蓝牙将它与您的智能手机连接来控制一盏灯。这个项目旨在使用最少的资源和代码来简单,但仍然包括整个过程的重要演示和描述。

重要说明:此项目涉及使用高压设备,因此建议格外小心,并尽可能在有经验的人的监督下进行此项目。

内容简介

在接下来的部分中,我们将按如下所示的顺序更详细地讨论一些主题。

  • 3.演示
  • 4.原理图
  • 5.Arduino代码
  • 6. Android 代码(源代码和 apk 在这里(github))

原理图

 
 
 
 
relay_bb_jHQ48yEvCR.png?auto=compress%2Cformat&w=740&h=555&fit=max
 
1 / 2扩展插头和插座在面包板示意图中无法很好地表示,所以我拍了一张照片(在下一张照片中)
 

让我们从设置蓝牙模块(图中左下方的组件)开始。如您所见,我们从 Arduino 一直拉出 5V 和地线,一直到蓝牙模块(红线和黑线)。对于通信引脚,我们需要将发射器引脚 (TX) 连接到 Arduino 的接收器引脚 (RX)(绿线),将发射器引脚连接到 Arduino 的接收器引脚(橙色线)。如果你检查蓝牙模块的背面,它会有一个标签显示它的接收器引脚可以处理多少电压(在我的例子中是 3.3V)并且由于我们的 Arduino 提供 5V,蓝牙模块在频繁使用后可能会烧毁,这是为什么我们需要将该电压降低到 3.3V。因此,它包含 2 个电阻器,用作穿过橙色线的“分压器”。电阻是 560 欧姆,而第二个R2是 1K 欧姆。我使用这些电阻器是因为它是我设备中的电阻器,但如果你有任何其他电阻器,也许你可以使用这个公式找到适合你的设置,这会使电压降低一点:

Vout = (Vsource x R2)/R1+R2

就我而言,我得到了:

Vout = 5 x 1000 / 1560 = 3.2V..足够接近..

(在谷歌上搜索分压器计算器,它会给你很好的解释和快速的计算)。

至于继电器,我们还为它提供电源和地线(红线和黑线)以及连接到 Arduino 的 10 号引脚的信号线,例如,它将命令继电器何时打开或关闭我们的灯。

蓝色线代表插头/插座扩展线。显然,您不希望将继电器直接连接到灯的内置插头延长件,这会破坏该项目,因此建议购买独立的插头/插座延长件。我所做的是购买一根较小的电线,并在上面的幻灯片照片中添加插头和插座。

现在您有了延长线,拉出 1 根延长线并剪断它,您最终会得到 2 个电线末端(如上面的 2 张照片所示)。但是你在哪里可以将这两个端点连接到继电器呢?Relay有3个端口,NC(常闭),C(Common),NO(常开)。这意味着,当继电器处于正常状态(没有信号发送到继电器)时,NC 和 C 端口之间将建立连接。当发送信号时,将在 C 和 NO 之间设置连接。这就是为什么我们将 2 线端连接到 C 和 NO 的原因,因为我们想向继电器发送信号,以便让电流流过这两个通道并为我们的设备提供能量。这里有很好的解释:http ://www.pcbheaven.com/wikipages/How_Relays_Work/

Arduino代码

#include 
#define RELAY 10 
#define LIGHT 13 
SoftwareSerial btm(2,3); // rx tx 
int index = 0; 
char data[10]; 
char c; 
boolean flag = false;
void setup() { 
 pinMode(RELAY,OUTPUT); 
 pinMode(LIGHT,OUTPUT); 
 digitalWrite(RELAY,HIGH); 
 digitalWrite(LIGHT,LOW); 
 btm.begin(9600); 
} 
void loop() { 
   if(btm.available() > 0){ 
     while(btm.available() > 0){ 
          c = btm.read(); 
          delay(10); //Delay required 
          data[index] = c; 
          index++; 
     } 
     data[index] = '\0'; 
     flag = true;   
   }  
   if(flag){ 
     processCommand(); 
     flag = false; 
     index = 0; 
     data[0] = '\0'; 
   } 
} 
void processCommand(){ 
 char command = data[0]; 
 char inst = data[1]; 
 switch(command){ 
   case 'R': 
         if(inst == 'Y'){ 
           digitalWrite(RELAY,LOW); 
           btm.println("Relay: ON"); 
         } 
         else if(inst == 'N'){ 
           digitalWrite(RELAY,HIGH); 
           btm.println("Relay: OFF"); 
         } 
   break; 
   case 'L': 
         if(inst == 'Y'){ 
           digitalWrite(LIGHT,HIGH); 
           btm.println("Light: ON"); 
         } 
         else if(inst == 'N'){ 
           digitalWrite(LIGHT,LOW); 
           btm.println("Light: OFF"); 
         } 
   break; 
 } 
} 

上面显示的 Arduino 代码通常分为 4 个阶段。

  • 5.1. 初始化
  • 52.设置
  • 5.3. 环形
  • 5.4. 处理命令

重要的是要注意上面的所有代码都是从 Arduino 的角度来看的,这意味着所有的“读取”操作都是 arduino 从其他来源接收数据的操作,写操作是 arduino 发送的操作消息也发送到其他来源。我们系统中的使用顺序是这样的:

  • 一种。用户单击智能手机中的按钮以执行蓝牙命令。
  • b. 蓝牙模块接收它并将该命令发送到 Arduino。
  • C。然后 Arduino 将处理该命令并向继电器发送信号以将其打开或关闭。
  • d. 然后 Arduino 向蓝牙模块发送成功消息,蓝牙模块将此消息发送回智能手机。

初始化

在前几行代码中,我们首先包含允许我们与蓝牙模块通信的 SoftwareSerial 库。它还允许我们将不同的引脚用作接收器和发射器引脚,而不是为 Arduino 预定义的引脚(引脚 0 = RX 和引脚 1 = TX)。相反,我们将为 Arduino 的 RX 使用 2 号引脚,为 Arduino 的 TX 使用 3 号引脚。然后我们创建常量来标识我们希望用于每个组件的引脚,在这种情况下,Arduino 控制继电器的引脚是 10 号,而控制 Arduino 内置灯的引脚是 13 号。(这是可选的,如果你不想使用它也没关系)。然后,名为“data”的“char”类型的数据结构充当我们从蓝牙模块传入的消息的缓冲区,循环部分。

设置

预定义方法“setup”将是我们实际预期的程序开始运行之前要执行的第一个方法。它基本上允许我们在主程序执行之前配置一些 Arduino 的引脚和其他东西。因此,我们首先说 RELAY 引脚将是一个 OUTPUT 引脚,因为我们要发送一个信号来关闭或打开继电器。LIGHT 引脚也是如此。此外,我们可以选择是否要启动程序并使用“digitalWrite”立即开始发送信号。在继电器的情况下,我们确实想要开始发送信号,因为继电器的工作方式有点违反直觉,因为继电器的工作方式是当继电器检测到信号时,它会自行关闭,否则它切换回 ON。

环形

顾名思义,循环是一种被迭代调用的方法,以便重复处理我们传递给它的任何信息。话虽如此,我们首先检查是否有来自蓝牙模块的传入消息,如果有,则我们进入一个循环以继续逐字节读取这些消息(每次迭代读取类型“char”)。关于代码 delay(10) 行,老实说,我不完全确定为什么代码只适用于该 delay(10)。当我尝试不使用那行代码时,消息没有被正确地接收到称为“数据”(我们的缓冲区)的字符数组中,我得到的只是缓冲区中的一堆垃圾。我最好的猜测,这就是我使用它的原因,事实上,我们的组件在接收和传输时有不同的处理速度,在这种情况下是 Arduino 和蓝牙模块。在网络上的一些 Arduino 代码中,可以看到一些延迟代码行,其中一些可能用于此目的。在这个延迟之后,假设我们已经读取了消息的第一个字节 (char),然后我们将它添加到我们的缓冲区并增加称为“索引”的计数器以继续沿着数组的维度迭代和递增地添加更多字节/缓冲。消息被读取后,我们退出 while 循环并说“是”,有一条消息要处理(通过将标志设置为 true)。并且还添加一个 '\0' 来指示缓冲区的结束。最后,

处理命令

最后,processCommand 方法将决定如何处理之前在循环代码部分收到的蓝牙消息。对于这个项目,我决定通过蓝牙将智能手机发送的简单命令发送到 Arduino。要打开继电器,基于 Android 应用程序构建的简单消息将以字节形式发送以下字符串:“RY”(继电器是)如果我们希望打开继电器或“RN”(继电器否)如果我们希望打开继电器关闭。如前所述,我还包括一个“附加组件”,您可以在其中控制 Arduino 的内置 LED(引脚号 13),因此命令为“LY”和“LN”,但您不必使用它。请记住,要打开继电器,我们需要从 Arduino 发送一个低电平信号,反之亦然。

安卓代码

在本节中,我们将讨论如何实现 Android 应用程序以与蓝牙模块通信。我不会介绍 Android 功能的某些方面以及您应该如何做某些事情,但主要介绍源代码的内容并对其进行更好的解释。但一般来说,该应用程序是使用 Android Studio (AS) 创建的,并且 AS 允许您选择一个简单的模板来启动您的应用程序,选择该基本模板。然后,AS 设置了启动应用程序所需的所有内容,包括 Android 清单,您可以在其中包含应用程序的一般属性,例如应用程序需要的权限。一个简单的空白布局,您可以在其中添加一些组件(如按钮)和一个 java 源代码,用于初始化应用程序并控制布局上的所有 UI 内容。如果你没有 如果不选择基本模板并选择空模板,那么您始终可以创建一个新活动来设置您需要的布局和相应的源文件。源代码和资源也可以在我的github上找到:https://github.com/serge144/ArduinoConnectorBT

下面是我们接下来要介绍的内容:

  • 安卓清单
  • 简单的布局
  • Java 源代码 (Ardcon.java + ConnectedThread.java)

6.1 安卓清单

 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="flipser.bluetooth">
   <uses-permission android:name="android.permission.BLUETOOTH"/>
   <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
   <application
 android:allowBackup="true"
 android:icon="@mipmap/ic_launcher"
 android:label="@string/app_name"
 android:supportsRtl="true"
 android:theme="@style/AppTheme">
        <activity android:name=".Bluetooth">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>
    application>
manifest>

android 清单描述了应用程序的主要结构以及应用程序将使用哪些操作系统的功能。在我们的例子中,我们需要包括与蓝牙接口相关的权限。从上面的代码中我们可以看出,为了使用操作系统的蓝牙功能,插入了以下几行:

简单布局

布局如下图所示:

 
layout_mqeu1pGl8O.png?auto=compress%2Cformat&w=740&h=555&fit=max
布局,图片直接取自Android Studio
 

文本视图仅用于输出 Arduino + 蓝牙模块发送的消息。例如,通过向 arduino 发送消息,如果成功,它将向智能手机发回响应,并在文本视图中显示该消息。然后我们有两个按钮,继电器按钮和灯按钮,用于打开/关闭 Arduino 上的内置 LED。我相信在这种情况下没有必要包含代码,因为您需要做的就是将两个按钮和一个文本视图拖到您想要放置的位置。

6.3 Java 源代码

(Ardcon.java)

我们将介绍的第一个源文件是Ardcon.java文件 (Arduino Connection)。这个文件主要做的是先初始化蓝牙,建立连接。然后它检查我们的两个按钮是否被点击,并做相应的操作。那么让我们从初始化开始:

public final static String MODULE_MAC = "98:D3:34:90:6F:A1";
public final static int REQUEST_ENABLE_BT = 1;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
BluetoothAdapter bta;                 //bluetooth stuff
BluetoothSocket mmSocket;             //bluetooth stuff
BluetoothDevice mmDevice;             //bluetooth stuff 
Button switchLight, switchRelay;      //UI stuff
TextView response;                    //UI stuff
boolean lightflag = false;            //flags to determ. if ON/OFF
boolean relayFlag = true;             //flags to determ. if ON/OFF
ConnectedThread btt = null;           //Our custom thread
public Handler mHandler;              //this receives messages from thread

在我的例子中,蓝牙模块的媒体访问控制或 MAC 地址是 为了找到你的蓝牙模块的MAC,你首先需要打开电路,或者只是设置一些基本电路来打开蓝牙模块。然后,使用您的智能手机检查蓝牙信号,您的蓝牙模块应该连同其 MAC 地址一起出现。如果未显示 MAC 地址,请尝试使用其他设备,例如您的 PC。然后蓝牙模块 HC-06 的 UUID始终是相同的,如果您使用的是 Android,这个数字可能会改变 iOS 使用。(查找 UUID 以获取更多信息)。"98:D3:34:90:6F:A1"00001101-0000-1000-8000-00805f9b34fb

然后我们初始化按钮、文本视图、标志以确定继电器和灯是关闭还是打开,一个 ConnectedThread 主要负责向/从 Arduino + 蓝牙发送/接收消息,最后是一个负责处理的处理程序从线程接收到的消息。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_ardcon);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    Log.i("[BLUETOOTH]", "Creating listeners");
    response = (TextView) findViewById(R.id.response);
    switchRelay = (Button) findViewById(R.id.relay);
    switchLight = (Button) findViewById(R.id.switchlight);
    switchLight.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.i("[BLUETOOTH]", "Attempting to send data");
            if (mmSocket.isConnected() && btt != null) { 
                if (!lightflag) {
                    String sendtxt = "LY";
                    btt.write(sendtxt.getBytes());
                    lightflag = true;
                } else {
                    String sendtxt = "LN";
                    btt.write(sendtxt.getBytes());
                    lightflag = false;
                }
            } else {
                Toast.makeText(Ardcon.this, "Something went wrong", Toast.LENGTH_LONG).show();
            }
        }
    });
    switchRelay.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.i("[BLUETOOTH]", "Attempting to send data");
            if (mmSocket.isConnected() && btt != null) {
                if(relayFlag){
                    String sendtxt = "RY";
                    btt.write(sendtxt.getBytes());
                    relayFlag = false;
                }else{
                    String sendtxt = "RN";
                    btt.write(sendtxt.getBytes());
                    relayFlag = true;
                }
              //disable the button and wait for 4 seconds to enable it again                switchRelay.setEnabled(false);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            Thread.sleep(4000);
                        }catch(InterruptedException e){
                            return;
                        }
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                switchRelay.setEnabled(true);
                            }
                        });
                    }
                }).start();
            } else {
                Toast.makeText(Ardcon.this, "Something went wrong", Toast.LENGTH_LONG).show();
            }
        }
    });
bta = BluetoothAdapter.getDefaultAdapter();
  //if bluetooth is not enabled then create Intent for user to turn it onif(!bta.isEnabled()){
  Intent enableBTIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  startActivityForResult(enableBTIntent, REQUEST_ENABLE_BT);
 }else{
  initiateBluetoothProcess();
 }
}

我现在将简要概述上面的代码。

这是初始化应用程序和所有 UI 的方法,我们首先从布局中获取按钮和文本视图的引用。然后我们向两个按钮添加一些逻辑。例如,对于 switchLight,点击侦听器将首先确定是否存在与模块的蓝牙连接,如果存在,则发送消息以关闭或打开灯。这同样适用于 switchRelay 按钮,但在这种情况下,我们还包括一个小计时器,在执行操作后将继电器按钮禁用 4 秒。这是一项安全措施,因为我们不会在快速按下按钮的同时快速打开/关闭来开始弄乱设备。然后,在完成所有按钮逻辑后,我们尝试在初始化时立即连接到蓝牙模块。ACTION_REQUEST_ENABLE这将触发一个对话框窗口以确认蓝牙激活。然后我们设置一个 onActivityResult 来接收用户的确认,如下所示:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(resultCode == RESULT_OK && requestCode == REQUEST_ENABLE_BT){
        initiateBluetoothProcess();
    }
}

然后,initiaBluetoothProcess() 方法与蓝牙模块建立连接并创建一个处理程序,该处理程序是从 ConnectedThread 接收信息的组件,同样,该线程负责从蓝牙模块接收/发送蓝牙信息阿杜伊诺。处理程序只需使用响应文本更新 TextView。

public void initiateBluetoothProcess(){
    if(bta.isEnabled()){
        //attempt to connect to bluetooth module        BluetoothSocket tmp = null;
        mmDevice = bta.getRemoteDevice(MODULE_MAC);
        //create socket        try {
            tmp = mmDevice.createRfcommSocketToServiceRecord(MY_UUID);
            mmSocket = tmp;
            mmSocket.connect();
            Log.i("[BLUETOOTH]","Connected to: "+mmDevice.getName());
        }catch(IOException e){
            try{mmSocket.close();}catch(IOException c){return;}
        }
        Log.i("[BLUETOOTH]", "Creating handler");
        mHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                //super.handleMessage(msg);                if(msg.what == ConnectedThread.RESPONSE_MESSAGE){
                    String txt = (String)msg.obj;
                    response.append("\n" + txt);
                }
            }
        };
        Log.i("[BLUETOOTH]", "Creating and running Thread");
        btt = new ConnectedThread(mmSocket,mHandler);
        btt.start();
    }
}

(ConnectedThread.java)

如前所述,ConnectedThread.java文件包含通过蓝牙发送和接收消息的必要代码。它还通过处理程序将信息发送到Ardcon.java源文件。

public class ConnectedThread extends Thread{
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public static final int RESPONSE_MESSAGE = 10;
Handler uih;
...

首先我们用 Thread 扩展我们的类,然后我们初始化一些允许我们接收和发送消息到蓝牙模块的对象。Handler 是负责将响应发送回 UI 线程 (Ardcon.java) 的组件,以便我们可以更新文本视图。

public ConnectedThread(BluetoothSocket socket, Handler uih){
    mmSocket = socket;
    InputStream tmpIn = null;
    OutputStream tmpOut = null;
    this.uih = uih;
    Log.i("[THREAD-CT]","Creating thread");
    try{
        tmpIn = socket.getInputStream();
        tmpOut = socket.getOutputStream();
    } catch(IOException e) {
        Log.e("[THREAD-CT]","Error:"+ e.getMessage());
    }
    mmInStream = tmpIn;
    mmOutStream = tmpOut;
    try {
        mmOutStream.flush();
    } catch (IOException e) {
        return;
    }
    Log.i("[THREAD-CT]","IO's obtained");
}

因此,我们的 ConnectedThread 接收到名为 BluetoothSocket 的蓝牙模块和 Handler 的连接。此构造函数初始化通信的输入和输出流。

public void run(){
    BufferedReader br;
    br = new BufferedReader(new InputStreamReader(mmInStream));
    while(true){
        try{            String resp = br.readLine();
            Message msg = new Message();
            msg.what = RESPONSE_MESSAGE;
            msg.obj = resp;
            uih.sendMessage(msg);
        }catch(IOException e){
            break;
        }
    }
    Log.i("[THREAD-CT]","While loop ended");
}

然后,我们覆盖Thread扩展的run方法,在这个方法中,有一个循环不断地寻找新到达的消息,如果是,则通过handler返回到UI线程。

public void write(byte[] bytes){
    try{
        Log.i("[THREAD-CT]", "Writting bytes");
        mmOutStream.write(bytes);
    }catch(IOException e){}
}
public void cancel(){
    try{
        mmSocket.close();
    }catch(IOException e){}
}

最后,这两个方法负责通过Outputstream向蓝牙模块写入(发送字节)和取消连接。


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

评论(0)
发评论

下载排行榜

全部0条评论

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