树莓派3B+的多功能家用传感设备

0. 概述

0.0 实物图

在这里插入图片描述
继电器和温度传感器的GPIO引脚 7 和 12 在程序中互换了

0.1 web界面

在这里插入图片描述

0.2 接线图

在这里插入图片描述
MCP3008的引脚0 1 2分别接光敏传感器、雨滴传感器、火焰传感器的模拟输出引脚
可燃气体传感器的模拟输出引脚接在了PCF8591的AIN3引脚

MCP3008与树莓派的连接方式https://www.basemu.com/raspberry-pi-analog-sensing-interfacing.html写得很详细
PCF8591连接方式百度即可

0.3 功能

在这里插入图片描述

1. 整合 Socket 服务器的SpringBoot 后端

1.0 Socket 服务器程序 Server.java

package pioneer.pfix.SmartHome;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
    static Map<String,Socket> stringSocketMap = new HashMap<>();
    static Map<Socket,String> socketStringMap = new HashMap<>(); //两个Hash表用来存放socket和mac地址字符串双向的映射
    static Map<String,JSONObject> stringJSONObjectMap = new HashMap<>(); //存放mac地址字符串和对应客户端状态的json对象
    private final static Logger logger = LoggerFactory.getLogger(Server.class);
    public static void start() throws IOException {
        {
            int port = 8086;
            ServerSocket server = new ServerSocket(port);
            logger.info("Server"+":   Socket Built");
            ExecutorService threadPool = Executors.newFixedThreadPool(100); //100个大小的线程池,存放多个socket线程
            while (true) {
                Socket socket = server.accept();
                socket.setSoTimeout(5100);//毫秒,设置超时时间
                logger.info("Connection from: "+socket.getRemoteSocketAddress());
                Runnable runnable=()->{
                    try {
                        InputStream inputStream = socket.getInputStream();
                        byte[] bytes = new byte[1024];
                        int len = inputStream.read(bytes);
                        if(len!=-1)
                        {
                            String s = new String(bytes,0, len, "UTF-8");
                            logger.info("message from"+socket.getRemoteSocketAddress()+" "+s);
                            if(s.split("-").length==6) //简易判断第一次接收到的信息是不是mac地址,筛去不合规链接
                            {
                                stringSocketMap.put(s,socket);
                                socketStringMap.put(socket,s); //将mac地址字符串和socket存入Hash表
                                while (true) //循环接收心跳或状态信息,超时将触发异常中断
                                {
                                    len = inputStream.read(bytes);
                                    if(len==-1) {
                                        break;
                                    }
                                    String s2 = new String(bytes,0, len, "UTF-8");
                                    String[] strs = s2.split("-");//"-"前边是"HeartBeat",后边是json格式字符串
                                    if(strs.length==2)
                                    {
                                        JSONObject jsonObjects = JSONObject.parseObject(strs[1]);//将strs转为json对象
                                        stringJSONObjectMap.put(s,jsonObjects); //将json对象存入Hash表
//                                        System.out.println(s+" "+jsonObjects.toString());
                                    }
                                    logger.info("message from"+socket.getRemoteSocketAddress()+" "+s2);
                                }
                            }
                            else //第一次发送的信息不是mac地址格式的直接断开socket连接
                            {
                                inputStream.close();
                                socket.close();
                            }
                        }
                        logger.info(socket.getInetAddress()+" disconnected");
                        String mac = socketStringMap.get(socket);
                        socketStringMap.remove(socket); //断开连接之后从hash表中删去映射
                        stringSocketMap.remove(mac);
                        stringJSONObjectMap.remove(mac); //从hash表中删除状态
                        inputStream.close();
                        socket.close();
                    } catch (SocketTimeoutException e)//超时异常处理
                    {
                        try {
                            socket.close();
                            String mac = socketStringMap.get(socket);
                            socketStringMap.remove(socket);
                            stringSocketMap.remove(mac);
                            stringJSONObjectMap.remove(mac);
                            logger.info("Timeout disconnection form:"+socket.getRemoteSocketAddress());
                        } catch (IOException ex) {ex.printStackTrace();}
                    }
                    catch (SocketException e) //其他异常处理
                    {
                        logger.info(socket.getRemoteSocketAddress()+" disconnected");
                        try {
                            socket.close();
                            String mac = socketStringMap.get(socket);
                            socketStringMap.remove(socket);
                            stringSocketMap.remove(mac);
                            stringJSONObjectMap.remove(mac);
                        } catch (IOException ex) {ex.printStackTrace();}
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                };
                threadPool.submit(runnable); //递交线程池
            }
        }
    }
    public static boolean sendMessage(String mac,String message) throws IOException { //向指定mac地址的客户端发送信息
        boolean online = stringSocketMap.containsKey(mac);
        logger.info(mac+" is on line:"+ online);
        Socket socket = stringSocketMap.get(mac);
        if(socket!=null) {
            logger.info("send message:"+message+" to "+mac);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(message.getBytes(),0,message.length());
            return true;
        }
        return false;
    }
    public static JSONObject getStatus(String mac) throws IOException{ //返回状态json对象
        return stringJSONObjectMap.get(mac);
    }
    public static void setSwitch(String mac,int value) throws IOException { //打开或关闭客户端开关
        sendMessage(mac,"switchValue-"+value);
        stringJSONObjectMap.get(mac).put("switchValue",value);
        logger.info("value"+stringJSONObjectMap.get(mac).get("switchValue"));
    }
}

1.1 Controller:SmartHomeFunction.java

package pioneer.pfix.SmartHome;

import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@CrossOrigin
@RestController
public class SmartHomeFunction {
    @RequestMapping(value = "/api2/GetStatus")
    public JSONObject GetStatus(String mac) throws IOException {
        JSONObject jsonObject = Server.getStatus(mac);
//        System.out.println(jsonObject.toString());
        return jsonObject;
    }
    @RequestMapping(value = "/api2/SetSwitch")
    public void SetSwitch(String mac,String value) throws IOException {
        int value2 = Integer.parseInt(value);
        Server.setSwitch(mac,value2);
    }
}

1.2 启动类 Application.java

package pioneer.pfix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import pioneer.pfix.SmartHome.Server;

import java.io.IOException;

@SpringBootApplication
public class PFixApplication {

    public static void main(String[] args) throws IOException {
        SpringApplication.run(PFixApplication.class, args);
        Server.start();  //智能设备的内容
    }
}

(这种整合SpringBoot和Socket的方式为博主首创,并没有考虑太多效率因素,不喜勿喷)

2. Vue前端页面

<template>
  <br>
  <el-card class="box-card" v-loading="!online">
    <template #header>
      <div class="card-header">
        <span>智能家居控制页面——<nobr v-if="online">在线</nobr><nobr v-if="!online">离线</nobr></span>
      </div>
    </template>
    <table style="margin: 0 auto;border-collapse: collapse;" border="1">
      <thead>
      <tr>
        <td style="height:50px;color: #fff;background-color: #3F3F3F;">设备</td>
        <td style="height:50px;color: #fff;background-color: #3F3F3F;">状态</td>
      </tr>
      </thead>
      <tbody>
      <tr>
        <td style="width:20%;height:30px"><label>远程开关</label></td>
        <td style="width:20%;height:30px"><el-switch
          style="display: block"
          v-model="switchValue"
          active-color="#13ce66"
          inactive-color="#ff4949"
          active-text="打开"
          inactive-text="关闭"
      ></el-switch></td>
      </tr>
      <tr>
        <td style="height:160px"><label>温度传感器-PCF8591</label></td>
        <td style="height:160px"><el-progress type="dashboard" :percentage="temperature" :color="customColors" >{{temperature}}°C</el-progress></td>
      </tr>
      <tr>
        <td style="height:160px"><label>温度传感器-DS18B20</label></td>
        <td style="height:160px"><el-progress type="dashboard" :percentage="temperature2" :color="customColors" >{{temperature2}}°C</el-progress></td>
      </tr>
      <tr>
        <td style="width:20%;height:160px"><label>光敏传感器1-数字信号</label></td>
        <td style="width:20%;height:160px"><img src="../../assets/lighton.svg" v-if="lightValue===0"/>
          <img src="../../assets/lightoff.svg" v-if="lightValue===1"/></td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>光敏传感器1-模拟信号-MCP3008</label></td>
        <td style="width:20%;height:30px">{{lightValue3}}</td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>光敏传感器2-模拟信号-PCF8591</label></td>
        <td style="width:20%;height:30px">{{lightValue2}}</td>
      </tr>
      <tr>
        <td style="width:20%;height:160px"><label>雨滴传感器-数字信号</label></td>
        <td style="width:20%;height:160px"><img src="../../assets/rainy.svg" v-if="rainValue===0"/>
          <img src="../../assets/sunny.svg" v-if="rainValue===1"/></td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>雨滴传感器-模拟信号-MCP3008</label></td>
        <td style="width:20%;height:30px">{{rainValue2}}</td>
      </tr>
      <tr>
        <td style="width:20%;height:160px"><label>可燃气体传感器-数字信号</label></td>
        <td style="width:20%;height:160px"><img src="../../assets/dangerous.svg" v-if="gasValue===0"/>
          <img src="../../assets/safe.svg" v-if="gasValue===1"/></td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>可燃气体传感器-模拟信号-PCF8591</label></td>
        <td style="width:20%;height:30px">{{gasValue2}}</td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>火焰传感器-数字信号</label></td>
        <td style="width:20%;height:30px"><img src="../../assets/dangerous.svg" v-if="fireValue2===1"/>
          <img src="../../assets/safe.svg" v-if="fireValue2===0"/></td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>火焰传感器-模拟信号-MCP3008</label></td>
        <td style="width:20%;height:30px">{{fireValue}}</td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>人体红外传感器-数字信号</label></td>
        <td style="width:20%;height:30px"><div v-if="peopleValue===1">有人在活动</div>
          <div v-if="peopleValue===0">没人在活动</div></td>
      </tr>
      <tr>
        <td style="width:20%;height:30px"><label>电位计-模拟信号-PCF8591</label></td>
        <td style="width:20%;height:30px">{{dianweiji}}</td>
      </tr>
      </tbody>
    </table>
  </el-card>
</template>

<script>
import axios from "axios";
export default {
  name: "SmartHome",
  data() {
    return {
      switchValue: "",
      online:false,
      temperature: 0,
      temperature2: 0,
      rainValue:1,
      gasValue:1,
      lightValue:1,
      fireValue2:0, //数字信号
      rainValue2:0,
      gasValue2:0,
      lightValue2:0,
      lightValue3:0,
      fireValue:-1,
      dianweiji:-1,
      peopleValue:0,
      customColors: [
        {color: '#f56c6c', percentage: 20},
        {color: '#e6a23c', percentage: 40},
        {color: '#5cb87a', percentage: 60},
        {color: '#1989fa', percentage: 80},
        {color: '#6f7ad3', percentage: 100}
      ]
    }
  },
  watch:{
    'switchValue':{
      handler: function(val,oldVal) {
        console.log(val,oldVal)
        var i = val ? 1 : 0;
        axios.get("/api2/SetSwitch?mac="+"11-11-11-11-11-11"+"&value="+i);
      },
      deep: true
    }
  },
  mounted(){
    setInterval(()=>{
      axios.get("/api2/GetStatus?mac="+"11-11-11-11-11-11").then(r => {
        // console.log("3 "+r.data)
        if(r.data!=='')
        {
          if(r.data.switchValue===1)
          {
            this.switchValue = true
          }
          else
          {
            this.switchValue = false
          }
          this.rainValue=r.data.rainValue
          this.gasValue = r.data.gasValue
          this.lightValue = r.data.lightValue
          this.online = true
          this.temperature=r.data.temperature
          this.temperature2=r.data.temperature2
          this.rainValue2=r.data.rainValue2
          this.gasValue2=r.data.gasValue2
          this.lightValue2=r.data.lightValue2
          this.lightValue3=r.data.lightValue3
          this.fireValue=r.data.fireValue
          this.dianweiji=r.data.dianweiji
          this.fireValue2=r.data.fireValue2
          this.peopleValue = r.data.peopleValue
          // console.log("1 "+this.online)
        }
        else{this.online=false}
      }).catch(error => {
        console.log(error);
      })
    }, 3*1000)
  }
}
</script>

<style scoped>
.box-card {
  width: 90%;
  margin: 0 auto;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>

(然后加到路由里边)

3. 树莓派上的python程序

3.0 主程序:SmartHome.py

import socket
import threading
# import ProcessMessage
import sys
import time
import RPi.GPIO as GPIO
import PCF8591 as ADC
import MCP3008 as ADC2
import DS18B20 as temp

def doConnect(host, port):
    sock = socket.socket()
    try:
        sock.connect((host, port))
    except:
        pass
    return sock

class myThread1(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        
    def run(self):
        while True:
            try:
#                 mac = hex(uuid.getnode())[2:]
#                 mac = '-'.join(mac[i:i + 2] for i in range(0, len(mac), 2)) # 获取真实mac地址
                mac = '11-11-11-11-11-11'
                global s, status
                s.send(mac.encode())
                print("\r连接成功:"+time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
                status = 1
                time.sleep(1)
                while True:
                    a = 'HeartBeat-'+str(object_status)
                    a = a.encode()
                    s.send(a)
                    print("\r发送心跳:"+time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
                    time.sleep(3)
            except socket.error:
                sys.stdout.write("\rsocket 错误,重连ing....")
                sys.stdout.flush()
                status = 0
                s = doConnect(host, port)
                time.sleep(1)


class myThread2(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global status
#         process=ProcessMessage.ProcessMessage()
        while True:
            if status == 1:
                print("持续接收:"+time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
                try:
                    str = s.recv(1024).decode()
                    print("接收:"+str+":"+time.strftime("%a %b %d %H:%M:%S %Y", time.localtime()))
                    strs = str.split("-")
                    if strs[0]=="switchValue":
                        object_status["switchValue"]=int(strs[1])
                        if int(strs[1]) == 1:
                            GPIO.output(16,GPIO.LOW)
                        elif int(strs[1]) == 0:
                            GPIO.output(16,GPIO.HIGH)
                        print(object_status)
                except:
                    status = 0

class myThread3(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        
    def run(self):
        while True:
            object_status["rainValue"]=GPIO.input(12)
            object_status["rainValue2"]=ADC2.interp(ADC2.analogInput(1), [0, 1023], [0, 100])
            object_status["gasValue"]=GPIO.input(11)
            object_status["gasValue2"]=ADC.read(3)
            object_status["lightValue"]=GPIO.input(22)
            object_status["lightValue2"]=ADC.read(1)
            object_status["lightValue3"]=ADC2.interp(ADC2.analogInput(0), [0, 1023], [0, 255])
            object_status["temperature"]=(143-ADC.read(2))
            object_status["dianweiji"]=ADC.read(0)
            object_status["fireValue"]=ADC2.interp(ADC2.analogInput(2), [0, 1023], [0, 100])
            object_status["fireValue2"]=GPIO.input(40)
            object_status["peopleValue"]=GPIO.input(37)
            object_status["temperature2"]=temp.read_temp()
            print("    rainValue:"+str(object_status["rainValue"])," rainValue2:"+str(object_status["rainValue2"]))
            print("    temperature:"+str(object_status["temperature"]),"temperature2:"+str(object_status["temperature2"]))
            print("    lightValue:"+str(object_status["lightValue"])," lightValue2:"+str(object_status["lightValue2"])," lightValue3:"+str(object_status["lightValue3"]))
            print("    gasValue:"+str(object_status["gasValue"])," gasValue2:"+str(object_status["gasValue"]))
            print("    fireValue:"+str(object_status["fireValue"])," fireValue2:"+str(object_status["fireValue2"]))
            print("    peopleValue:"+str(object_status["peopleValue"])," dianweiji:"+str(object_status["dianweiji"]))
            time.sleep(3)
            
# host = "172.20.10.9"
host = "172.18.3.183" # 设置服务器端SocketServer的ip
port = 8086
object_status = {
    "switchValue":0,
    "rainValue":1,
    "rainValue2":-1,
    "gasValue":1,
    "gasValue2":-1,
    "lightValue":1,
    "lightValue2":-1,
    "lightValue3":-1,
    "temperature"-1,
    "temperature2":-1,
    "fireValue":0,
    "dianweiji":-1,
    "fireValue2":1,
    "peopleValue":0,
    }
ADC.setup(0x48)
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16,GPIO.OUT)  # 继电器
GPIO.setup(12,GPIO.IN)  # 雨滴传感器
GPIO.setup(11,GPIO.IN)  # 可燃气体传感器
GPIO.setup(22,GPIO.IN)  # 光敏传感器
GPIO.setup(40,GPIO.IN)  # 火焰传感器
GPIO.setup(37,GPIO.IN)  # 人体红外传感器
if object_status["switchValue"] == 1:
    GPIO.output(16,GPIO.LOW)
elif object_status["switchValue"] == 0:
    GPIO.output(16,GPIO.HIGH)
s = doConnect(host, port)
status = 0
# 创建新线程
thread1 = myThread1()
thread2 = myThread2()
thread3 = myThread3()
# 开启新线程
thread1.start()
thread2.start()
thread3.start()
thread1.join()
thread2.join()
thread3.join()
print("退出主线程")

3.1 AD转换模块①:PCF8591.py

#!/usr/bin/env python
#------------------------------------------------------
#
#       您可以使用下面语句将此脚本导入另一个脚本:
#           “import PCF8591 as ADC”                
#   
#   ADC.Setup(Address)  # 查询PCF8591的地址:“sudo i2cdetect -y 1”
# i2cdetect  is  a  userspace  program to scan an I2C bus for devices.
# It outputs a table with the list of detected devices on the specified bus.
#   ADC.read(channal)   # Channal范围从0到3 
#   ADC.write(Value)    # Value范围从0到255
#
#------------------------------------------------------
#SMBus (System Management Bus,系统管理总线) 
import smbus   #在程序中导入“smbus”模块
import time

# for RPI version 1, use "bus = smbus.SMBus(1)"
# 0 代表 /dev/i2c-0, 1 代表 /dev/i2c-1 ,具体看使用的树莓派那个I2C来决定
bus = smbus.SMBus(1)         #创建一个smbus实例

#在树莓派上查询PCF8591的地址:“sudo i2cdetect -y 1”
def setup(Addr):
    global address
    address = Addr

def read(chn): #channel
    if chn == 0:
        bus.write_byte(address,0x40)   #发送一个控制字节到设备
    if chn == 1:
        bus.write_byte(address,0x41)
    if chn == 2: #temperature
        bus.write_byte(address,0x42)
    if chn == 3:
        bus.write_byte(address,0x43)
    bus.read_byte(address)         # 从设备读取单个字节,而不指定设备寄存器。
    return bus.read_byte(address)  #返回某通道输入的模拟值A/D转换后的数字值

def write(val):
    temp = val  # 将字符串值移动到temp
    temp = int(temp) # 将字符串改为整数类型
    # print temp to see on terminal else comment out
    bus.write_byte_data(address, 0x40, temp) 
    #写入字节数据,将数字值转化成模拟值从AOUT输出

3.2 AD转换模块②:MCP3008.py

# Importing modules
import spidev # To communicate with SPI devices
from numpy import interp	# To scale values
from time import sleep	# To add delay
import RPi.GPIO as GPIO	# To use GPIO pins

# Start SPI connection
spi = spidev.SpiDev() # Created an object
spi.open(0,0)	

# Read MCP3008 data
def analogInput(channel):
  spi.max_speed_hz = 1350000
  adc = spi.xfer2([1,(8+channel)<<4,0])
  data = ((adc[1]&3) << 8) + adc[2]
  return data

3.3 温度传感器模块:DS18B20.py

# Importing modules
import spidev # To communicate with SPI devices
from numpy import interp	# To scale values
from time import sleep	# To add delay
import RPi.GPIO as GPIO	# To use GPIO pins

# Start SPI connection
spi = spidev.SpiDev() # Created an object
spi.open(0,0)	

# Read MCP3008 data
def analogInput(channel):
  spi.max_speed_hz = 1350000
  adc = spi.xfer2([1,(8+channel)<<4,0])
  data = ((adc[1]&3) << 8) + adc[2]
  return data

(MCP3008、PCF8591、DS18B20驱动代码来源于网络,侵权删除)

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叼辣条闯天涯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值