【Lora智慧农业系统】让农民伯伯轻松坐等收割!

一、概述

​ 随着智能化的普及,利用物联网等信息技术改造传统农业,对农业生产要素进行数字化设计、智能化控制也快速发展了起来。为了提高农业的产量以及改善农业生态环境,提高生产经营效率,方便人们日常生活,我们设计了一种更加自动化、智能化、人性化的智慧农业方案。

1、功能

​ (1)支持APP进行远程控制设备的运行;

​ (2)支持控制屏进行本地控制设备的运行;

​ (3)可以实时远程观看所需数据情况。

2、硬件框图

image-20210821164927855

3、软件框图

image-20210824203335908

二、硬件方案介绍

1、主控部分

​ 发送部分采用GD32E230C8T6的一款单片机,接收部分采用的是ST的一款NUCLEO-L476RGDE开发板。

发送部分:
(1)原理图(点击下载

image-20210824141734196

(2)PCB(点击下载

image-20210824161328551

接收部分:
(1)原理图(点击下载

注意:下面原理图只截取部分,完整的原理图请参照上面的下载链接。

image-20210823173833493

(2)PCB(点击下载

image-20210823173717514

2、传感器部分

(1)照度检测(点击下载

​ 光照度检测我们选取一个 BH1750照度检测模块来实现。BH1750 照度检测模块搭载一个BH1750FVI,是I2C总线接口的数字环境光传感器IC。可以准确读取1-65535XL的环境照度。

image-20210308163608700

原理图如下:

image.png

管脚介绍

名称VCCGNDSCLSDAADDR
功能描述3~5V供电参考地IIC时钟线IIC数据线地址线
(2)温湿度传感器

​ 温湿度检测我们选取涂鸦的SHT30模块来实现。涂鸦三明治温湿度传感功能板为三明治开发板的应用部分,方便开发者快速实现温湿度硬件产品原型的一款开发板。功能板主要包含一颗 SENSIRION 温湿度传感器 SHT30-DIS,通过 I2C 协议进行通信,I2C时钟频率最高支持1MHz。

image-20210823103536447

关键器件介绍

器件说明
U1(SHT30-DIS)SENSIRION 温湿度传感器,工作电压 2.4~5.5V,湿度精度 ±2%RH,温度精度 ±0.3℃,封装 8 脚 DFN

涂鸦三明治温湿度传感功能板需要用到的管脚介绍

I/O说明
VCC电源供电脚
GND电源参考地
SCLI2C时钟信号
SDAI2C数据信号
INT告警信号,预留

电源技术要求

  • 电源供电电压参照传感器工作电压范围:2.4~5.5V

  • 非测量状态典型电流:0.2uA

  • 低功耗连续测量模式典型电流:800uA

    (1)原理图

    ​ 涂鸦三明治温湿度传感功能板的原理图如下所示:

温湿度传感功能板

(2)PCB图

​ 涂鸦三明治温湿度传感功能板的 PCB 如下图所示:

温湿度传感功能板

注意事项

  • 功能板为应用部分,需配合控制板与电源板使用。
  • 电源接口不要触碰 I/O 管脚,避免击穿模块对应 I/O 口。
  • 传感器本体附着灰尘与油污等会导致测量精度下降。
  • 传感器本体不能与清洁剂接触,例如洗板水。
  • 不能使用会释放化学分子的材料包装,否则可能受污染导致数据偏移或完全损坏。

3、无线通信部分

(1)LORA通信(发送部分)

​ LORA通信发送部分采用的是WPG公司的LLCC68的芯片。该芯片和SX1268管脚兼容。此次设计没有使用开关芯片来进行发送与接收模式的切换。直接使用双天线,采用半双工的通信方式。

image-20210823105202586

(2)LORA通信(接收部分)

​ LORA通信接收部分采用的是WPG公司的SX1268模块。image-20210823105948905

注意:SX1268和LLCC68管脚兼容,但参数设置有些区别。SX1268扩频因子可以支持到SF12,但LLCC68只能到SF11。所以在程序设计的时候要注意了。

SX1268参数如下图:

image-20210823111328952

LLCC68参数如下图:

image-20210823111733813

(3)WB3S通信

WB3S是由涂鸦智能开发的一款低功耗嵌入式Wi-Fi+蓝牙BLE双协议云模组。它由一个高集成度的无线射频芯片BK7231T和少量外围器件构成,内置了Wi-Fi网络协议栈和丰富的库函数。MCU通过串口和WB3S进行通信,采用透传的模式。

4、控制屏部分

​ 显示控制部分采用的是迪文的4.3寸串口屏。

​ 正面图:

image-20210823113755984

​ 背面图:

image-20210823113205652
​ 接口图:

image-20210823113311517

此次设计中界面设计的主界面效果如下图:

image-20210823114244754

MCU和显示屏通过串口通信,来实现控制和显示。

三、产品创建

  • 进入 涂鸦IoT平台,点击创建产品,选择标准类目->电工->开关。(当时为了测试用,所以选择一个标准类目,也可以在平台上其他品类中去创建也是可以的)
    image-20210823115534415
  • 选择自定义方案,输入产品名称,选择通讯协议为Wi-Fi+蓝牙,点击创建产品
  • 根据要实现的设备功能,创建好DP功能点。
    image-20210823115756972
  • 设定完功能点后,下一步点击设备面板,选择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图片格式。接着采用迪文的一款上位机软件进行显示和控制设计。

温湿度界面:

image-20210823163804336

image-20210824152538656

光照度界面:

image-20210824153113907

image-20210823164138158

节点控制界面:

image-20210823164537015

image-20210824152634111

控制屏界面设计完整工程:点此下载

驱动程序设计

调用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平台丰富它的功能,也可以更加方便的搭建更多智能产品原型,加速智能产品的开发流程。

  • 12
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值