一、概述
随着智能化的普及,利用物联网等信息技术改造传统农业,对农业生产要素进行数字化设计、智能化控制也快速发展了起来。为了提高农业的产量以及改善农业生态环境,提高生产经营效率,方便人们日常生活,我们设计了一种更加自动化、智能化、人性化的智慧农业方案。
1、功能
(1)支持APP进行远程控制设备的运行;
(2)支持控制屏进行本地控制设备的运行;
(3)可以实时远程观看所需数据情况。
2、硬件框图
3、软件框图
二、硬件方案介绍
1、主控部分
发送部分采用GD32E230C8T6的一款单片机,接收部分采用的是ST的一款NUCLEO-L476RGDE开发板。
发送部分:
(1)原理图(点击下载)
(2)PCB(点击下载)
接收部分:
(1)原理图(点击下载)
注意:下面原理图只截取部分,完整的原理图请参照上面的下载链接。
(2)PCB(点击下载)
2、传感器部分
(1)照度检测(点击下载)
光照度检测我们选取一个 BH1750照度检测模块来实现。BH1750 照度检测模块搭载一个BH1750FVI,是I2C总线接口的数字环境光传感器IC。可以准确读取1-65535XL的环境照度。
原理图如下:
管脚介绍
名称 | VCC | GND | SCL | SDA | ADDR |
---|---|---|---|---|---|
功能描述 | 3~5V供电 | 参考地 | IIC时钟线 | IIC数据线 | 地址线 |
(2)温湿度传感器
温湿度检测我们选取涂鸦的SHT30模块来实现。涂鸦三明治温湿度传感功能板为三明治开发板的应用部分,方便开发者快速实现温湿度硬件产品原型的一款开发板。功能板主要包含一颗 SENSIRION 温湿度传感器 SHT30-DIS,通过 I2C 协议进行通信,I2C时钟频率最高支持1MHz。
关键器件介绍
器件 | 说明 |
---|---|
U1(SHT30-DIS) | SENSIRION 温湿度传感器,工作电压 2.4~5.5V,湿度精度 ±2%RH,温度精度 ±0.3℃,封装 8 脚 DFN |
涂鸦三明治温湿度传感功能板需要用到的管脚介绍
I/O | 说明 |
---|---|
VCC | 电源供电脚 |
GND | 电源参考地 |
SCL | I2C时钟信号 |
SDA | I2C数据信号 |
INT | 告警信号,预留 |
电源技术要求
-
电源供电电压参照传感器工作电压范围:2.4~5.5V
-
非测量状态典型电流:0.2uA
-
低功耗连续测量模式典型电流:800uA
(1)原理图
涂鸦三明治温湿度传感功能板的原理图如下所示:
(2)PCB图
涂鸦三明治温湿度传感功能板的 PCB 如下图所示:
注意事项
- 功能板为应用部分,需配合控制板与电源板使用。
- 电源接口不要触碰 I/O 管脚,避免击穿模块对应 I/O 口。
- 传感器本体附着灰尘与油污等会导致测量精度下降。
- 传感器本体不能与清洁剂接触,例如洗板水。
- 不能使用会释放化学分子的材料包装,否则可能受污染导致数据偏移或完全损坏。
3、无线通信部分
(1)LORA通信(发送部分)
LORA通信发送部分采用的是WPG公司的LLCC68的芯片。该芯片和SX1268管脚兼容。此次设计没有使用开关芯片来进行发送与接收模式的切换。直接使用双天线,采用半双工的通信方式。
(2)LORA通信(接收部分)
LORA通信接收部分采用的是WPG公司的SX1268模块。
注意:SX1268和LLCC68管脚兼容,但参数设置有些区别。SX1268扩频因子可以支持到SF12,但LLCC68只能到SF11。所以在程序设计的时候要注意了。
SX1268参数如下图:
LLCC68参数如下图:
(3)WB3S通信
WB3S是由涂鸦智能开发的一款低功耗嵌入式Wi-Fi+蓝牙BLE双协议云模组。它由一个高集成度的无线射频芯片BK7231T和少量外围器件构成,内置了Wi-Fi网络协议栈和丰富的库函数。MCU通过串口和WB3S进行通信,采用透传的模式。
4、控制屏部分
显示控制部分采用的是迪文的4.3寸串口屏。
正面图:
背面图:
接口图:
此次设计中界面设计的主界面效果如下图:
MCU和显示屏通过串口通信,来实现控制和显示。
三、产品创建
- 进入 涂鸦IoT平台,点击创建产品,选择标准类目->电工->开关。(当时为了测试用,所以选择一个标准类目,也可以在平台上其他品类中去创建也是可以的)
- 选择自定义方案,输入产品名称,选择通讯协议为Wi-Fi+蓝牙,点击创建产品。
- 根据要实现的设备功能,创建好DP功能点。
- 设定完功能点后,下一步点击设备面板,选择App的面板样式。推荐选择自由配置面板,比较直观,方便灵活。
至此,产品的创建基本完成,可以正式开始嵌入式软件部分的开发。
四、软件方案介绍
1、发送部分(即GD32采集传感器数据通过LORA发送出去)
(1)程序设计入口
打开demo例程,其中GD32_LORA_TRANSMIT文件夹内就是demo的应用代码。应用代码结构如下:
├── Application
│ ├── main.c
│ ├── gd32e23x_it.c
│ ├── systick.c
│ ├── gd32e23x_it.h
│ ├── systick.h
│ ├── gd32e23x_libopt.h
├── GD32E23x_Firmware_Library
│ ├── CMSIS├── Include│ ├──gd32e23x.h│ ├──system_gd32e23x.h├── Source│ ├──startup_gd32e23x.s│ ├──system_gd32e23x.h
│ ├── GD32E23x_standard_peripheral├── Include├── Source
├──User
│ ├── BH1750.c
│ ├── BH1750.h
│ ├──delay.c
│ ├──delay.h
│ ├──sht3x.c
│ ├──sht3x.h
│ ├──soft_i2c.c
│ ├──soft_i2c.h
│ ├──SPI.c
│ ├──SPI.h
│ ├──sx126x_v01.c
│ ├──sx126x_v01.h
│ ├──usart.c
└──────usart.h
(2)光照度传感器的驱动
为了检测光照度,选用的传感器型号为BH1750,通过I2C协议与GD32进行通信,相关接口封装都在BH1750.c 文件中。模块具体使用流程如下:
调用Init_BH1750初始化模块:
//初始化BH1750,根据需要请参考pdf进行修改****
void Init_BH1750()
{ /*开启GPIOB的外设时钟*/
rcu_periph_clock_enable(RCU_GPIOB);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6|GPIO_PIN_7);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6|GPIO_PIN_7);
Single_Write_BH1750(0x01);
Delay_ms(180); //延时180ms
}
调用mread连续读出BH1750内部数据:
//连续读出BH1750内部数据
void mread(void)
{ uchar i; BH1750_Start(); //起始信号BH1750_SendByte(SlaveAddress+1); //发送设备地址+读信号for (i=0; i<3; i++) //连续读取6个地址数据,存储中BUF{BUF[i] = BH1750_RecvByte(); //BUF[0]存储0x32地址中的数据if (i == 3){BH1750_SendACK(1); //最后一个数据需要回NOACK}else{ BH1750_SendACK(0); //回应ACK}}BH1750_Stop(); //停止信号Delay_ms(5);
}
调用read_BH1750获取光照强度值:
uint16_t read_BH1750(void)
{int dis_data; //变量 float temp1;float temp2;Single_Write_BH1750(0x01); // power onSingle_Write_BH1750(0x10); // H- resolution modeDelay_ms(180); //延时180msmread(); //连续读出数据,存储在BUF中dis_data=BUF[0];dis_data=(dis_data<<8)+BUF[1]; //合成数据 temp1=dis_data/1.2;temp2=10*dis_data/1.2; temp2=(int)temp2%10; return (uint16_t)temp1;
}
(3)温湿度传感器的驱动
为了测量温湿度,选取涂鸦的SHT30温湿度模块来实现。通过 I2C 协议与GD32进行通信,I2C时钟频率最高支持1MHz。
调用SHT3x_reset复位模块:
/**
* @brief 复位SHT3x
* @param none
* @retval none
*/
void SHT3x_reset(void)
{SHT3x_Send_Cmd(SOFT_RESET_CMD);delay_1ms(20);
}
调用SHT3x_Init初始化模块:
/* 描述:SHT3x初始化函数,并将其设置为周期测量模式* 参数:无* 返回值:初始化成功返回0,初始化失败返回1 */uint8_t SHT3x_Init(void){uint8_t ret;IIC_Init();IIC_Start();ret = SHT3x_Send_Cmd(MEDIUM_2_CMD);IIC_Stop();return ret;}
调用 SHT3x_Get_Humiture_periodic获取温湿度值:
/* 描述:温湿度数据获取函数,周期读取,注意,需要提前设置周期模式
* 参数Tem_val:存储温度数据的指针, 温度单位为°C
* 参数Hum_val:存储湿度数据的指针, 温度单位为%
* 返回值:0-读取成功,1-读取失败
********************************************************************/
uint8_t SHT3x_Get_Humiture_periodic(double *Tem_val,double *Hum_val)
{uint8_t ret=0;uint8_t buff[6]={0};uint16_t tem,hum;double Temperature=0;double Humidity=0;IIC_Start();ret = SHT3x_Send_Cmd(READOUT_FOR_PERIODIC_MODE); IIC_Start();ret = SHT3x_Recv_Data(6,buff);IIC_Stop();/* 校验温度数据和湿度数据是否接收正确 */if(CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5]){ printf("CRC_ERROR,ret = 0x%x\r\n",ret);return 1;}/* 转换温度数据 */tem = (((uint16_t)buff[0]<<8) | buff[1]);//温度数据拼接Temperature= (175.0*(double)tem/65535.0-45.0) ; // T = -45 + 175 * tem / (2^16-1)/* 转换湿度数据 */hum = (((uint16_t)buff[3]<<8) | buff[4]);//湿度数据拼接Humidity= (100.0*(double)hum/65535.0); // RH = hum*100 / (2^16-1)/* 过滤错误数据 */if((Temperature>=-20)&&(Temperature<=125)&&(Humidity>=0)&&(Humidity<=100)){*Tem_val = Temperature;*Hum_val = Humidity;return 0;}elsereturn 1;
}
(4)LORA芯片驱动
为了远距离,低功耗的传输数据,采用的是WPG公司的LLCC68的芯片。通过SPI 协议与GD32进行通信。采用半双工的通信方式。
调用RadioInit初始化模块:
//RADIO 层 ///
void RadioInit(void)
{//RadioEvents = events;//这里进行了函数的初始化
#ifdef USE_TCXO
printf("USE TCXO\n");
#else
printf("USE CRYSTAL\n");
#endifSX126xInit();中断的回调函后续在其他地方去定义SX126xSetStandby( STDBY_RC );SX126xSetRegulatorMode( USE_DCDC);//USE_LDO//USE_DCDCSX126xSetBufferBaseAddress( 0x00, 0x00 );SX126xSetTxParams( 0, RADIO_RAMP_200_US );//DIO_0的中断MASK全部打开,在RadioSend()会再继续细分中断SX126xSetDioIrqParams( IRQ_RADIO_ALL, IRQ_RADIO_ALL, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
}
调用 SX126xOnDio1Irq进行数据处理:
//DIO1的中断函数
void SX126xOnDio1Irq(void)
{uint16_t irqRegs = SX126xGetIrqStatus( );SX126xClearIrqStatus( IRQ_RADIO_ALL );//这里清掉中断标志//发送结束if( ( irqRegs & IRQ_TX_DONE ) == IRQ_TX_DONE ){TXDone=true;gpio_bit_toggle(LED_GPIO_Port, LED_Pin);OnTxDone();}//在SX126xSetTx()设置了一个超时时间 可以检测改功能 --okif( ( irqRegs & IRQ_RX_TX_TIMEOUT ) == IRQ_RX_TX_TIMEOUT ){TimeOutFlag=true;printf(" RX/TX timeout\n");}if( ( irqRegs & IRQ_RX_DONE ) == IRQ_RX_DONE ){SX126xGetPayload( RadioRxPayload, &RadioRxPacketSize , 255 );SX126xGetPacketStatus( &RadioPktStatus );gpio_bit_toggle(LED_GPIO_Port, LED_Pin);OnRxDone();RXDoneFlag=true;}if( ( irqRegs & IRQ_CRC_ERROR ) == IRQ_CRC_ERROR ){printf("CRC fail\n");CRCFail=true;}if( ( irqRegs & IRQ_CAD_DONE ) == IRQ_CAD_DONE ){if ( ( irqRegs & IRQ_CAD_ACTIVITY_DETECTED ) == IRQ_CAD_ACTIVITY_DETECTED ) {//printf("IRQ_CAD_ACTIVITY_DETECTED\n"); //CadDetect=true;} }if( ( irqRegs & IRQ_PREAMBLE_DETECTED ) == IRQ_PREAMBLE_DETECTED ){__NOP( );}if( ( irqRegs & IRQ_SYNCWORD_VALID ) == IRQ_SYNCWORD_VALID ){__NOP( );}if( ( irqRegs & IRQ_HEADER_VALID ) == IRQ_HEADER_VALID ){__NOP( );}
}
(5)主程序设计
#define LORA_MODE 1
#define FSK_MODE 0
#define TRANSMITTER 1
#define RECEIVER 0
int main(void)
{uint8_t i=0;uint16_t light;double Tem_val,Hum_val;bool DetectTruetable[100]={0};//CAD成功的分布bool RXTruetable[100]={0};//CAD后能接收正确的分布uint8_t CadDetectedTime=0;//检测到的cad的次数uint8_t RxCorrectTime=0;//RX 接收正确次数uint8_t TxTime=0; //TX 次数int random_number=0;RadioStatus_t RadioStatus;//连续发送的时候用uint8_t ModulationParam[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };uint8_t PacketParam[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };/* Configure the system clock */systick_config(); /* Initialize all configured peripherals */MX_GPIO_Init();rcu_config();gpio_config();spi_config();USART0_Init();USART1_Init();Init_BH1750();SHT3x_reset();if( 0 == SHT3x_Init())printf("SHT3x_Init OK \r\n");elseprintf("SHT3x_Init ERR \r\n");gpio_bit_toggle(LED_GPIO_Port, LED_Pin);Delay_ms(250);SX126xReset();i=SX126xReadRegister(REG_LR_CRCSEEDBASEADDR);if(i==0x1D){ printf("SPI SUCCESS!\n\r");}else{printf("SPI Fail! REG_LR_CRCSEEDBASEADDR=%x\n\r",i);}RadioInit();SX126xWriteRegister(0x889, SX126xReadRegister(0x889) & 0xfB);//SdCfg0 (0x889) sd_res (bit 2) = 0 printf("RadioInit Done!\n\r");
while (1){
#if (TRANSMITTER==1)while(1){ light =read_BH1750();/* 采集温湿度数据 */if(SHT3x_Get_Humiture_periodic(&Tem_val,&Hum_val) == 0){memcpy(Buffer,(double*)(&Tem_val),8); memcpy(Buffer+8,(double*)(&Hum_val),8);}elseprintf("Get_Humiture ERR\r\n");memcpy(Buffer+16, (uint16_t*)&light, sizeof((uint16_t*)&light));RadioSend(Buffer ,18);printf("1=%d\n",read_BH1750()); while(TXDone==false && TimeOutFlag==false);//一直等待tx doneTXDone=false;TimeOutFlag=false;printf("TxTime=%d\n",TxTime);Delay_ms(1000); //1s//读取状态RadioStatus=SX126xGetStatus();printf("RadioStatus is(after TX_DONE) %d\n",(((RadioStatus.Value)>>4)&0x07)); }
#elif (RECEIVER==1) while(1){#if (RX_CONTINOUS==1)//开始接收RadioRx(0xFFFFFF);//50MS(0XC80)超时 0-单次接收 无超时printf("continous RX...\n");while(1);//连续接收
#endifRadioRx(2000);//50MS(0XC80)超时 0-单次接收 无超时while(RXDoneFlag==false && TimeOutFlag==false && CRCFail==false);if(RXDoneFlag==true || TimeOutFlag==true || CRCFail==true){if(CRCFail==false) //CRC无错误{if(RXDoneFlag==true){printf("\n%d:RxCorrect-PING\n",RxCorrectTime);RxCorrectTime++;}}CRCFail=false;RXDoneFlag=false;TimeOutFlag=false;}}
#endif }
}
本demo发送部分完整工程:[点此下载](Tuya-Community/tuya-iotos-embeded-mcu-demo-wifi-ble-GD32_LORA_TRANSMIT (github.com))
2、接收部分(即STM32收到LORA模块的数据进行处理控制)
(1)程序设计入口
打开demo例程,其中STM32_LORA_LCD_RECEIVE文件夹内就是demo的应用代码。应用代码结构如下:
├── Src
│ ├── main.c
│ ├── connect_wifi.c
│ ├── delay.c
│ ├── lcd.c
│ ├── myOS.c
│ ├── stm32l4xx_hal_msp.c
│ ├── stm32l4xx_it.c
│ ├── sx126x_v01.c
│ ├── system_stm32l4xx.c
│ ├── time.c
│ ├──usart.c
├── Inc
│ ├── main.h
│ ├── connect_wifi.h
│ ├── delay.h
│ ├── lcd.h
│ ├── myOS.h
│ ├── stm32l4xx_hal_conf.h
│ ├── stm32l4xx_it.h
│ ├── sx126x_v01.h
│ ├── time.h
│ ├── type.h
│ ├──usart.h
├── Drivers
│ ├── CMSIS├── Device│ ├──STM32L4xx├── DSP_Lib│ ├──Source├── Include├── Lib│ ├──ARM│ ├──GCC├── RTOS│ ├──Template
│ ├── STM32L4xx_HAL_Driver├── Inc├── Src
└── MCU_SDK├── mcu_api.c├── mcu_api.h├── protocol.c├── protocol.h├── system.c├── system.h└── wifi.h
(2)LORA模块驱动
采用的是WPG公司的SX1268模块。通过SPI 协议与STM32进行通信。采用半双工的通信方式。驱动同LLCC68,但二者参数设置有些差别。差别如下所示:
SX1268扩频因子可以支持到SF12,但LLCC68只能到SF11。为了适用这两款芯片,同时满足要求,此处扩频因子选择SF10。
#define LORA_BANDWIDTH 1 // [0: 125 kHz,// 1: 250 kHz, // 2: 500k// 3 :20.83kHz// 4:31.25kHz// 5:62.5kHz4//6:41.67
#define LORA_SPREADING_FACTOR 10 // [SF7..SF12]
(3)控制屏程序设计
STM32通过串口和控制屏进行通信,波特率115200。
界面设计
首先采用PS软件做出自己需要的图片,然后保存成800*480分辨率的BMP图片格式。接着采用迪文的一款上位机软件进行显示和控制设计。
温湿度界面:
光照度界面:
节点控制界面:
控制屏界面设计完整工程:点此下载
驱动程序设计
调用WriteDataToLCD对控制屏写入数据:
/*******************************************************************************
** Function Name :void WriteDataToLCD(uint16_t startAddress,uint16_t return_data_start_addr,uint16_t length)
** Description : 数据写入触摸屏变量寄存器
** Input : uint16_t startAddress,uint16_t return_data_start_addr,uint16_t length
** Output : None
** Return : None
** Attention :
*******************************************************************************/
void WriteDataToLCD(uint16_t startAddress,uint16_t return_data_start_addr,uint16_t length)
{/*命令的长度由帧头(2个字节)+数据长度(1个字节)+指令(1个字节)+起始地址(2个字节)+数据(长度为length)*/uint8_t i;usart1_txBuf[0]=0x5a;usart1_txBuf[1]=0xa5;usart1_txBuf[2]=length+3;usart1_txBuf[3]=0x82;usart1_txBuf[4]=(uint8_t)((startAddress>>8)&0xff);//起始地址usart1_txBuf[5]=(uint8_t)(startAddress&0XFF);//起始地址for(i=0;i<length;i++){usart1_txBuf[i+6]=((SEND_BUF[i+return_data_start_addr]));}HAL_UART_Transmit(&huart1, usart1_txBuf, length+6, 20);
}
调用ReadDataFromLCD读取控制屏数据:
/*******************************************************************************
** Function Name :void ReadDataFromLCD(uint16_t startAddress,uint8_t readWordLength)
** Description : 读变量存储器数据
** Input : uint16_t startAddress,uint8_t readWordLength
** Output : None
** Return : None
** Attention :
*******************************************************************************/
void ReadDataFromLCD(uint16_t startAddress,uint16_t readWordLength)
{//命令的长度由帧头(2个字节)+数据长度(1个字节)+指令(1个字节)+起始地址(2个字节)+读取的字长度(1个字节)usart1_txBuf[0]=0x5a;usart1_txBuf[1]=0xa5;usart1_txBuf[2]=0x04;usart1_txBuf[3]=0x83;usart1_txBuf[4]=(uint8_t)((startAddress>>8)&0xff);//起始地址usart1_txBuf[5]=(uint8_t)(startAddress&0xff);//起始地址usart1_txBuf[6]=readWordLength;//读取长度HAL_UART_Transmit(&huart1, usart1_txBuf, 7 , 20);
}
调用void send_tz控制页面跳转:
/*******************************************************************************
** Function Name :void send_tz(void))
** Description : 跳转页面函数
** Input : None
** Output : None
** Return : None
** Attention :
*******************************************************************************/
void send_tz(void)
{uint8_t i;usart1_txBuf[0]=0x5a;usart1_txBuf[1]=0xa5;usart1_txBuf[2]=0x07;usart1_txBuf[3]=0x82;usart1_txBuf[4]=0x00;usart1_txBuf[5]=0x84;usart1_txBuf[6]=0x5a;usart1_txBuf[7]=0x01;for(i=0;i<2;i++){usart1_txBuf[i+8]=((SEND_BUF[i]));}HAL_UART_Transmit(&huart1, usart1_txBuf, 10, 20);
}
(4)WB3S模组
STM32通过串口和WB3S进行通信,采用透传的模式。
调用wifi_protocol_init初始化模块串口协议:
/**
* @brief 协议串口初始化函数
* @param Null
* @return Null
* @note 在MCU初始化代码中调用该函数
*/
void wifi_protocol_init(void)
{//#error " 请在main函数中添加wifi_protocol_init()完成wifi协议初始化,并删除该行"rx_buf_in = (unsigned char *)wifi_uart_rx_buf;rx_buf_out = (unsigned char *)wifi_uart_rx_buf;stop_update_flag = DISABLE;#ifndef WIFI_CONTROL_SELF_MODEwifi_work_state = WIFI_SATE_UNKNOW;
#endif
}
调用 wifi_uart_service对串口数据进行处理:
/**
* @brief wifi串口数据处理服务
* @param Null
* @return Null
* @note 在MCU主函数while循环中调用该函数
*/
void wifi_uart_service(void)
{//#error "请直接在main函数的while(1){}中添加wifi_uart_service(),调用该函数不要加任何条件判断,完成后删除该行" static unsigned short rx_in = 0;unsigned short offset = 0;unsigned short rx_value_len = 0;while((rx_in < sizeof(wifi_data_process_buf)) && with_data_rxbuff() > 0){wifi_data_process_buf[rx_in ++] = take_byte_rxbuff();}if(rx_in < PROTOCOL_HEAD)return;while((rx_in - offset) >= PROTOCOL_HEAD) {if(wifi_data_process_buf[offset + HEAD_FIRST] != FRAME_FIRST){offset ++;continue;}if(wifi_data_process_buf[offset + HEAD_SECOND] != FRAME_SECOND){offset ++;continue;} if(wifi_data_process_buf[offset + PROTOCOL_VERSION] != MCU_RX_VER) {offset += 2;continue;} rx_value_len = wifi_data_process_buf[offset + LENGTH_HIGH] * 0x100;rx_value_len += (wifi_data_process_buf[offset + LENGTH_LOW] + PROTOCOL_HEAD);if(rx_value_len > sizeof(wifi_data_process_buf) + PROTOCOL_HEAD){offset += 3;continue;}if((rx_in - offset) < rx_value_len) {break;}//数据接收完成if(get_check_sum((unsigned char *)wifi_data_process_buf + offset,rx_value_len - 1) != wifi_data_process_buf[offset + rx_value_len - 1]){offset += 3;continue;}data_handle(offset);offset += rx_value_len;}//end whilerx_in -= offset;if(rx_in > 0) {my_memcpy((char *)wifi_data_process_buf, (const char *)wifi_data_process_buf + offset, rx_in);}
}
调用以下DP处理函数对控制屏和GPIO进行控制:
/*****************************************************************************
函数名称 : dp_download_switch_1_handle
功能描述 : 针对DPID_SWITCH_1的处理函数
输入参数 : value:数据源数据: length:数据长度
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 可下发可上报类型,需要在处理完数据后上报处理结果至app
*****************************************************************************/
static unsigned char dp_download_switch_1_handle(const unsigned char value[], unsigned short length)
{//示例:当前DP类型为BOOLunsigned char ret;//0:关/1:开unsigned char switch_1;switch_1 = mcu_get_dp_download_bool(value,length);if(switch_1 == 0){//开关关SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x00; WriteDataToLCD(0x1000,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET); }else {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x01; WriteDataToLCD(0x1000,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);//开关开}//处理完DP数据后应有反馈ret = mcu_dp_bool_update(DPID_SWITCH_1,switch_1);if(ret == SUCCESS)return SUCCESS;elsereturn ERROR;
}
/*****************************************************************************
函数名称 : dp_download_switch_2_handle
功能描述 : 针对DPID_SWITCH_2的处理函数
输入参数 : value:数据源数据: length:数据长度
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 可下发可上报类型,需要在处理完数据后上报处理结果至app
*****************************************************************************/
static unsigned char dp_download_switch_2_handle(const unsigned char value[], unsigned short length)
{//示例:当前DP类型为BOOLunsigned char ret;//0:关/1:开unsigned char switch_2;switch_2 = mcu_get_dp_download_bool(value,length);if(switch_2 == 0) {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x00; WriteDataToLCD(0x1001,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET); //开关关}else {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x01; WriteDataToLCD(0x1001,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET); //开关开}//处理完DP数据后应有反馈ret = mcu_dp_bool_update(DPID_SWITCH_2,switch_2);if(ret == SUCCESS)return SUCCESS;elsereturn ERROR;
}
/*****************************************************************************
函数名称 : dp_download_switch_3_handle
功能描述 : 针对DPID_SWITCH_3的处理函数
输入参数 : value:数据源数据: length:数据长度
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 可下发可上报类型,需要在处理完数据后上报处理结果至app
*****************************************************************************/
static unsigned char dp_download_switch_3_handle(const unsigned char value[], unsigned short length)
{//示例:当前DP类型为BOOLunsigned char ret;//0:关/1:开unsigned char switch_3;switch_3 = mcu_get_dp_download_bool(value,length);if(switch_3 == 0) {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x00; WriteDataToLCD(0x1002,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET); //开关关}else {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x01; WriteDataToLCD(0x1002,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET); //开关开}//处理完DP数据后应有反馈ret = mcu_dp_bool_update(DPID_SWITCH_3,switch_3);if(ret == SUCCESS)return SUCCESS;elsereturn ERROR;
}
/*****************************************************************************
函数名称 : dp_download_switch_4_handle
功能描述 : 针对DPID_SWITCH_4的处理函数
输入参数 : value:数据源数据: length:数据长度
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 可下发可上报类型,需要在处理完数据后上报处理结果至app
*****************************************************************************/
static unsigned char dp_download_switch_4_handle(const unsigned char value[], unsigned short length)
{//示例:当前DP类型为BOOLunsigned char ret;//0:关/1:开unsigned char switch_4;switch_4 = mcu_get_dp_download_bool(value,length);if(switch_4 == 0) {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x00; WriteDataToLCD(0x1003,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET); //开关关}else {SEND_BUF[0]=0x00;SEND_BUF[1]=0x06; send_tz();SEND_BUF[0]=0x00;SEND_BUF[1]=0x01; WriteDataToLCD(0x1003,0,2);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET); //开关开}//处理完DP数据后应有反馈ret = mcu_dp_bool_update(DPID_SWITCH_4,switch_4);if(ret == SUCCESS)return SUCCESS;elsereturn ERROR;
}
(5)按键配网
调用Connect_Wifi进行配网设置,通过WB3S模组连接涂鸦云平台。
void Connect_Wifi(void)
{
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(WIFI_KEY_GPIO_Port, WIFI_KEY_Pin)){delay_ms(10);if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(WIFI_KEY_GPIO_Port, WIFI_KEY_Pin)){mcu_set_wifi_mode(0);printf("begin connect wifi\r\n");}}switch(mcu_get_wifi_work_state()){case SMART_CONFIG_STATE: printf("smart config\r\n");HAL_GPIO_TogglePin(WIFI_LED_GPIO_Port, WIFI_LED_Pin);delay_ms(250);break;case AP_STATE: printf("AP config\r\n");HAL_GPIO_TogglePin(WIFI_LED_GPIO_Port, WIFI_LED_Pin);delay_ms(500);break;case WIFI_NOT_CONNECTED: printf("connect wifi\r\n");HAL_GPIO_WritePin(WIFI_LED_GPIO_Port, WIFI_LED_Pin, GPIO_PIN_SET); break;case WIFI_CONNECTED:printf("connect success\r\n");HAL_GPIO_WritePin(WIFI_LED_GPIO_Port, WIFI_LED_Pin, GPIO_PIN_SET); case WIFI_CONN_CLOUD:HAL_GPIO_WritePin(WIFI_LED_GPIO_Port, WIFI_LED_Pin, GPIO_PIN_SET);break;default:HAL_GPIO_WritePin(WIFI_LED_GPIO_Port, WIFI_LED_Pin, GPIO_PIN_RESET); printf ("connect fail\r\n");break;}
}
(6)系统管理函数接口设计
由于使用串口较多,防止出现串口数据传输过程中被干扰的情况出现,此处利用定时器3中断,写了一个系统管理函数。不同定时时间处理不同的任务。
//系统管理函数
void System_Management(void)
{static __IO uint8_t timer_50ms = 0U;static __IO uint8_t timer_500ms = 0U;static __IO uint8_t timer_1000ms = 0U;static __IO uint8_t timer_10s = 0U;system_running_timer++; System_Run_2ms(); /*2ms task*/timer_50ms++;/*write if-else to aviod multiple task are running at same timeonly running 2ms task and one of 50ms, 500ms and 1s every 2ms.*/if(timer_50ms >= 25u){timer_50ms = 0u;Systen_Run_50ms();timer_500ms ++;}else if(timer_500ms >= 10u){timer_500ms = 0u;System_Run_500ms();timer_1000ms ++;}else if(timer_1000ms >= 2u){timer_1000ms = 0u;System_Run_1000ms();timer_10s ++;}else{}
}
调用System_Run_2ms和Systen_Run_50ms分别处理WiFi模组和LORA模块的数据。
/*2ms task
*/
void System_Run_2ms(void)
{system_running_timer += 2; /*Record system running time*/ wifi_uart_service();//wifi串口数据处理服务Connect_Wifi(); //配网
}/*50ms task
*/
void Systen_Run_50ms(void)
{ OnRxDone();
}
(7)主程序设计
#define LORA_MODE 1
#define FSK_MODE 0
#define TRANSMITTER 0
#define RECEIVER 1int main(void)
{uint8_t i=0;bool DetectTruetable[100]={0};//CAD成功的分布bool RXTruetable[100]={0};//CAD后能接收正确的分布uint8_t CadDetectedTime=0;//检测到的cad的次数uint8_t RxCorrectTime=0;//RX 接收正确次数uint8_t TxTime=0; //TX 次数//连续发送的时候用uint8_t ModulationParam[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };uint8_t PacketParam[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI1_Init();MX_USART1_UART_Init();MX_USART2_UART_Init();MX_USART3_UART_Init();wifi_protocol_init(); //wifi协议初始化TIM3_Init(20-1,8000-1); //定时器3初始化,定时2msfor(i=0;i<1;i++){HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);HAL_Delay(1000);HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);HAL_Delay(1000);}SX126xReset();i=SX126xReadRegister(REG_LR_CRCSEEDBASEADDR);if(i==0x1D){ printf("SPI SUCCESS!\n\r");}else{printf("SPI Fail! REG_LR_CRCSEEDBASEADDR=%x\n\r",i);}RadioInit();SX126xWriteRegister(0x889, SX126xReadRegister(0x889) & 0xfB);//SdCfg0 (0x889) sd_res (bit 2) = 0 printf("RadioInit Done!\n\r");#if (TEST_MODE==0) //infinite preamble TX mode//连续发送SX126xSetStandby( STDBY_RC );SX126xSetPacketType(PACKET_TYPE_LORA);//todo: 增加发射FSK模式下的改指令printf("set lora params\n");ModulationParam[0]=LORA_SPREADING_FACTOR;ModulationParam[1]=Bandwidths_copy[LORA_BANDWIDTH];ModulationParam[2]=LORA_CODINGRATE;ModulationParam[3]=0;//1:SF11 and SF12 0:其他 低速率优化 SX126xWriteCommand( RADIO_SET_MODULATIONPARAMS, ModulationParam, 4 );//lora发射参数配置//设置lora包参数PacketParam[0]=(LORA_PREAMBLE_LENGTH>>8)& 0xFF;PacketParam[1]=LORA_PREAMBLE_LENGTH;PacketParam[2]=LORA_FIX_LENGTH_PAYLOAD_ON;//head typePacketParam[3]=0xFF;//0Xff is MaxPayloadLengthPacketParam[4]=true;//CRC onPacketParam[5]=LORA_IQ_INVERSION_ON;SX126xWriteCommand( RADIO_SET_PACKETPARAMS, PacketParam, 6 );//SX126xWriteBuffer( 0x00, SendData, 10 );//连续发送loraSX126xSetRfFrequency( RF_FREQUENCY );SX126xSetRfTxPower( TX_OUTPUT_POWER );SX126xSetTxInfinitePreamble();printf("TxContinuousWave Now--infinite preamble!\n\r");while(1);
#elif (TEST_MODE==1) //TX CWRadioSetTxContinuousWave( RF_FREQUENCY, TX_OUTPUT_POWER, TX_TIMEOUT );printf("TxContinuousWave Now---CW!\n\r");while(1);#endif#if (FSK_MODE==1)SX126xSetRfFrequency(RF_FREQUENCY);RadioSetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, FSK_BANDWIDTH,FSK_DATARATE, 0,FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON,true, 0, 0, 0, 3000 );RadioSetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE,0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH,0, FSK_FIX_LENGTH_PAYLOAD_ON, FSK_FIX_LENGTH_PAYLOAD, FSK_CRC,0, 0,false, RX_CONTINOUS );printf("FSK:%d,Fdev=%ld,BitRate=%ld,BW=%ld,PWR=%d,PreLen=%d,PYLOAD=%d\n\r",RF_FREQUENCY,FSK_FDEV,FSK_DATARATE,FSK_BANDWIDTH,TX_OUTPUT_POWER,FSK_PREAMBLE_LENGTH,BUFFER_SIZE);printf("configure FSK parameters done\n!");#elif (LORA_MODE==1)SX126xSetRfFrequency(RF_FREQUENCY);RadioSetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,LORA_SPREADING_FACTOR, LORA_CODINGRATE,LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );RadioSetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,0, true, 0, 0, LORA_IQ_INVERSION_ON, RX_CONTINOUS );//最后一个参数设置是否是连续接收printf("LORA:%d,SF=%d,codeRate=%d,BW=%d,PWR=%d,PreLen=%d,PYLOAD=%d\n\r",RF_FREQUENCY,LORA_SPREADING_FACTOR,LORA_CODINGRATE,LORA_BANDWIDTH,TX_OUTPUT_POWER,LORA_PREAMBLE_LENGTH,BUFFER_SIZE);if (RadioPublicNetwork.Previous==true && RadioPublicNetwork.Current==false)printf("public\n\r");else if (RadioPublicNetwork.Previous==false && RadioPublicNetwork.Current==false)printf("private\n\r");printf("configure LORA parameters done\n!");#endifwhile (1){#if (TRANSMITTER==1)while(1){Buffer[0] = TxTime++;Buffer[1] = 1;Buffer[2] = 2;Buffer[3] = 3;Buffer[4] = 0;Buffer[5] = 0;RadioSend(Buffer,20);while(TXDone==false && TimeOutFlag==false);//一直等待tx doneTXDone=false;TimeOutFlag=false;printf("TxTime=%d\n",TxTime);HAL_Delay(500); ///1s//读取状态RadioStatus=SX126xGetStatus();printf("RadioStatus is(after TX_DONE) %d\n",(((RadioStatus.Value)>>4)&0x07));}
#elif (RECEIVER==1)
while(1){
#if (RX_CONTINOUS==1)//开始接收 RadioRx(0xFFFFFF);//50MS(0XC80)超时 0-单次接收 无超时printf("continous RX...\n");while(1);//连续接收
#endifRadioRx(2000);//50MS(0XC80)超时 0-单次接收 无超时while(RXDoneFlag==false && TimeOutFlag==false && CRCFail==false);if(RXDoneFlag==true || TimeOutFlag==true || CRCFail==true){if(CRCFail==false) //CRC无错误{if(RXDoneFlag==true){printf("\n%d:RxCorrect-PING\n",RxCorrectTime);RxCorrectTime++;}}CRCFail=false;RXDoneFlag=false;TimeOutFlag=false;}}
#endif
}
}
本demo接收部分完整工程:[点此下载](Tuya-Community/tuya-iotos-embeded-mcu-demo-wifi-ble-STM32_LORA_LCD_RECEIVE (github.com))
五、小结
至此智能农业场景搭建就完成了,它可以APP远程控制、本地控制等。大家可以在此基础上实现尽可能多的功能。同时您可以基于涂鸦IoT平台丰富它的功能,也可以更加方便的搭建更多智能产品原型,加速智能产品的开发流程。