需求分析
本次需要做一个App,该App可以通过WiFi获取温湿度、烟雾浓度和一氧化碳的环境参数,并在界面上显示出来,能够评判空气质量,同时,在App内也可以实现这些环境参数的阈值更改功能,并向32单片机上的WiFi模块回传这些阈值参数,当参数超过阈值时手机通过震动发出报警。
本App通过WiFi与32单片机进行通信,采用的是TCP协议。WiFi模块会不断地向App发送以空格为间隔的 7 个数据,一共21个字节,如“20 80 10 10 40 20 20 ”,把这串数据以空格拆分并依次赋值给温度、湿度、烟雾浓度、一氧化碳浓度、温度阈值、烟雾阈值和一氧化碳阈值。发送阈值时,也以空格为间隔发送,只不过空格是在前面,如“ 40 20 20”。
App界面效果
各种功能实现
权限的申请
android:usesCleartextTraffic="false"用于明文传输
<!--需要对WiFi进行操作,所以需要设置网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--手机震动权限-->
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:usesCleartextTraffic="false"
</application>
显示当前的参数
/******显示当前环境的参数******/
textView_humidity.setText(Integer.toString(data_humidity) + "%RH");
textView_temp.setText(Integer.toString(data_temp) + " ℃");
textView_smog.setText(Integer.toString(data_smog));
textView_co.setText(Integer.toString(data_co));
/******显示当先阈值******/
editText_temp.setText(Integer.toString(temp_yu_setting));
editText_smog.setText(Integer.toString(smog_yu_setting));
editText_co.setText(Integer.toString(co_yu_setting));
textView_temp_yu.setText(Integer.toString(temp_yu));
textView_smog_yu.setText(Integer.toString(smog_yu));
textView_co_yu.setText(Integer.toString(co_yu));
设置温度加减功能关键代码
/******设置温度加减功能******/
button_decline_temp = findViewById(R.id.decline_button_of_temp);
button_decline_temp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean isInt = true;
try {
int tempInput = Integer.parseInt(editText_temp.getText().toString());
if (tempInput >= 1 && tempInput <= 99) {
temp_yu = tempInput;
} else {
Toast.makeText(MainActivity.this, "请输入 1~99 之间的整数", Toast.LENGTH_SHORT).show();
editText_temp.setText(String.valueOf(temp_yu));
}
} catch (NumberFormatException e) {
isInt = false;//转化失败
Toast.makeText(MainActivity.this, "请输入一个整数", Toast.LENGTH_SHORT).show();
}
if (isInt) {//如果没有发生异常
if (temp_yu > 1) {
temp_yu--;
editText_temp.setText(String.valueOf(temp_yu));
} else {
editText_temp.setText(String.valueOf(temp_yu));
}
} else {
editText_temp.setText(String.valueOf(temp_yu));
}
}
});
连接按钮底层代码
// 连接按钮底层代码
button_connect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
a = editText_ip.getText().toString();
String c = editText_port.getText().toString();
if ("".equals(a) || "".equals(c)) {
Toast.makeText(MainActivity.this, "请输入ip和端口号", Toast.LENGTH_SHORT).show();
receive.append("请输入ip和端口号" + "\r\n");
} else {
b = Integer.parseInt(c);
lianjie = new connectthread();
lianjie.start();
}
}
});
发送数据按键底层代码
// 发送数据 按键底层代码
button_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//子线程中进行网络操作
new Thread(new Runnable() {
@Override
public void run() {
if (socket != null) {
try {
String text_temp = " " + editText_temp.getText().toString();//温度栏的阈值
String text_smog = " " + editText_smog.getText().toString();//烟雾栏的阈值
String text_co = " " + editText_co.getText().toString();//CO栏的阈值
lianjie.outputStream.write((text_temp + text_smog + text_co + "\r\n").getBytes());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "已发送", Toast.LENGTH_SHORT).show();
}
});
} catch (IOException e) {
e.printStackTrace();
}
} else {
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "请先建立连接", Toast.LENGTH_SHORT).show();
receive.append("请先建立连接" + "\r\n");
}
});
}
}
}).start();
}
});
清除按钮功能
//清除按钮功能
button_clear = findViewById(R.id.clear_button);
button_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
receive.setText("");
}
});
子线程中进行网络相关操作
//子线程中进行网络相关操作
// 联网子线程
class connectthread extends Thread {
OutputStream outputStream = null;
InputStream inputStream = null;
@SuppressWarnings("InfiniteLoopStatement")
@Override
public void run() {
//连接
try {
socket = new Socket(a, b);
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run() {
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
receive.append("连接成功" + "\r\n");
}
});
} catch (UnknownHostException e) {
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run() {
Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
receive.append("连接失败" + "\r\n");
}
});
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run() {
Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
receive.append("连接失败" + "\r\n");
}
});
}
if (socket != null) {
//获取输出流对象
try {
outputStream = socket.getOutputStream();
outputStream.write(123);
} catch (IOException e) {
e.printStackTrace();
}
try {
do {
final byte[] buffer = new byte[1024];//创建接收缓冲区
inputStream = socket.getInputStream();
final int len = inputStream.read(buffer);//数据读出来,并且返回数据的长度
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run() {
String receivedData = new String(buffer, 0, len);
// 拆分字符串为子字符串数组
String[] tokens = receivedData.split(" ");
//获取环境参数
data_humidity = Integer.parseInt(tokens[0]);
data_temp = Integer.parseInt(tokens[1]);
data_smog = Integer.parseInt(tokens[2]);
data_co = Integer.parseInt(tokens[3]);
//获取环境阈值
temp_yu = Integer.parseInt(tokens[4]);
smog_yu = Integer.parseInt(tokens[5]);
co_yu = Integer.parseInt(tokens[6]);
//显示环境参数
textView_humidity.setText(Integer.toString(data_humidity) + "%RH");
textView_temp.setText(Integer.toString(data_temp) + " ℃");
textView_smog.setText(Integer.toString(data_smog));
textView_co.setText(Integer.toString(data_co));
//显示环境阈值
textView_temp_yu.setText(Integer.toString(temp_yu));
textView_smog_yu.setText(Integer.toString(smog_yu));
textView_co_yu.setText(Integer.toString(co_yu));
receive.append(receivedData + "\r\n");
/*空气质量判断并显示*/
if (data_co < 25 && data_smog < 25 && data_temp <= 25 && data_temp >=15)
quality = 1;
else if (data_co > 35 || data_smog > 35 || data_temp > 35)
quality = 3;
else quality = 2;
switch (quality) {
case 1:
textView_quality.setText("优");
textView_quality.setTextColor(Color.rgb(0, 205, 0));
break;
case 2:
textView_quality.setText("良");
textView_quality.setTextColor(Color.YELLOW);
break;
case 3:
textView_quality.setText("差");
textView_quality.setTextColor(Color.RED);
break;
default:
break;
}
/*阈值判断*/
peizhi();
}
});
} while (true);
} catch (IOException ignored) {
}
}
}
}
阈值判断并震动的函数
public void peizhi() {
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);//定义震动
if ( data_temp >= temp_yu ){
textView_temp.setTextColor(Color.RED);
vibrator.vibrate(1000);
}
else{
textView_temp.setTextColor(Color.GRAY);
vibrator.cancel();
}
if (data_smog >= smog_yu){
textView_smog.setTextColor(Color.RED);
vibrator.vibrate(1000);
}
else{
textView_smog.setTextColor(Color.GRAY);
vibrator.cancel();
}
if (data_co >= co_yu){
textView_co.setTextColor(Color.RED);
vibrator.vibrate(1000);
}
else{
textView_co.setTextColor(Color.GRAY);
vibrator.cancel();
}
}
XML文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#f2f2f2">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="15dp"
android:text="当前环境"
android:textColor="#000000"
android:textSize="25dp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200px"
android:orientation="horizontal">
<TextView
android:id="@+id/Quality"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40px"
android:layout_marginTop="50px"
android:paddingTop="10px"
android:text="空气质量:"
android:textSize="20dp" />
<TextView
android:id="@+id/data_of_quality"
android:layout_width="100px"
android:layout_height="100px"
android:layout_marginTop="50px"
android:layout_toRightOf="@+id/Quality"
android:paddingTop="10px"
android:textSize="20dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200px"
android:orientation="horizontal">
<TextView
android:id="@+id/Temperature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40px"
android:layout_marginTop="50px"
android:paddingTop="10px"
android:text="温度:"
android:textSize="20dp" />
<TextView
android:id="@+id/data_of_Temperature"
android:layout_width="150px"
android:layout_height="100px"
android:layout_marginTop="50px"
android:layout_toRightOf="@+id/Temperature"
android:paddingTop="10px"
android:textSize="20dp" />
<TextView
android:id="@+id/humidity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50px"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/data_of_Temperature"
android:paddingTop="10px"
android:text="湿度:"
android:textSize="20dp" />
<TextView
android:id="@+id/data_of_humidity"
android:layout_width="200px"
android:layout_height="100px"
android:layout_marginTop="50px"
android:layout_toRightOf="@+id/humidity"
android:paddingTop="10px"
android:textSize="20dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200px"
android:orientation="horizontal">
<TextView
android:id="@+id/Smog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40px"
android:layout_marginTop="50px"
android:paddingTop="10px"
android:text="烟雾浓度:"
android:textSize="20dp" />
<TextView
android:id="@+id/data_of_smog"
android:layout_width="100px"
android:layout_height="100px"
android:layout_marginTop="50px"
android:layout_toRightOf="@+id/Smog"
android:paddingTop="10px"
android:textSize="20dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200px"
android:orientation="horizontal">
<TextView
android:id="@+id/CO_ppm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="40px"
android:layout_marginTop="50px"
android:paddingTop="10px"
android:text="CO_ppm:"
android:textSize="20dp" />
<TextView
android:id="@+id/data_of_co"
android:layout_width="100px"
android:layout_height="100px"
android:layout_marginTop="50px"
android:layout_toRightOf="@+id/CO_ppm"
android:paddingTop="10px"
android:textSize="20dp" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="#e0e0e0" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingTop="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/temperature_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:text="温度阈值:"
android:textSize="20dp" />
<Button
android:id="@+id/decline_button_of_temp"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/temperature_setting"
android:background="#f2f2f2"
android:text="—" />
<EditText
android:id="@+id/setting_data_of_Temperature"
android:layout_width="50dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:inputType="number"
android:layout_toRightOf="@+id/decline_button_of_temp" />
<Button
android:id="@+id/increase_button_of_temp"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/setting_data_of_Temperature"
android:background="#f2f2f2"
android:text="+" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/smog_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:text="烟雾阈值:"
android:textSize="20dp" />
<Button
android:id="@+id/decline_button_of_smog"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/smog_setting"
android:background="#f2f2f2"
android:text="—" />
<EditText
android:id="@+id/setting_data_of_smog"
android:layout_width="50dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:inputType="number"
android:layout_toRightOf="@+id/decline_button_of_smog" />
<Button
android:id="@+id/increase_button_of_smog"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/setting_data_of_smog"
android:background="#f2f2f2"
android:text="+" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/co_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:text="CO阈值:"
android:textSize="20dp" />
<Button
android:id="@+id/decline_button_of_co"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/co_setting"
android:layout_marginLeft="13dp"
android:background="#f2f2f2"
android:text="—" />
<EditText
android:id="@+id/setting_data_of_co"
android:layout_width="50dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:inputType="number"
android:layout_toRightOf="@+id/decline_button_of_co" />
<Button
android:id="@+id/increase_button_of_co"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/setting_data_of_co"
android:background="#f2f2f2"
android:text="+" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingTop="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/ip_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:text="IP地址:"
android:textSize="20dp" />
<EditText
android:id="@+id/setting_data_of_ip"
android:layout_width="200dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@+id/ip_setting"
android:hint="192.168.4.1"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/port_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:text="端口号:"
android:textSize="20dp" />
<EditText
android:id="@+id/setting_data_of_port"
android:layout_width="200dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@+id/port_setting"
android:hint="333"/>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp">
<Button
android:id="@+id/send_button"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@color/my_color"
android:text="发送"
android:textSize="20dp" />
<Button
android:id="@+id/connect_button"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@+id/send_button"
android:background="@color/my_color"
android:text="连接"
android:textSize="20dp" />
<Button
android:id="@+id/clear_button"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/send_button"
android:background="@color/my_color"
android:text="清除"
android:textSize="20dp" />
</RelativeLayout>
<TextView
android:id="@+id/receive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
工程文件
该工程的项目文件已上传,需要的话可以下载使用。