湖南省物联网应用创新竞赛(技能赛)---基于物联网的校园直饮水管理系统

 物联网赛题
1.应用场景描述

直饮水在校园和公共场所到处可见,如校园教室楼、图书馆、体育馆、食堂和宿舍,以及公共场所如火车站、机场、购物中心、公园等。直饮水给我们学习、工作和生活带来很多便利。然而在直饮水广泛使用的同时,我们也面临如下问题:

到了一个陌生地方,如何知道哪里有水喝?
所供饮水设备是否工作正常?水质是否合格?
作为直饮水管理部门,如何快速、准确、方便了解所管控设备的状况?
基于物联网的校园直饮水管理系统让饮水、饮水人、饮水机、饮水管理和关注者等所有与“饮水”关联的人和物之间实现饮水信息相连相息。

2. 竞赛题目

不限平台,搭建基于物联网的校园直饮水管理系统。假设系统由直饮水机、后端服务器、前端应用终端、以及直饮水机专用巡检装置组成,各部分功能作用如下:

A. 直饮水机


设直饮水机组成和控制如图所示:

1) 每台饮水机有一个唯一的ID号, 如:ID1=2001 ID2=2002等;。

2) 饮水机可以读取学生校园卡 ( RFID卡,数量2个及以上)卡号CardNumber,只有当合法学生校园卡放置在读卡区(比赛场景仅要求读面到校园卡号,无需核实其合法性),饮水机才提供饮水服务;

3) 每台饮水机有“复位”、“暂停”“正常”三种状态,其工作能被远皮远程后台控制: a)饮水机在 “复位”、“暂停”状态时不提供饮水服务,仅“正常状态提供饮水服务;

b) 饮水机每次上电后进入“复位"并向后台发送复位消息。后台收到复位消息后,自动执行:

向饮水机发送系统时钟(时分秒),以帮助饮水机同步时间(饮水机后续计时可由本地处理);

根据系统设定情况向饮水机发出“正常”或“暂停”指令。

4) 常温水箱内部设有高水位(Wh)、 低水位(W1)检测传感器(竞赛场景可用接近开关、红外开关、微动开关、或按键等代替):

a. “正常”状态时,当水位低于Wl时自动启动加压水泵M (竞赛中用电机代替),经M加压的高压水通过净化装置净化后流入常温水箱;当水位高于Wh时加压泵M停止工作。

b. 如果水位传感器状态指示:低于WI、同时高于Wh,判断为设备不正常,系统应提示设备故障(现场显示故障,蜂鸣器循环发“响0.4秒、停0.6秒”报警声,并向后台发出设备故障代码“Error1”);

5) TDS ( 水中固体物质含量指标,单位ppm)是衡量水质的一一个重要指标。TDS测定与测量探头设计、测量电路、被测水温、标定方法等因素相关,具体到水质测量一般可简化为水温和电压的测量。例如:

TDS=110*(Vcc-V)/V)*(1+0.02*(T-25))
是一款水质传感器的TDS计算公式。假设在水源(净化前)和常温水箱(净化后)两处测定TDS以监测饮水机工作状况和饮水水质情况,并假设被测水温T=25°C、水质测量电路供 电电压Vcc=5 (伏):
a. 水质计算公 式为:

水源水质: TDS1=110*((Vcc-V1)/V1)(V1不等于0时)

饮水水质: TDS2=110*(Vcc-V2)/V2) (V2 不等于0时)

式中: V1、V2为水源和常温水箱两处水质探测得到的电压(竞赛场景用电压代替)。b)当水质TDS2值大于100ppm表示饮水水质不合格(现场显示警告,蜂鸣器循环发“响0.1秒、停0.9秒”警告声,并向后台发出警告代码“Warning1");

6) 饮水量由流量计F (竞赛场景可用编码器、按键等代替,每一个转动量、或脉冲计数代表一定流量饮水)测定;

7) 热饮水加热器P (可用指示灯代替)受热饮水温度T2自动控制:
a) 供热饮水时,当T2低于设定值TH时加热、高于TH时停止加热。
b)TH可本地或经移动终端尚设定和改变。

8) 电磁水阀s1或s2 (竞赛中电磁水阀可用指示灯代替,设常温饮水和热饮水共用一个出水口,控制常温饮水或热饮水开闭,S1和S2不能同时“开”(即不能同时出常温水和热)。

9) 按键K1 (常温水)和K2 (热水)操控饮水机出水
a)停止出水(S1闭、S2闭)情况下:

按下K1出常温水,松开K1维持出常温水状态不变:
或按下K2出热水,松开K2维持出热水状态不变:

b) 出常温水(S1开、S2闭)时:

按下K1停止出水;
或按下K2转换成出热水;

c) 出热水(S1闭、S2开)时:

按下K1转换成出常温水;
或按下K2停止出水;

d) 一次出水量(流量计F计数值)超过一定流量值时,也自动停止出水。

10) 饮水机上能够显示(同时、分时、切换均可):

a) 本地时间(时分秒)

b) 水温(T1、T2)

c) 水质(TDS1、 TDS2)

11) 饮水机具备与后台服务器通信功能,将下列信息传到后台服务器供管理和应用:
a) 饮水机ID;
b) 饮水学生校园卡卡号(CardNumber);
c)饮水时间(时分秒)
d)饮水量
e)故障时设备故障代码“Error1”
f)警告时警告代码“Warning1”。

12) 挑战性功能1:当饮水机临时断电,本地或经移动终端设置的热水控制参数TH,在饮水机复电后仍然自动有效;

13) 挑战性功能2:当饮水机与后台服务器“断网”,联网后能够将“断网”期间发生的相关饮水事件数据不丢失地传送至后端服务器。竞赛申假设“断网”期间发生的饮水事件不小于2次。

B. 后端服务器
构建校园直饮水信息物联网后台服务器,支撑校园直饮水信息收集、储存、管理、应用。竞赛题目要求后台服务器:
1) 与各饮水机之间建立通信联系;

2) 收到某饮水机“复位”消息后,向饮水机回送系统时钟(时分秒) ,系统设定情况向饮水机发出正常”或“暂停”指令(参见A3.b)

3) 后台服务器数据或数据库包含:

a)饮水机位置信息, 如:

1D=2001位置:图书馆、或其它
1D-2002位置:教学楼、或其它

b)饮水机工作属性:

复位:(上电未与服务器连通时状态、不提供饮水服务)
暂停: (该饮水机因故暂停工作、不提供饮水服务)
正常: (该饮水机正常工作)

4) 接收、记录饮水机发出的各种饮水信息(参见A.11);

5) 给应用前端(移动端用户、计算机用户等)提供查询、统计和控制操作等。

C. 前端应用终端
校园直饮水信息系统前端应用如通过网络终端(如计算机)或移动终端(如智能手机App)提供的应用(竞赛作品简化,不区分饮水用户和管理用户),要求:

1)选择实现:移动终端(智能手机App)或网络终端(网页浏览器),二选一即可,移动终端优先;

2)应用终端上可修改后台服务器上饮水机工作属性(“正常”或“暂停”)。修改后,后台服务器应根据工作属性对饮水机进行同步控制;

3)应用终端上可查询全部饮水机(不少于2台)位置、工作状态及水质信息;4)应用终端上可查询某饮水机某段时间内输出的饮水总量;

5)应用终端上可查询某学生某段时间内在所有饮水机(不少于2台)上的饮水总量;6)饮水机“故障”、或“报警”时,应用终端上同步显示。

D. 巡检装置
专用于饮水机维护维修的移动装置。当巡检人员携带该巡检装置到达饮水机附近时,可与饮水机进行信息交互,方便维护维修人员快速了解饮水机状况,要求:

1)与饮水机之间不经过后台服务器、公共通信网络,仅巡检装置与饮水机之间现场点对点、无线方式通信;

2)巡检设备可显示连接的饮水机: ID 号,水质检测电压V1、V2,报警信息。

 

一、思维导图


1. 处理模块
以Stm32开发板作为中心节点,还要两块Stm32开发板作为子节点,外接多路传感器,分别采集实时数据,打印在oled显示屏上。

2. 服务器
可以选择Linux平台作为服务器,通过MQTT     将Stm32上采集到的数据发送至服务器上然后解析出数据,插入本地数据库。

3. Android或Web
①用Java spring boot+mysql开发Android,通过查看服务器中的数据库更新,发现异样可通过服务器进而控制Stm32端的操作。
②用uniapp+echarts开发Android,通过查看服务器中的数据库更新,发现异样可通过服务器进而控制Stm32端的操作。
③用vue +element +echarts来编写Web开发,通过查看服务器中的数据库更新,发现异样可通过服务器进而控制Stm32端的操作。

4. 巡检装置
外加一块Stm32板子,通过zigbee通信 查看各处理模块的状态

二、硬件选择

饮水机控制模块: 3块Stm32开发板,3块zigbee模块
冷、热水显示: 三色LED灯(红灯表示热水,绿灯表示冷水)
温度、湿度: DHT11
光照强度: BH1750
校园卡号: RFID
电泵:直流电机
通信模块: ESP8266
报警: 蜂鸣器
热冷水切换: 矩阵按键
电压、电流: ADC
显示屏:0.96寸OLED、串口助手、

三、STM32端


本篇采用的模块有:

RFID(串口通信)
ESP8266(串口通信)

zigbee(串口通信)
bh1750(IIC通信)
OLED(IIC通信)
Timer(调制电机速度)
Flash (断网重传)
Beep (报警)
LED (状态显示)
ADC (测电压)
Usart (数据传输)
Key (控制逻辑)

1. RFID

 /*********接受到来自串口2的消息***************/
    if(usart_recev2.end_flag == 1)
    {
      usart_recev2.end_flag = 0;
      uint8_t i;
      if(Send_ID_flag == 0)
      {
        status1 = RxCheckSum(usart_recev2.buf, usart_recev2.len); //对接收到的数据校验
        status2 = usart_recev2.buf[4];
        if ((usart_recev2.buf[0] == 0x01) && (usart_recev2.buf[2] == 0xa1)&&(status1 == STATUS_OK)&&(status2 == STATUS_OK)) //判断是否为读卡号返回的数据包
        {
          for(i=7;i<11;i++)
          {
            IDcard = IDcard<<8|usart_recev2.buf[i];
          }
          Send_ID_flag = 1;//读数据
          printf_usart1("U2rec %08x,   U3Send %s\r\n",IDcard,Send_Message);
        }
        else IDcard = 0;
      }
      else if(Send_ID_flag == 1)
      {
        status1 = RxCheckSum(usart_recev2.buf, usart_recev2.len); //对接收到的数据校验
        status2 = usart_recev2.buf[4];	 //获取返回包状态
        if ((usart_recev2.buf[0] == 0x01) && (usart_recev2.buf[2] == 0xa3)&&(status1 == STATUS_OK)&&(status2 == STATUS_OK)) //判断是否为读块数据返回的数据包
        {
          IDdate[0] = usart_recev2.buf[5];
          IDdate[1] = usart_recev2.buf[6];
          printf_usart1("ID数据为:%d,%d\r\n",IDdate[0],IDdate[1]);
          Send_ID_flag = 2;
        }
        if(IDdate[1] == 0x11) 
        {
          if(IDdate[0] == 0x01) ID_state = 2;
          else if(IDdate[0] == 0x00) ID_state = 1;
        }
        else ID_state = 3;
      }
       uart2_rec_buf_clean();
        
    }

2.zigbee--stm32代码

static uint8_t last_Sec,cont;
		  if(last_Sec!=GetTime.Seconds)
		  {
        char message1[526];
			if(++cont>=3)//3s发送一次
			{
          cont=0;
        Message_flag ^= 1;
        
	sprintf(Send_Message,Send_Message_Zigbee,\
            Mode,\
            LEDpwmA.LED_nt,\
            LEDpwmA.LED_NF,\
            ST,\
            GetTime.Hours,\
            GetTime.Minutes,\
            GetTime.Seconds);
      printf_usart3("%s",Send_Message);//发给协调器
      printf_usart1("Uart3Send %s",Send_Message);//打印到串口查看是否错误
           }
}

3.bh1750

/**
 * @brief  向BH1750发送一条指令
 * @param  cmd —— BH1750工作模式指令(在BH1750_MODE中枚举定义)
 * @retval 成功返回HAL_OK
*/
static uint8_t BH1750_Send_Cmd(BH1750_MODE cmd)
{
    return HAL_I2C_Master_Transmit(&bh1750_i2c, BH1750_ADDR_WRITE, (uint8_t*)&cmd, 1, 0xFFFF);
}


/**
 * @brief  从BH1750接收一次光强数据
 * @param  dat —— 存储光照强度的地址(两个字节数组)
 * @retval 成功 —— 返回HAL_OK
*/
static uint8_t BH1750_Read_Dat(uint8_t* dat)
{
    return HAL_I2C_Master_Receive(&bh1750_i2c, BH1750_ADDR_READ, dat, 2, 0xFFFF);
}


/**
 * @brief  将BH1750的两个字节数据转换为光照强度值(0-65535)
 * @param  dat  —— 存储光照强度的地址(两个字节数组)
 * @retval 成功 —— 返回光照强度值
*/
static uint16_t BH1750_Dat_To_Lux(uint8_t* dat)
{
    uint16_t lux = 0;
    lux = dat[0];
    lux <<= 8;
    lux |= dat[1];
    lux = (int)(lux / 1.2);

    return lux;
}
/**
 * @brief  返回光照强度值
 * @param  无
 * @retval 成功 —— 返回光照强度值
*/
uint16_t Get_BH1750_Value(void)
{
    uint8_t dat[2] = {0};     //dat[0]是高字节,dat[1]是低字节
    uint16_t lux;

    if(HAL_OK != BH1750_Send_Cmd(ONCE_H_MODE))
    {
        return 0;
    }
    HAL_Delay(120);
    if(HAL_OK != BH1750_Read_Dat(dat))
    {
        return 0;
    }

    lux = BH1750_Dat_To_Lux(dat);

    return lux;
}

4.OLED

void OLED_Init(void)
{

	
	OLED_RES_Clr();
	HAL_Delay(200);
	OLED_RES_Set();
	
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
	OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x30,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_Clear();
	OLED_WR_Byte(0xAF,OLED_CMD);
}

5.ADC

/***********水质读取*************/
void Read_TDS(void)
{
  if(ReadTDS_flag == 1)
  {
   ReadTDS_flag = 0;
   HAL_ADC_Start(&hadc1);     //启动ADC转换
   HAL_ADC_PollForConversion(&hadc1, 50);   //等待转换完成,50为最大等待时间,单位为ms
   if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
   {
      V1 = HAL_ADC_GetValue(&hadc1);   //获取AD值
      V1=V1*3.3f/4096;
    }
    TDS1 = 100*((5-V1)/V1);
    HAL_Delay(1000);
      
   HAL_ADC_Start(&hadc2);     //启动ADC转换
   HAL_ADC_PollForConversion(&hadc2, 50);   //等待转换完成,50为最大等待时间,单位为ms
   if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2), HAL_ADC_STATE_REG_EOC))
   {
      V2 = HAL_ADC_GetValue(&hadc2);   //获取AD值
      V2=V2*3.3f/4096;
      V2 = (V2-0.6)*(3.3/2.7);
     if(V2 < 0) V2 = 0;
   }
   if(V2 != 0)
     TDS2 = 100*((5-V2)/V2);
   
   if(TDS2 > 100)
   {
    Machine1.worn = 1;
   }
   else Machine1.worn = 0;
 }
}

6.zigbee数据传输

MT_UartInit();                  //串口初始化
  MT_UartRegisterTaskID(task_id); //注册串口任务
//  P0SEL &= 0x7f;                  //P0_7配置成通用io
  mytask_id = task_id; 

   RegisterForKeys(mytask_id); 
   MT_UartRegisterTaskID(mytask_id);

   line = osal_msg_allocate(LEN);
   len_rcv = 0;
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )//接收
{
  uint16 flashTime;

  switch ( pkt->clusterId )
  {
    case SAMPLEAPP_P2P_CLUSTERID:
      if ((SampleApp_NwkState == DEV_ZB_COORD))
      {
        HalUARTWrite(0, (pkt->cmd).Data, (pkt->cmd).DataLength); //输出接收到的数据
        HalUARTWrite(0, "\r\n", 2);         // 回车换行
      }
      break;    
    case SAMPLEAPP_PERIODIC_CLUSTERID:
      HalUARTWrite(0, (pkt->cmd).Data, (pkt->cmd).DataLength);
      break;

    case SAMPLEAPP_FLASH_CLUSTERID:
       HalUARTWrite(0, (pkt->cmd).Data, (pkt->cmd).DataLength);
      break;
  }
}

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值