本項目以RISC-V架構(gòu)的D1 Dock Pro和D1 Nezha開發(fā)板為硬件平臺,應(yīng)用物聯(lián)網(wǎng)和區(qū)塊鏈技術(shù),設(shè)計開發(fā)一套分布式能源智慧管理小型示范系統(tǒng),在該系統(tǒng)上實現(xiàn)能源生產(chǎn)和消費數(shù)據(jù)的實時監(jiān)測。該項目在“玄鐵杯第二屆RISC-V應(yīng)用創(chuàng)新大賽”活動中榮獲一等獎。
關(guān)于分布式能源智慧管理和M2M交易系統(tǒng)的技術(shù)細(xì)節(jié),請看本文詳細(xì)介紹。
01項目介紹
能源和環(huán)保是關(guān)乎人類未來的重要課題。為實現(xiàn)碳中和目標(biāo),大力發(fā)展可再生清潔能源以替代傳統(tǒng)化石能源,提高能源系統(tǒng)監(jiān)控和消費的智能化水平,是可行的重要途徑之一。本項目以RISC-V架構(gòu)的D1 Dock Pro和D1 Nezha開發(fā)板為硬件平臺,應(yīng)用物聯(lián)網(wǎng)和區(qū)塊鏈技術(shù),設(shè)計開發(fā)一套分布式能源智慧管理小型示范系統(tǒng),在該系統(tǒng)上實現(xiàn)能源生產(chǎn)和消費數(shù)據(jù)的實時監(jiān)測。
02技術(shù)方案
項目使用 D1 Dock Pro 開發(fā)板設(shè)計開發(fā)一款專用網(wǎng)關(guān),實時采集電池控制器、氣象環(huán)境傳感器等其它傳感器的數(shù)據(jù),并通過無線通信方式(WiFi)以HTTP協(xié)議或MQTT協(xié)議將傳感器數(shù)據(jù)上傳至物聯(lián)網(wǎng)后臺。
??
圖1.專用網(wǎng)關(guān)示意

圖2.專用網(wǎng)關(guān)實物圖
智能開關(guān)用于能源消費端,實現(xiàn)對能源消費者(電器負(fù)載)的供電控制、電能消費數(shù)據(jù)的采集和傳輸?shù)裙δ堋T撝悄荛_關(guān)基于 D1 Dock Pro 開發(fā)板進(jìn)行設(shè)計開發(fā),通過開發(fā)板的I/O口控制繼電器、UART接收電能計量模塊的數(shù)據(jù)。設(shè)計一個擴(kuò)展電路板與開發(fā)板配合使用,擴(kuò)展電路板集成電能計量模塊、繼電器等。本文設(shè)計的智慧開關(guān)的功能主要是控制電器開關(guān)與計量電器用電參數(shù)以及環(huán)境參數(shù)并上傳到云端服務(wù)器。

圖3.智能開關(guān)示意圖

圖4.智能開關(guān)實物圖
03核心業(yè)務(wù)代碼
3.1智能開關(guān)電能采集分析
// 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電能數(shù)據(jù)上云
// 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智能開關(guān)
// 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主函數(shù)
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中對驅(qū)動函數(shù)進(jìn)行了詳細(xì)地講解并附有相關(guān)例程:文檔首頁· GitBook (t-head.cn)
但并未對具體的底層配置修改進(jìn)行說明,在一開始編寫串口部分的代碼時,一直未能成功初始化并調(diào)通串口,在工程師的幫助之下對D1 dock pro的底層配置有了一定的了解。這里以led_demo為例,演示如何在此基礎(chǔ)之上成功配置uart5。
改動1

改動2

改動3

改動4

做完以上三處改動,即可參考UART·GitBook(t-head.cn)中的使用示例對uart5進(jìn)行驗證,注意需要先csi_pin_set_mux(PB4,PB4_UART5_TX);和csi_pin_set_mux(PB5,PB5_UART5_RX);
05項目總結(jié)
“我們對‘碳中和’比較感興趣,學(xué)校也鼓勵我們探索交叉學(xué)科,我負(fù)責(zé)系統(tǒng)架構(gòu)搭建和區(qū)塊鏈技術(shù),另外兩位隊員負(fù)責(zé)硬件編程及網(wǎng)頁編程。從最初簡單的能源物聯(lián)網(wǎng)演示,到利用區(qū)塊鏈技術(shù)實現(xiàn)M2M自主交易,我們做了很多討論和嘗試,終于在RISC-V平臺上跑通了程序!”“萌新隊”隊長、華東師范大學(xué)大四學(xué)生龔丹妮說。
審核編輯 :李倩
-
能源系統(tǒng)
+關(guān)注
關(guān)注
0文章
114瀏覽量
12210 -
開發(fā)板
+關(guān)注
關(guān)注
26文章
6291瀏覽量
118141 -
RISC-V
+關(guān)注
關(guān)注
48文章
2886瀏覽量
53025
原文標(biāo)題:應(yīng)用速遞 | 摘得頭獎的小隊究竟是做了什么項目?
文章出處:【微信號:芯片開放社區(qū),微信公眾號:芯片開放社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
探索RISC-V在機(jī)器人領(lǐng)域的潛力
【CIE全國RISC-V創(chuàng)新應(yīng)用大賽】MUSE Pi Pro開發(fā)板ROS系統(tǒng)
全志D1開發(fā)板(哪吒 RISCV64)開箱評測
d1哪吒開發(fā)板的啟動流程分析
WEMOS D1 R32 用Arduino IDE 點燈程序 及搭建
【CIE全國RISC-V創(chuàng)新應(yīng)用大賽】+基于MUSE Pi Pro的3d激光里程計實現(xiàn)
【匠芯創(chuàng)D133CBS KunLun Pi開發(fā)板試用體驗】1、開發(fā)板開箱及介紹
【BPI-CanMV-K230D-Zero開發(fā)板體驗】開箱以及開發(fā)板簡介
Comake D1 開發(fā)板 快速開始
Comake PI D1開箱使用體驗分享
星宸Comake PI D1開箱測評
匠芯創(chuàng)D133CBS RISC-V KunLun Pi V1.0開發(fā)板開發(fā)資料
搭載雙核玄鐵C908 ?RISC-V CPU,BPI-CanMV-K230D-Zero開發(fā)板試用
【免費試用】開發(fā)板評測大賽開啟!OH 、RISC-V、Rockchip頂級開發(fā)板等你試用~
關(guān)稅取消后,國產(chǎn)的這款RISC-V開發(fā)板能否稱霸市場?進(jìn)迭時空Muse Pi Pro開發(fā)板
以RISC-V架構(gòu)的D1 Dock Pro和D1 Nezha開發(fā)板
評論