这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » STM32H7S78-DK基于TCP-CLIENT的冷链管理

共8条 1/1 1 跳转至

STM32H7S78-DK基于TCP-CLIENT的冷链管理

助工
2024-08-12 20:37:23   被打赏 70 分(兑奖)     打赏

【前言】

STM32H7S78-DK移植LWIP-电子产品世界论坛 (eepw.com.cn)

在实现连网络连接后,移植TouchGFX,动态获取RTC时间,以及环境温湿度,并实现动态展示。

1、添加RTC外设,在STM32CubeMX中使能RTC:

image.png

添加RTC时间初始化后,系统会自动生成RTC的初始化。

2、添加tcpclient.c/h文件。代码如下:

/*
 * tcpclinet.c
 *
 *  Created on: Mar 17, 2024
 *      Author: liujianhua
 */

#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <time.h>

#include <stdio.h>
#include <string.h>
#include "main.h"
#include "SHT3x.h"
void TCP_Client_Init(void);
uint8_t send_buf_led1on[] = "led1on\n";
uint8_t send_buf_led2on[] = "led2on\n";

uint8_t send_buf_led1off[] = "led1off\n";
uint8_t send_buf_led2off[] = "led2off\n";
uint8_t send_buf_err[] = "errcmd\n";

extern RTC_HandleTypeDef hrtc;
extern SHT3x_ReadData SHT3x_Data;
int String2Int(char *str);//字符串转数字
void sntp_set_time(uint32_t sntp_time);
static void client_err(void *arg, err_t err)       //出现错误时调用这个函数,打印错误信息,并尝试重新连接
{
  printf("连接错误!!\n");
        printf("尝试重连!!\n");

  //连接失败的时候释放TCP控制块的内存
        printf("关闭连接,释放TCP控制块内存\n");
  //tcp_close(client_pcb);


  //重新连接
        printf("重新初始化客户端\n");
        TCP_Client_Init();

}


static err_t client_send(void *arg, struct tcp_pcb *tpcb)   //发送函数,调用了tcp_write函数
{
  uint8_t send1_buf[48]= {0};

  //发送数据到服务器
	sprintf(send1_buf,"Tem:%.1f,Hum:%.1f",SHT3x_Data.SHT3x_Temperature,SHT3x_Data.SHT3x_Humidity);
  tcp_write(tpcb, send1_buf, strlen(send1_buf), 1);

  return ERR_OK;
}

static err_t client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
	uint32_t set_time;

  if (p != NULL)
  {
    /* 接收数据*/
		
    tcp_recved(tpcb, p->tot_len);
    printf("收到:%s 长度为:%d\n",p->payload,p->tot_len);
    if((p->tot_len) == 7 )
    {
            if(strncmp("LED1OFF",p->payload,p->tot_len) == 0)
            {
//                    HAL_GPIO_WritePin(USER_LED1_GPIO_Port, USER_LED1_Pin, GPIO_PIN_SET);
                    tcp_write(tpcb, send_buf_led1off, sizeof(send_buf_led1off), 1);
            }
            else if(strncmp("LED2OFF",p->payload,p->tot_len) == 0)
            {
//                    HAL_GPIO_WritePin(USER_LED2_GPIO_Port, USER_LED2_Pin, GPIO_PIN_SET);
                    tcp_write(tpcb, send_buf_led2off, sizeof(send_buf_led2off), 1);
            }
    }
    else if((p->tot_len) == 6)
    {
            if(strncmp("LED1ON",p->payload,p->tot_len) == 0)
            {
//                    HAL_GPIO_WritePin(USER_LED1_GPIO_Port, USER_LED1_Pin, GPIO_PIN_RESET);
                    tcp_write(tpcb, send_buf_led1on, sizeof(send_buf_led1on), 1);
            }
            else if(strncmp("LED2ON",p->payload,p->tot_len) == 0)
            {
//                    HAL_GPIO_WritePin(USER_LED1_GPIO_Port, USER_LED2_Pin, GPIO_PIN_RESET);
                    tcp_write(tpcb, send_buf_led2on, sizeof(send_buf_led2on), 1);
            }
    }
		else if((p->tot_len) == 12)
		{
			set_time = String2Int(p->payload);
			sntp_set_time(set_time);
		}
    else
    {
            tcp_write(tpcb, send_buf_err, sizeof(send_buf_err), 1);
    }
    /* 返回接收到的数据*/
    memset(p->payload, 0 , p->tot_len);
    pbuf_free(p);
  }
  else if (err == ERR_OK)
  {
    //服务器断开连接
    printf("服务器断开连接!\n");
    tcp_close(tpcb);

    //重新连接
    TCP_Client_Init();
  }
  return ERR_OK;
}

static err_t client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
  printf("connected ok!\n");

  //注册一个周期性回调函数
  tcp_poll(pcb,client_send,2);

  //注册一个接收函数
  tcp_recv(pcb,client_recv);

  return ERR_OK;
}


void TCP_Client_Init(void)
{
  struct tcp_pcb *client_pcb = NULL;   //这一句一定要放在里面,否则会没用
  ip4_addr_t server_ip;     //因为客户端要主动去连接服务器,所以要知道服务器的IP地址
  /* 创建一个TCP控制块  */
  client_pcb = tcp_new();

  IP4_ADDR(&server_ip, 192,168,3,180);//合并IP地址

  printf("客户端开始连接!\n");

  //开始连接
  tcp_connect(client_pcb, &server_ip, 777, client_connected);
        ip_set_option(client_pcb, SOF_KEEPALIVE);

        printf("已经调用了tcp_connect函数\n");

  //注册异常处理
  tcp_err(client_pcb, client_err);
        printf("已经注册异常处理函数\n");
}

/*!
* @brief SNTP 获取时间戳的处理函数
*        执行条件:无
*
* @param [in] : sntp 获取的时间戳
*
* @retval: 无
*/
void sntp_set_time(uint32_t sntp_time)
{
	if(sntp_time == 0)
	{
		printf("sntp_set_time: wrong!@@\n");
		return;
	}
	
	printf("sntp_set_time: c00, enter!\n");
	printf("sntp_set_time: c01, get time = %u\n", sntp_time);

	struct tm *time;
	RTC_TimeTypeDef sTime = {0};
	RTC_DateTypeDef sDate = {0};

	sntp_time += (8 * 60 * 60); ///北京时间是东8区需偏移8小时

	time = localtime(&sntp_time);

	/*
	 * 设置 RTC 的 时间
	 */
	sTime.Hours = time->tm_hour;
	sTime.Minutes = time->tm_min;
	sTime.Seconds = time->tm_sec;
	sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
	sTime.StoreOperation = RTC_STOREOPERATION_RESET;
	if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
	{
		Error_Handler();
	}
	
	/*
	 * 设置 RTC 的 日期
	 */
	sDate.WeekDay = time->tm_wday;
	sDate.Month = (time->tm_mon) + 1;
	sDate.Date = time->tm_mday;
	sDate.Year = (time->tm_year) + 1900 - 2000;
	if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
	{
		Error_Handler();
	}

	printf("sntp_set_time: c02, decode time: 20%d-%02d-%02d %d:%d:%d\n", \
				sDate.Year, sDate.Month, sDate.Date, sTime.Hours, sTime.Minutes, sTime.Seconds);
	
	printf("sntp_set_time: c04, set rtc time done\n");
}

int String2Int(char *str)//字符串转数字 
{
    char flag = '+';//指示结果是否带符号 
    long res = 0;
    
    if(*str=='-')//字符串带负号 
    {
        ++str;//指向下一个字符 
        flag = '-';//将标志设为负号 
    } 
    //逐个字符转换,并累加到结果res 
    while(*str>=48 && *str<=57)//如果是数字才进行转换,数字0~9的ASCII码:48~57 
    {
        res = 10*res+  *str++-48;//字符'0'的ASCII码为48,48-48=0刚好转化为数字0 
    } 
 
    if(flag == '-')//处理是负数的情况
    {
        res = -res;
    }
		printf("chang:%d",res);
    return (int)res;
}

这个函数中,主要实现的功能为创建一个client_pcb,并绑定到指定的端口上。

注册一个连接成功的static err_t client_connected(void *arg, struct tcp_pcb *pcb, err_t err)。在连接成功之后,注册一个周期发送数据包的函数static err_t client_send(void *arg, struct tcp_pcb *tpcb)   //发送函数,调用了tcp_write函数。用tcp_poll进行调用,同时注册一个tcp_recv函数,用于处理接收数据的功能。

还需要注册一个连接错误的处理函数,用重新连接服务器。

在接收处理函数中,判断一下接收到的数据,我这里添加了一个用于处理设置时间的set_time函数,用于设置RTC时间,这样就可以通过tcp_server发送一个时间戳记,来实现远程的时间同步。

同时,我添加了一个sht3x的驱动库,用于驱动一个sht30的温湿度处理,在周期发送函数中,发送实时的湿湿度。

【TouchGFX】

在TouchGFX中,添加一个用于展示数据的屏幕,添加两个gauge,一个显示温度,一个显示湿度,同时添加日期、时间、IP地址。

image.png【代码添加】

在screenview.cpp中添加handleEvent周期函数,用于动态展示IP地址、日期时间,温湿度。

代码如下:

void ScreenVaccView::handleTickEvent ()

{
	int sht3x_sta;
	tickCounter++;
	if(tickCounter>6000)
	{
		tickCounter = 0;
	}

	

	 if (tickCounter % 60 == 0)
	{
		HAL_RTC_GetTime(&hrtc, &gSystemTime, RTC_FORMAT_BIN);//获取时间
		/* Get the RTC current Date */
		HAL_RTC_GetDate(&hrtc, &gSystemDate, RTC_FORMAT_BIN);//获取日期

		Unicode::snprintf(textDateBuffer, TEXTDATE_SIZE, "%4d-%02d-%02d", 2000 + gSystemDate.Year,gSystemDate.Month, gSystemDate.Date);
		textDate.invalidate();
		digitalClock.setTime24Hour(gSystemTime.Hours, gSystemTime.Minutes, gSystemTime.Seconds);
		
		
	}
	if (tickCounter % 120 == 0)
	{
		sht3x_sta = SHT3x_Get_TemperatureHumidity();
		if(0 == sht3x_sta)
		{	
			gaugeTmp.updateValue(SHT3x_Data.SHT3x_Temperature, 30);
			gaugeHum.updateValue(SHT3x_Data.SHT3x_Humidity, 30);
			Unicode::snprintfFloat(textTmpBuffer,20,"%.1f",SHT3x_Data.SHT3x_Temperature);
			Unicode::snprintfFloat(textHumBuffer,20,"%.1f",SHT3x_Data.SHT3x_Humidity);
			textTmp.invalidate();
			textHum.invalidate();
		}
	}
		


}

实现的效果:

image.png




关键词: STM32H7S     STM32CubeMAX     LwI    

专家
2024-08-13 06:57:17     打赏
2楼

看一下


院士
2024-08-14 20:04:34     打赏
3楼

效果真棒啊


院士
2024-08-14 20:05:50     打赏
4楼

话说这是使用的C++来编写的代码吗?


院士
2024-08-16 17:31:48     打赏
5楼

学习了谢谢分享。


菜鸟
2024-09-09 11:06:25     打赏
6楼

过来学习学习


工程师
2024-09-12 16:14:17     打赏
7楼

不错,看起来真棒


工程师
2024-10-11 09:38:14     打赏
8楼

嗯 ,不错


共8条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]