Hispark Wi-Fi IOT套件试用的最后一篇连载,将介绍我做的一个demo。使用的套件板子包括扩展底板、主控板子、显示板、环境监测板、红绿灯板、炫彩灯板。
一、主要功能
火灾报警,在检测到颗粒烟雾或者有毒气体时,发声报警并把报警状态发送到手机端;
防盗报警,在布防状态下,检测到人时,发声报警并把报警状态发送到手机端;
拥有手动、自动模式设置,布防、撤防状态设置功能。在手动模式下,可以人为进行设置布撤防状态;在手动模式下,会自动根据设定的布撤防时间进行布撤防。在布防下,才进行防盗检测;
具备联网功能,设备能够获取网络时间和最近3天的气候情况,并在显示屏上进行显示;
可以与手机进行数据传输,把温湿度情况、防火报警状态、防盗报警状态、手自动模式、布撤防状态发送手机APP端,可以接收手机APP下发的手自动模式、布撤防状态切换的命令;
具备灯开关控制功能,可以根据环境亮度,调节灯亮度;
可以设置自动模式下布撤防的时间;
可以通过按键或者手机APP端设置手自动模式、布撤防状态;
启动后会自动获取网络时间或者网络天气数据,也可以通过按键手动获取网络时间或者网络天气数据;
二、硬件使用情况介绍
主要使用到下面硬件资源
1、主控板
2、扩展底板
3、显示板
4、环境监测板
5、红绿灯板
6、炫彩灯板
三、主控IO资源分配
Hi3861的GPIO口不多,一共有15个IO口,但是IO口的复用功能较多。其中GPIO3、GPIO4用来作为调试串口,如果使用了串口功能,就不能使用GPIO3、4口作为其他功能。还需要注意的是,有些IO口是复用PWM通道和ADC通道功能。如果使用了该IO作为PWM功能,ADC通道功能就无法正常使用,应该是PWM会影响ADC正常使用。
下面是demo的IO分配表,GPIO3/GPIO4作为调试输出口,没有接其他器件。
四、软件设计
我把整个工程软件分为了七个任务,显示任务、按键控制任务、环境监测任务、灯控制任务、RTC任务、TCP数据发送任务、数据接收任务。
1、显示任务显示任务主要是控制OLED的显示。设置有六个显示界面,时间显示界面、现在天气情况显示界面、明天天气情况显示界面、后天天气显示界面、布撤防时间调整显示界面、设置模式界面。显示任务根据变量Now_Screen的值来选择显示什么界面。Now_Screen的值会在按键任务中进行改变。在获取网络时间或者网络天气数据时,会显示获取状态提示内容。
时间显示界面如下
现在天气情况显示界面
明天天气情况显示界面
后天天气情况显示界面
布撤防时间调整显示界面
设置模式显示界面
获取状态提示显示
任务代码如下
- static void OledTask(void *arg)
- {
- (void)arg;
- //Oled初始化
- GpioInit();
- OledInit();
- OledFillScreen(0x00);
- while (1)
- {
- //自动模式下,自动进行布撤防
- Zdbcf();
- //界面更新时,需要先删除界面显示的内容
- if(Now_Screen != Last_Screen)
- {
- Last_Screen = Now_Screen;
- OledFillScreen(0);
- Get_Status = GET_NORMAL;
- Last_Get_Status = GET_NORMAL;
- i=0;
- }
- //获取时间和天气数据状态提示,会隔一段时间进行清除
- if((Get_Status == GET_FAIL) ||(Get_Status == GET_SUC))
- {
- i++;
- if(i > 20)
- {
- Get_Status = GET_NORMAL;
- i=0;
- switch(Now_Screen)
- {
- case TIMESCREEN:
- OledClearString(16,7,"Sync time...",1); //清除提示
- break;
- case NOWSCREEN:
- case TOSCREEN:
- case ATOSCREEN:
- printf("clear oled i = %dn",i);
- OledClearString(0,7,"Get Weather...",1); //清除提示
- break;
- case BCTIMESCREEN:
- break;
- default :
- break;
- }
- }
- }
- switch (Now_Screen)
- {
- case TIMESCREEN:
- TimeScreenDisp(); //显示时间显示界面
- DefendDisplay();
- break;
- case NOWSCREEN:
- WeatherDisp(NOWSCREEN); //显示现在的天气显示界面
- break;
- case TOSCREEN: //显示明天天气显示界面
- WeatherDisp(TOSCREEN);
- break;
- case ATOSCREEN: //显示后天天气显示界面
- WeatherDisp(ATOSCREEN);
- break;
- case BCTIMESCREEN:
- OLED_ShowCHinese(32,0,14);//时
- OLED_ShowCHinese(48,0,15);//间
- OLED_ShowCHinese(64,0,18);//设
- OLED_ShowCHinese(80,0,19);//值
- sprintf((char *)TimeStr,"%02d:00-%02d:00",starttime,stoptime);
- OledShowString(20,4,(char *)TimeStr,2); //显示布撤防时间
- break;
- case BCADJUSTSCEEN:
- OLED_ShowCHinese(32,0,16);//模
- OLED_ShowCHinese(48,0,17);//式
- OLED_ShowCHinese(64,0,18);//设
- OLED_ShowCHinese(80,0,19);//值
- DefendDisplay(); //显示手自动模式,布撤防状态
- break;
- default:
- break;
- }
- usleep(100000);
- }
- }
复制代码
2、按键控制任务本demo中使用了三个按键,显示板上的两个模拟按键、红绿灯板的一个按键。显示板右边按键主要功能是切换界面,通过改变Now_Screen的值。Now_Screen可以取得值为
- //显示界面枚举类型
- typedef enum
- {
- TIMESCREEN=0, //时间显示界面,开机后的界面
- NOWSCREEN, //现在天气情况的显示界面
- TOSCREEN, //明天天气情况的显示界面
- ATOSCREEN, //后天天气情况的显示界面
- BCTIMESCREEN, //布撤防时间调整的显示界面
- BCADJUSTSCEEN, //设置手自动模式、布撤防状态的界面
- NOSCREEN, //无界面
- } SCREEN_STATUS;
复制代码
显示板左边按键和红绿灯板按键在不同的显示界面,功能是不一样的。
- 在时间显示界面时,显示板左按键为获取网络时间功能按键,红绿灯板按键为开关灯功能按键。
- 在现在、明天、后天天气情况的显示界面,显示板左按键为获取网络天气数据功能按键,红绿灯板按键无功能。
- 在布撤防时间调整显示界面,显示板左按键为调整布防时间功能按键,红绿灯板按键为调整撤防时间功能按键。
- 在设置模式显示界面,显示板左按键为切换手自动模式功能按键,红绿灯板按键为切换布撤防状态的功能按键。
按键都只能单次触发,按下后必须松开才能再一次触发功能。
任务代码如下
3、环境监测任务该任务主要功能是测量温度、湿度,获得可燃性气体传感器和人体红外感应器ADC值,并根据获得值进行火灾和防盗检查。获得的可燃气性气体值低于一定数值时,可判断有火灾或者可燃性气体泄漏,触发火灾报警,置位烟雾报警标志位,蜂鸣器发声。只要检测的数据高于设定的数据,报警状态则恢复为未报警状态。获得的人体红外感应器ADC数值超过一定值,则视为触发防盗报警。但是需要在布防状态下,才能进行防盗检测。防盗报警触发后,蜂鸣器发声报警,置位防盗报警标志位。一旦触发防盗报警,只有通过切换到撤防状态,才能把防盗报警状态恢复到未报警。
任务代码如下
- static void EnvironmentTask(void *arg)
- {
- (void)arg;
- uint32_t retval = 0;
-
- float gasSensorResistance = 0.0f;
-
- /*I2C IO口初始化化*/
- GpioInit();
- IoSetFunc(WIFI_IOT_IO_NAME_GPIO_13, WIFI_IOT_IO_FUNC_GPIO_13_I2C0_SDA);
- IoSetFunc(WIFI_IOT_IO_NAME_GPIO_14, WIFI_IOT_IO_FUNC_GPIO_14_I2C0_SCL);
- I2cInit(AHT20_I2C_IDX, AHT20_BAUDRATE);
-
- // 初始化AHT20
- uint8_t i=0;
- while (WIFI_IOT_SUCCESS != AHT20_Calibrate())
- {
- printf("AHT20 sensor init failed!rn");
- usleep(1000);
- i++;
- if(i>10)
- break;
- }
-
- //初始化Led灯
- Led_Init();
- //初始化蜂鸣器
- Beep_Init();
-
- while(1)
- {
- //AHT20开始测量
- retval = AHT20_StartMeasure();
- if (retval != WIFI_IOT_SUCCESS)
- {
- printf("trigger measure failed!rn");
- }
-
- //获取AHT20的测量结果,包括温度和湿度
- retval = AHT20_GetMeasureResult(&temperature, &humidity);
- if (retval != WIFI_IOT_SUCCESS)
- {
- printf("get humidity data failed!rn");
- }
-
- //烟雾(火灾)检测
- unsigned short data = 0;
- if (AdcRead(GAS_SENSOR_CHAN_NAME, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0)
- == WIFI_IOT_SUCCESS)
- {
- //数值转换
- float Vx = ConvertToVoltage(data);
- gasSensorResistance = 5 / Vx - 1;
-
- //检测到烟雾或者有毒气体
- if((gasSensorResistance < 11) && (ywflag ==0))
- {
- ywflag = 1; //置位烟雾报警标志位
- Beep_Start(); //蜂鸣器发声报警
- }
- else if((gasSensorResistance >=11) && (ywflag ==1)) //恢复每检测到烟雾状态
- {
- ywflag = 0; //复位标志位
- if(rtflag==0) //没有防盗报警时
- Beep_Stop(); //停止蜂鸣器发声
- }
-
- }
-
- //防盗检测
- unsigned short humandata = 0;
- if(bcflag) //在布防状态下,防盗检测功能才开启
- {
- if (AdcRead(HUMAN_SENSOR_CHAN_NAME, &humandata, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0)
- == WIFI_IOT_SUCCESS)
- {
- //布防功能开启下,检测到人。
- //注意一旦触发防盗报警,就算恢复到没检测到人状态,也是会一直发声报警,只有切换到撤防状态,才能复位防盗报警标志位。
- if((humandata>1500)&&(rtflag==0))
- {
- rtflag = 1; //置位防盗报警标志位
- Beep_Start(); //蜂鸣器发声报警
- }
- }
- }
- else
- {
- //切换到撤防状态,复位防盗报警标志位
- if((rtflag == 1) && (ywflag == 0)) //之前触发防盗报警,停止蜂鸣器发声。
- Beep_Stop();
- rtflag = 0;
- }
-
- sleep(1);
- Led_Set(GREEN,ON); //系统心跳灯开
- sleep(1);
- Led_Set(GREEN,OFF); //系统心跳灯关
- }
- }
复制代码
4、灯控制任务该任务主要任务是控制灯开关,灯随着环境亮度变化亮度。在灯开启情况下,会根据获得的光敏电阻ADC值来进行灯亮度的调节。使用PWM控制灯的亮度。根据标志ledflag来判断是需要开关还是关灯。ledflag值会在按键任务中进行设置。
任务代码如下
- //灯开关标志位,0为灯关,1为灯开
- uint8_t ledflag = 0;
- static void LightTask(void *arg)
- {
- (void)arg;
- //初始化IO
- GpioInit();
- IoSetFunc(RED_LED_PIN_NAME, WIFI_IOT_IO_FUNC_GPIO_1_PWM4_OUT);
- IoSetFunc(GREEN_LED_PIN_NAME, WIFI_IOT_IO_FUNC_GPIO_0_PWM3_OUT);
- IoSetFunc(BLUE_LED_PIN_NAME, WIFI_IOT_IO_FUNC_GPIO_8_PWM1_OUT);
- GpioSetDir(RED_LED_PIN_NAME, WIFI_IOT_GPIO_DIR_OUT);
- GpioSetDir(GREEN_LED_PIN_NAME, WIFI_IOT_GPIO_DIR_OUT);
- GpioSetDir(BLUE_LED_PIN_NAME, WIFI_IOT_GPIO_DIR_OUT);
- //初始化PWM
- PwmInit(WIFI_IOT_PWM_PORT_PWM4); // R
- PwmInit(WIFI_IOT_PWM_PORT_PWM1); // B
- PwmInit(WIFI_IOT_PWM_PORT_PWM3); // G
-
- while (1) {
- unsigned short data = 0;
- unsigned short duty = 0;
- //获取光敏电阻的值
- if (AdcRead(LIGHT_SENSOR_CHAN_NAME, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0)
- == WIFI_IOT_SUCCESS) {
- //根据值调整PWM波
- duty = PWM_FREQ_DIVITION * (unsigned int)data/ MAX_ADCVALUE;
- }
- //灯为开启状态,开始PWM
- if(ledflag)
- {
- PwmStart(WIFI_IOT_PWM_PORT_PWM4, duty, PWM_FREQ_DIVITION);
- PwmStart(WIFI_IOT_PWM_PORT_PWM1, duty, PWM_FREQ_DIVITION);
- PwmStart(WIFI_IOT_PWM_PORT_PWM3, duty, PWM_FREQ_DIVITION);
- }
- else
- {
- //灯为关闭状态,停止PWM
- PwmStart(WIFI_IOT_PWM_PORT_PWM4, 0, PWM_FREQ_DIVITION);
- PwmStart(WIFI_IOT_PWM_PORT_PWM1, 0, PWM_FREQ_DIVITION);
- PwmStart(WIFI_IOT_PWM_PORT_PWM3, 0, PWM_FREQ_DIVITION);
- PwmStop(WIFI_IOT_PWM_PORT_PWM4);
- PwmStop(WIFI_IOT_PWM_PORT_PWM1);
- PwmStop(WIFI_IOT_PWM_PORT_PWM3);
- }
- usleep(10000);
- }
- }
复制代码
5、RTC任务RTC任务主要是为了达到时间更新的功能。任务中每隔一秒,时间戳timedata加1。时间戳timedata1在显示任务的时间显示界面中会使用。通过时间戳转换函数,把时间戳转化为北京时间,则得到我们需要的实时时间。
任务代码如下
- uint32_t timedata=1608362368; //时间戳默认值
- static void RtcTask(void *arg)
- {
- (void)arg;
- while(1)
- {
- //每隔一秒,时间戳加1
- timedata++;
- sleep(1);
- }
- }
复制代码
6、TCP数据发送任务进入该任务后,先获取网络时间和网络天气数据,到达系统启动后获取时间和天气数据的功能。如果与TCP服务器连接为已连接状态下,发送数据到服务器端,服务器会把数据转发手机APP端。如果数据发送失败,则视为连接断开,在连接断开情况下,会尝试与服务器进行连接。上传到服务器的数据有温度、湿度、手自动模式、布撤防状态、火灾报警、防盗报警情况。
任务代码如下
- //数据发送任务
- static void TcpSendTask(void *arg)
- {
- (void)arg;
- sleep(3);
- //先获取时间和天气数据
- getNtpTime();
- getWeather();
- uint8_t i = 0;
-
- //尝试与与远程服务器进行连接
- while(1){
- if(TcpConnect())
- {
- printf("Tcp Connect Sucn");
- break;
- }
- else{
- i++;
- }
- if(i>10) //超过十次则视为连接不成功,跳出
- break;
- }
- if(i>10)
- printf("Tcp Connect failn");
-
- while(1)
- {
-
- if(connect_status == CONNECTED)
- {
- //发送数据到远程服务器
- sprintf(sendData,"T:%02dH:%02dZ:%01dB:%01dD:%01dY:%01d",
- (uint32_t)temperature,(uint32_t)humidity,(uint32_t)zdflag,(uint32_t)bcflag,(uint32_t)rtflag,(uint32_t)ywflag);
- //如果发送不成功,则视为连接已经断开
- if(!TcpSend(sendData,sizeof(sendData)-1)){
- connect_status = DISCONNECTED;
- TcpDisconnect(); //主动断开与远程TCP服务器的连接
- }
- }
- else{
- //在与远程服务器为未连接状态,重新建立连接
- if(Get_Status == GET_NORMAL){ //没有在获取时间或者天气数据,则重新尝试与远程服务器建立连接
- if(TcpConnect()){
- printf("Tcp Connect Sucn");
- }
- }
- }
- sleep(2);
- }
- }
复制代码
7、TCP数据接收任务该任务中,如果与服务器连接为已连接状态情况下,则使用函数接收服务器下发的数据,数据接收函数使用超时机制,超过一定时间没接收到数据则退出,不会一直阻塞直到接收到数据。根据下发的数据设置手自动模式、布撤防状态。
任务代码如下
- //远程服务器数据接收任务
- static void TcpRevTask(void *arg){
- (void)arg;
- while(1)
- {
- //为已连接状态,接收数据
- if(connect_status == CONNECTED)
- {
- if(TcpRev()){
- //成功接收到手机端发送的数据
- if(strcmp(revData,"s")==0) //设置手动模式
- {
- zdflag = 0;
- }
- else if(strcmp(revData,"z")==0) //设置自动模式
- {
- zdflag = 1;
- }
- else if(strcmp(revData,"b")==0) //设置布防状态
- {
- bcflag =1;
- }
- else if(strcmp(revData,"c")==0) //设置撤防状态
- {
- bcflag =0;
- }
- }
- }
- usleep(10000);
- }
- }
复制代码
五、总结
这一次HiSpark Wi-Fi IoT 套件试用暂时告一段落了,还是感谢电子发烧友和润和给的这次使用机会。我最后做的小DIY Demo,基本也全部使用完套件的板子,除NFC板没使用。主控板的IO口资源还是挺有限的,IO口基本使用完了。这次的demo还是有很多需要改进地方,例如操作改变连接网络,无法修改连接的服务器等。