本项目以risc-v架构的d1 dock pro和d1 nezha开发板为硬件平台,应用物联网和区块链技术,设计开发一套分布式能源智慧管理小型示范系统,在该系统上实现能源生产和消费数据的实时监测。该项目在“玄铁杯第二届risc-v应用创新大赛”活动中荣获一等奖。
关于分布式能源智慧管理和m2m交易系统的技术细节,请看本文详细介绍。
01 项目介绍
能源和环保是关乎人类未来的重要课题。为实现碳中和目标,大力发展可再生清洁能源以替代传统化石能源,提高能源系统监控和消费的智能化水平,是可行的重要途径之一。本项目以risc-v架构的d1 dock pro和d1 nezha开发板为硬件平台,应用物联网和区块链技术,设计开发一套分布式能源智慧管理小型示范系统,在该系统上实现能源生产和消费数据的实时监测。
02 技术方案
项目使用 d1 dock pro 开发板设计开发一款专用网关,实时采集电池控制器、气象环境传感器等其它传感器的数据,并通过无线通信方式(wifi)以http协议或mqtt协议将传感器数据上传至物联网后台。
图1.专用网关示意
图2.专用网关实物图
智能开关用于能源消费端,实现对能源消费者(电器负载)的供电控制、电能消费数据的采集和传输等功能。该智能开关基于 d1 dock pro 开发板进行设计开发,通过开发板的i/o口控制继电器、uart接收电能计量模块的数据。设计一个扩展电路板与开发板配合使用, 扩展电路板集成电能计量模块、继电器等。本文设计的智慧开关的功能主要是控制电器开关与计量电器用电参数以及环境参数并上传到云端服务器。
图3.智能开关示意图
图4.智能开关实物图
03 核心业务代码
3.1 智能开关电能采集分析
// sensor variablefloat sensor_data[9] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9};int recv_cmd; unsigned long voltage_data, current_data, power_data, energy_data, pf_data, co2_data, hz;// read the consumer datevoid read_consumer_data(void){//send the instructionunioncrc_data{unsigned int word16;unsigned char byte[2];} crc_now;tx_buffer[0] = 0x01;tx_buffer[1] = 0x03;tx_buffer[2] = 0x01;tx_buffer[3] = 0x00;tx_buffer[4] = 0x02;tx_buffer[5] = 0x08;crc_now.word16 = chk_crc(tx_buffer, 6);tx_buffer[6] = crc_now.byte[1];//crc verificationtx_buffer[7] = crc_now.byte[0];ret = csi_uart_send_async(&g_uart, tx_buffer,8);//wait until send finishedwhile(1) {if (tx_async_flag) {tx_async_flag = 0;break;}}printf(send succeed); ret = csi_uart_receive_async(&g_uart, rx_buffer, 1); //wait until receieve finished while(1) {// printf(not_receieved); aos_msleep(200); if (rx_async_flag) { break; } } printf(line 358: got data); parse_data(); publish_sensor_data(client, publish); }//analyze the consumer datevoid parse_data(void){csi_error_t ret;unsigned char i;union crc_data{unsigned int word16;unsigned char byte[2];} crc_now;if (rx_async_flag == 1){ // check if receieve finishedrx_async_flag = 0;if ((rx_buffer[0] == 0x01)) { //check the id of the devicecrc_now.word16 = chk_crc(rx_buffer, recieve_data_num - 2);//crc verificationif ((crc_now.byte[0] == rx_buffer[recieve_data_num - 1]) && (crc_now.byte[1] == rx_buffer[recieve_data_num - 2])) {//parse voltagevoltage_data = (((unsigned long)(rx_buffer[3])) << 24) | (((unsigned long)(rx_buffer[4])) << 16) | (((unsignedlong)(rx_buffer[5])) << 8) | rx_buffer[6];sensor_data[0] = (float)(voltage_data * 0.0001);//parse currentcurrent_data = (((unsigned long)(rx_buffer[7])) << 24) | (((unsigned long)(rx_buffer[8])) << 16) | (((unsignedlong)(rx_buffer[9])) << 8) | rx_buffer[10];sensor_data[1] = (float)(current_data * 0.0001);//parse powerpower_data = (((unsignedlong)(rx_buffer[11])) << 24) | (((unsigned long)(rx_buffer[12])) << 16) | (((unsignedlong)(rx_buffer[13])) << 8) | rx_buffer[14];sensor_data[2] = (float)(power_data * 0.0001);//parse energyenergy_data = (((unsignedlong)(rx_buffer[15])) << 24) | (((unsigned long)(rx_buffer[16])) << 16) | (((unsignedlong)(rx_buffer[17])) << 8) | rx_buffer[18];sensor_data[3] = (float)(energy_data * 0.0001);//parse power factorpf_data = (((unsignedlong)(rx_buffer[19])) << 24) | (((unsigned long)(rx_buffer[20])) << 16) | (((unsignedlong)(rx_buffer[21])) << 8) | rx_buffer[22];sensor_data[4] = (float)(pf_data * 0.001);//parse co2co2_data = (((unsigned long)(rx_buffer[23])) << 24) | (((unsigned long)(rx_buffer[24])) << 16) | (((unsignedlong)(rx_buffer[25])) << 8) | rx_buffer[26];sensor_data[5] = (float)(co2_data * 0.0001);//parse frequency of the single phase alternating currenthz = (((unsigned long)(rx_buffer[31])) << 24) | (((unsigned long)(rx_buffer[32])) << 16) | (((unsignedlong)(rx_buffer[33])) << 8) | rx_buffer[34];sensor_data[6] = (float)(hz * 0.01);} else {printf(crc_error);}}} else {printf(receieve_not_finished); }}// eof uart
3.2 mqtt电能数据上云
// mqtt char pub_topic[] = wattnode/data; char sub_topic[] = wattnode/cmd; mqtt_client_t *client;int is_mqtt_ready = 0;void mqtt_do_connect(mqtt_client_t *client);static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags);void publish_sensor_data(mqtt_client_t *client, void *arg);/* called when publish is complete either with sucess or failure */static void mqtt_pub_request_cb(void *arg, err_t result){ if(result != err_ok) { printf(publish result: %d, result); }}/* the idea is to demultiplex topic and create some reference to be used in data callbacks example here uses a global variable, better would be to use a member in arg if ram and cpu budget allows it, the easiest implementation might be to just take a copy of the topic string and use it in mqtt_incoming_data_cb*/static int inpub_id;static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len){ printf(incoming publish at topic %s with total length %u, topic, (unsigned int)tot_len); /* decode topic string into a user defined reference */ if(strcmp(topic, print_payload) == 0) { inpub_id = 0; } else if(topic[0] == 'a') { /* all topics starting with 'a' might bwhile(1)e handled at the same way */ inpub_id = 1; } else { /* for all other topics */ inpub_id = 2; }}static void mqtt_sub_request_cb(void *arg, err_t result){ /* just print the result code here for simplicity, normal behaviour would be to take some action if subscribe fails like notifying user, retry subscribe or disconnect from server */ printf(subscribe result: %d, result);}static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status){ err_t err; if(status == mqtt_connect_accepted) { printf(mqtt_connection_cb: successfully connected); /* setup callback for incoming publish requests */ mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg); /* subscribe to a topic named subtopic with qos level 1, call mqtt_sub_request_cb with result */ err = mqtt_subscribe(client, sub_topic, 1, mqtt_sub_request_cb, arg); if(err != err_ok) { printf(mqtt_subscribe return: %d, err); } printf(ready to read data); is_mqtt_ready = 1; } else { printf(mqtt_connection_cb: disconnected, reason: %d, status); /* its more nice to be connected, so try to reconnect */ mqtt_do_connect(client); } }static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags){ printf(incoming publish payload with length %d, flags %u, len, (unsigned int)flags); if(flags & mqtt_data_flag_last) { /* last fragment of payload received (or whole part if payload fits receive buffer see mqtt_var_header_buffer_len) */ /* call function or do action depending on reference, in this case inpub_id */ if(inpub_id == 0) { /* don't trust the publisher, check zero termination */ if(data[len-1] == 0) { //printf(mqtt_incoming_data_cb: %s, (const char *)data); } } else if(inpub_id == 1) { /* call an 'a' function... */ } else { // printf(mqtt_incoming_data_cb: ignoring payload...); // printf(mqtt_incoming_data_cb: %s, (const char *)data); recv_cmd = atoi((const char *)data); printf(receive data: %d, recv_cmd); } } else { /* handle fragmented payload, store in buffer, write to file or whatever */ }}void publish_sensor_data(mqtt_client_t *client, void *arg){ err_t err; u8_t qos = 0; /* 0 1 or 2, see mqtt specification */ u8_t retain = 0; /* no don't retain such crappy payload... */ const int len = 9; // cat all float data to string char sep = ';'; // char *prefix = data=; char *prefix = ; char _str_data[10]; char post_str[128]; strcpy(post_str, prefix); for (int i = 0; i 1;crc = crc & 0x7fff;if (chk == 1) crc = crc ^ 0xa001;crc = crc & 0xffff;}return crc;}// verify crc functionunsigned int chk_crc(unsigned char* buf, unsigned char len){unsigned char hi, lo;unsigned int i;unsigned int crc;crc = 0xffff;for (i = 0; i < len; i++){crc = calc_crc(*buf, crc);buf++;}hi = (unsigned char)(crc % 256);lo = (unsigned char)(crc / 256);crc = (((unsigned int)(hi)) << 8) | lo;return crc;}
3.5 主函数
int main(void){ cxx_system_init(); board_yoc_init(); switch_init(); uart_init(); /* subscribe */ event_subscribe(event_netmgr_got_ip, network_event, null); event_subscribe(event_netmgr_net_discon, network_event, null); aos_task_new_ext(&task_date_uart5, task_date_uart5, date_uart5_entry, null, 4096, aos_default_app_pri);// aos_task_new_ext(&uart5_proc, uart5_proc, data_proc, null, 1024, 30); app_wifi_init(); // while (1) { // if (is_mqtt_ready == 1) { // // read_consumer_data(); // printf(ok); // // aos_msleep(1000); // } // // test_uart(); // // aos_msleep(2000); // }}
04 问题汇总
uart的配置
在官方的gitbook中对驱动函数进行了详细地讲解并附有相关例程:文档首页 · gitbook (t-head.cn)
但并未对具体的底层配置修改进行说明,在一开始编写串口部分的代码时,一直未能成功初始化并调通串口,在工程师的帮助之下对d1 dock pro的底层配置有了一定的了解。这里以led_demo为例,演示如何在此基础之上成功配置uart5。
改动1
改动2
改动3
改动4
做完以上三处改动,即可参考uart · gitbook (t-head.cn)中的使用示例对uart5进行验证,注意需要先 csi_pin_set_mux(pb4, pb4_uart5_tx); 和 csi_pin_set_mux(pb5, pb5_uart5_rx);
05 项目总结
“我们对‘碳中和’比较感兴趣,学校也鼓励我们探索交叉学科,我负责系统架构搭建和区块链技术,另外两位队员负责硬件编程及网页编程。从最初简单的能源物联网演示,到利用区块链技术实现m2m自主交易,我们做了很多讨论和尝试,终于在risc-v平台上跑通了程序!”“萌新队”队长、华东师范大学大四学生龚丹妮说。
基于4G物联网网关的二次供水泵站远程监控解决方案
汽车厂商如何缓解芯片供应的紧张局面?
送餐机器人可以优化就餐体验和提高服务效率
无人机热红外大范围区域影像的数据处理和温度分析
Google Glass项目遭谷歌关停的原因分析
以RISC-V架构的D1 Dock Pro和D1 Nezha开发板
中国移动坚持稳中求进、创新发展,确定了新阶段的战略目标
什么是碳化硅SiC?碳化硅的特性分析
700MA-(265V转24V)开关非隔离方案-AH8665
苹果在中国首设总规模为3亿美元的清洁能源基金,未来将推广至全球
中芯国际已将明导国际Calibre产品认证DFM签核参考平台
一加5最新消息:一加5性价比超小米6,你们期待吗?
关于多种LED灯内部电路分析
Weiking灌封型小体积DC-DC变换器WK6028**S-30G简析
恩智浦携手INTRINSIC-ID共同提高芯片安全标准
Instagram的AR试妆功能能否开启下一个风口
运算放大器datasheet参数详细中文解析
基于树莓派的面部识别系统设计方案
光模块与光端机的区别?两者在进行光电、电光转换时有什么不同?
力士乐叶片泵油封被击穿串油的原因