本項目以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文章
85瀏覽量
11121 -
開發板
+關注
關注
25文章
5032瀏覽量
97373 -
RISC-V
+關注
關注
45文章
2270瀏覽量
46129
原文標題:應用速遞 | 摘得頭獎的小隊究竟是做了什么項目?
文章出處:【微信號:芯片開放社區,微信公眾號:芯片開放社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論