450 lines
13 KiB
C
450 lines
13 KiB
C
#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"
|
|
|
|
/* 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
|
|
|
|
/* TCP Server Configuration */
|
|
#define SERVER_IP_ADDR "192.168.1.116" /* PC IP Address */
|
|
#define SERVER_PORT 5588
|
|
|
|
/* Blink Thread */
|
|
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);
|
|
|
|
while (1)
|
|
{
|
|
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);
|
|
// osal_log_i("System Running...");
|
|
osal_thread_mdelay(1000);
|
|
}
|
|
}
|
|
|
|
/* Ethernet Input Thread */
|
|
static void ethernet_input_entry(void *parameter)
|
|
{
|
|
while(1)
|
|
{
|
|
/* Block until packet received */
|
|
ethernetif_input(&gnetif);
|
|
}
|
|
}
|
|
|
|
/* Ping Configuration */
|
|
#define PING_ID 0xAFAF
|
|
#define PING_DATA_SIZE 32
|
|
#define PING_RCV_TIMEO 5000 // 5 seconds
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* TCP Client Thread */
|
|
static void tcp_client_entry(void *parameter)
|
|
{
|
|
int sock = -1;
|
|
struct sockaddr_in server_addr;
|
|
char *send_data = "Hello from RT-Thread Nano TCP Client\n";
|
|
char recv_data[128];
|
|
int bytes_received;
|
|
|
|
/* Wait for IP address ready */
|
|
if (osal_sem_take(sem_ip_ready, OSAL_WAIT_FOREVER) != OSAL_OK)
|
|
{
|
|
osal_log_e("Failed to take IP ready semaphore");
|
|
return;
|
|
}
|
|
|
|
osal_log_i("TCP Client Starting...");
|
|
|
|
while (1)
|
|
{
|
|
/* Check Link Status */
|
|
if (!netif_is_link_up(&gnetif)) {
|
|
osal_log_w("Link down, waiting for link up...");
|
|
osal_thread_mdelay(1000);
|
|
continue;
|
|
}
|
|
|
|
/* Create socket */
|
|
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
osal_log_e("Socket error");
|
|
osal_thread_mdelay(1000);
|
|
continue;
|
|
}
|
|
|
|
/* Set non-blocking mode */
|
|
int flags = fcntl(sock, F_GETFL, 0);
|
|
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = htons(SERVER_PORT);
|
|
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR);
|
|
memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
|
|
|
|
osal_log_i("Connecting to server %s:%d...", SERVER_IP_ADDR, SERVER_PORT);
|
|
|
|
int ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
|
|
if (ret == -1 && errno != EINPROGRESS)
|
|
{
|
|
osal_log_e("Connect failed");
|
|
closesocket(sock);
|
|
osal_thread_mdelay(2000);
|
|
continue;
|
|
}
|
|
|
|
/* Use select to wait for connection completion */
|
|
fd_set wset;
|
|
struct timeval tv;
|
|
FD_ZERO(&wset);
|
|
FD_SET(sock, &wset);
|
|
tv.tv_sec = 5;
|
|
tv.tv_usec = 0;
|
|
|
|
if (select(sock + 1, NULL, &wset, NULL, &tv) > 0)
|
|
{
|
|
int error = 0;
|
|
socklen_t len = sizeof(error);
|
|
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
|
|
if (error != 0)
|
|
{
|
|
osal_log_e("Connect failed: %d", error);
|
|
closesocket(sock);
|
|
osal_thread_mdelay(2000);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
osal_log_e("Connect timeout");
|
|
closesocket(sock);
|
|
continue;
|
|
}
|
|
|
|
osal_log_i("Connected to server!");
|
|
|
|
/* Send greeting */
|
|
send(sock, send_data, strlen(send_data), 0);
|
|
|
|
while (1)
|
|
{
|
|
/* Check Link Status */
|
|
if (!netif_is_link_up(&gnetif)) {
|
|
osal_log_w("Link lost during connection");
|
|
break;
|
|
}
|
|
|
|
/* Send heartbeat every 1 second */
|
|
if (send(sock, "Heartbeat", 9, 0) < 0) {
|
|
if (errno != EWOULDBLOCK) {
|
|
osal_log_e("Send error: %d", errno);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fd_set rset;
|
|
FD_ZERO(&rset);
|
|
FD_SET(sock, &rset);
|
|
tv.tv_sec = 1; /* Check periodically */
|
|
tv.tv_usec = 0;
|
|
|
|
int n = select(sock + 1, &rset, NULL, NULL, &tv);
|
|
if (n > 0) {
|
|
bytes_received = recv(sock, recv_data, sizeof(recv_data) - 1, 0);
|
|
if (bytes_received > 0)
|
|
{
|
|
recv_data[bytes_received] = '\0';
|
|
osal_log_i("Received: %s", recv_data);
|
|
}
|
|
else if (bytes_received == 0)
|
|
{
|
|
osal_log_w("Connection closed by server");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (errno != EWOULDBLOCK) {
|
|
osal_log_e("Recv error: %d", errno);
|
|
break;
|
|
}
|
|
}
|
|
} else if (n < 0) {
|
|
osal_log_e("Select error: %d", errno);
|
|
break;
|
|
}
|
|
/* n == 0 is timeout, continue loop */
|
|
}
|
|
|
|
closesocket(sock);
|
|
osal_thread_mdelay(2000);
|
|
}
|
|
}
|
|
|
|
/* 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!");
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 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, ðernetif_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");
|
|
}
|
|
|
|
/* Create TCP Client Thread */
|
|
tid = osal_thread_create("tcp_client", tcp_client_entry, NULL, 2048, 15);
|
|
if (tid != NULL)
|
|
{
|
|
osal_thread_start(tid);
|
|
rt_kprintf("Thread 'tcp_client' created.\n");
|
|
}
|
|
else
|
|
{
|
|
rt_kprintf("Failed to create thread 'tcp_client'\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;
|
|
}
|