阅读 208

esp8266采集温湿度并上传到树莓派

ESP8266+DHT11+MicroPython读取温湿度
ESP8266+ssd1306四引脚oled MicroPython Hello World
用到的东西都在上边,接下来上效果图
image.png 这是树莓派收到的数据

image.png
时间每小时校准一次,本来是一天一次,后来发现这玩意不太准,一天下来得慢个几分钟。
温湿度的话是每10分钟采集一次并上传到树莓派。

时间

import ntptime;
ntptime.NTP_DELTA = 3155644800   # UTC+8偏移时间(秒)
ntptime.host = "ntp.ntsc.ac.cn"  # ntp服务器
ntptime.settime();               # 修改设备时间
复制代码

调用settime会从服务器同步时间,接下来是获取当前时间需要用到datetime,拿到的是一个元组
image.png
需要把他格式化成时间格式

from machine import RTC;
rtc = RTC();
# 这里补0
def toDoubleStr(n):
    return "0" + str(n) if n < 10 else str(n);

def getCurrentTime():
    rtc = RTC();
    timeTup = rtc.datetime();
    timeList = list(timeTup);
    Y = str(timeList[0]);
    M = toDoubleStr(timeList[1]);
    D = toDoubleStr(timeList[2]);
    h = toDoubleStr(timeList[4]);
    m = toDoubleStr(timeList[5]);
    s = toDoubleStr(timeList[6]);
    return {
        "date": Y + "/" + M + "/" + D,
        "time": h + ":" + m + ":" +s,
        "h": h
    }
复制代码

到这里时间就处理完成了

温湿度

这个没啥说的,直接读就行了

import dht;
import machine;
from service import uploadHT;

def getHT():
    d = dht.DHT11(machine.Pin(2));
    d.measure();
    t = str(d.temperature());
    h = str(d.humidity());
    data = {
        "temp": t,
        "humi": h
    };
    uploadHT(data);
    return data;
复制代码

屏幕显示

这里做了个封装,因为屏幕上要显示多段文字,需要每秒刷新一次

from machine import Pin, I2C;
from ssd1306 import SSD1306_I2C;

i2c = I2C(scl=Pin(14), sda=Pin(0));
oled = SSD1306_I2C(128, 64, i2c);

def refreshDisplay(data):
    oled.fill(0);
    for i in range(len(data)):
        item = data[i];
        oled.text(item[0], item[1], item[2]);
    oled.show();
复制代码

这里要把日期温湿度同时传进来,data的格式如下

[
    [文本, x坐标, y坐标],
    ...
]
复制代码

树莓派跑个服务

这里用了FastApi,比较简单,目前只写了个接口
main.py

from fastapi import FastAPI;
from fastapi.middleware.cors import CORSMiddleware;
from routes import moduleRouter;

app = FastAPI();

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(moduleRouter.router, prefix="/api")

@app.get('/heart_beat')
async def heartBeat():
    return True;

if __name__ == "__main__":
    import uvicorn;
    port = 6080;
    uvicorn.run(app="main:app", host="0.0.0.0", port=port, reload=True, debug=True);
复制代码

router

from fastapi import APIRouter;
from pydantic import BaseModel;

router = APIRouter();

class HT(BaseModel):
    temp: str
    humi: str

class HTData(BaseModel):
    macAddress: str
    data: HT

@router.post("/HT/update")
async def updateHT(data: HTData):
    print(data)
    return {
        "result": True
    }
复制代码

接收到数据打印出来

数据上传到树莓派

后边准备把数据存到数据库,这里把esp8266的mac地址一起传过去 首先是拿到mac地址

import network;
wlan = network.WLAN(network.STA_IF);
b = wlan.config('mac');
复制代码

到这里拿到的是bytes

b'0\x83\x98\xa3\x1a\xac'
复制代码

存表的话肯定是要用字符串的,再做一下转换

import binascii;
mac = binascii.hexlify(b).decode()[2:]; # '8398a31aac'
复制代码

到这mac地址就ok了
发送请求的话用的是urequests

import urequests;
import ujson;
def uploadHT(data):
    b = {
        "macAddress": mac,
        "data": data
    }
    urequests.post("http://192.168.31.222:6080/api/HT/update", headers = {'content-type': 'application/json'}, data = ujson.dumps(b));
复制代码

跑起来

esp8266每次重置或者上电之后会自动执行main.py

from machine import Timer;
from thermometer import getHT;
from clock import initMachineTime, getCurrentTime;
from screen import refreshDisplay;
import time;

HT = {
    "temp": "0",
    "humi": ""
}

currentTime = {
    "date": "-/-/-",
    "time": "-:-:-"
}

currentHour = "";

htTimer = Timer(0);
clockTimer = Timer(1);

def refreshHT(t):
    global HT;
    HT = getHT();
# 校准时间
def initTime():
    global currentTime, currentHour;
    initMachineTime();
    currentTime = getCurrentTime();
    print("initTime: ", currentTime);
    currentHour = currentTime["h"];

def updateTime(t):
    global currentTime;
    currentTime = getCurrentTime();
    h = currentTime["h"];
    # 每小时校准一次时间
    if h != currentHour:
        initTime();
    refreshDisplay([
        [currentTime["date"], 2, 2],
        [currentTime["time"], 2, 17],
        ["Temp:" + HT["temp"], 2, 32],
        ["Humi:" + HT["humi"], 2, 47]
    ]);

def main():
    refreshDisplay([["waiting...", 5, 5]]);
    # 读取温湿度的时候会发送请求,如果不sleep的话请求会发送失败
    time.sleep(2);
    refreshHT(0);
    # 每10分钟读一次温湿度
    htTimer.init(period = 1000 * 60 * 10, mode = Timer.PERIODIC, callback = refreshHT);
    # 每秒刷新一次时间
    clockTimer.init(period = 1000, mode = Timer.PERIODIC, callback = updateTime);

if __name__ == "__main__":
    main();
复制代码

把文件上传到esp8266,按RST就ok了,全程开着串口调试工具,有报错会打印出来。

把数据存进mysql

安装搜一下就可以了,直接sudo apt install mysql会找不到,要装mariadb-server
先整个库,再整个表,要把mac地址、温湿度、时间戳再生成个uuid做主键存进去。

create database iot;
use iot;
create table ht (
    id varchar(50) not null,
    macAddress varchar(20) not null,
    temp varchar(5) not null,
    humi varchar(5) not null,
    timestamp bigint not null,
    primary key (`id`)
);
describe ht;
复制代码

image.png
到这数据库就ok了,接下来就是写代码,操作数据库用了pymysql

import pymysql;
from uuid import uuid4;
import time;

db = pymysql.connect(
    host = "localhost",
    user = "root",
    password = "数据库的密码",
    database = "iot"
);
cursor = db.cursor();

def executeSql(sql):
    try:
       # 执行sql语句
       cursor.execute(sql);
       # 提交到数据库执行
       db.commit();
    except:
       # 如果发生错误则回滚
       db.rollback();

def insertHT(data):
    t = int(round(time.time() * 1000));
    uuid = uuid4();
    macAddress = data["macAddress"];
    temp = data["temp"];
    humi = data["humi"];
    sql = f"INSERT INTO ht(id, macAddress, temp, humi, timestamp) VALUES ('{uuid}', '{macAddress}', '{temp}', '{humi}', {t})";
    executeSql(sql);
复制代码

跑起来复位一下esp8266,上传个温湿度,不出意外的话就已经存进库里了

select * from ht;
复制代码

image.png
代码已上传到github

文章分类
代码人生
文章标签