本项目以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 variable
float 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 date
void read_consumer_data(void)
{
//send the instruction
unioncrc_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 verification
tx_buffer[7] = crc_now.byte[0];
ret = csi_uart_send_async(&g_uart, tx_buffer,8);
//wait until send finished
while(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 date
void 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 finished
rx_async_flag = 0;
if ((rx_buffer[0] == 0x01)) { //check the ID of the device
crc_now.word16 = chk_crc(rx_buffer, recieve_data_num - 2);//crc verification
if ((crc_now.byte[0] == rx_buffer[recieve_data_num - 1]) && (crc_now.byte[1] == rx_buffer[recieve_data_num - 2])) {
//parse voltage
Voltage_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 current
Current_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 power
Power_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 energy
Energy_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 factor
Pf_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 CO2
CO2_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 current
HZ = (((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.2MQTT电能数据上云
// 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 < LEN-1; i++) { sprintf(_str_data, "%.3f", sensor_data[i]); _str_data[strlen(_str_data)-1] = sep; strcat(post_str, _str_data); } sprintf(_str_data, "%.3f", sensor_data[LEN-1]); strcat(post_str, _str_data); err = mqtt_publish(client, pub_topic, post_str, strlen(post_str), qos, retain, mqtt_pub_request_cb, arg); if(err != ERR_OK) { printf("Publish err: %d ", err); } } void mqtt_do_connect(mqtt_client_t *client) { struct mqtt_connect_client_info_t ci; err_t err; ip4_addr_t ip_addr; IP4_ADDR(&ip_addr, 106, 14, 44, 95); /* Setup an empty client info structure */ memset(&ci, 0, sizeof(ci)); /* Minimal amount of information required is client identifier, so set it here */ ci.client_id = "wattnode1"; /* Initiate client and connect to server, if this fails immediately an error code is returned otherwise mqtt_connection_cb will be called with connection result after attempting to establish a connection with the server. For now MQTT version 3.1.1 is always used */ err = mqtt_client_connect(client, &ip_addr, MQTT_PORT, mqtt_connection_cb, 0, &ci); /* For now just print the result code if something goes wrong */ if(err != ERR_OK) { printf("mqtt_connect return %d ", err); } } static void mqtt_main_task(void *d) { printf("Enter mqtt_main_task "); // mqtt_client_t *client = mqtt_client_new(); client = mqtt_client_new(); if(client != NULL) { mqtt_do_connect(client); } } // EOF mqtt
3.3智能开关
// switch variable
const int SWITCH_PIN = PC1;
static csi_gpio_pin_t pin;
#define GPIO_CHECK_RETURN(ret)
do {
if (ret != CSI_OK) {
return -1;
}
} while(0);
//init the switch
void switch_init()
{
ret = csi_gpio_pin_init(&pin, SWITCH_PIN);
GPIO_CHECK_RETURN(ret);
/* Set output mode */
ret = csi_gpio_pin_dir(&pin, GPIO_DIRECTION_OUTPUT);
GPIO_CHECK_RETURN(ret);
}
// EOF switch
3.4串口处理部分
// uart variable
static csi_uart_t g_uart;
static volatile uint8_t rx_async_flag = 0;
static volatile uint8_t tx_async_flag = 0;
static uint8_t tx_buffer[140];
static uint8_t rx_buffer[140];
int recieve_data_num = 37;
#define DATE_UART_BAUDRATE 4800
#define DATE_UART_IDX 5
#define UART_CHECK_RETURN(ret)
do {
if (ret != CSI_OK) {
return -1;
}
} while(0);
// crc function
unsigned int calc_crc(unsigned char crcbuf, unsigned int crc);
unsigned int chk_crc(unsigned char* buf, unsigned char len);
//task
static aos_task_t task_date_uart5;
void date_uart5_entry()
{
while(1)
{
switch(recv_cmd){
case 0:
break;
case 1:
csi_gpio_pin_write(&pin, GPIO_PIN_HIGH);
recv_cmd = 0;
break;
case 2:
csi_gpio_pin_write(&pin, GPIO_PIN_LOW);
recv_cmd = 0;
break;
case 3:
read_consumer_data();
recv_cmd = 0;
break;
default:
break;
}//eof switch
aos_msleep(2000);
}//eof while
}//eof func
//callback function of uart
static void uart_event_cb(csi_uart_t *uart, csi_uart_event_t event, void *arg)
{
switch (event) {
case UART_EVENT_SEND_COMPLETE:
tx_async_flag = 1;
break;
case UART_EVENT_RECEIVE_COMPLETE:
rx_async_flag = 1;
break;
default:
break;
}//eof switch
}//eof func
void uart_init()
{
csi_pin_set_mux(PB4, PB4_UART5_TX);
csi_pin_set_mux(PB5, PB5_UART5_RX);
/* init uart, DATE_UART_IDX == 5 */
ret = csi_uart_init(&g_uart, DATE_UART_IDX);
UART_CHECK_RETURN(ret);
/* set uart baudrate */
ret = csi_uart_baud(&g_uart, DATE_UART_BAUDRATE);
UART_CHECK_RETURN(ret);
/* set uart format */
ret = csi_uart_format(&g_uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1);
UART_CHECK_RETURN(ret);
/* attach callback to uart device */
ret = csi_uart_attach_callback(&g_uart, uart_event_cb, NULL);
UART_CHECK_RETURN(ret);
}
// calculate crc function
unsigned int calc_crc(unsigned char crcbuf, unsigned int crc)
{
unsigned char i;
unsigned char chk;
crc = crc ^ crcbuf; for (i = 0; i < 8; i++)
{
chk = (unsigned char)(crc & 1);
crc = crc >> 1;
crc = crc & 0x7fff;
if (chk == 1) crc = crc ^ 0xa001;
crc = crc & 0xffff;
}
return crc;
}
// verify crc function
unsigned 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平台上跑通了程序!”“萌新队”队长、华东师范大学大四学生龚丹妮说。
审核编辑 :李倩
-
能源系统
+关注
关注
0文章
110浏览量
12016 -
开发板
+关注
关注
25文章
6140浏览量
113706 -
RISC-V
+关注
关注
48文章
2809浏览量
51977
原文标题:应用速递 | 摘得头奖的小队究竟是做了什么项目?
文章出处:【微信号:芯片开放社区,微信公众号:芯片开放社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
探索RISC-V在机器人领域的潜力
d1哪吒开发板的启动流程分析
WEMOS D1 R32 用Arduino IDE 点灯程序 及搭建
【匠芯创D133CBS KunLun Pi开发板试用体验】1、开发板开箱及介绍
【BPI-CanMV-K230D-Zero开发板体验】开箱以及开发板简介
Comake D1 开发板 快速开始
Comake PI D1开箱使用体验分享
匠芯创D133CBS RISC-V KunLun Pi V1.0开发板开发资料
搭载双核玄铁C908 RISC-V CPU,BPI-CanMV-K230D-Zero开发板试用
【免费试用】开发板评测大赛开启!OH 、RISC-V、Rockchip顶级开发板等你试用~
关税取消后,国产的这款RISC-V开发板能否称霸市场?进迭时空Muse Pi Pro开发板
请问TVP5158分辨率D1与HalfD1是如何转换的?
RISC-V架构及MRS开发环境回顾
SiFive 推出高性能 Risc-V CPU 开发板 HiFive Premier P550

以RISC-V架构的D1 Dock Pro和D1 Nezha开发板
评论