基于CH2601的冷链智能管理系统
作者:刘建华
作者长期从事免疫规划工作,免疫规划的重中之重,是疫苗的冷链管理,疫苗从生产到运输,最终接种到受接者身体,都有严格的储存要求。《中华人民共和国疫苗管理法》颁布以后,对疫苗储存以法律的方式规定,我们免疫规划人经常开玩笑说,一不小心就会违法。以往对温度监测都用人工的方式进行,这样疫苗管理人员没有休息,没有上班下班,都在担心疫苗保管会出什么问题。虽然现在也出现了很多疫苗的温度监测系统,但是一来产品卖得极其的贵,一个温度监测点动则几千。管理软件也不是很规范。作者从一个没有单片机的知识的医师,从零开始学习单片机知识,从原来的stm8L、到ESP8266,到现在的平头哥RVB2601,为这个领域提供廉价的解决方案。而且现在的温度监测系统没有对冷链设备的工作状况进行监测,有些报警功能,也是在温度超过报警条件下才有报警动作。我这次的作品考虑温度监测+冷链设备的电源电压、电流、功耗的连续监测。在服务器端分析出设备的运行状态,提前做出预警。
这次的作品设计是采集冷链设备的温度,冷链设备的工作电压、电流、功耗、温度。并通过互相网上传给服务器。服务器同时推送给用户APP。
RVB2601效果图:
温度计:
电源监控:
APP效果图:
/*
* Copyright (C) 2019-2020 Alibaba Group Holding Limited
*/
#include <stdlib.h>
#include <string.h>
#include <aos/aos.h>
#include "aos/cli.h"
#include "main.h"
#include "app_init.h"
#include "oled.h"
#include "aos/hal/uart.h"
#include "drv/rtc.h"
#include "http.h"
#define TAG "app"
extern int w800_living_wjap(const char *myssid,const char *mypassword);
extern int w800_living_idmau(const char *mykey,const char *myname,const char *mysecret,const char *mypsecretconst);
extern int w800_living_idmcon(void);
#define UART_BUF_SIZE 40
#define UART_TX_TIMEOUT 100
#define UART_RX_TIMEOUT 500
extern uart_dev_t uart1;
u8g2_t u8g2;
char disp_buff[40];
void w800_data_receive_callback(int linkid, void *data, size_t len, char remote_ip[16], uint16_t remote_ports);
static csi_rtc_t g_rtc;
static aos_task_t app_task1_handle;
static aos_task_t app_task2_handle;
char disp_Vrms[10]; //显示电压值
char disp_Irms[10]; //显示电流值
char disp_PActive[10]; //显示功耗值
char disp_Frequency[10]; //显示频率值
char disp_Temper[10];//显示温度
/* data buffer */
char uart_data_buf[UART_BUF_SIZE];
int iot_connect_dome(void)
{
char *my_ssid = " ";//2.4GHZ WiFi ssid
char *my_password = " ";//2.4GHZ WiFi password
char *my_key = " ";//ProductKey
char *my_name = " ";//DeviceName
char *my_secret = " ";//DeviceSecret
char *my_p_secret = "";//Product Secret
int ret1 = -1;
int ret2 = -1;
int ret3 = -1;
ret1 = w800_living_wjap(my_ssid,my_password);
if (ret1 == 0){
printf("AT+WJAP:OK!\n");
}
else{
printf("AT+WJAP:ERROR!\n");
}
ret2 = w800_living_idmau(my_key,my_name,my_secret,my_p_secret);
if (ret2 == 0){
printf("AT+IDMAU:OK!\n");
}
else{
printf("AT+IDMAU:ERROR!\n");
}
ret3 = w800_living_idmcon();
if (ret3 == 0){
printf("AT+IDMCON:OK!\n");
}
else{
printf("AT+IDMCON:ERROR!\n");
}
if(ret1 == 0 && ret2 == 0 && ret3 == 0){
return 0;
}else{
return -1;
}
}
/*
* 功能:创建串口1接收任务,接收数据并把数据解析出来,存放在buff中,由U8g2显示函数定时刷新。上传服务器
*
*
*/
static void application_task1_entry(void *arg)
{
int ret = -1;
uint32_t rx_size = 0;
float Irms=0; //电流有效值
float Vrms=0; //电压有效值
float Frequency=0; //频率
float PowerFactor=1;//功率因数
float PActive=0; //有功功率
double W_KWH=0; //累积功耗
int n=0;
const char *dev_id = "0";
int pkt_id = 0;
char report_buf[128];
while (1) {
ret = hal_uart_recv_II(&uart1, uart_data_buf, 24,
&rx_size, UART_RX_TIMEOUT);
// LOGD(TAG, "RECV len:%d data:%x",rx_size,uart_data_buf[0]);
if (rx_size == 24)
{
if((uart_data_buf[0] == 0x55) && (uart_data_buf[1] == 0x55))
{
Vrms=(double)(((uint32_t)uart_data_buf[6]<<24)|((uint32_t)uart_data_buf[7]<<16)|((uint32_t)uart_data_buf[8]<<8)|((uint32_t)uart_data_buf[9]<<0))/1000.0;
Irms=(double)(((uint32_t)uart_data_buf[10]<<24)|((uint32_t)uart_data_buf[11]<<16)|((uint32_t)uart_data_buf[12]<<8)|((uint32_t)uart_data_buf[13]<<0))/1000.0;
PActive=(double)(((uint32_t)uart_data_buf[14]<<24)|((uint32_t)uart_data_buf[15]<<16)|((uint32_t)uart_data_buf[16]<<8)|((uint32_t)uart_data_buf[17]<<0))/1000.0;
Frequency=(double)(((uint32_t)uart_data_buf[18]<<24)|((uint32_t)uart_data_buf[19]<<16)|((uint32_t)uart_data_buf[n++]<<8)|((uint32_t)uart_data_buf[20]<<0))/1000.0;
sprintf(disp_Vrms,"%.1fV",Vrms);
sprintf(disp_Irms,"%.2fA",Irms);
sprintf(disp_PActive,"%.2fW",PActive);
sprintf(disp_Frequency,"%.2fHz",Frequency);
snprintf(report_buf,128,"{\\\"LightVolt\\\":%.1f,\\\"ActivePower\\\":%.2f,\\\"LightCurrent\\\":%.2f}",Vrms,PActive,Irms);
w800_living_idmpp(dev_id, report_buf, &pkt_id);
memset(uart_data_buf,0,24);
}
else if((uart_data_buf[0] == 0x66) && (uart_data_buf[1] == 0x66))
{
if(uart_data_buf[2] == 0x01)
{
sprintf(disp_Temper,"-%d.%d C",uart_data_buf[3],uart_data_buf[4]);
snprintf(report_buf,60,"\\\"temperature\\\":-%d.%d}",uart_data_buf[3],uart_data_buf[4]);
}
else
{
sprintf(disp_Temper,"%d.%d C",uart_data_buf[3],uart_data_buf[4]);
snprintf(report_buf,60,"{\\\"temperature\\\":%d.%d}",uart_data_buf[3],uart_data_buf[4]);
}
w800_living_idmpp(dev_id, report_buf, &pkt_id);
}
}
aos_msleep(50);
}
aos_task_exit(0);
}
/*
* 功能:创建串口1发送任务,向电源监控模块发送获取监控数据的命令
*
*
*
*/
static void application_task2_entry(void *arg)
{
int ret = -1;
uint8_t uart_tx_buf[] = {0x55,0x55,0x01,0x02,0x00,0x00,0xAD};
while (1) {
ret = hal_uart_send(&uart1, uart_tx_buf, sizeof(uart_tx_buf), UART_TX_TIMEOUT);
if (ret == 0) {
//printf("uart1 data send succeed !\n");
}
aos_msleep(1000);
}
aos_task_exit(0);
}
void test_getIP_task()
{
char ssid[32];
int bssid[6];
int channel;
int rssi;
char disp[40];
//先获取AP信息,判断是否联网
//偿试连接到服务器
//发送数据
char ip[16];
char gw[16];
char mask[16];
csi_rtc_time_t this_time;
csi_error_t ret;
int ipinfo = -1;
while(1){
//获取时间
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2,u8g2_font_7x13_tr);
u8g2_DrawStr(&u8g2,12,12,"Cold Manage SYS");//字符显示
//u8g2_SendBuffer(&u8g2);
u8g2_SetFont(&u8g2,u8g2_font_7x13_tr);
u8g2_DrawStr(&u8g2,4,30,disp_Vrms);//显示电压值
u8g2_DrawStr(&u8g2,80,30,disp_Irms);//显示电压值
u8g2_DrawStr(&u8g2,4,50,disp_PActive);//显示功率值
u8g2_DrawStr(&u8g2,80,50,disp_Temper);//显示频率值
u8g2_SendBuffer(&u8g2);
aos_msleep(500);
}
}
void w800_data_receive_callback(int linkid, void *data, size_t len, char remote_ip[16], uint16_t remote_ports)
{
uint8_t *buf;
buf = (uint8_t *)data;
if(len == 0)
{
return;
}
printf("receive data len: %d\r\n",len);
printf("receive data:");
for(uint16_t i = 0; i < len; i++)
{
printf("%c ",buf);
}
printf("\r\n");
}
int main(void)
{
board_yoc_init();
LOGD(TAG, "%s\n", aos_get_app_version());
u8g2Init(&u8g2);
u8g2_SetFontMode(&u8g2, 1);
u8g2_SetFont(&u8g2, u8g2_font_unifont_t_symbols);
u8g2_ClearBuffer(&u8g2);
u8g2_DrawStr(&u8g2,0,12,"RVB2601");//字符显示
u8g2_SendBuffer(&u8g2);
sleep(5);
aos_task_new("test_get ip", test_getIP_task,NULL, 1024);
aos_task_new_ext(&app_task1_handle, "app_task1", application_task1_entry,
NULL, 4096, AOS_DEFAULT_APP_PRI);
aos_task_new_ext(&app_task2_handle, "app_task2", application_task2_entry,
NULL, 4096, AOS_DEFAULT_APP_PRI);
iot_connect_dome();
while (1) {
aos_msleep(1000);
}
return 0;
}
七、项目总结
经过两个多月的对RVB2601的学习了解到了平头哥的操作系统、wifi联网、阿里IOT云平台的操作等等,对平头哥RVB2601的各项性能都有所了解。这是一款很有前途的芯片。
虽然用这个平台实现了基本的功能,但是还是跟我当时的设想有一定的差距,因为我本来就有现实的项目在运行。这里要说说这一系列的短板,希望对阿里云平台在今后发展有所帮助。
1、工单系统:遇到问题后,有工单系统是非常好的,但是这工单系统着实让人郁闷,发一个工单,半天没有回,回一下就是一连串什么什么的,一直在质疑提工单自己是不是有问题(我没问题找你干嘛、咱十几年的老工程师,没事会找你玩吗?)。效率极其低,我中途也想把板子退回去,中断评测,但是工作人员劝说下继续完成项目。我提了好些工单,基本是一个星期后才能完成,后面我要了即时联系的钉钉系统,处理效果才好一些。这里说阿里的技术支持确实需要加强。
2、W800这款wifi芯片其他的地方早就有MQTT协议开放了,但是平头哥这次结合没有把AT的协议做好,相比http,MQTT要轻量化,用户自己使用起来也方便一些。
3、我原来设计要走自己的服务器的,但是平头哥也没有给出https协议解决方法,所以跟我原来的设计也大有出入。
4、NTP这个是个大坑。我花了好几天也没整明白。
5、CDK也是坑了我好多的时间。里面的版本特别的多,让人一下子难以适应,目前的工程师都得用好几种IDE,但是这个CDK是最让我郁闷的一款IDE。举例:明明文件在那里,也include进来了,他就是说没有存在。还有刚刚使用时,CDK启动要十多分钟者能进去,动不动卡死。等等,希望平头哥以后的版本里需要改进。
6、还有博文系统,发一个博文来来回回让你修改十几次才通过,我们评测人员又不是拿你家的工资,这样要求是不是太严,让人想起就心烦!
7、APP的更新问题,面板修改后,需要一个小时到一天不等才能在手机端实时更新。发个工单,技术支持竞然说我操不正确,那为什么什么都没动,第二天,或者半天后自己更新了。看来阿里工单的技术支持也得自己去学习一下业务知识。让开发者白瞎了好多的宝贵时间。
总的来说,有过20年编程经验,6年单片机开发,有过十几款芯片的成功开发案例,虽然我说不编程的高手,但是也不是小白,这次使用平头哥,是让人最不爽的一次。纯粹为了当时提交申请时的承诺,花了很多无用功,交了一把作业。
最后,要感谢这次试用RVB2601的小伙伴们,在你们优秀的作品中,让我学习了很多知识,谢谢!
PS:演示视频
不错,将2款开发板的特点都集合到一起了。
总结的身有同感!!!
jinglixixi 发表于 2022-5-10 23:02 总结的身有同感!!!
是呀,感觉平头哥的东西太臃肿,一进去,满头雾水。
本帖最后由 lugl4313820 于 2022-5-11 07:07 编辑平头哥的平台还是用不习惯,希望他们能慢慢改进吧!
梦溪开物 发表于 2022-5-11 10:23 请问楼主在文中说到“冷链设备”是集生产、储存、运输中用到设备的总和吗?
冷链设备是从供应链的角度来定义的。各类产品有其独特性,产品的供应链也具有独特性;冷冻类产品,由于产品要求所处的环境通常为低温或低湿共同特性,所以称为冷冻产品,冷冻产品的供应链称为冷链;用于制造低温、低湿环境的设备,称为冷链设备。
具体的冷链设备有:低温冷库、常温冷库、低温冰箱、普通冰箱、冷藏车、冷藏箱、疫苗运输车、备用冰排等。
引用: 梦溪开物 发表于 2022-5-11 10:25 请问这里电流监测是怎么做的呀?
某鱼上淘来的10块钱一个的模块。好象是个单相计量模块,还可以采集电流频率,累计功耗等等的东西。