功能概述
经过一段时间的探索,终于完成了一个基础版本的物联网网关。
物联网网关主要实现了以下功能:
采集HS3003的数据,获取温湿度,然后通过串口和DA16200 将数据使用mqtt协议发送到mqtt服务器上。整体的框图如下:
硬件连接图
整体连接如下图所示:
![整体连接图1.jpg]
其中,1处为开发板的I2C座子,和HS3003引脚正好对应,直接插上。2处为SWD口,一开始我使用CHLINKE连接,一直下载不进去程序,后来看到了群里小伙伴用stlink可以下载,就去某宝10几块买了一个,结果还真的能下载。不过要注意的是,stlink如果下载时无法识别型号,需要下载个STM32CubeProgrammer软件,更新下固件,就可以下载了。stlink的具体配置如下图:
3处为PMOD2口,这个PMOD口其实也就是串口,此处使用的uart0,将DA16200直接插上去即可,具体怎么连接可以参考我上一篇帖子。
4处也就是这个开发板的uart9了,这了自带了一个ch340,因此我们用这个串口来打印日志,来实现调试。
软件部分
想要完成这个物联网网关,需要满足两点:
1.代码编写
2.mqtt服务器
其中代码编写我放在后面说,主要说下mqtt服务器。
由于调试的有点晚了,阿里云需要审核3几天的审核时间,因此,我使用了然也物联的公用mqtt服务器,http://www.ranye-iot.net/,这个服务器配置还挺简单。
电脑端mqtt客户端我选择了mqtt.fx1.7.1,这个版本时免费的,后续的版本就要收费了。
还有就是mqtt协议的学习,我是跟着bilibili的[太极创客]老师学的,这老师讲的真心不错,通俗易懂,推荐学习mqtt的同学去听一下。
mqtt.fx1.7.1的安装我就不讲了,一直下一步就好了,主要讲下怎么配置。
因为我们目前只实现一个简单的mqtt收发,因此配置是很简单的,如下图所示:
其中,2为任务名,3为然也公用mqtt服务器的地址,4为client-id,client-id最好起一个不容易重复的,因为这是个公用服务器,重复的id可能会连接失败。
代码部分
代码部分,我参考了demo中的代码,进行了少量的修改,主要功能点代码有:
hs3003.c,这里主要实现了对i2c的初始化和启动,以及回调函数的编写,而具体获取温湿度值放在了wifidemo中,每获取一条记录,就会向mqtt发送数据
void g_comms_i2c_bus0_quick_setup(void)
{
fsp_err_t err;
err = R_SCI_I2C_Open(&hs3003_i2c3_ctrl, &hs3003_i2c3_cfg);
assert(FSP_SUCCESS == err);
}
void hs3003_i2c_callback(i2c_master_callback_args_t * p_args)
{
if (HS3003_CALLBACK_STATUS_SUCCESS == p_args->event)
{
hs3003_callback_status = HS3003_CALLBACK_STATUS_SUCCESS;
}
else
{
hs3003_callback_status = HS3003_CALLBACK_STATUS_REPEAT;
}
}
bap_uart.c,这里主要实现了对printf/scanf函数的重写,每次printf都会向uart9发数据
#include "bsp_debug_uart.h"
void bsp_uart_init(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SCI_UART_Open (&debug_uart_ctrl, &debug_uart_cfg);
assert(FSP_SUCCESS == err);
}
volatile bool uart_send_complete_flag = false;
volatile bool uart_recv_complete_flag = false;
volatile uint32_t uart_recv_char = '\0';
void debug_uart_callback (uart_callback_args_t * p_args)
{
switch (p_args->event)
{
case UART_EVENT_RX_CHAR:
{
R_SCI_UART_Write(&debug_uart_ctrl, (uint8_t *)&(p_args->data), 1);
break;
}
case UART_EVENT_TX_COMPLETE:
{
uart_send_complete_flag = true;
break;
}
default:
break;
}
}
#if defined __GNUC__ && !defined __clang__
int _write(int fd, char *pBuffer, int size);
int _write(int fd, char *pBuffer, int size)
{
(void)fd;
R_SCI_UART_Write(&g_uart0_ctrl, (uint8_t *)pBuffer, (uint32_t)size);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return size;
}
#else
int fputc(int ch, FILE *f)
{
(void)f;
R_SCI_UART_Write(&debug_uart_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return ch;
}
int fgetc(FILE *f)
{
(void)f;
while (uart_recv_complete_flag == false)
{}
uart_recv_complete_flag = false;
return (int)uart_recv_char;
}
da16200AT.c
这个文件中主要定义了所有wifi设置,初始化,mqtt设置,收发订阅所要用到的AT命令,以及AT命令发送函数等,基本也是照着官方demo改几个字段就好了。由于文件太长,此处就只粘贴主要命令了。
da16200_at_cmd_set_t g_da16200_cmd_set[] =
{
[DA16200_AT_CMD_INDEX_ATZ] =
{
.p_cmd = (uint8_t *) "ATZ\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_10,
.retry_delay = DA16200_DELAY_500MS
},
[DA16200_AT_CMD_INDEX_ATE] =
{
.p_cmd = (uint8_t *) "ATE\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_32,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_ATF] =
{
.p_cmd = (uint8_t *) "ATF\r\n",
.p_success_resp = (uint8_t *) "DONE",
.max_resp_length = DA16200_STR_LEN_128,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_500MS
},
[DA16200_AT_CMD_INDEX_AT_TMRFNOINIT] =
{
.p_cmd = (uint8_t *) "AT+TMRFNOINIT=0\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_32,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[DA16200_AT_CMD_INDEX_AT_WFMODE] =
{
.p_cmd = (uint8_t *) "AT+WFMODE=0\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_32,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_RESTART] =
{
.p_cmd = (uint8_t *) "AT+RESTART\r\n",
.p_success_resp = (uint8_t *) "DONE",
.max_resp_length = DA16200_STR_LEN_128,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_1000MS
},
[DA16200_AT_CMD_INDEX_AT_WFJAP] =
{
.p_cmd = (uint8_t *) "AT+WFJAP=35_102,4,1,990351102\r\n",
.p_success_resp = (uint8_t *) "+WFJAP:1",
.max_resp_length = DA16200_STR_LEN_128,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_2000MS
},
[DA16200_AT_CMD_INDEX_AT_TRTC] =
{
.p_cmd = (uint8_t *) "AT+TRTC=192.168.0.103,8000\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_128,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_1000MS
},
[DA16200_AT_CMD_INDEX_AT_SEND_DATA] =
{
.p_cmd = (uint8_t *) "AT+WFMODE=0\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_512,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_1000MS
},
[DA16200_AT_CMD_INDEX_AT_WFCC] =
{
.p_cmd = (uint8_t *) "AT+WFCC=CH\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_32,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_WFSAP] =
{
.p_cmd = (uint8_t *) "AT+WFSAP=Renesas_Wifi,3,1,12345678,1,CH\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_128,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_NWIP] =
{
.p_cmd = (uint8_t *) "AT+NWIP\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_1000MS
},
[ DA16200_AT_CMD_INDEX_AT_NWDHS] =
{
.p_cmd = (uint8_t *) "AT+NWDHS=1\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_NWDHR] =
{
.p_cmd = (uint8_t *) "AT+NWDHR=192.168.10.3,192.168.10.10\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_TRTS] =
{
.p_cmd = (uint8_t *) "AT+TRTS=80\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_TRSAVE] =
{
.p_cmd = (uint8_t *) "AT+TRSAVE\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_32,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_NWIP1] =
{
.p_cmd = (uint8_t *) "AT+NWIP\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_32,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_NWMQTCL ] =
{
.p_cmd = (uint8_t *) "AT+NWMQCL=1\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_128,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_200MS
},
[ DA16200_AT_CMD_INDEX_AT_NWMQBR ] =
{
.p_cmd = (uint8_t *) "AT+NWMQBR=test.ranye-iot.net,1883\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_500MS
},
[ DA16200_AT_CMD_INDEX_AT_NWMQTS ] =
{
.p_cmd = (uint8_t *) "AT+NWMQTS=1,simon-zf\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_500MS
},
[ DA16200_AT_CMD_INDEX_AT_NWMQTP ] =
{
.p_cmd = (uint8_t *) "AT+NWMQTP=simon-zfpub\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_500MS
},
[ DA16200_AT_CMD_INDEX_AT_NWMQMSG ] =
{
.p_cmd = (uint8_t *) "AT+NWMQMSG=simon test\r\n",
.p_success_resp = (uint8_t *) "OK",
.max_resp_length = DA16200_STR_LEN_64,
.retry = DA16200_RETRY_VALUE_5,
.retry_delay = DA16200_DELAY_500MS
}
};
dialog_wifi_demo.c
这个模块是在官方的demo基础上改的,基本上流程如下:
具体代码如下:
void dialog_wifi_demo(void)
{
static unsigned char operation = 0;
static uint8_t cnt=0;
fsp_err_t err;
uint8_t r_buf[4] = {0};
uint16_t humi, temp;
float tmp_f = 0.0;
float tmp_f2 = 0.0;
uint8_t data[2] = {0x00,0x00};
demo_sequence_t sequence = DEMO_SEQUENCE_1;
while(1)
{
switch(operation)
{
case 0:
g_ioport.p_api->pinWrite(g_ioport.p_ctrl, LED1, BSP_IO_LEVEL_HIGH);
printf("begin wifi init succes!\r\n");
wifi_init();
printf("begin wifi set succes!\r\n");
err = wifi_set();
printf("wifi set succes!\r\n");
err = wifi_mqtt_set();
printf("mqtt set succes!\r\n");
g_comms_i2c_bus0_quick_setup();
printf("i2c set succes!\r\n");
g_ioport.p_api->pinWrite(g_ioport.p_ctrl, LED1, BSP_IO_LEVEL_LOW);
if(err)
{
R_BSP_SoftwareDelay(2000,BSP_DELAY_UNITS_MILLISECONDS);
}
else
{
R_BSP_SoftwareDelay(2000,BSP_DELAY_UNITS_MILLISECONDS);
operation = 1;
}
break;
case 1:
cnt++;
printf("cal tmp and humi\r\n");
err = R_SCI_I2C_Write(&hs3003_i2c3_ctrl,data,1,0);
R_BSP_SoftwareDelay(40000,BSP_DELAY_UNITS_MICROSECONDS);
err = R_SCI_I2C_Read(&hs3003_i2c3_ctrl,&r_buf,4,1);
if(err == FSP_SUCCESS)
{
printf("0x%X,0x%X,0x%X,0x%X\n", r_buf[0], r_buf[1], r_buf[2], r_buf[3]);
printf("state:%x\n", r_buf[0] & RM_HS300X_MASK_STATUS_0XC0);
if ((r_buf[0] & RM_HS300X_MASK_STATUS_0XC0) != RM_HS300X_DATA_STATUS_VALID)
{
printf("转换时间不哆");
}
humi = (r_buf[0] & RM_HS300X_MASK_HUMIDITY_UPPER_0X3F) << 8 | r_buf[1];
temp = (r_buf[2] << 8 | (r_buf[3] & RM_HS300X_MASK_TEMPERATURE_LOWER_0XFC)) >> 2;
tmp_f = (float)humi;
tmp_f = (tmp_f * RM_HS300X_CALC_HUMD_VALUE_100) / RM_HS300X_CALC_STATIC_VALUE;
printf("humi: %.2f\n",tmp_f );
tmp_f2 = (float)temp;
tmp_f2 = ((tmp_f2 * RM_HS300X_CALC_TEMP_C_VALUE_165) / RM_HS300X_CALC_STATIC_VALUE) - RM_HS300X_CALC_TEMP_C_VALUE_40;
printf("temp: %.2f\n", tmp_f2);
}
printf("begin mqtt\r\n");
sprintf((char *)user_buffer,"AT+NWMQMSG=humi: %.2f--temp:%.2f\n",tmp_f,tmp_f2);
g_da16200_cmd_set[DA16200_AT_CMD_INDEX_AT_NWMQMSG].p_cmd = user_buffer;
printf("AT:%s",user_buffer);
TEST();
if(err)
{
cnt--;
R_BSP_SoftwareDelay(2000,BSP_DELAY_UNITS_MILLISECONDS);
operation = 0;
}
else
{
R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
g_ioport.p_api->pinWrite(g_ioport.p_ctrl, LED2, BSP_IO_LEVEL_HIGH);
R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
g_ioport.p_api->pinWrite(g_ioport.p_ctrl, LED2, BSP_IO_LEVEL_LOW);
}
break;
default:
operation = 0;
break;
}
}
}
以上就是代码部分了,最终实现的效果见置顶视频,即在mqtt.fx软件中可以实时看到采集的数据。
工程代码如下:
*附件:FSP_Project2.rar
任务感受
这个任务做到现在,只实现了最基础的一个mqtt服务器收发的网关。但在这个任务过程中,我学了挺多东西,包括keil工程的建立,FSP包的使用,以及DA16200的使用,还有mqtt协议。这是个非常大的收获。在这过程中,也多亏了·群里大佬的帮助,大佬写的文章给了我很多帮助,例如程序下载,激活DA16200等,在此向大佬表示感谢。
最后,这个任务还有很多优化的地方,比如增加mqtt服务器和客户端的交互,增加服务器收到数据的处理流程,以及增加采集的数据传感器等。这个我预计这周能完善下,现在先把任务提交下。