Files
ETH_TCP_Demo/app/main.c
2026-03-04 08:50:04 +08:00

497 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <rtthread.h>
#include "osal.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "lwip/dhcp.h"
#include "lwip/sockets.h"
#include "drv_eth.h"
#include "lwip/icmp.h"
#include "lwip/inet_chksum.h"
#include <string.h>
#include "lwip/prot/ip4.h"
#include "sht40.h"
#include "tcp_server.h"
#include "event_queue.h"
#include "event_handler.h"
#include "event_trigger.h"
#include "transaction.h"
#include "state_manager.h"
#include "error_handler.h"
/* Utility macros */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
/* Network Interface */
struct netif gnetif;
osal_sem_t sem_ip_ready = NULL;
/* Static IP Configuration (Fallback) */
#define STATIC_IP_ADDR0 192
#define STATIC_IP_ADDR1 168
#define STATIC_IP_ADDR2 1
#define STATIC_IP_ADDR3 10
#define STATIC_NETMASK0 255
#define STATIC_NETMASK1 255
#define STATIC_NETMASK2 255
#define STATIC_NETMASK3 0
#define STATIC_GW_ADDR0 192
#define STATIC_GW_ADDR1 168
#define STATIC_GW_ADDR2 1
#define STATIC_GW_ADDR3 1
/* Blink Thread */
/**
* @brief LED闪烁任务入口函数
* @param parameter 任务参数(本函数中未使用)
* @note 该函数初始化PA6引脚为输出模式并在循环中翻转该引脚状态实现LED闪烁效果
*/
static void blink_entry(void *parameter)
{
/* Initialize PA6 */
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 初始化为关闭状态 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
while (1)
{
/* 检查数据上传成功标志 */
if (data_upload_success)
{
/* 亮LED */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
osal_thread_mdelay(500); /* 亮500ms */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
/* 重置标志 */
data_upload_success = 0;
}
osal_thread_mdelay(100);
}
}
/* Ethernet Input Thread */
/**
* @brief 以太网输入任务入口函数
* @param parameter 任务参数(本函数中未使用)
* @note 该函数在循环中调用ethernetif_input处理接收到的以太网数据包
*/
static void ethernet_input_entry(void *parameter)
{
while(1)
{
/* Block until packet received */
ethernetif_input(&gnetif);
}
}
/**
* @brief Ping配置参数
* @note 该结构体定义了Ping操作的相关参数包括ID、数据大小、接收超时时间
*/
#define PING_ID 0xAFAF
#define PING_DATA_SIZE 32
#define PING_RCV_TIMEO 5000 // 5 seconds
/**
* @brief 准备Ping回显包
* @param iecho 指向icmp_echo_hdr结构体的指针用于存储回显包
* @param len 回显包的总长度包括icmp_echo_hdr头和数据
* @param seq Ping包的序列号
* @note 该函数填充icmp_echo_hdr结构体设置类型为ICMP_ECHOID为PING_ID序列号为seq
* 并填充数据部分为0到(data_len-1)的连续字节
*/
static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len, u16_t seq)
{
size_t i;
size_t data_len = len - sizeof(struct icmp_echo_hdr);
ICMPH_TYPE_SET(iecho, ICMP_ECHO);
ICMPH_CODE_SET(iecho, 0);
iecho->chksum = 0;
iecho->id = PING_ID;
iecho->seqno = lwip_htons(seq);
for(i = 0; i < data_len; i++) {
((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
}
iecho->chksum = inet_chksum(iecho, len);
}
/**
* @brief 检查目标IP是否可达
* @param target_ip 目标IP地址字符串
* @return 如果目标IP可达返回1否则返回0
* @note 该函数通过发送ICMP Echo请求包到目标IP并等待响应来检查目标IP是否可达
*/
static int ping_check(const char *target_ip)
{
int s;
struct timeval timeout;
struct sockaddr_in to;
struct icmp_echo_hdr *iecho;
size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
int ret = 0;
int seq_num = 0;
/* Create raw socket */
s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (s < 0) {
osal_log_e("Ping: Failed to create socket");
return 0;
}
/* Set receive timeout */
timeout.tv_sec = PING_RCV_TIMEO / 1000;
timeout.tv_usec = (PING_RCV_TIMEO % 1000) * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
/* Prepare destination address */
memset(&to, 0, sizeof(to));
to.sin_len = sizeof(to);
to.sin_family = AF_INET;
to.sin_addr.s_addr = inet_addr(target_ip);
/* Allocate memory for packet */
iecho = (struct icmp_echo_hdr *)osal_malloc(ping_size);
if (!iecho) {
osal_log_e("Ping: Failed to allocate memory");
closesocket(s);
return 0;
}
osal_log_i("Ping: Pinging %s...", target_ip);
/* Try to ping a few times */
for (int i = 0; i < 3; i++) {
ping_prepare_echo(iecho, (u16_t)ping_size, ++seq_num);
if (sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)) <= 0) {
osal_log_e("Ping: Send failed");
continue;
}
char buf[64];
struct sockaddr_in from;
socklen_t fromlen = sizeof(from);
int len;
while ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen)) > 0) {
if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) {
struct ip_hdr *iphdr = (struct ip_hdr *)buf;
struct icmp_echo_hdr *iecho_reply = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
if (ICMPH_TYPE(iecho_reply) != ICMP_ER) {
continue;
}
if (iecho_reply->id == PING_ID && iecho_reply->seqno == lwip_htons(seq_num)) {
osal_log_i("Ping: Reply from %s", inet_ntoa(from.sin_addr));
ret = 1;
goto exit;
}
}
}
}
osal_log_w("Ping: Timeout or no reply");
exit:
osal_free(iecho);
closesocket(s);
return ret;
}
/* Network Monitor Thread (DHCP & Fallback) */
static void network_monitor_entry(void *parameter)
{
ip4_addr_t ipaddr, netmask, gw;
int dhcp_timeout = 100; /* 100 * 100ms = 10 seconds */
osal_log_i("Starting DHCP...");
dhcp_start(&gnetif);
while (dhcp_timeout > 0)
{
if (gnetif.ip_addr.addr != 0)
{
osal_log_i("DHCP Success!");
/* 触发网络连接事件 */
event_trigger(EVENT_TYPE_NETWORK_CONNECTED, EVENT_PRIORITY_NORMAL, NULL, 0);
break;
}
osal_thread_mdelay(100);
dhcp_timeout--;
/* Print a dot every second */
if (dhcp_timeout % 10 == 0) osal_kprintf(".");
}
osal_kprintf("\n");
if (gnetif.ip_addr.addr == 0)
{
osal_log_w("DHCP Timeout! Fallback to Static IP.");
dhcp_stop(&gnetif);
IP4_ADDR(&ipaddr, STATIC_IP_ADDR0, STATIC_IP_ADDR1, STATIC_IP_ADDR2, STATIC_IP_ADDR3);
IP4_ADDR(&netmask, STATIC_NETMASK0, STATIC_NETMASK1, STATIC_NETMASK2, STATIC_NETMASK3);
IP4_ADDR(&gw, STATIC_GW_ADDR0, STATIC_GW_ADDR1, STATIC_GW_ADDR2, STATIC_GW_ADDR3);
netif_set_addr(&gnetif, &ipaddr, &netmask, &gw);
netif_set_up(&gnetif);
/* 触发网络连接事件 */
event_trigger(EVENT_TYPE_NETWORK_CONNECTED, EVENT_PRIORITY_NORMAL, NULL, 0);
}
osal_log_i("IP Address: %d.%d.%d.%d", ip4_addr1(&gnetif.ip_addr), ip4_addr2(&gnetif.ip_addr), ip4_addr3(&gnetif.ip_addr), ip4_addr4(&gnetif.ip_addr));
osal_log_i("Netmask: %d.%d.%d.%d", ip4_addr1(&gnetif.netmask), ip4_addr2(&gnetif.netmask), ip4_addr3(&gnetif.netmask), ip4_addr4(&gnetif.netmask));
osal_log_i("Gateway: %d.%d.%d.%d", ip4_addr1(&gnetif.gw), ip4_addr2(&gnetif.gw), ip4_addr3(&gnetif.gw), ip4_addr4(&gnetif.gw));
/* Notify TCP Client */
osal_sem_release(sem_ip_ready);
/* Periodic Link Check */
while(1)
{
ethernet_link_check_state(&gnetif);
osal_thread_mdelay(1000);
}
}
/* 事件处理函数 */
static int sensor_data_handler(event_t *event, void *user_data)
{
osal_log_i("Handling sensor data event");
/* 这里可以添加传感器数据处理逻辑 */
return 0;
}
static int network_connected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling network connected event");
state_manager_set_network_state(NETWORK_STATE_CONNECTED);
return 0;
}
static int network_disconnected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling network disconnected event");
state_manager_set_network_state(NETWORK_STATE_DISCONNECTED);
return 0;
}
static int tcp_client_connected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling TCP client connected event");
state_manager_set_tcp_state(TCP_STATE_CONNECTED);
return 0;
}
static int tcp_client_disconnected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling TCP client disconnected event");
state_manager_set_tcp_state(TCP_STATE_LISTENING);
return 0;
}
static int timer_handler(event_t *event, void *user_data)
{
osal_log_i("Handling timer event");
/* 这里可以添加定时任务逻辑 */
return 0;
}
static int error_handler(event_t *event, void *user_data)
{
osal_log_i("Handling error event");
error_code_t error = ERROR_UNKNOWN;
if (event->data != NULL)
{
error = *(error_code_t *)event->data;
}
error_handler_process(error, user_data);
return 0;
}
int main(void)
{
osal_thread_t tid;
/* OSAL Initialization */
osal_init();
rt_kprintf("Main: OSAL Log Level = %d\n", OSAL_LOG_LEVEL);
osal_log_i("Test osal_log_i from main");
/* Initialize event queue */
if (event_queue_init() != 0)
{
osal_log_e("Failed to initialize event queue");
return -1;
}
/* Initialize event handler */
if (event_handler_init() != 0)
{
osal_log_e("Failed to initialize event handler");
return -1;
}
/* Initialize transaction management */
if (transaction_init() != 0)
{
osal_log_e("Failed to initialize transaction management");
return -1;
}
/* Initialize state manager */
if (state_manager_init() != 0)
{
osal_log_e("Failed to initialize state manager");
return -1;
}
/* Initialize error handler */
if (error_handler_init() != 0)
{
osal_log_e("Failed to initialize error handler");
return -1;
}
/* Register event handlers */
event_handler_register(EVENT_TYPE_SENSOR_DATA, sensor_data_handler, NULL);
event_handler_register(EVENT_TYPE_NETWORK_CONNECTED, network_connected_handler, NULL);
event_handler_register(EVENT_TYPE_NETWORK_DISCONNECTED, network_disconnected_handler, NULL);
event_handler_register(EVENT_TYPE_TCP_CLIENT_CONNECTED, tcp_client_connected_handler, NULL);
event_handler_register(EVENT_TYPE_TCP_CLIENT_DISCONNECTED, tcp_client_disconnected_handler, NULL);
event_handler_register(EVENT_TYPE_TIMER, timer_handler, NULL);
event_handler_register(EVENT_TYPE_ERROR, error_handler, NULL);
/* Create event dispatch thread */
tid = osal_thread_create("event_dispatch", event_dispatch_thread, NULL, 1024, 8);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'event_dispatch' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'event_dispatch'\n");
}
/* Initialize TCP/IP stack */
rt_kprintf("Initializing TCP/IP stack...\n");
tcpip_init(NULL, NULL);
rt_kprintf("TCP/IP stack initialized.\n");
/* Initialize Network Interface */
ip4_addr_t ipaddr, netmask, gw;
/* Initialize with 0.0.0.0 */
IP4_ADDR(&ipaddr, 0, 0, 0, 0);
IP4_ADDR(&netmask, 0, 0, 0, 0);
IP4_ADDR(&gw, 0, 0, 0, 0);
/* Add Network Interface */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);
netif_set_default(&gnetif);
netif_set_up(&gnetif);
sem_ip_ready = osal_sem_create("sem_ip", 0);
/* Create Network Monitor Thread (DHCP) */
tid = osal_thread_create("net_mon", network_monitor_entry, NULL, 1024, 12);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'net_mon' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'net_mon'\n");
}
/* Create Ethernet Input Thread */
/* Increased priority to 6 (Higher than TCPIP thread which is 10) for better responsiveness */
tid = osal_thread_create("eth_input", ethernet_input_entry, NULL, 1536, 6);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'eth_input' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'eth_input'\n");
}
/* Initialize SHT40 sensor */
if (sht40_init() != 0)
{
osal_log_e("SHT40 sensor initialization failed");
error_code_t error = ERROR_SENSOR;
event_trigger(EVENT_TYPE_ERROR, EVENT_PRIORITY_HIGH, &error, sizeof(error));
}
else
{
osal_log_i("SHT40 sensor initialized successfully");
state_manager_set_sensor_state(SENSOR_STATE_READY);
/* Use heater once during initialization for self-calibration */
osal_log_i("Performing SHT40 self-calibration using heater...");
if (sht40_heater_enable(1) == 0) // Use medium power (110mW)
{
osal_log_i("SHT40 self-calibration completed successfully");
}
else
{
osal_log_e("SHT40 self-calibration failed");
error_code_t error = ERROR_SENSOR;
event_trigger(EVENT_TYPE_ERROR, EVENT_PRIORITY_HIGH, &error, sizeof(error));
}
}
/* Create TCP Server Thread */
tid = osal_thread_create("tcp_server", tcp_server_entry, NULL, 2048, 15);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'tcp_server' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'tcp_server'\n");
}
/* Create Blink/Status Thread */
tid = osal_thread_create("blink", blink_entry, NULL, 1024, 20);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'blink' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'blink'\n");
}
rt_size_t total, used, max_used;
rt_memory_info(&total, &used, &max_used);
rt_kprintf("Memory Info: Total=%d, Used=%d, MaxUsed=%d\n", total, used, max_used);
/* OSAL Start */
osal_start();
return 0;
}