基于物联网的校园直饮水管理系统(2022年湖南省物联网应用创新竞赛(技能赛))
文章目录
- 物联网赛题
- A. 直饮水机
- B. 后端服务器
- C. 前端应用终端
- D. 巡检装置
- 一、思维导图
- 二、硬件选择
- 三、STM32端
- 四、服务器端
物联网赛题
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开发板作为中心节点,外接多路传感器,分别采集实时数据,打印在LCD显示屏上。
2. 服务器
①可以选择Linux平台作为服务器,通过TCP将Stm32上采集到的数据发送至服务器上然后解析出数据,插入本地数据库。
②可以选择用Qt开发 作服务器,并连接数据库,同样将Stm32传来的数据插入数据库中。
3. Android或Web
①可以用Java开发Android,通过查看服务器中的数据库更新,发现异样可通过服务器进而控制Stm32端的操作。
②可以用Qt开发Android,通过查看服务器中的数据库更新,发现异样可通过服务器进而控制Stm32端的操作。
③可以用JSP来编写Web开发,通过查看服务器中的数据库更新,发现异样可通过服务器进而控制Stm32端的操作。
4. 巡检装置
①可以外加一块Stm32板子,通过LoRa通信 查看各处理模块的状态
②可以通过蓝牙模块在手机上直接查看
二、硬件选择
以该赛题为例。
- 饮水机控制模块: Stm32开发板(原子哥的战舰、野火的指南针等),
- 冷、热水显示: LED灯(红灯表示热水,绿灯表示冷水)
- 温度、湿度: DHT11
- 温度、湿度、光照强度、海拔、压强: GY39
- 校园卡号: RFID
- 电泵: L298n、直流电机
- 通信模块: ESP8266、蓝牙、LoRa
- 报警: 蜂鸣器
- 热冷水切换: 按键
- 电压、电流: ADC
- 显示屏: LCD、OLED、串口助手、终端
- 以及若干按键、开关、LED灯、万用表、电烙铁、电池、杜邦线等等。
三、STM32端
本篇采用的模块有:
- RFID(串口通信)
- GY39(IIC通信)
- ESP8266(串口通信)
- OLED(IIC通信)
- Timer(调制电机速度)
- Flash (断网重传)
- Beep (报警)
- LED (状态显示)
- ADC (测电压)
- Usart (数据传输)
- Key (控制逻辑)
1. RFID
unsigned char status;unsigned char i;Cmd_Read_Id[5] = 0x01;TxCheckSum(Cmd_Read_Id,Cmd_Read_Id[1]); //计算校验和Uart3_Send_Data(Cmd_Read_Id,Cmd_Read_Id[1]); //发送读卡号ID命令Delay(1600000);//等待模块返回数据,大于150MSif(Rx3Flag == 1){ Rx3Flag = 0;status = RxCheckSum(Uart3RxBuf,Uart3RxBuf[1]);//对接收到的数据校验if(status != 0) //判断校验和是否正确{return 1;}status = Uart3RxBuf[4];if(status != 0) //判断是否正确的读到卡{return 1;}if((Uart3RxBuf[0] == 0x01)&&(Uart3RxBuf[2] == 0xa1))//判断是否为读卡号返回的数据包{rfid_id=0;for(i=0;i<6;i++)//获取卡号ID,6字节 {idout[i] = Uart3RxBuf[i+5];//从数组的第5个字节开始为卡号,长度为6字节Number=Uart3RxBuf[5+i];rfid_id=rfid_id*10+Number;}return rfid_id; //成功返回}} return 1; //失败返回1
2. LCD
void LCD_Init(void); //初始化
void LCD_DisplayOn(void); //开显示
void LCD_DisplayOff(void); //关显示
void LCD_Clear(u16 Color); //清屏
void LCD_SetCursor(u16 Xpos, u16 Ypos); //设置光标
void LCD_DrawPoint(u16 x,u16 y); //画点
void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color); //快速画点
u16 LCD_ReadPoint(u16 x,u16 y); //读点
void LCD_Draw_Circle(u16 x0,u16 y0,u8 r); //画圆
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2); //画线
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2); //画矩形
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color); //填充单色
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color); //填充指定颜色
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode); //显示一个字符
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size); //显示一个数字
void LCD_ShowHexNum(u16 x,u16 y,uint32_t num,u8 len,u8 size); //显示一个16数字
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode); //显示 数字
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p); //显示一个字符串,12/16字体void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue);
u16 LCD_ReadReg(u16 LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(u16 RGB_Code);
void LCD_SSD_BackLightSet(u8 pwm); //SSD1963 背光控制
void LCD_Scan_Dir(u8 dir); //设置屏扫描方向
void LCD_Display_Dir(u8 dir); //设置屏幕显示方向
void LCD_Set_Window(u16 sx,u16 sy,u16 width,u16 height); //设置窗口
3. oled
OLED_I2C_Init(); //端口初始化OLED_WriteCommand(0xAE); //关闭显示OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率OLED_WriteCommand(0x80);OLED_WriteCommand(0xA8); //设置多路复用率OLED_WriteCommand(0x3F);OLED_WriteCommand(0xD3); //设置显示偏移OLED_WriteCommand(0x00);OLED_WriteCommand(0x40); //设置显示开始行OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置OLED_WriteCommand(0xDA); //设置COM引脚硬件配置OLED_WriteCommand(0x12);OLED_WriteCommand(0x81); //设置对比度控制OLED_WriteCommand(0xCF);OLED_WriteCommand(0xD9); //设置预充电周期OLED_WriteCommand(0xF1);OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别OLED_WriteCommand(0x30);OLED_WriteCommand(0xA4); //设置整个显示打开/关闭OLED_WriteCommand(0xA6); //设置正常/倒转显示OLED_WriteCommand(0x8D); //设置充电泵OLED_WriteCommand(0x14);OLED_WriteCommand(0xAF); //开启显示OLED_Clear(); //OLED清屏
4. Key
u8 KEY_Scan(u8 mode)
{ static u8 key_up=1;//按键按松开标志if(mode)key_up=1; //支持连按 if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1)){delay_ms(10);//去抖动 key_up=0;if(KEY0==0)return KEY0_PRES;else if(KEY1==0)return KEY1_PRES;else if(KEY2==0)return KEY2_PRES;else if(WK_UP==1)return WKUP_PRES;}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1; return 0;// 无按键按下
}u8 KEY2_Scan(void)
{uint8_t LIE,HANG,k,i=0;GPIO_Write(GPIOC, 0xF0); //D0-D3拉低 D4-D7拉高if((GPIO_ReadInputData(GPIOC)&0xF0)!=0xF0) //有按键按下{delay_ms(40); //消抖if((GPIO_ReadInputData(GPIOC)&0xF0)!=0xF0) //再次判断是否有按下{LIE=GPIO_ReadInputData(GPIOC); //读取按键按下后得到的代码HANG=LIE; //将代码复制给行LIE=~LIE; //将键码取反,如按下某个键得到0111 0000 取反得到1000 1111LIE=LIE&0XF0; //得到列1000 1111&1111 0000得到1000 0000,得到列数for(i=0;i<4&&((HANG&0xF0)!=0xF0);i++) //逐次将行拉高,判断列数中原来变低的位是否变高{ //得到之前检测到低的列变高则退出GPIO_Write(GPIOC, (HANG&0xF0)|(0x01<<i)); //进行行扫描,逐次将行口线拉高,列保持为按下时的状态HANG=GPIO_ReadInputData(GPIOC); //读取IO口,用以判断是否扫描到行坐标 }HANG&=0x0F; //将行值取出k=LIE|HANG; //行列相加得到键码GPIO_Write(GPIOC, 0xF0); //D0-D3拉低 D4-D7拉高 此处将行列状态初始化为未按下时的状态while((GPIO_ReadInputData(GPIOC)&0xF0)!=0xF0) //判释放{delay_ms(40); //后沿消抖,时间需要长一点,小按键消抖,时间可以短一点,大按键消抖严重消抖需要长一点}return k; //·返回键码}} return (0); //无按键按下,返回0}
5. beep
void BEEP_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //BEEP-->PB.8 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //根据参数初始化GPIOB.8GPIO_ResetBits(GPIOB,GPIO_Pin_8);//输出0,关闭蜂鸣器输出}void beep(void){ delay_init(); BEEP=0; delay_ms(300);//延时300ms BEEP=1; delay_ms(300);//延时300ms
}
6. ADC
ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M//PA1 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); //复位ADC1 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1ADC_ResetCalibration(ADC1); //使能复位校准 while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束ADC_StartCalibration(ADC1); //开启AD校准while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
7. Timer
void TIM7_IRQHandler(void)
{ if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断{ USART2_RX_STA|=1<<15; //标记接收完成TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 TIM_Cmd(TIM7, DISABLE); //关闭TIM7 }
}//通用定时器7中断初始化,这里时钟选择为APB1的2倍
//arr:自动重装值 psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//通用定时器中断初始化
void TIM7_Int_Init(u16 arr,u16 psc)
{ NVIC_InitTypeDef NVIC_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 //定时器TIM7初始化TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断TIM_Cmd(TIM7,ENABLE);//开启定时器7NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器}
8. ESP8266
void esp8266_start_trans(void)
{printf("等待初始化\r\n");while(esp8266_send_cmd((u8 *)"AT",(u8 *)"OK",1000));//printf("等待初始化1\r\n");//设置工作模式 1:station模式 2:AP模式 3:兼容 AP+station模式while(esp8266_send_cmd((u8*)"AT+CWMODE=3",(u8*)"OK",1000));printf("设置工作模式成功\r\n");delay_ms(1000);//让模块连接上自己的路由while(esp8266_send_cmd((u8*)"AT+CWJAP=\"xxx\",\"xxxxxxxxx\"",(u8*)"WIFI GOT IP",1000));printf("连接路由器成功\r\n");delay_ms(1000);//=0:单路连接模式 =1:多路连接模式while(esp8266_send_cmd((u8*)"AT+CIPMUX=0",(u8*)"OK",200)){printf("设置单路连接模式失败\r\n");}printf("设置单路连接模式成功\r\n");delay_ms(1000);//建立TCP连接 这四项分别代表了 要连接的ID号0~4 连接类型 远程服务器IP地址 远程服务器端口号while(esp8266_send_cmd((u8*)"AT+CIPSTART=\"TCP\",\"192.168.124.32\",8117",(u8*)"OK",5000));printf("TCP连接成功\r\n");delay_ms(1000);//是否开启透传模式 0:表示关闭 1:表示开启透传esp8266_send_cmd((u8*)"AT+CIPMODE=1",(u8*)"OK",200);printf("开启透传模式\r\n");//透传模式下 开始发送数据的指令 这个指令之后就可以直接发数据了esp8266_send_cmd((u8*)"AT+CIPSEND",(u8*)"OK",50);printf("开启透传成功\r\n");
}
9. GY39
//**************************************
//向IIC总线发送一个字节数据
/*
一个字节8bit,当SCL低电平时,准备好SDA,SCL高电平时,从机采样SDA
*/
//**************************************
void I2C_SendByte(u8 dat)
{u8 i;SCL_L;//SCL拉低,给SDA准备for (i=0; i<8; i++) //8位计数器{if(dat&0x80)//SDA准备SDA_H; else SDA_L;SCL_H; //拉高时钟,给从机采样delay_1us(5); //延时保持IIC时钟频率,也是给从机采样有充足时间SCL_L; //拉低时钟,给SDA准备delay_1us(5); //延时保持IIC时钟频率dat <<= 1; //移出数据的最高位 }
}
//**************************************
//从IIC总线接收一个字节数据
//**************************************
u8 I2C_RecvByte()
{u8 i;u8 dat = 0;SDA_H;//释放SDA,给从机使用delay_1us(1); //延时给从机准备SDA时间 for (i=0; i<8; i++) //8位计数器{ dat <<= 1;SCL_H; //拉高时钟线,采样从机SDAif(SDA_read) //读数据 dat |=0x01; delay_1us(5); //延时保持IIC时钟频率 SCL_L; //拉低时钟线,处理接收到的数据delay_1us(5); //延时给从机准备SDA时间} return dat;
}
//**************************************
//向IIC设备写入一个字节数据
//**************************************
u8 Single_WriteI2C_byte(u8 Slave_Address,u8 REG_Address,u8 data)
{if(I2C_Start()==0) //起始信号{I2C_Stop(); return RESET;} I2C_SendByte(Slave_Address); //发送设备地址+写信号if(!I2C_WaitAck()){I2C_Stop(); return RESET;}I2C_SendByte(REG_Address); //内部寄存器地址,if(!I2C_WaitAck()){I2C_Stop(); return RESET;}I2C_SendByte(data); //内部寄存器数据,if(!I2C_WaitAck()){I2C_Stop(); return RESET;}I2C_Stop(); //发送停止信号return SET;
}
//**************************************
//从IIC设备读取一个字节数据
//**************************************
u8 Single_ReadI2C(u8 Slave_Address,u8 REG_Address,u8 *REG_data,u8 length)
{if(I2C_Start()==0) //起始信号{I2C_Stop(); return RESET;} I2C_SendByte(Slave_Address); //发送设备地址+写信号if(!I2C_WaitAck()){I2C_Stop(); return RESET;} I2C_SendByte(REG_Address); //发送存储单元地址if(!I2C_WaitAck()){I2C_Stop(); return RESET;} if(I2C_Start()==0) //起始信号{I2C_Stop(); return RESET;} I2C_SendByte(Slave_Address+1); //发送设备地址+读信号if(!I2C_WaitAck()){I2C_Stop(); return RESET;}while(length-1){*REG_data++=I2C_RecvByte(); //读出寄存器数据I2C_SendACK(0); //应答length--;}*REG_data=I2C_RecvByte(); I2C_SendACK(1); //发送停止传输信号I2C_Stop(); //停止信号return SET;
}
10. Usart
void USART2_init(u32 bound)
{ NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOB时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口3时钟使能USART_DeInit(USART2); //复位串口3//USART2_TX PB10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PB10GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB10//USART2_RX PB11GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB11USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART2, &USART_InitStructure); //初始化串口 3USART_Cmd(USART2, ENABLE); //使能串口 //使能接收中断USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器TIM7_Int_Init(1000-1,7200-1); //10ms中断USART2_RX_STA=0; //清零TIM_Cmd(TIM7,DISABLE); //关闭定时器7}
11. LED
//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 端口配置, 推挽输出GPIO_Init(GPIOE, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHzGPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
}void LED1_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitTypeDef GPIO_InitStructure1;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PE端口时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); //使能PB,PE端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 |GPIO_Pin_10|GPIO_Pin_11 ; //LED0-->PB.5 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOC, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11 ; //LED0-->PB.5 端口配置GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOD, &GPIO_InitStructure1); //根据设定参数初始化GPIOB.5GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 |GPIO_Pin_10|GPIO_Pin_11 ; //端口配置, 推挽输出GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11 ; //端口配置, 推挽输出GPIO_Init(GPIOD, &GPIO_InitStructure1); //推挽输出 ,IO口速度为50MHzGPIO_Init(GPIOC, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz//GPIO_SetBits(GPIOC,GPIO_Pin_0 | GPIO_Pin_1 |GPIO_Pin_2); //PE.5 输出高 }
12. Motor
void motor_gpio()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);/*初始为低电平*/GPIO_ResetBits(GPIOB,GPIO_Pin_14 | GPIO_Pin_15);
}void TIM3_PWM_Init(u16 per,u16 psc)
{/*使能TIM4时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);/*使能GPIO*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);/*使能AFIO*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);/*配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init(GPIOB,&GPIO_InitStructure);/*设置重映射*///GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//部分重映射 /*初始化定时器参数*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频为1分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//选择计数模式为向上计数TIM_TimeBaseInitStructure.TIM_Period = per;//配置周期(ARR自动重装器的值)TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//配置PSC预分频器的值//TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,高级计数器才需配置TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//TIM_ClearFlag(TIM4,TIM_FLAG_Update);//先清除标志位,避免刚初始化就进入中断/*初始化PWM参数*/TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; //选择空闲状态下的非工作状态 低电平TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set; //选择互补空闲状态下的非工作状态 低电平TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM1模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性:高电平有效TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出比较使能TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补输出比较使能TIM_OC1Init(TIM3,&TIM_OCInitStructure);
// TIM_OC2Init(TIM3,&TIM_OCInitStructure);
// TIM_OC3Init(TIM3,&TIM_OCInitStructure);
// TIM_OC4Init(TIM3,&TIM_OCInitStructure);/*使能TIMX在CCRX上的预装载寄存器*/TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);
// TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
// TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
// TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);TIM_CtrlPWMOutputs(TIM3,ENABLE);/*使能TIMX在ARR上的预装载寄存器允许位*///TIM_ARRPreloadConfig(TIM4,ENABLE);/*开启定时器*/TIM_Cmd(TIM3,ENABLE);
}
13. Flash
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{u32 secpos; //扇区地址u16 secoff; //扇区内偏移地址(16位字计算)u16 secremain; //扇区内剩余地址(16位字计算) u16 i; u32 offaddr; //去掉0X08000000后的地址if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址FLASH_Unlock(); //解锁offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围while(1) { STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容for(i=0;i<secremain;i++)//校验数据{if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 }if(i<secremain)//需要擦除{FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区for(i=0;i<secremain;i++)//复制{STMFLASH_BUF[i+secoff]=pBuffer[i]; }STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. if(NumToWrite==secremain)break;//写入结束了else//写入未结束{secpos++; //扇区地址增1secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移WriteAddr+=(secremain*2); //写地址偏移 NumToWrite-=secremain; //字节(16位)数递减if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完else secremain=NumToWrite;//下一个扇区可以写完了} }; FLASH_Lock();//上锁
}
四、服务器端
1. TCP
//创建一个socket文件,也就是打开一个网络通讯端口,类型是IPV4(AF_INET)+TCP(SOCK_STREAM)int serv_sock = socket(AF_INET, SOCK_STREAM,0);// 设置SO_REUSEADDR选项int optval = 1;if (setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {perror("setsockopt() failed");exit(EXIT_FAILURE);} //绑定服务器ip和端口到这个socketstruct sockaddr_in serv_addr;//这里因为是ipv4,使用的结构体是ipv4的地址类型sockaddr_inmemset(&serv_addr, 0, sizeof(serv_addr));//先清空一下初始的值,写上地址和端口号,可以用bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("192.168.124.32");//本机ip环回地址,这里还可以使用inet_pton函数进行地址转换serv_addr.sin_port = htons(8117);//随意选了一个端口8899bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//将socket设置为监听状态if(listen(serv_sock,128)==-1){//设置最大连接数为128printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);exit(0);}else{printf("Waiting for client's request...\n");}
//接收客户端的请求连接后,返回一个新的socket(clnt_sock)用于和对应的客户端进行通信struct sockaddr_in clnt_addr;//作为一个传出参数socklen_t clnt_addr_size = sizeof(clnt_addr);//作为一个传入+传出参数int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);if(clnt_sock!=-1) {printf("Connect success!\n\n");}
2. MYSQL
MYSQL *sql; //创造一个MYSQL句柄sql = mysql_init(NULL); //初始化MYSQL句柄int res; int ret;if(!sql) //若初始化句柄失败{ printf("connect mysql failed");return -1;}/*尝试与mysql数据库连接*/if(!mysql_real_connect(sql,"localhost","root","123456","test",0,NULL,0)){printf("failed to coonect mysql:%s\n",mysql_error(sql));}printf("connect success...........\n");char insertDataStr[200] ;sprintf(insertDataStr,"INSERT INTO water(NAME,ID,INSERTTIME,SUM,STATUS) VALUES ('%s',%s,now(),%s,'%s');",rc1,rc2,rc4,rc5);ret = mysql_query(sql,insertDataStr);if(ret >= 0){printf("insert ISMILELI info is success!\n");student_get_all(sql);}else{printf("insert ISMILELI info is failurel!\n");}
mysql_close(sql); //关闭连接,释放对象的内存空间
mysql_library_end(); //如果不调用该函数,可能造成内存泄露
return 0;
基于物联网的校园直饮水管理系统(2022年湖南省物联网应用创新竞赛(技能赛))相关推荐
- 基于Java毕业设计校园闲置物品信息管理系统源码+系统+mysql+lw文档+部署软件
基于Java毕业设计校园闲置物品信息管理系统源码+系统+mysql+lw文档+部署软件 基于Java毕业设计校园闲置物品信息管理系统源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构 ...
- 基于javaweb+SSM校园快递物流管理系统
基于javaweb+SSM校园快递物流管理系统 开发工具:eclipse/idea/myeclipse/sts等均可配置运行 适用 课程设计,大作业,毕业设计,项目练习,学习演示等 <if te ...
- 基于javaweb的校园共享自行车管理系统(java+ssm+jsp+easyui+bootstrap+mysql)
基于javaweb的校园共享自行车管理系统(java+ssm+jsp+easyui+bootstrap+mysql) 运行环境 Java≥8.MySQL≥5.7.Tomcat≥8 开发工具 eclip ...
- 基于php实现校园兼职网站管理系统【项目源码+论文说明】
基于php实现校园兼职网站管理系统项目演示 研究现状 校园兼职平台可分为两类,一类是"轻O2O",即学生利用平台发布的兼职信息寻找自己喜欢的工作.这类模型相当于一个信息聚合平台,操 ...
- 基于SSM的校园二手书管理系统 基于java的二手平台代码 二手商城系统下载
基于SSM的校园二手书管理系统 基于java的二手平台代码 二手商城系统下载 注意:该项目只展示部分功能,如需了解,评论区咨询即可. 1.开发环境 开发语言:Java 后台框架:SSM(Spring+ ...
- Qt 物联网系统界面开发 “ 2022湖南省大学生物联网应用创新设计竞赛技能赛 ——应用物联网的共享电动自行车 ”
文章目录 前言 一.实现效果 视频演示 二.程序设计 1. 界面背景图设计 2. 信号槽设计 3. 定时器设计 4. 动态曲/折线图的设计 5. 摄像头扫码 6. 注册设计 7. 登录设计 8. 巡检 ...
- 2022年全国大学生智能汽车竞赛西部赛 竞赛方案与实施流程
全国大学生智能汽车竞赛西部赛竞赛是全国大学生智能汽车竞赛的预选赛,系国家级赛事,各参赛学校必须高度重视,诚实参赛.由于新冠疫情防控的要求,2022年全国大学生智能汽车竞赛西部赛竞赛(以下简称西部赛 ...
- 基于PHP的校园共享单车管理系统
有需要请私信或看评论链接哦 可远程调试 基于PHP校园共享单车管理系统 一 介绍 此校园共享单车管理系统基于原生PHP开发,数据库mysql,前端jquery.js.系统角色分为学生,维修人员和管理员 ...
- 基于Android的校园考研论坛管理系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件 ...
最新文章
- 在ubuntu下安显卡驱动
- java纯粹面向对象_Java的面向对象特征
- 告别2017!2018投影市场变革机遇与挑战并存
- WINFORM 多条件动态查询 通用代码的设计与实现
- C++unique函数应用举例
- VScode配置eslint保存自动格式化,eslint格式化去掉分号和双引号。vscode自动保存去掉分号和双引号;““
- vue-slicksort拖拽组件
- iOS 代码命名规范 及Android 代码命名规范(2)Android
- [2018.07.21 T3] Booom
- Qt4 在x86和arm平台上的一些配置
- 如何用jsp和mysql实现简单的登陆功能
- 【图像识别】人脸识别原理及CNN讲解
- 【零基础入门MyBatis系列】第三篇——使用MyBatis完成CRUD
- 转行学IT,你真的准备好了吗?
- 南京农业大学计算机学硕分数线,2020南京农业大学考研复试分数线已公布
- 华为交换机配置Guest vlan
- 根域名服务器都在国外,中国安全吗?安全
- 直播软件搭建Android音视频方向进阶路线及资源合集
- 计算机专业研究生西安就业,计算机专业需要考研吗?毕业之后该怎么发展?
- JavaScript发展历史(JS)
热门文章
- 哈工大2021秋数据结构期末试题
- 产品说明文档 BlackBerry 9930 用户手册:用户指南,此处开始(快速入门)
- 39.文本颜色设置SetTextColor、SetBkColor、SetCharacterExtra、GetSysColor、InvalidateRect
- MATLAB | 官方自带的绘图代码生成功能咋用
- python:批量改名脚本
- Navicat Premium 15 “rsa public key not find”解决方法
- 五金进销存软件,流程可以处理好业务中的人、客、货、账
- java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must b
- 对象数组根据多个属性排序
- HDU 4997 Biconnected (状态压缩DP)