1.试验材料
Pyboard板、dht11温湿度传感器、NB-IoT模块、4G天线、已激活的物联网卡、0.96寸4P的OLED屏和安卓数据线各一个
接线方式
如有不明白的可看下图
配件接口 | pyboard板接口 |
---|---|
USB安卓数据线 | DFU |
DHT11温湿度传感器 | PB13-Y6引脚 |
NB-IoT的RX | PC6-Y1 |
NB-IoT的TX | PC7-Y2 |
NB-IoT的3.3V | 3.3V |
NB-IoT的GND | GND |
OLED屏的SCL | PB10-Y9引脚 |
OLED屏的SDA | PB11-Y10引脚 |
DHT11连接引脚图
NB-IoT TX和RX连接引脚图
NB-IoT TX和RX连接引脚图
2.程序烧录
开发软件uPyCraft_V1.1.exe
将以下4个程序创建并下载至Pyboard
板中
main.py
主函数
import pyb
from pyb import Timer,LED
from machine import I2C,Pin
from ssd1306 import SSD1306_I2C
from pyb import Accel
import time
from pyb import UART
import dht
#初始化OLED
i2c = I2C(sda=Pin("Y10"), scl=Pin("Y9"))
oled = SSD1306_I2C(128, 64, i2c, addr=0x3c)
#初始化DHT11
dht = dht.DHT11(Pin("B13"))
time.sleep(1)
accel=Accel()
#初始化串口uart6
uart6 = UART(6,115200)
uart6.init(115200, bits=8, parity=None, stop=1)
time.sleep(1)
connectStatus="0"
#呼吸灯
ia =1 #定义变量
da =1
def fa(t): #定义回调函数
global ia, da #声明全局变量
if (ia==0) or (ia==255): #判断亮度方向
da = 256 -da
ia = (ia + da) % 256 #改变亮度参数
LED(3).intensity(ia)
tm=Timer(1, freq=50, callback=fa) #设置定时器 1,50Hz,回调函数 fa
start = time.time() #启动计时
words=('\r','\n',' ')
oled.fill(0) # 清屏,背景黑色
oled.text('Initializing...', 0, 0)
oled.show()
time.sleep(60) #等待BC26初始化
uart6.write('AT\r\n') #测试AT指令
time.sleep(1)
buf=uart6.read().decode()
for word in words:
if word in buf:
buf = buf.replace(word,'')
print(buf)
if buf.find('ATOK') > -1 :
uart6.write('AT+CGATT?\r\n') #测试网络是否附着
oled.fill(0) # 清屏,背景黑色
oled.text('AT Succ', 0, 0)
oled.show()
time.sleep(1)
buf=uart6.read().decode()
for word in words:
if word in buf:
buf = buf.replace(word,'')
print(buf)
else:
print('AT Fail')
oled.fill(0) # 清屏,背景黑色
oled.text('AT Fail', 0, 0)
oled.show()
time.sleep(0.5)
if buf.find('AT+CGATT?+CGATT:1OK') > -1 :
uart6.write('AT+QMTOPEN=0,"mq.tongxinmao.com",18830\r\n') #打开MQTT客户端网络
oled.fill(0) # 清屏,背景黑色
oled.text('CGATT Succ', 0, 0)
oled.show()
time.sleep(8)
buf=uart6.read().decode()
for word in words:
if word in buf:
buf = buf.replace(word,'')
print(buf)
if buf.find('AT+QMTOPEN=0,"mq.tongxinmao.com",18830OK+QMTOPEN:0,0') > -1 :
uart6.write('AT+QMTCONN=0,"BC26"\r\n')
oled.fill(0) # 清屏,背景黑色
oled.text('QMTOPEN Succ', 0, 0)
oled.show()
time.sleep(2)
buf=uart6.read().decode()
for word in words:
if word in buf:
buf = buf.replace(word,'')
print(buf)
elif buf.find('AT+QMTOPEN=0,"mq.tongxinmao.com",18830OK') > -1 : #有时候会连接很长时间,需要循环判断是否收到消息
while True:
testdata=uart6.read()
if testdata:
buf=testdata.decode()
for word in words:
if word in buf:
buf = buf.replace(word,'')
if buf.find('+QMTOPEN:0,0') > -1 :
uart6.write('AT+QMTCONN=0,"BC26"\r\n') #将BC26连接至MQTT服务器
oled.fill(0) # 清屏,背景黑色
oled.text('QMTOPEN Succ', 0, 0)
oled.show()
time.sleep(2)
buf=uart6.read().decode()
for word in words:
if word in buf:
buf = buf.replace(word,'')
print(buf)
break
else:
print('QMTOPEN Fail')
oled.fill(0) # 清屏,背景黑色
oled.text('QMTOPEN Fail', 0, 0)
oled.show()
else:
print("waiting for conn")
oled.fill(0) # 清屏,背景黑色
oled.text('waiting for conn', 0, 0)
oled.show()
time.sleep(5)
else:
print('QMTOPEN Fail')
oled.fill(0) # 清屏,背景黑色
oled.text('QMTOPEN Fail', 0, 0)
oled.show()
else:
print('CGATT Fail')
oled.fill(0) # 清屏,背景黑色
oled.text('CGATT Fail', 0, 0)
oled.show()
if buf.find('AT+QMTCONN=0,"BC26"OK+QMTCONN:0,0,0') > -1 :
connectStatus="1"
oled.fill(0) # 清屏,背景黑色
oled.text('QMTCONN Succ', 0, 0)
oled.show()
time.sleep(2)
else:
print('QMTCONN Fail')
oled.fill(0) # 清屏,背景黑色
oled.text('QMTCONN Fail', 0, 0)
oled.show()
while connectStatus=="1":
dht.measure() #读取温湿度
Tem=dht.temperature()
Hum=dht.humidity()
uart6.write('AT+QMTPUB=0,1,1,0,"/mqtt/pub","{"tem":%s'%Tem+',"hum":%s'%Hum+'}"\r\n') #秒传温湿度
oled.fill(0) # 清屏,背景黑色
oled.text('Send Succ', 0, 0)
oled.text('t:%s' %Tem+'C h:%s' %Hum+'%', 0, 12) #oled显示收到的温湿度
oled.text('Acceleration:', 0, 24)
oled.text("x:%d, y:%d, z:%d"%(accel.x(),accel.y(),accel.z()), 0, 36) #oled显示加速度数值
end = time.time()
oled.text('T:'+str(end-start)+'s', 0, 48) #oled显示程序运行时间
oled.show()
time.sleep(1)
boot.py
import machine
import pyb
dht.py
dht11温湿度传感器驱动
from pyb import dht_readinto
class DHTBase:
def __init__(self, pin):
self.pin = pin
self.buf = bytearray(5)
def measure(self):
buf = self.buf
dht_readinto(self.pin, buf)
if (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff != buf[4]:
raise Exception("checksum error")
class DHT11(DHTBase):
def humidity(self):
return self.buf[0]
def temperature(self):
return self.buf[2]
ssd1306.py
OLED屏驱动
from micropython import const
import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4)
SET_NORM_INV = const(0xa6)
SET_DISP = const(0xae)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0)
SET_MUX_RATIO = const(0xa8)
SET_COM_OUT_DIR = const(0xc0)
SET_DISP_OFFSET = const(0xd3)
SET_COM_PIN_CFG = const(0xda)
SET_DISP_CLK_DIV = const(0xd5)
SET_PRECHARGE = const(0xd9)
SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d)
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV, 0x80,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# display
SET_CONTRAST, 0xff, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.temp[0] = self.addr << 1
self.temp[1] = 0x40 # Co=0, D/C#=1
self.i2c.start()
self.i2c.write(self.temp)
self.i2c.write(buf)
self.i2c.stop()
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)
3.使用java建立MQTT客户端,监听温湿度数据
使用通信猫(提取码:3j0l)等MQTT监听软件来监听温湿度数据均可
代码参考自JAVA连接MQTT服务器
在pom.xml
中引入maven依赖,里面的ip地址
更改为MQTT部署的服务器地址
<!-- MQTT依赖 -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
MQTT工具类代码
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* MQTT工具类操作
*/
@Slf4j
@Component
public class MQTTConnect {
private String HOST = "tcp://mq.tongxinmao.com:18830"; //mqtt服务器的地址和端口号
private final String clientId = "DC" + (int) (Math.random() * 100000000);
private MqttClient mqttClient;
/**
* 客户端connect连接mqtt服务器
*
* @param userName 用户名
* @param passWord 密码
* @param mqttCallback 回调函数
**/
public void setMqttClient(String userName, String passWord, MqttCallback mqttCallback) throws MqttException {
MqttConnectOptions options = mqttConnectOptions(userName, passWord);
if (mqttCallback == null) {
mqttClient.setCallback(new Callback());
} else {
mqttClient.setCallback(mqttCallback);
}
mqttClient.connect(options);
}
/**
* MQTT连接参数设置
*/
private MqttConnectOptions mqttConnectOptions(String userName, String passWord) throws MqttException {
mqttClient = new MqttClient(HOST, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(userName);
options.setPassword(passWord.toCharArray());
options.setConnectionTimeout(10);///默认:30
options.setAutomaticReconnect(true);//默认:false
options.setCleanSession(false);//默认:true
//options.setKeepAliveInterval(20);//默认:60
return options;
}
/**
* 关闭MQTT连接
*/
public void close() throws MqttException {
mqttClient.close();
mqttClient.disconnect();
}
/**
* 向某个主题发布消息 默认qos:1
*
* @param topic:发布的主题
* @param msg:发布的消息
*/
public void pub(String topic, String msg) throws MqttException {
MqttMessage mqttMessage = new MqttMessage();
//mqttMessage.setQos(2);
mqttMessage.setPayload(msg.getBytes());
MqttTopic mqttTopic = mqttClient.getTopic(topic);
MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
token.waitForCompletion();
}
/**
* 向某个主题发布消息
*
* @param topic: 发布的主题
* @param msg: 发布的消息
* @param qos: 消息质量 Qos:0、1、2
*/
public void pub(String topic, String msg, int qos) throws MqttException {
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setQos(qos);
mqttMessage.setPayload(msg.getBytes());
MqttTopic mqttTopic = mqttClient.getTopic(topic);
MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
token.waitForCompletion();
}
/**
* 订阅某一个主题 ,此方法默认的的Qos等级为:1
*
* @param topic 主题
*/
public void sub(String topic) throws MqttException {
mqttClient.subscribe(topic);
}
/**
* 订阅某一个主题,可携带Qos
*
* @param topic 所要订阅的主题
* @param qos 消息质量:0、1、2
*/
public void sub(String topic, int qos) throws MqttException {
mqttClient.subscribe(topic, qos);
}
/**
* main函数自己测试用
*/
public static void main(String[] args) throws MqttException {
MQTTConnect mqttConnect = new MQTTConnect();
mqttConnect.setMqttClient("", "", new Callback());
mqttConnect.sub("/mqtt/pub");
//mqttConnect.pub("/mqtt/sub", "can you hear me?" + (int) (Math.random() * 100000000));
}
}
MQTT的回调函数代码
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
/**
* MQTT回调函数
*/
@Slf4j
public class Callback implements MqttCallback {
/**
* MQTT 断开连接会执行此方法
*/
@Override
public void connectionLost(Throwable throwable) {
log.info("断开了MQTT连接 :{}", throwable.getMessage());
log.error(throwable.getMessage(), throwable);
}
/**
* publish发布成功后会执行到这里
*/
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
log.info("发布消息成功");
}
/**
* subscribe订阅后得到的消息会执行到这里
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// TODO 此处可以将订阅得到的消息进行业务处理、数据存储
log.info("收到来自 " + topic + " 的消息:{}", new String(message.getPayload()));
}
}
MQTT监听器代码
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 项目启动 监听主题
*/
@Slf4j
@Component
public class MQTTListener implements ApplicationListener<ContextRefreshedEvent> {
private final MQTTConnect server;
@Autowired
public MQTTListener(MQTTConnect server) {
this.server = server;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
server.setMqttClient("", "", new Callback());
server.sub("/mqtt/sub");
} catch (MqttException e) {
log.error(e.getMessage(), e);
}
}
}
4.程序测试
Pyboard板上电运行,经过初始化→AT测试→网络是否附着→打开MQTT客户端网络→将BC26连接至MQTT服务器→读取温湿度数据→发送温湿度数据
运行MQTT工具类代码
中的main函数,开始监听/mqtt/pub
主题的消息
Pyboard利用NB-IoT模块通过MQTT协议发送dht11传感器的温湿度数据完成,在JAVA项目中对数据进行解析,编写存储数据的业务逻辑即可将数据存储至数据库中。
欢迎小伙伴的讨论,若有问题请在评论区评论或私信,谢谢你。