动手实现自己的物联
2019 年 11 月 13 日 星期三(已编辑)
这篇文章上次修改于 2021 年 3 月 31 日 星期三,可能部分内容已经不适用,如有疑问可询问作者。
UI
ESP8266
涉及三个角色,小灯、服务器、APP。思路App 发送指令是开还是关,小灯循环Get 数据。服务器用来管理指令。
涉及的技术栈
服务器
maven依赖
<dependencies>
<!--Thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--Json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
<!--Test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--Build-->
<build>
<finalName>arduino</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
简单起见,创建最基本的开关状态。 这里使用了Lombok插件 可以自动添加get
set
方法
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Light {
private int flag; // 状态
private String message; // 响应消息
}
控制层
注意
这里有一个坑,使用SpringBoot默认@RestController
会使Arduino接收莫名的参数,导致我提取不出来传输数据。所以使用fastJson
@Controller
@RequestMapping("light")
public class IndexController {
Light light; // 创建对象
@ResponseBody
@RequestMapping("set")
public String setData(@RequestParam("flag") String flag){
light = new Light();
if(flag.equals("on")){
light.setFlag(1);
light.setMessage("success");
}else if(flag.equals("off")){
light.setMessage("success");
light.setFlag(0);
}else{
light.setMessage("error");
light.setFlag(0);
}
return "{\"isOk\": \"success\"}";
}
@ResponseBody
@RequestMapping("get")
public String getData(HttpServletResponse response, HttpServletRequest request){
response.setHeader("Author","Linis");
if(light==null){
light = new Light();
light.setFlag(0);
light.setMessage("success");
}else{
System.out.println(light);
}
String res = JSON.toJSONString(light);
return res;
}
}
当访问host/light/set?flag=on
或者host/light/set?flag=off
会致light对象的flag
然后访问host/light/get
获取light对象。
然后打包成jar包上传到Linux服务器上
上面的意思是不挂断运行命令,在后台执行jar文件
APP
使用Flutter开发APP
加入依赖pubspec.yaml
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
# HTTP
dio: 3.0.0
UI
没有封装HTTP Server。直接在里面写了…
ESP-8266
步骤
Get
[root@localhost Arduino]# nohup java -jar arduino.jar &
import 'dart:io';
import 'dart:ui';
import "package:dio/dio.dart";
import "dart:convert";
import "package:flutter/material.dart";
import 'package:flutter/services.dart';
class OnOffSwitch extends StatefulWidget {
OnOffSwitch({Key key}) : super(key: key);
@override
_OnOffSwitchState createState() => _OnOffSwitchState();
}
class _OnOffSwitchState extends State<OnOffSwitch> {
double screenHeight = 0;
double screenWidth;
double btnStartY = 360.0;
String lightStatus = "关";
@override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIOverlays([]); // 隐藏StatusBar
}
Future postData(String key) async{
String url = "http://112.124.30.55:9001/light/set";
try {
Response response;
Dio dio = new Dio();
response = await dio.get(url, queryParameters: {"flag":key});
print(response.data);
} catch (e) {
print(e);
}
}
// 颜色过渡处理
Color getBaColors() {
// black->white
return Color.lerp(Color.fromRGBO(43, 43, 40, 0.5), Color(0xffFFD80A),
(btnStartY - 360.0) / 120.0);
}
Color getTextStyle() {
return Color.lerp(Colors.white, Colors.black, (btnStartY - 360.0) / 120.0);
}
double getOpacity() {
return (btnStartY - 360.0) / 120.0 == 0 ? 0.9 : 0.2;
}
bool lightFlag = false;
open() {
if (lightFlag) {
print("正在开灯...");
}
lightFlag = false;
}
@override
Widget build(BuildContext context) {
screenHeight = MediaQuery.of(context).size.height;
screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
//backgroundColor: Color.fromRGBO(43, 43, 40, 0.5),// Dark模式
// backgroundColor: Color(0xffFFD80A),
backgroundColor: getBaColors(),
// Body
body: Stack(
children: <Widget>[
// Header
AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
),
// 左边文字
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(left: 32, bottom: screenHeight * 0.2),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
"灯",
style: TextStyle(
fontSize: 52,
color: getTextStyle(),
fontWeight: FontWeight.w600),
),
Text(
lightStatus,
style: TextStyle(
fontSize: 25,
color: getTextStyle(),
fontWeight: FontWeight.w400
),
)
],
),
SizedBox(height: 22),
Text(
"26°",
style: TextStyle(
fontSize: 24,
color: getTextStyle(),
fontWeight: FontWeight.w400),
)
],
),
),
),
// 圆角框 40*150
Positioned(
right: 32,
top: 360,
child: Container(
height: 150,
width: 40,
decoration: BoxDecoration(
color: Colors.black.withOpacity(getOpacity()),
borderRadius: BorderRadius.circular(32)),
),
),
// 线Line
Positioned(
top: 0,
right: 50,
child: Container(
width: 4,
height: 360.0 + (btnStartY - 360.0),
color: Colors.white.withOpacity(0.8),
),
),
// 小球
Positioned(
top: btnStartY,
right: 37,
child: new GestureDetector(
// 手势监听小球你们
onVerticalDragUpdate: (DragUpdateDetails details) {
// 垂直滑动更新
print(details.delta.dy);
this.btnStartY += details.delta.dy;
this.btnStartY = this.btnStartY.clamp(360.0, 480.0);
print(btnStartY);
setState(() {});
},
onVerticalDragEnd: (DragEndDetails details) {
if (this.btnStartY == 360.0) {
print("顶部..");
setState(() {
this.lightStatus = "关";
});
postData("off");
}
if (this.btnStartY == 480.0) {
print("底部..");
setState(() {
this.lightStatus = "开";
});
postData("on");
}
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 5,
offset: Offset(0, 5))
]),
),
),
)
],
),
);
}
}
class LeftTitle extends StatefulWidget {
LeftTitle({Key key}) : super(key: key);
@override
_LeftTitleState createState() => _LeftTitleState();
}
class _LeftTitleState extends State<LeftTitle> {
@override
Widget build(BuildContext context) {
var screenHeight = MediaQuery.of(context).size.height;
var screenWidth = MediaQuery.of(context).size.width;
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(left: 32, bottom: screenHeight * 0.2),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"灯",
style: TextStyle(
fontSize: 52,
color: Colors.black,
fontWeight: FontWeight.w600),
),
SizedBox(height: 22),
Text(
"26°",
style: TextStyle(
fontSize: 24,
color: Colors.black87,
fontWeight: FontWeight.w400),
)
],
),
),
);
}
}
#include <ESP8266WiFi.h>
#include <ArduinoJson.h> // 6.x
const char* ssid = "TP-LINK_0A1F";//无线名称
const char* password = "873010963";//无线密码
const char* apiHost = "112.124.30.55";
int ledPIN = D2;
WiFiClient client;
/*
初始化
*/
void setup() {
// 小灯初始化
pinMode(ledPIN, OUTPUT);
digitalWrite(ledPIN , LOW);
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi 连接成功 ");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
void loop(){
// 创建TCP连接
WiFiClient client;
if(!client.connect(apiHost, 9001)){
Serial.println("请求失败");
return ;
}
String req =(String)("GET ") +"/light/get" + "/ HTTP/1.1\r\n" +
"Content-Type: text/html;charset=utf-8\r\n" +
"Host: " + apiHost + "\r\n" +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36\r\n" +
"Connection: close \r\n\r\n";
Serial.println("...开始请求...");
client.print(req); // 发送HTTP请求
delay(200);
while(client.available()){
bool skipHeader = client.find("\r\n\r\n"); // 跳过响应的Header头
if(skipHeader){
String line = client.readStringUntil('\r');
Serial.print(line);
parseData(line);
}
}
Serial.println();
Serial.println("...请求完毕...");
Serial.println();
delay(600);
}
void parseData(String data){
DynamicJsonDocument jsonBuffer(2048);
deserializeJson(jsonBuffer, data);
JsonObject root = jsonBuffer.as<JsonObject>();
delay(1000);
int code = root["flag"];
String message = root["message"];
Serial.println();
Serial.println(code);
Serial.println(message);
if(code==1){
digitalWrite(ledPIN, HIGH);
}else{
digitalWrite(ledPIN, LOW);
}
}