简单的远程开关制作(纯代码、无解析版、UDP通信)
引言
本篇文章,我们将做一个超级简单的开关,当你学会该操作后,就可以自己进行一些简单的添加、修改从而实现一些自定义的功能。你可以做一个简单的远程开关,或驱动小车,以及众多有意思的玩意,现在就来跟着我一起学习吧!当然了,请注意我们这里的“远程”是打引号的,因为我们是用局域网通过UDP协议进行数据传输的,因此我们必须保证设备与客户端在同一局域网下才可进行以下实验。
文章干货满满,不讲废话,点赞收藏不迷路~
介绍
如果你有想在床上不用下床就把灯关了,把窗帘打开,或者自制一个机器人的话,那么本教程将是你入门的不二之选。我们采用了开销较小的UDP协议进行传输数据,同时也降低了传输的延时,从而使你能够在进行一些需要高精度的机械设计时减少误差。我们的8266与手机在接入到同一站点后,即可进行UDP通信进行指令的传输了。我们暂且称8266为服务端,移动端为客户端,我们在服务端进行数据的接收与回传,同时对硬件设备进行相应的控制,在客户端主要进行就是控制指令的设计,并向服务端进行数据发送与接收。具体的相关信息,我们将在后续的教程中更新!请及时关注。
准备工作
硬件
- 一颗ESP8266 某宝很便宜,十几块钱就有,这里就不推荐了,直接去搜就可以。
- 一个单路继电器开关。
- 公母头杜邦线、公头杜邦线、母头杜邦线等。
- 一台安卓手机。
- 如有进行其他改造想法的可自行采购相关硬件。
软件
- Android Studio 下载地址
- Arduino 网盘下载Arduino 提取码:q5ye
安卓工程文件
- 安卓工程文件 下载地址提取码:boaj
代码部分
8266代码
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char *ssid = "esp8266"; //网络名称
const char *password = "asd123456"; //网络密码
WiFiUDP Udp;
unsigned int localUdpPort = 2333; // 本地端口号
char incomingPacket[537]; // 接收缓冲区
void setup()
{
//以下为基本功能初始化,初始化串口和网络和LED
pinMode(2, OUTPUT);
Serial.begin(115200);
Serial.println();
WiFi.softAP(ssid,password);
Serial.print("Access Point: "); // 通过串口监视器输出信息
Serial.println(ssid); // 告知用户NodeMCU所建立的WiFi名
Serial.print("IP address: "); // 以及NodeMCU的IP地址
Serial.println(WiFi.softAPIP()); // 通过调用WiFi.softAPIP()可以得到NodeMCU的IP地址
//以下开启UDP监听并打印输出信息
Udp.begin(localUdpPort);
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
}
void loop()
{
int packetSize = Udp.parsePacket(); //获取当前队首数据包长度
Serial.print(packetSize);
delay(2000); //这里做了延时处理 如果你觉得慢可以注释掉
if (packetSize) // 有数据可用
{
Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
int len = Udp.read(incomingPacket, 536); // 读取数据到incomingPacket
if (len > 0) // 如果正确读取
{
incomingPacket[len] = 0; //末尾补0结束字符串
Serial.printf("UDP packet contents: %s\n", incomingPacket);
if (strcmp(incomingPacket, "Turn off") == 0) // 如果收到Turn off
{
digitalWrite(2, HIGH); // 关闭Switch
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write("Switch has been turn off"); // 回复Switch has been turn off
Udp.endPacket();
}
else if (strcmp(incomingPacket, "Turn on") == 0) // 如果收到Turn on
{
digitalWrite(2, LOW); // 打开Switch
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write("Switch has been turn on"); // 回复Switch has been turn on
Udp.endPacket();
}
else // 如果非指定消息
{
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write("Data Error!"); // 回复Data Error!
Udp.endPacket();
}
}
}
}
APP代码
MainActivity
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
TextView textView;
String recvStr = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.status);
new Thread(){
@Override
public void run(){
try {
DatagramSocket ds = new DatagramSocket();
byte[] bytes = "Get status".getBytes();
int length = bytes.length;
InetAddress address = InetAddress.getByName("192.168.4.1");
int port = 2333;
DatagramPacket dp = new DatagramPacket(bytes,length,address,port);
ds.send(dp);
// 接收消息
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket
= new DatagramPacket(recvBuf , recvBuf.length);
ds.receive(recvPacket);
recvStr = new String(recvPacket.getData() , 0 ,recvPacket.getLength());
if(recvStr.equalsIgnoreCase("3")){
textView.setText("开");
}else if(recvStr.equalsIgnoreCase("4")){
textView.setText("关");
}else{
;
}
ds.close();
}catch (IOException e){
e.printStackTrace();
}
}
}.start();
}
public void turnOn(View view) throws IOException {
new Thread(){
@Override
public void run(){
try {
DatagramSocket ds = new DatagramSocket();
byte[] bytes = "Turn on".getBytes();
int length = bytes.length;
InetAddress address = InetAddress.getByName("192.168.4.1");
int port = 2333;
DatagramPacket dp = new DatagramPacket(bytes,length,address,port);
ds.send(dp);
ds.close();
}catch (IOException e){
e.printStackTrace();
}
}
}.start();
textView.setText("开");
}
public void turnOff(View view) throws IOException {
new Thread(){
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket();
byte[] bytes = "Turn off".getBytes();
int length = bytes.length;
InetAddress address = InetAddress.getByName("192.168.4.1");
int port = 2333;
DatagramPacket dp = new DatagramPacket(bytes,length,address,port);
ds.send(dp);
ds.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}.start();
textView.setText("关");
}
}
样式代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/teal_700"
app:title="ESP8266_UDP_Test_Pro"
app:titleTextColor="@color/white"
app:titleMarginStart="100dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:editable="false"
android:ems="10"
android:textSize="20dp"
android:layout_marginBottom="10dp"
android:text="控制开关按钮"
android:gravity="center">
</EditText>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="当前状态:"
android:textSize="26dp"
android:textColor="@color/black"/>
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="未知"
android:textSize="26dp"
android:paddingLeft="20dp"
android:textColor="@color/teal_700"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="30dp"
android:onClick="turnOn"
android:text="打开" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="turnOff"
android:text="关闭" />
</LinearLayout>
</LinearLayout>
Tip
可能你无法直接将代码直接运行上手进行的实验,或者不知道如何进行试验,又或者遇到了各种各样的问题,这是因为你还没有具备一些必要的知识,你可以在评论区发表你的疑问,为防止文章篇幅过长不易观看,我在另一篇文章里进行了详细的介绍。
后期将会更新教程!
后期将会更新教程!
后期将会更新教程!
分割线来了!快一年了才来更新~~~真是抱歉!其实现在也没多少时间,但怕越往后越忘完了索性大概讲一下需要更改的地方就行,后边如果有机会的话再详细讲一下
正文开始。首先你需要有两个工程文件,一个是Arduino的一个工程文件,这个工程文件你自己创建即可(可能需要ESP8266的一些库,可以自己下去搜一下,由于时间太久了我也忘了需要哪些库了~),创建好文件之后直接把我上边的代码内容粘贴进去即可。注意,还不要直接运行,这里有需要修改的信息,就是ssid网络名称,后边的字符串内容改成你自己的WiFi热点名称,password是网络密码,改成你设置的密码,注意,最好用手机开热点,不要连路由WiFi(理论上路由WiFi也没问题),还有一个端口号需要注意,即localUdpPort,这里的端口号要与安卓工程文件中的端口号相一致,不要动这个端口号,因为我已经在安卓工程文件中设置好了端口号,这两个端口号必须相一致,一旦改了其中任何一个都无法接收到对方发送的数据,到这里你就可以点击运行将代码烧录进ESP8266了。
安卓工程文件请翻到上面,直接下载(前提你的电脑已经下好了Android Studio),然后打开该文件,可以在虚拟机上进行运行,也可以直接将该文件进行打包成apk安装到你的手机上,然后用手机开热点,注意要与你在Arduino设置的名称密码相一致,否则ESP8266连接不上。然后你就可以打开该软件进行操作了。
需要提醒的是,软件并没有多少异常处理,比如你点了开,但实际上ESP8266根本没连上WiFi,这时也会显示开,也就是没有数据反馈处理,当然如果已经连上的话就没什么问题了。
好了,终于把这篇文章差不多彻底结束了,希望对各位小伙伴有帮助!
如果这篇文章对您有帮助的话,不妨点个赞吧~