老师布置的单片机大作业是自选命题,随便怎么写都行。我就随便做个有三层架构的小系统玩玩,毕竟大创就是做一套物联网系统。(等结题答辩后再写吧) 基友打算做一个六面LED贪吃蛇,而且是随重力感应改变方向。结果可想而知。。。。。。时间紧,板子画出来,再到调试。。。。反正最后鸽了。。。。。。
系统原理图
系统功能
- 将STC89C52单片机作为温度采集下位机,通过串口与上位机通信。将下位机采集得到的温度信息通过串口,发送至上位机。上位机发送协议指令,控制LED灯光。
- 上位机存在一个服务器程序,作为网关,处理来自手机APP的网络请求。包括:获取温度信息、打开LED灯、关闭LED灯等请求操作。
- 实现的基本功能包括:手机APP连接局域网WIFI、配置好IP地址后,实时获取环境温度信息,使用折线图动态实时呈现;按下APP中的按钮可以实现打开LED灯、关闭LED灯的操作。
系统工作原理
- 下位机部分使用的是基于STC89C52的单片机来开发。温度采集部分使用的是DS18B20温度传感器,获取的温度数值较为精确。在与上位机进行通信时,采用的是串口通信,接受与发送数据均采用这一方式。
- 上位机部分串口通信程序与服务器程序均使用Go语言来开发。由于Go语言是新兴语言,开发多线程程序十分快速,而且写出来的程序十分健壮,程序运行效率高。同时,该语言有良好的服务器库,开发服务器程序也是十分便捷。因此使用这一语言便成为首选。
- 移动端开发自然采用Android框架,使用okhttp网络请求框架、HelloChart图表框架,开发起来同样十分便捷。尤其在动态显示温度曲线时,使用多线程来实现这一效果,使得程序的运行效率较高,界面流畅。
移动端
- 网络通信采用的okhttp框架。协议嘛,就是简单的GET请求。具体的全在代码里。
- 图表部分使用的是HelloChart框架,动态图表十分方便。
- 为了实现实时温度展示,当然要使用多线程啦。使用的Timer类定时任务,每秒采集一次温度数据来更新。
界面
-
网络配置界面
-
主界面
上位机
- 使用Go语言来设计该部分程序。原因就是那时候正在学这个新语言,毕竟原生的goroutine十分方便。
- 主要涉及的是串口通信与服务器编程。
- 串口通信使用的是“github.com/tarm/serial”这个库,我将其封装为CommPort类。在实例化该类后,调用Listen方法开始接收串口发送的信息。
package CommPort
import
(
"github.com/tarm/serial"
"log"
"fmt"
)
// Commport : the comm struct for connecting
type Commport struct {
port *serial.Port
SPort string
IBaud int
CBReceive chan string
CBSend chan string
CBQuit chan bool
}
func (comm * Commport) openport() {
c := &serial.Config{Name:comm.SPort, Baud:comm.IBaud}
commio, err := serial.OpenPort(c);
if err != nil {
log.Fatal("open port error ")
log.Fatal(err)
}
comm.port = commio
}
func (comm * Commport) receive() {
buf := make([]byte, 128)
n, err := comm.port.Read(buf)
if err != nil {
log.Fatal("receive error ")
log.Fatal(err)
}
if n > 1 {
//log.Printf(">>>> reveice data size:%d %q", n, buf[:n])
data := fmt.Sprintf("%d%d.%d%d", buf[0], buf[1], buf[2], buf[3])
comm.CBReceive <- data
} else {
comm.CBReceive <- ""
}
}
func (comm * Commport) send(data string) {
_, err := comm.port.Write([]byte(data))
if err != nil {
log.Fatal(err)
}
}
// Listen the comm port and receive the data
func (comm * Commport) Listen() {
comm.openport()
if comm.port != nil {
for {
select {
case flag := <- comm.CBQuit:
if flag {
break
} else {
go comm.receive()
}
case data := <- comm.CBSend:
go comm.send(data)
}
}
}
}
下位机
- 使用DS18B20温度传感器来获取环境温度。
- 实际工程中的下位机都是被动的,受上位机的指令控制。不过,这里为了方便,我做成自动向上位机发送数据。
系统效果
-
温度采集显示
-
开启LED灯
-
关闭LED灯
总结
- 移动端手机APP通过局域网WIFI实现控制下位机时,实际是要向上位机的服务器程序发送请求,由服务器程序通过串口向下位机发送指令。在本系统中,我将不同的控制指令映射成不同的链接,在访问不同的链接时就实现了控制下位机的操作。
- 在服务器端,我将串口采集程序与服务器程序合并在了一起,使用Go语言完成。其中,Go语言本身的多线程以及通道特性,对于我的程序有很大的帮助。串口采集、服务器程序属于同时进行的两项服务,分别处于不同的线程来运行。而两个线程间的信息传递则靠channel(通道)来完成。如此一来,程序的运行效率提高,应对大量请求的能力随之提升。