Lora智慧农业系统让农民伯伯轻松坐等收割!
Posted 三明治开发社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lora智慧农业系统让农民伯伯轻松坐等收割!相关的知识,希望对你有一定的参考价值。
一、概述
随着智能化的普及,利用物联网等信息技术改造传统农业,对农业生产要素进行数字化设计、智能化控制也快速发展了起来。为了提高农业的产量以及改善农业生态环境,提高生产经营效率,方便人们日常生活,我们设计了一种更加自动化、智能化、人性化的智慧农业方案。
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 on
Single_Write_BH1750(0x10); // H- resolution mode
Delay_ms(180); //延时180ms
mread(); //连续读出数据,存储在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;
}
else
return 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");
#endif
SX126xInit();中断的回调函后续在其他地方去定义
SX126xSetStandby( STDBY_RC );
SX126xSetRegulatorMode( USE_DCDC);//USE_LDO//USE_DCDC
SX126xSetBufferBaseAddress( 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()设置了一个超时时间 可以检测改功能 --ok
if( ( 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");
else
printf("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);
}
else
printf("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 done
TXDone=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);//连续接收
#endif
RadioRx(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_MODE
wifi_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 while
rx_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类型为BOOL
unsigned 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;
else
return 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类型为BOOL
unsigned 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;
else
return 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类型为BOOL
unsigned 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;
else
return 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类型为BOOL
unsigned 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;
else
return 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 time
only 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 1
int 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初始化,定时2ms
for(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 type
PacketParam[3]=0xFF;//0Xff is MaxPayloadLength
PacketParam[4]=true;//CRC on
PacketParam[5]=LORA_IQ_INVERSION_ON;
SX126xWriteCommand( RADIO_SET_PACKETPARAMS, PacketParam, 6 );
//SX126xWriteBuffer( 0x00, SendData, 10 );
//连续发送lora
SX126xSetRfFrequency( RF_FREQUENCY );
SX126xSetRfTxPower( TX_OUTPUT_POWER );
SX126xSetTxInfinitePreamble();
printf("TxContinuousWave Now--infinite preamble!\\n\\r");
while(1);
#elif (TEST_MODE==1) //TX CW
RadioSetTxContinuousWave( 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!");
#endif
while (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 done
TXDone=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);//连续接收
#endif
RadioRx(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平台丰富它的功能,也可以更加方便的搭建更多智能产品原型,加速智能产品的开发流程。
以上是关于Lora智慧农业系统让农民伯伯轻松坐等收割!的主要内容,如果未能解决你的问题,请参考以下文章