由于最近在做水果识别系统,打算将识别出的结果传输到微信小程序,由于微信小程序限制太多,先在网页上进行实验
mqtt.js是一个开源的js库,而且兼容微信小程序,使用也比较简单,github网址mqtt.js,这里使用cdn的方式使用。
<script src="https://unpkg.com/[email protected]/dist/mqtt.min.js"></script>
首先初始化信息
const options = {
// 认证信息
clientId: '设备id',
username: '用户名',
password: '密码',
}
然后进行连接
const client = mqtt.connect('ws://域名或ip:端口号/mqtt', options);
因为我使用的是自己搭建的mqtt服务端,没有域名,所以这里我选择ws方式,除了ws还有wss方式,两者的区别是ws不安全,wss较为安全,ws的默认端口为8083,wss默认端口为443
订阅主题
client.subscribe('主题名');
然后是发送
client.publish(topic, message, 可选q0s)
然后是接受函数
client.on('message', function (topic, message) {
在这里处理
})
由于esp32的ram较小,只有512k,运行是用户可用的ram只剩200k左右,在传输图片是采用jpeg压缩,jpeg是一种有损压缩,压缩后可将图片体积大大减小,k210采集的图片为224224,如果采用rgb565,需要的大小大概为224224*2/1024=98k,这个大小对于esp32来说太大了,故在k210上对图片进行压缩,采用网上的一个c语言压缩代码,转换为jpg文件,压缩后大概15k。
压缩完成之后为二进制,二进制在传输过程中比较麻烦,一般为了传输方便都会对二进制流进行base64编码,base64编码完成之后可以完全用字符串表示
base64编码的原理就是将3个字节的内容用4个字节表示,可能这个时候就有些困惑,这不体积变大了吗?是的,编码后体积会变大三分之四,但是编码后传输更加方便
这里借鉴披荆又斩棘的讲解base64原理
在编码完之后所以的数据都可以用字符串表示,而且js可以直接显示base64流。下面为esp32 base64的编码代码
static const char *ALPHA_BASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *encode(const char *buf, const long size, char *base64Char) {
int a = 0;
int i = 0;
while (i < size) {
char b0 = buf[i++];
char b1 = (i < size) ? buf[i++] : 0;
char b2 = (i < size) ? buf[i++] : 0;
int int63 = 0x3F; // 00111111
int int255 = 0xFF; // 11111111
base64Char[a++] = ALPHA_BASE[(b0 >> 2) & int63];
base64Char[a++] = ALPHA_BASE[((b0 << 4) | ((b1 & int255) >> 4)) & int63];
base64Char[a++] = ALPHA_BASE[((b1 << 2) | ((b2 & int255) >> 6)) & int63];
base64Char[a++] = ALPHA_BASE[b2 & int63];
}
switch (size % 3) {
case 1:
base64Char[--a] = '=';
case 2:
base64Char[--a] = '=';
}
return base64Char;
}
由于我买杜邦线的时候全买成公对公的了,故现在只能模拟mqtt发送图片,这里我使用win画板做一幅图,然后使用python转换为.h文件
这里附赠一个简单的jpg转.h代码
import os
filename = 'test.jpg'
wd=[]
with open(filename, 'rb') as f:
filename2= 'test.h'
with open(filename2,'w+') as f2:
k='const char test[]={'
k2=','
f2.write(str(k))
size = os.path.getsize(filename) #获得文件大小
for i in range(size):
m = f.read(1)
n=int.from_bytes(m, byteorder='big', signed=False)
f2.write(str(n))
if i<size-1:
f2.write(str(k2))
print(i)
i=i+1
k='};'
f2.write(str(k))
f2.close()
f2.close()
转换之后jpg文件就变成了c语言的数组
int len=8842*4/3+1;
char* data = (char*) malloc(len);
char *base64=encode(test, 8842, data);
esp_mqtt_client_publish(client, "photo", (const char *)base64, len, 1, 0);
数组长度为8842,这里首先计算需要申请的内存大小,编码后为原文件的三分之四,然后进行编码,发送到photo主题。
接下来是js部分,创建一个div
<img src="apple.jpg" id="userImge" style="width: 50%;height:50%;" name="userImge" >
然后在client.on(‘message’, function (topic, message)mqtt接受函数里判断主题,并显示,在js里显示base64只需要一句代码
$("#userImge").attr("src","data:image/jpg;base64,"+p1);
web可视化选用百度开源可视化库echarts,官方连接百度可视化库,因为是国人制作,里面的文档都挺详细,而且官方速度访问快多了,可以在官网在线修改。
上面有很多例程,基本涵盖需要的可视化工具。
最后附上一个简单的代码,这个代码是移动端布局,电脑布局排版会乱。如果没有服务器,想在手机上随时查看,可以放到github博客上运行。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
<title>myProject</title>
<!-- 引入 js/echarts.min.js -->
<script src="js/echarts.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/mqtt.min.js"></script>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<style>
body{
background:url("smart.jpg") no-repeat;
background-size: 100%;
}
.t{
margin-top:10%;
}
.div1{
margin-top:70px;
width:100%;
display: flex;
}
.div2{
margin-left:5%;
display: flex;
}
</style>
</head>
<body>
<div class="t"><center><h1>水果分拣系统设计</h1></center></div>
<div class=div1>
<img src="apple.jpg" id="userImge" style="width: 50%;height:50%;" name="userImge" >
<div style="width: 50%;height:200px;">
<center><h3 >第<a id="num">0</a>个苹果</h3></center>
<center><h3>体积:中</h3></center>
<center><h3>成熟度:<a id="csd">85</a>%</h3></center>
<div class="div2">
<center><button id="bu" style="width: 80px;height:30px">接收图片</button></center>
<center><button id="but" style="width: 80px;height:30px;margin-left:5%;">ledon</button></center>
</div>
</div>
</div>
<div id="dev" style="width: 100%;height:300px;"></div>
<script>
//初始化仪表盘
var myChart = new Array();
var option = new Array();
myChart = echarts.init(document.getElementById('dev'));
option = {
backgroundColor: '#2c343c',
title: {
text: '苹果数量统计',
left: 'center',
top: 20,
textStyle: {
color: '#ccc'
}
},
tooltip: {
trigger: 'item',
formatter: '{a}
{b} : {c} ({d}%)'
},
visualMap: {
show: false,
min: 80,
max: 600,
inRange: {
colorLightness: [0, 1]
}
},
series: [
{
name: '访问来源',
type: 'pie',
radius: '55%',
center: ['50%', '50%'],
data: [
{value: 200, name: '大苹果'},
{value: 100, name: '标准苹果'},
{value: 100, name: '小苹果'},
].sort(function (a, b) { return a.value - b.value; }),
roseType: 'radius',
label: {
color: 'rgba(255, 255, 255, 0.3)'
},
labelLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)'
},
smooth: 0.2,
length: 10,
length2: 20
},
itemStyle: {
color: '#c23531',
shadowBlur: 200,
shadowColor: 'rgba(0, 0, 0, 0.5)'
},
animationType: 'scale',
animationEasing: 'elasticOut',
animationDelay: function (idx) {
return Math.random() * 200;
}
}
]
};
//初始化mqtt信息
const options = {
// 认证信息
clientId: '设备id',
username: '用户名',
password: '密码',
}
const client = mqtt.connect('wss://域名:443/mqtt', options);
client.subscribe('up');
client.subscribe('photo');
$("#bu").click(function(){
str1="{\"id\":\"up\"}";
client.publish("sw_led",str1,1);
});
$("#but").click(function(){
var st=$("#but").text();
console.log(st);
if(st=="ledon")
{
str1="{\"id\":\"on\"}";
client.publish("sw_led",str1,1);
$("#but").text("ledoff");
}else
{
str1="{\"id\":\"off\"}";
client.publish("sw_led",str1,1);
$("#but").text("ledon");
}
//str1="{\"id\":\"up\"}";
// client.publish("sw_led",str1,1);
});
//myChart.setOption(option, true)
myChart.setOption(option, true)
//接受信息
client.on('message', function (topic, message) {
var p1 = message.toString();
//console.log(p1);
if(topic=="up")
{
var p2 = JSON.parse(p1);
console.log(p2);
$("#csd").html(p2.val);
option.series[0].data[0].value = p2.val;
myChart.setOption(option, true)
}
else if(topic=="photo")
{
$("#userImge").attr("src","data:image/jpg;base64,"+p1);
}
})
client.on('reconnect', (error) => {
console.log('正在重连:', error)
})
client.on('connect', (error) => {
console.log('连接成功:', error)
})
client.on('error', (error) => {
console.log('连接失败:', error)
})
</script>
</body>
</html>