From 5cad26715761b58144bc9dffe39b2f878c967ab8 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 31 May 2024 18:35:48 +0200 Subject: [PATCH 01/27] Review of the note to do for NTP and DNS. I add some code for read the datetime and give a way for RTC or send datetime via ethershield log --- src/ip_arp_udp_tcp.c | 96 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 8 deletions(-) diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index c882c30..b1d2c65 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -762,9 +762,93 @@ uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) } // copy time from the transmit time stamp field: *time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); + + //for the all paquet, this is where are the data: + //((uint32_t)buf[82] << 24) | ((uint32_t)buf[83] << 16) | ((uint32_t)buf[84] << 8) | (uint32_t)buf[85]; + + // Convert NTP timestamp to UNIX timestamp (seconds since 1970) + // by subtracting the seconds between 1900 and 1970. + // Note: This only takes into account the seconds part of the NTP timestamp and ignores fractional seconds. + if (*time > 2208988800UL) { + *time -= 2208988800UL; + } else { + // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) + *time = 0; + + // Create structures for time and date + // RTC_TimeTypeDef sTime = {0}; + // RTC_DateTypeDef sDate = {0}; + + // Calculate time components + uint32_t seconds = *time % 60; + uint32_t minutes = (*time / 60) % 60; + uint32_t hours = (*time / 3600) % 24; + uint32_t days = *time / 86400; // Total number of days since 1970 + + // The start of 1970 was a Thursday (day 4 of the week) + uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday + + // Determine the year + uint32_t year = 1970; + while (days >= 365) { + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + if (days < 366) break; + days -= 366; + } else { + if (days < 365) break; + days -= 365; + } + year++; + } + + // Determine the month and the day of the month + uint32_t month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + month_lengths[1] = 29; // February has 29 days in leap years + } + + uint32_t month = 0; + for (month = 0; month < 12; month++) { + if (days < month_lengths[month]) { + break; + } + days -= month_lengths[month]; + } + month++; // months are 1-12 + uint32_t dayOfMonth = days + 1; // days are 1-31 + + // Configure time +// sTime.Hours = hours; +// sTime.Minutes = minutes; +// sTime.Seconds = seconds; +// sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; +// sTime.StoreOperation = RTC_STOREOPERATION_RESET; + + // Configure date +// sDate.WeekDay = dayOfWeek; +// sDate.Month = month; +// sDate.Date = dayOfMonth; +// sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 + + // Update the RTC +// HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); +// HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); + +// char timeStr[32]; // Adjusted size to hold formatted date and time +// sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", +// sDate.Date, +// sDate.Month, +// year, +// sTime.Hours, +// sTime.Minutes, +// sTime.Seconds); +// #if ETHERSHIELD_DEBUG +// ethershieldDebug(timeStr); +// #endif return(1); + } } -#endif +#endif // NTP_client #ifdef UDP_client // -------------------- send a spontanious UDP packet to a server @@ -1347,16 +1431,12 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { if (eth_type_is_ip_and_my_ip(buf, plen) == 0) { return (0); } - #ifdef NTP_client - // TODO - does this work? - // If NTP response, drop out to have it processed elsewhere +#ifdef NTP_client if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { return (UDP_DATA_P); } - #endif // NTP_client +#endif // NTP_client #ifdef DNS_client - // TODO - does this work? - // If DNS response, drop out to have it processed elsewhere if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { return (UDP_DATA_P); } @@ -1452,7 +1532,7 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { if (tcp_client_state == 4) { //&& len>0){ // our first real data packet #if ETHERSHIELD_DEBUG - // ethershieldDebug( "First Data Packet\n"); + //ethershieldDebug( "First Data Packet\n"); #endif // Removed this as there is no code to handle state 4. Only 1st packet will be available. //tcp_client_state=4; From da789ea166e298d6a736c0fcef995dbf94f66d42 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 31 May 2024 19:19:49 +0200 Subject: [PATCH 02/27] minor cleanup --- src/ip_arp_udp_tcp.c | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index b1d2c65..888bc37 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -711,7 +711,6 @@ void __attribute__((weak)) ES_PingCallback(void) #ifdef NTP_client // ntp udp packet // See http://tools.ietf.org/html/rfc958 for details -// void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) { uint16_t ck; @@ -762,7 +761,6 @@ uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) } // copy time from the transmit time stamp field: *time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); - //for the all paquet, this is where are the data: //((uint32_t)buf[82] << 24) | ((uint32_t)buf[83] << 16) | ((uint32_t)buf[84] << 8) | (uint32_t)buf[85]; @@ -776,8 +774,8 @@ uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) *time = 0; // Create structures for time and date - // RTC_TimeTypeDef sTime = {0}; - // RTC_DateTypeDef sDate = {0}; +// RTC_TimeTypeDef sTime = {0}; +// RTC_DateTypeDef sDate = {0}; // Calculate time components uint32_t seconds = *time % 60; @@ -909,25 +907,15 @@ void send_udp_transmit(uint8_t *buf,uint16_t datalen) void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport) { send_udp_prepare(buf,sport, dip, dport); - // limit the length: Why??? ADL - // NOTE: We dont need such silly limits on powerful STM32s - // if (datalen>220){ - // datalen=220; - // } - // copy the data: memcpy(&buf[UDP_DATA_P], data, datalen); - // send_udp_transmit(buf,datalen); } #endif // UDP_client #ifdef WOL_client -// -------------------- special code to make a WOL packet - // A WOL (Wake on Lan) packet is a UDP packet to the broadcast // address and UDP port 9. The data part contains 6x FF followed by // 16 times the mac address of the host to wake-up -// void send_wol(uint8_t *buf,uint8_t *wolmac) { uint16_t ck; @@ -974,7 +962,6 @@ void send_wol(uint8_t *buf,uint8_t *wolmac) // make a arp request void client_arp_whohas(uint8_t *buf,uint8_t *ip_we_search) { - // memset(&buf[ETH_DST_MAC], 0xFF, 6); memcpy(&buf[ETH_SRC_MAC], macaddr, 6); buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; @@ -1001,7 +988,6 @@ uint8_t client_waiting_gw(void) return(1); } - // store the mac addr from an arp reply // no len check here, you must first call eth_type_is_arp_and_my_ip uint8_t client_store_gw_mac(uint8_t *buf) @@ -1009,7 +995,6 @@ uint8_t client_store_gw_mac(uint8_t *buf) if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4)) { return 0; } - memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); return 1; } @@ -1425,23 +1410,21 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { } #endif // NTP_client||UDP_client||TCP_client||PING_client return (0); - } // check if ip packets are for us: if (eth_type_is_ip_and_my_ip(buf, plen) == 0) { return (0); } -#ifdef NTP_client + #ifdef NTP_client if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { return (UDP_DATA_P); } -#endif // NTP_client + #endif // NTP_client #ifdef DNS_client if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { return (UDP_DATA_P); } - #endif - + #endif // NTP_client if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) { if (icmp_callback) { ( * icmp_callback)( & (buf[IP_SRC_P])); @@ -1479,7 +1462,6 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { } // Determine what to do with packed depending on state - len = get_tcp_data_len(buf); if (tcp_client_state == 2) { if ((buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)) { @@ -1532,7 +1514,7 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { if (tcp_client_state == 4) { //&& len>0){ // our first real data packet #if ETHERSHIELD_DEBUG - //ethershieldDebug( "First Data Packet\n"); + ethershieldDebug( "First Data Packet\n"); #endif // Removed this as there is no code to handle state 4. Only 1st packet will be available. //tcp_client_state=4; @@ -1567,7 +1549,7 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { if (tcp_client_state == 3) { // && len>0){ // our first real data packet #if ETHERSHIELD_DEBUG - // ethershieldDebug( "First Data Packet\n"); + ethershieldDebug( "First Data Packet\n"); #endif // Removed this as there is no code to handle state 4. Only 1st packet will be available. tcp_client_state = 4; @@ -1626,7 +1608,6 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { return (0); } #endif // WWW_client||TCP_client - // // tcp port web server start if (buf[TCP_DST_PORT_H_P] == wwwport_h && buf[TCP_DST_PORT_L_P] == wwwport_l) { if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) { @@ -1658,7 +1639,6 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { } return (0); } - #endif /* end of ip_arp_udp.c */ From 33b898a215ce8d35b21c64125347c71b6ace330c Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 31 May 2024 21:16:25 +0200 Subject: [PATCH 03/27] I add the function ES_FullConnection on the file EtherShield for easy to use of a complet connection of internet. DCHP, DNS, Gateway, NTP ... --- README.md | 13 +++++++++++++ inc/EtherShield.h | 1 + src/EtherShield.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/README.md b/README.md index fba821f..13b02c0 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,19 @@ If you need to use another STM32 MCU then don't forget to edit file [`inc/stm32i * Either just copy it into your project and set up your project to be built with this library. * Or use the [stm32-cmake](https://github.com/ObKo/stm32-cmake) (see the next section). + +### News on branch V3 + +Quick Start for Full Network Connection +To quickly set up a complete network connection (DHCP, DNS, Gateway, etc.), use the following function: +`void ES_FullConnection();` in the file: `EtherShield.c` +Call this fonction just before the while and ping your device ! + +and in your while think to use this for treat everything from your network : +`void paquetweb() { + packetloop_icmp_tcp(packet, enc28j60PacketReceive(200, packet)); +}` + ## CMake This project based on [stm32-cmake](https://github.com/ObKo/stm32-cmake), read it's readme and install, then set appropriate environment variables, see [`stm32-env-example.ps1`](stm32-env-example.ps1) diff --git a/inc/EtherShield.h b/inc/EtherShield.h index d4701e8..98f4d69 100644 --- a/inc/EtherShield.h +++ b/inc/EtherShield.h @@ -31,6 +31,7 @@ #include "ip_arp_udp_tcp.h" #include "net.h" +void ES_FullConnection(); void ES_enc28j60SpiInit( SPI_HandleTypeDef *hspi ); void ES_enc28j60Init( uint8_t* macaddr); void ES_enc28j60clkout(uint8_t clk); diff --git a/src/EtherShield.c b/src/EtherShield.c index 38d7d6b..955deb5 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -59,6 +59,43 @@ The file should be extracted to the sketchbook/libraries/ folder so that there i #include "EtherShield.h" +#define BUFFER_SIZE 500 + +void ES_FullConnection(SPI_HandleTypeDef *hspi) { + enc28j60_set_spi(hspi); + enc28j60Init( macaddrin ); + enc28j60clkout(3); + HAL_Delay(10); + + int f; + for( f=0; f<3; f++ ) { + // 0x880 is PHLCON LEDB=on, LEDA=on + // enc28j60PhyWrite(PHLCON,0b0011 1000 1000 00 00); + enc28j60PhyWrite(PHLCON,0x3880); + HAL_Delay(500); + + // 0x990 is PHLCON LEDB=off, LEDA=off + // enc28j60PhyWrite(PHLCON,0b0011 1001 1001 00 00); + enc28j60PhyWrite(PHLCON,0x3990); + HAL_Delay(500); + } + + // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit + // enc28j60PhyWrite(PHLCON,0b0011 0100 0111 01 10); + enc28j60PhyWrite(PHLCON,0x3476); + HAL_Delay(100); + static uint8_t bufDHCP[BUFFER_SIZE]; + allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); + client_set_gwip(gwipin); + static uint8_t bufDNS[BUFFER_SIZE]; + dnslkup_request(bufDNS, hostname); + udp_client_check_for_dns_answer(bufDNS, plen); + dnslkup_set_dnsip(dnsipaddr); + resolveHostname(bufDNS, BUFFER_SIZE, hostname); + static uint8_t bufNTP[BUFFER_SIZE]; + ES_client_ntp_request(bufNTP, ntpip, 123); +} + /** * Initialise SPI, separate from main initialisation so that * multiple SPI devices can be used together From 757d6c15130dcad4292871fc79898879f5a9595d Mon Sep 17 00:00:00 2001 From: DtNeo Date: Sat, 1 Jun 2024 18:05:46 +0200 Subject: [PATCH 04/27] paquetweb will be ES_ProcessWebPacket in the futur. A function for ready new entrancy paquets --- inc/EtherShield.h | 1 + src/EtherShield.c | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/inc/EtherShield.h b/inc/EtherShield.h index 98f4d69..1747c37 100644 --- a/inc/EtherShield.h +++ b/inc/EtherShield.h @@ -32,6 +32,7 @@ #include "net.h" void ES_FullConnection(); +void paquetweb(); void ES_enc28j60SpiInit( SPI_HandleTypeDef *hspi ); void ES_enc28j60Init( uint8_t* macaddr); void ES_enc28j60clkout(uint8_t clk); diff --git a/src/EtherShield.c b/src/EtherShield.c index 955deb5..e86f370 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -44,7 +44,7 @@ The file should be extracted to the sketchbook/libraries/ folder so that there i Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - +#include "defines.h" #include "enc28j60.h" #include "ip_arp_udp_tcp.h" #include "websrv_help_functions.h" @@ -63,8 +63,8 @@ The file should be extracted to the sketchbook/libraries/ folder so that there i void ES_FullConnection(SPI_HandleTypeDef *hspi) { enc28j60_set_spi(hspi); - enc28j60Init( macaddrin ); - enc28j60clkout(3); + enc28j60Init(macaddrin); + enc28j60clkout(2); HAL_Delay(10); int f; @@ -96,6 +96,10 @@ void ES_FullConnection(SPI_HandleTypeDef *hspi) { ES_client_ntp_request(bufNTP, ntpip, 123); } +void paquetweb() { + static uint8_t packet[500]; + packetloop_icmp_tcp(packet, enc28j60PacketReceive(500, packet)); +} /** * Initialise SPI, separate from main initialisation so that * multiple SPI devices can be used together From f3d49f5942c9919b483cde64d0fa00ff46e73c33 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Sat, 1 Jun 2024 18:05:46 +0200 Subject: [PATCH 05/27] udplogs --- src/ip_arp_udp_tcp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index 888bc37..97ada5a 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -28,9 +28,18 @@ #if ETHERSHIELD_DEBUG void ethershieldDebug(char *message) { printf("%s\r\n", message); + udpLog2("ETHERSHIELD_DEBUG", message); } #endif +void udpLog2(char* alerte, char* text) { + static uint8_t udp[500]; + char data[450]; + snprintf(data, 450, "Project %s - %s", alerte, text); + send_udp(udp, data, strlen(data), sport, dip, dport); +} + + // Web server port, used when implementing webserver static uint8_t wwwport_l=80; // server port static uint8_t wwwport_h=0; // Note: never use same as TCPCLIENT_SRC_PORT_H From f33378cb604043340f36454a87563227f379c2eb Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 30 May 2024 18:35:48 +0200 Subject: [PATCH 06/27] includes --- inc/EtherShield.h | 2 +- inc/dhcp.h | 6 ++---- inc/dnslkup.h | 5 ++--- inc/enc28j60.h | 2 +- inc/ip_arp_udp_tcp.h | 3 +-- inc/net.h | 2 +- inc/websrv_help_functions.h | 6 +----- src/dhcp.c | 4 +--- src/dnslkup.c | 3 +-- src/enc28j60.c | 1 + src/ip_arp_udp_tcp.c | 4 +--- src/websrv_help_functions.c | 2 +- 12 files changed, 14 insertions(+), 26 deletions(-) diff --git a/inc/EtherShield.h b/inc/EtherShield.h index 1747c37..f1f5bc6 100644 --- a/inc/EtherShield.h +++ b/inc/EtherShield.h @@ -20,7 +20,7 @@ #ifndef ETHERSHIELD_H #define ETHERSHIELD_H -#include "stm32includes.h" +#include "defines.h" #define bool _Bool #define TRUE 1 diff --git a/inc/dhcp.h b/inc/dhcp.h index f3ff66c..6bafed9 100644 --- a/inc/dhcp.h +++ b/inc/dhcp.h @@ -12,13 +12,11 @@ #ifndef DHCP_H #define DHCP_H -#include "stm32includes.h" +#include "defines.h" -// to use this you need to enable UDP_client in the file ip_config.h +// to use this you need to enable UDP_client in the file defines.h #if defined (UDP_client) -// Some defines - #define DHCP_BOOTREQUEST 1 #define DHCP_BOOTRESPONSE 2 diff --git a/inc/dnslkup.h b/inc/dnslkup.h index b6d1202..861f4f9 100644 --- a/inc/dnslkup.h +++ b/inc/dnslkup.h @@ -12,10 +12,9 @@ #ifndef DNSLKUP_H #define DNSLKUP_H -#include "stm32includes.h" +#include "defines.h" -// to use this you need to enable UDP_client in the file ip_config.h -// +// to use this you need to enable UDP_client in the file defines.h #if defined (UDP_client) // look-up a hostname (you should check client_waiting_gw() before calling this function): diff --git a/inc/enc28j60.h b/inc/enc28j60.h index 387235e..fb03775 100644 --- a/inc/enc28j60.h +++ b/inc/enc28j60.h @@ -1,7 +1,7 @@ #ifndef __ENC28J60_H #define __ENC28J60_H -#include "stm32includes.h" +#include "defines.h" #define Delay HAL_Delay /* diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h index 1c2b75f..365d4f1 100644 --- a/inc/ip_arp_udp_tcp.h +++ b/inc/ip_arp_udp_tcp.h @@ -15,8 +15,7 @@ #ifndef IP_ARP_UDP_TCP_H #define IP_ARP_UDP_TCP_H -#include "stm32includes.h" -#include +#include "defines.h" void __attribute__((weak)) ES_PingCallback(void); diff --git a/inc/net.h b/inc/net.h index 14b85e1..30447ab 100644 --- a/inc/net.h +++ b/inc/net.h @@ -19,7 +19,7 @@ #ifndef NET_H #define NET_H -#include "stm32includes.h" +#include "defines.h" // ******* ETH ******* #define ETH_HEADER_LEN 14 diff --git a/inc/websrv_help_functions.h b/inc/websrv_help_functions.h index e8d1bf8..fad4b30 100644 --- a/inc/websrv_help_functions.h +++ b/inc/websrv_help_functions.h @@ -8,11 +8,7 @@ #ifndef WEBSRV_HELP_FUNCTIONS_H #define WEBSRV_HELP_FUNCTIONS_H -#include "stm32includes.h" -#include -#include -#include -#include +#include "defines.h" // These functions are documented in websrv_help_functions.c extern uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key); diff --git a/src/dhcp.c b/src/dhcp.c index 2d9a528..36fd79c 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -12,9 +12,7 @@ * DHCP look-up functions based on the udp client * http://www.ietf.org/rfc/rfc2131.txt *********************************************/ -#include -#include - +#include "defines.h" #include "dhcp.h" #include "enc28j60.h" #include "ip_arp_udp_tcp.h" diff --git a/src/dnslkup.c b/src/dnslkup.c index de6de16..0ae9272 100644 --- a/src/dnslkup.c +++ b/src/dnslkup.c @@ -9,8 +9,7 @@ * DNS look-up functions based on the udp client * *********************************************/ -#include -#include +#include "defines.h" #include "net.h" #include "ip_arp_udp_tcp.h" diff --git a/src/enc28j60.c b/src/enc28j60.c index 0e5c1a4..ae3d571 100644 --- a/src/enc28j60.c +++ b/src/enc28j60.c @@ -1,3 +1,4 @@ +#include "defines.h" #include "enc28j60.h" #include "error_handler.h" diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index 97ada5a..b013316 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -17,9 +17,7 @@ * * Chip type : ATMEGA88/168/328 with ENC28J60 *********************************************/ -#include -#include -#include +#include "defines.h" #include "net.h" #include "enc28j60.h" #include "ip_arp_udp_tcp.h" diff --git a/src/websrv_help_functions.c b/src/websrv_help_functions.c index a4e71a7..227b744 100644 --- a/src/websrv_help_functions.c +++ b/src/websrv_help_functions.c @@ -6,7 +6,7 @@ * * Some common utilities needed for IP and web applications *********************************************/ - +#include "defines.h" #include "websrv_help_functions.h" #ifdef FROMDECODE_websrv_help From f7d8bd0265ad88e3e4f23325f633fe494312619a Mon Sep 17 00:00:00 2001 From: DtNeo Date: Sun, 2 Jun 2024 13:49:47 +0200 Subject: [PATCH 07/27] Delete the delay for this function --- src/EtherShield.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EtherShield.c b/src/EtherShield.c index e86f370..a64c045 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -64,9 +64,6 @@ The file should be extracted to the sketchbook/libraries/ folder so that there i void ES_FullConnection(SPI_HandleTypeDef *hspi) { enc28j60_set_spi(hspi); enc28j60Init(macaddrin); - enc28j60clkout(2); - HAL_Delay(10); - int f; for( f=0; f<3; f++ ) { // 0x880 is PHLCON LEDB=on, LEDA=on @@ -94,6 +91,7 @@ void ES_FullConnection(SPI_HandleTypeDef *hspi) { resolveHostname(bufDNS, BUFFER_SIZE, hostname); static uint8_t bufNTP[BUFFER_SIZE]; ES_client_ntp_request(bufNTP, ntpip, 123); + udpLog2("test", "test"); } void paquetweb() { From cb6e3ba45c24370b75a9207eb13ff4b4bb647bf0 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 31 May 2024 16:53:28 +0200 Subject: [PATCH 08/27] Optimisation defines.h for user-friendly configuration --- README.md | 8 ++++---- inc/enc28j60.h | 40 ++++++++++++---------------------------- inc/stm32includes.h | 22 ++-------------------- 3 files changed, 18 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 13b02c0..666a87b 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ If you need to use another STM32 MCU then don't forget to edit file [`inc/stm32i Quick Start for Full Network Connection To quickly set up a complete network connection (DHCP, DNS, Gateway, etc.), use the following function: -`void ES_FullConnection();` in the file: `EtherShield.c` +`ES_FullConnection();` in the file: `EtherShield.c` Call this fonction just before the while and ping your device ! and in your while think to use this for treat everything from your network : -`void paquetweb() { - packetloop_icmp_tcp(packet, enc28j60PacketReceive(200, packet)); -}` +` paquetweb();` + +Options for NSS Output Signal in defines.h that's a simple desactivation and activation on the file enc28j60.h. ## CMake diff --git a/inc/enc28j60.h b/inc/enc28j60.h index fb03775..773183c 100644 --- a/inc/enc28j60.h +++ b/inc/enc28j60.h @@ -14,40 +14,24 @@ static inline void uDelay(uint32_t useconds) { #pragma GCC pop_options */ -#ifndef ETHERNET_LED_GPIO -# error Please define ETHERNET_LED_GPIO, for example by gcc option -DETHERNET_LED_GPIO=GPIOA -#endif -#ifndef ETHERNET_LED_PIN -# error Please define ETHERNET_LED_PIN, for example by gcc option -DETHERNET_LED_PIN=GPIO_PIN_5 -#endif - -#ifndef ETHERNET_CS_GPIO -# error Please define chip-select GPIO port ETHERNET_CS_GPIO, for example by gcc option -DETHERNET_CS_GPIO=GPIOA -#endif -#ifndef ETHERNET_CS_PIN -# error Please define chip-select pin ETHERNET_CS_PIN, for example by gcc option -DETHERNET_CS_PIN=GPIO_PIN_4 -#endif - -#ifndef ETHERNET_CS_DELAY -# warning ETHERNET_CS_DELAY is not defined. Setting to "2" (mseconds). Adapter may work very slow or not properly. If the latency on "1" is too big, but on "0" the adapter is not working properly, you can try values >= 10: it will use other delay method (for example try value 1000). -# define ETHERNET_CS_DELAY 2 -#endif - #if ETHERNET_CS_DELAY >= 10 # define ETHERNET_CS_DELAY_PROC {volatile uint32_t i=ETHERNET_CS_DELAY; while(i--);} #else # define ETHERNET_CS_DELAY_PROC Delay(ETHERNET_CS_DELAY) #endif -#define disableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN;\ - ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN << 16;\ - ETHERNET_CS_DELAY_PROC; -#define enableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN<<16;\ - ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN;\ - ETHERNET_CS_DELAY_PROC; -//#define disableChip {} -//#define enableChip {} - +#ifdef CS_Only +//#define disableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN;\ +// ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN << 16;\ +// ETHERNET_CS_DELAY_PROC; +//#define enableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN<<16;\ +// ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN;\ +// ETHERNET_CS_DELAY_PROC; +#endif +#ifdef NSS_OutputSignal +#define disableChip __HAL_SPI_DISABLE(hspi); +#define enableChip __HAL_SPI_ENABLE(hspi); +#endif // ENC28J60 Control Registers // Control register definitions are a combination of address, diff --git a/inc/stm32includes.h b/inc/stm32includes.h index d837da1..ba95bd7 100644 --- a/inc/stm32includes.h +++ b/inc/stm32includes.h @@ -1,26 +1,8 @@ #ifndef __STM32INCLUDES_H #define __STM32INCLUDES_H -#if STM32F0 -/*# if STM32F091xC -# include "stm32f091xc.h" -# endif*/ -# include "stm32f0xx.h" -# include "stm32f0xx_hal_def.h" -# include "stm32f0xx_hal_spi.h" -#elif STM32F1 -//# include "stm32f103xb.h" -# include "stm32f1xx.h" -# include "stm32f1xx_hal_def.h" -# include "stm32f1xx_hal_spi.h" -#elif STM32F4 -# include "stm32f4xx.h" -# include "stm32f4xx_hal.h" -# include "stm32f4xx_hal_gpio.h" -# include "stm32f4xx_hal_def.h" -# include "stm32f4xx_hal_spi.h" -#endif -#include + + #endif From 49e68c45172fc9d3b7880be52f57eeac833a9a96 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Mon, 3 Jun 2024 23:24:49 +0200 Subject: [PATCH 09/27] Few adjustments --- inc/enc28j60.h | 16 ++++++++-------- src/ip_arp_udp_tcp.c | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/inc/enc28j60.h b/inc/enc28j60.h index 773183c..ddb8025 100644 --- a/inc/enc28j60.h +++ b/inc/enc28j60.h @@ -13,21 +13,21 @@ static inline void uDelay(uint32_t useconds) { } #pragma GCC pop_options */ - +#ifdef CS_Only #if ETHERNET_CS_DELAY >= 10 # define ETHERNET_CS_DELAY_PROC {volatile uint32_t i=ETHERNET_CS_DELAY; while(i--);} #else # define ETHERNET_CS_DELAY_PROC Delay(ETHERNET_CS_DELAY) #endif -#ifdef CS_Only -//#define disableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN;\ -// ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN << 16;\ -// ETHERNET_CS_DELAY_PROC; -//#define enableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN<<16;\ -// ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN;\ -// ETHERNET_CS_DELAY_PROC; +#define disableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN;\ + ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN << 16;\ + ETHERNET_CS_DELAY_PROC; +#define enableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN<<16;\ + ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN;\ + ETHERNET_CS_DELAY_PROC; #endif + #ifdef NSS_OutputSignal #define disableChip __HAL_SPI_DISABLE(hspi); #define enableChip __HAL_SPI_ENABLE(hspi); diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index b013316..de65fba 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -22,6 +22,14 @@ #include "enc28j60.h" #include "ip_arp_udp_tcp.h" +//uint8_t dest_ip[4] = {10, 0, 0, 17}; // Adresse IP de l'Orange pi zero 3 - ArmDebian +//uint16_t dest_port = 10001; +//uint16_t srcport = 10002; +//uint8_t dstport_h = 0x27; // Partie haute du port 10001 +//uint8_t dstport_l = 0x11; // Partie basse du port 10001 +//uint8_t dip[] = {10, 0, 0, 17}; // Adresse IP de l'Orange pi zero 3 - ArmDebian +//uint16_t dport = 514; // UDP Port de l'Orange pi zero 3 - ArmDebian +//uint16_t sport = 8; #if ETHERSHIELD_DEBUG void ethershieldDebug(char *message) { From 3de6ac235cd4b89f9a2614dd67cc7aa6eb9416cf Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 31 May 2024 21:16:25 +0200 Subject: [PATCH 10/27] dhcp From e4404c90a692618441a9dad62c6edd47f20cb39a Mon Sep 17 00:00:00 2001 From: DtNeo Date: Sun, 2 Jun 2024 13:49:47 +0200 Subject: [PATCH 11/27] License --- README.md | 34 +++++++++++++++++++++++++ src/EtherShield.c | 64 +++++++++-------------------------------------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 666a87b..52c231c 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,37 @@ To make it work for your MCU family and model you must change `MCU_FAMILY` and ` * [dc-thermal-logger](https://github.com/mephi-ut/dc-thermal-logger/blob/master/collector/firmware/Src/main.c) * [STM32_Devel](https://github.com/mephi-ut/STM32_Devel) * [nucleo-f091rc-rs232-enc28j60](https://github.com/mephi-ut/nucleo-f091rc-rs232-enc28j60/blob/master/Src/main.c) + +# License +Mainpage Arduino ENC28J60 EtherShield Library +## section Introduction +This library is derived from original code by Guido Socher and Pascal Stang, and hence licensed as GPL2. See http://www.gnu.org/licenses/gpl.html +It comprises a C++ class wrapper and a number of C files. It still follows pretty much the same structure as the original code that it was based on. +The Arduino EtherShield Library was initially created by Xing Yu of Nuelectronics, http://www.nuelectronics.com/estore/index.php?main_page=product_info&cPath=1&products_id=4 +The library was heavily modified and improved by Andrew D. Lindsay (http://blog.thiseldo.co.uk) with extra code from the Tuxgraphics.org ethernet library (http://www.tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml), which also originated from the Pascal Stang code. +Further additions include the DHCP implementation with some assistance from JCW at http://jeelabs.org which is now used in their own version of the library for their EtherCard at http://jeelabs.net/projects/hardware/wiki/Ether_Card. +The library is now being used successfully with the Nanode, as minimal Ethernet connected Arduino compatible board, details available from http://wiki.london.hackspace.org.uk/view/Project:Nanode + +## section Download +Download the latest library and examples from https://github.com/thiseldo/EtherShield +To fully utilise the Nanode board, you will also require a library that can access the onboard MAC address chip. +One such library is available from https://github.com/thiseldo/NanodeMAC and is used in the examples provided with this library. + +## section Instalation +The library .zip file downloaded from https://github.com/thiseldo/EtherShield should be renamed to EtherShield.zip or EtherShield.tar.gz depending on the archive file you're downloading. +The file should be extracted to the sketchbook/libraries/ folder so that there is a subdirectory called EtherSheild containing all the files from the archive. + +## section License + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + A copy of the GNU Lesser General Public + License is available from http://www.gnu.org/licenses/gpl.html; or write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/src/EtherShield.c b/src/EtherShield.c index a64c045..5bc2d6e 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -1,50 +1,5 @@ -/*! \mainpage Arduino ENC28J60 EtherShield Library - -\section Introduction - -This library is derived from original code by Guido Socher and Pascal Stang, and hence licensed as GPL2. See http://www.gnu.org/licenses/gpl.html - -It comprises a C++ class wrapper and a number of C files. It still follows pretty much the same structure as the original code that it was based on. - -The Arduino EtherShield Library was initially created by Xing Yu of Nuelectronics, http://www.nuelectronics.com/estore/index.php?main_page=product_info&cPath=1&products_id=4 - -The library was heavily modified and improved by Andrew D. Lindsay (http://blog.thiseldo.co.uk) with extra code from the Tuxgraphics.org ethernet library (http://www.tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml), which also originated from the Pascal Stang code. - -Further additions include the DHCP implementation with some assistance from JCW at http://jeelabs.org which is now used in their own version of the library for their EtherCard at http://jeelabs.net/projects/hardware/wiki/Ether_Card. - -The library is now being used successfully with the Nanode, as minimal Ethernet connected Arduino compatible board, details available from http://wiki.london.hackspace.org.uk/view/Project:Nanode - -\section Download - -Download the latest library and examples from https://github.com/thiseldo/EtherShield - -To fully utilise the Nanode board, you will also require a library that can access the onboard MAC address chip. -One such library is available from https://github.com/thiseldo/NanodeMAC and is used in the examples provided with this library. - -\section Instalation - -The library .zip file downloaded from https://github.com/thiseldo/EtherShield should be renamed to EtherShield.zip or EtherShield.tar.gz depending on the archive file you're downloading. - -The file should be extracted to the sketchbook/libraries/ folder so that there is a subdirectory called EtherSheild containing all the files from the archive. - -\section License - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - A copy of the GNU Lesser General Public - License is available from http://www.gnu.org/licenses/gpl.html; or write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - */ #include "defines.h" +#include "main.h" #include "enc28j60.h" #include "ip_arp_udp_tcp.h" #include "websrv_help_functions.h" @@ -61,37 +16,42 @@ The file should be extracted to the sketchbook/libraries/ folder so that there i #define BUFFER_SIZE 500 +uint8_t bufDHCP[BUFFER_SIZE]; +uint8_t bufDNS[BUFFER_SIZE]; +uint8_t bufNTP[BUFFER_SIZE]; +uint16_t plen; + +char hostname[] = "www.google.com"; + + void ES_FullConnection(SPI_HandleTypeDef *hspi) { enc28j60_set_spi(hspi); enc28j60Init(macaddrin); + enc28j60clkout(3); + HAL_Delay(10); int f; for( f=0; f<3; f++ ) { // 0x880 is PHLCON LEDB=on, LEDA=on // enc28j60PhyWrite(PHLCON,0b0011 1000 1000 00 00); enc28j60PhyWrite(PHLCON,0x3880); HAL_Delay(500); - // 0x990 is PHLCON LEDB=off, LEDA=off // enc28j60PhyWrite(PHLCON,0b0011 1001 1001 00 00); enc28j60PhyWrite(PHLCON,0x3990); HAL_Delay(500); } - // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit // enc28j60PhyWrite(PHLCON,0b0011 0100 0111 01 10); enc28j60PhyWrite(PHLCON,0x3476); HAL_Delay(100); - static uint8_t bufDHCP[BUFFER_SIZE]; allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); client_set_gwip(gwipin); - static uint8_t bufDNS[BUFFER_SIZE]; dnslkup_request(bufDNS, hostname); udp_client_check_for_dns_answer(bufDNS, plen); dnslkup_set_dnsip(dnsipaddr); resolveHostname(bufDNS, BUFFER_SIZE, hostname); - static uint8_t bufNTP[BUFFER_SIZE]; ES_client_ntp_request(bufNTP, ntpip, 123); - udpLog2("test", "test"); + udpLog2("ES_FullConnection", "WORKS!"); } void paquetweb() { From f3bad7a69c595b60d3a29b3a0e3bc1d9a435e37c Mon Sep 17 00:00:00 2001 From: DtNeo Date: Tue, 4 Jun 2024 01:27:05 +0200 Subject: [PATCH 12/27] I delete some codes, this give me a lot faster start. I add a better answer of UTP but don't work yet with this code, work in progress. Need a focus on hostname and DomainName, I will work on it. --- inc/ip_arp_udp_tcp.h | 2 +- src/EtherShield.c | 39 +++++-------------- src/dnslkup.c | 4 +- src/ip_arp_udp_tcp.c | 92 +++++++++++++++++++------------------------- 4 files changed, 53 insertions(+), 84 deletions(-) diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h index 365d4f1..5e48e60 100644 --- a/inc/ip_arp_udp_tcp.h +++ b/inc/ip_arp_udp_tcp.h @@ -161,7 +161,7 @@ void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, c #ifdef NTP_client void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport); -uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l); +void client_ntp_process_answer(uint8_t *buf); #endif #ifdef UDP_client diff --git a/src/EtherShield.c b/src/EtherShield.c index 5bc2d6e..c36d955 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -14,43 +14,28 @@ #include "EtherShield.h" -#define BUFFER_SIZE 500 +#define BUFFER_SIZE 400 -uint8_t bufDHCP[BUFFER_SIZE]; -uint8_t bufDNS[BUFFER_SIZE]; -uint8_t bufNTP[BUFFER_SIZE]; uint16_t plen; - -char hostname[] = "www.google.com"; - +uint8_t hostname[]= "www.google.com"; void ES_FullConnection(SPI_HandleTypeDef *hspi) { enc28j60_set_spi(hspi); enc28j60Init(macaddrin); - enc28j60clkout(3); - HAL_Delay(10); - int f; - for( f=0; f<3; f++ ) { - // 0x880 is PHLCON LEDB=on, LEDA=on - // enc28j60PhyWrite(PHLCON,0b0011 1000 1000 00 00); - enc28j60PhyWrite(PHLCON,0x3880); - HAL_Delay(500); - // 0x990 is PHLCON LEDB=off, LEDA=off - // enc28j60PhyWrite(PHLCON,0b0011 1001 1001 00 00); - enc28j60PhyWrite(PHLCON,0x3990); - HAL_Delay(500); - } - // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit - // enc28j60PhyWrite(PHLCON,0b0011 0100 0111 01 10); + enc28j60Write(ECOCON, 3 & 0x7); + enc28j60PhyWrite(PHLCON,0x3880); + enc28j60PhyWrite(PHLCON,0x3990); enc28j60PhyWrite(PHLCON,0x3476); - HAL_Delay(100); + uint8_t bufDHCP[BUFFER_SIZE]; allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); client_set_gwip(gwipin); - dnslkup_request(bufDNS, hostname); + uint8_t bufDNS[BUFFER_SIZE]; + dnslkup_request(bufDNS, domainName); udp_client_check_for_dns_answer(bufDNS, plen); dnslkup_set_dnsip(dnsipaddr); resolveHostname(bufDNS, BUFFER_SIZE, hostname); - ES_client_ntp_request(bufNTP, ntpip, 123); + uint8_t bufNTP[BUFFER_SIZE]; + client_ntp_request(bufNTP, ntpip, 123); udpLog2("ES_FullConnection", "WORKS!"); } @@ -293,10 +278,6 @@ void ES_client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline void ES_client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) { client_ntp_request(buf,ntpip,srcport); } - -uint8_t ES_client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) { - return client_ntp_process_answer(buf,time,dstport_l); -} #endif // NTP_client void ES_register_ping_rec_callback(void (*callback)(uint8_t *srcip)) { diff --git a/src/dnslkup.c b/src/dnslkup.c index 0ae9272..29437f3 100644 --- a/src/dnslkup.c +++ b/src/dnslkup.c @@ -43,7 +43,7 @@ uint8_t *dnslkup_getip() // and http://www.ietf.org/rfc/rfc1035.txt // //void dnslkup_request(uint8_t *buf,const prog_char *progmem_hostname) -void dnslkup_request(uint8_t *buf, uint8_t *hostname) +void dnslkup_request(uint8_t *buf, uint8_t *domainName) { uint8_t i,lenpos,lencnt; char c; @@ -72,7 +72,7 @@ void dnslkup_request(uint8_t *buf, uint8_t *hostname) i=13; lencnt=1; // while ((c = pgm_read_byte(progmem_hostname++))) { - while ((c = *hostname++)) { + while ((c = *domainName++)) { if (c=='.'){ buf[UDP_DATA_P+lenpos]=lencnt-1; lencnt=0; diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index de65fba..4381ea8 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -762,41 +762,32 @@ void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) enc28j60PacketSend(90,buf); } // process the answer from the ntp server: -// if dstport==0 then accept any port otherwise only answers going to dstport // return 1 on sucessful processing of answer -uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l){ - if (dstport_l){ - if (buf[UDP_DST_PORT_L_P]!=dstport_l){ - return(0); - } - } - if (buf[UDP_LEN_H_P]!=0 || buf[UDP_LEN_L_P]!=56 || buf[UDP_SRC_PORT_L_P]!=0x7b){ - // not ntp - return(0); - } +void client_ntp_process_answer(uint8_t *buf){ + uint32_t time; // copy time from the transmit time stamp field: - *time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); + time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); //for the all paquet, this is where are the data: - //((uint32_t)buf[82] << 24) | ((uint32_t)buf[83] << 16) | ((uint32_t)buf[84] << 8) | (uint32_t)buf[85]; + //time=((uint32_t)buf[82] << 24) | ((uint32_t)buf[83] << 16) | ((uint32_t)buf[84] << 8) | (uint32_t)buf[85]; // Convert NTP timestamp to UNIX timestamp (seconds since 1970) // by subtracting the seconds between 1900 and 1970. // Note: This only takes into account the seconds part of the NTP timestamp and ignores fractional seconds. - if (*time > 2208988800UL) { - *time -= 2208988800UL; + if (time > 2208988800UL) { + time -= 2208988800UL; } else { // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) - *time = 0; + time = 0; // Create structures for time and date -// RTC_TimeTypeDef sTime = {0}; -// RTC_DateTypeDef sDate = {0}; + RTC_TimeTypeDef sTime = {0}; + RTC_DateTypeDef sDate = {0}; // Calculate time components - uint32_t seconds = *time % 60; - uint32_t minutes = (*time / 60) % 60; - uint32_t hours = (*time / 3600) % 24; - uint32_t days = *time / 86400; // Total number of days since 1970 + uint32_t seconds = time % 60; + uint32_t minutes = (time / 60) % 60; + uint32_t hours = (time / 3600) % 24; + uint32_t days = time / 86400; // Total number of days since 1970 // The start of 1970 was a Thursday (day 4 of the week) uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday @@ -830,35 +821,32 @@ uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) month++; // months are 1-12 uint32_t dayOfMonth = days + 1; // days are 1-31 - // Configure time -// sTime.Hours = hours; -// sTime.Minutes = minutes; -// sTime.Seconds = seconds; -// sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; -// sTime.StoreOperation = RTC_STOREOPERATION_RESET; - - // Configure date -// sDate.WeekDay = dayOfWeek; -// sDate.Month = month; -// sDate.Date = dayOfMonth; -// sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 - - // Update the RTC -// HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); -// HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); - -// char timeStr[32]; // Adjusted size to hold formatted date and time -// sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", -// sDate.Date, -// sDate.Month, -// year, -// sTime.Hours, -// sTime.Minutes, -// sTime.Seconds); -// #if ETHERSHIELD_DEBUG -// ethershieldDebug(timeStr); -// #endif - return(1); +// Configure time + sTime.Hours = hours; + sTime.Minutes = minutes; + sTime.Seconds = seconds; + sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + sTime.StoreOperation = RTC_STOREOPERATION_RESET; + +// Configure date + sDate.WeekDay = dayOfWeek; + sDate.Month = month; + sDate.Date = dayOfMonth; + sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 + +// Update the RTC + HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); + HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); + + char timeStr[32]; // Adjusted size to hold formatted date and time + sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", + sDate.Date, + sDate.Month, + year, + sTime.Hours, + sTime.Minutes, + sTime.Seconds); + udpLog2("NTP indique la date suivante :", timeStr); } } #endif // NTP_client @@ -1432,7 +1420,7 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { } #ifdef NTP_client if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { - return (UDP_DATA_P); + client_ntp_process_answer(UDP_DATA_P); } #endif // NTP_client #ifdef DNS_client From ef8a461fda5d3a871711bd7e1969f057b0fdb28c Mon Sep 17 00:00:00 2001 From: DtNeo Date: Wed, 5 Jun 2024 21:36:39 +0200 Subject: [PATCH 13/27] function for UDP Command Handler (files will comes for manage order from network via UDP) Will be helpfull for domotic and giving order for STM32 from network --- inc/ip_arp_udp_tcp.h | 3 +- src/EtherShield.c | 2 - src/ip_arp_udp_tcp.c | 222 ++++++++++++++++++++++++------------------- 3 files changed, 126 insertions(+), 101 deletions(-) diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h index 5e48e60..09526d0 100644 --- a/inc/ip_arp_udp_tcp.h +++ b/inc/ip_arp_udp_tcp.h @@ -161,7 +161,7 @@ void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, c #ifdef NTP_client void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport); -void client_ntp_process_answer(uint8_t *buf); +void client_ntp_process_answer(uint8_t *buf, uint16_t plen); #endif #ifdef UDP_client @@ -178,6 +178,7 @@ void send_udp_transmit(uint8_t *buf,uint16_t datalen); // send_udp sends via gwip, you must call client_set_gwip at startup, datalen must be less than 220 bytes void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport); +void udp_packet_process(uint8_t *buf, uint16_t plen); #endif // UDP_client diff --git a/src/EtherShield.c b/src/EtherShield.c index c36d955..3c3f1e3 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -16,7 +16,6 @@ #define BUFFER_SIZE 400 -uint16_t plen; uint8_t hostname[]= "www.google.com"; void ES_FullConnection(SPI_HandleTypeDef *hspi) { @@ -52,7 +51,6 @@ void ES_enc28j60SpiInit(SPI_HandleTypeDef *hspi){ enc28j60_set_spi(hspi); } - /** * Initialise the ENC28J60 using default chip select pin * Flash the 2 MagJack LEDs diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index 4381ea8..7ec884e 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -21,15 +21,7 @@ #include "net.h" #include "enc28j60.h" #include "ip_arp_udp_tcp.h" - -//uint8_t dest_ip[4] = {10, 0, 0, 17}; // Adresse IP de l'Orange pi zero 3 - ArmDebian -//uint16_t dest_port = 10001; -//uint16_t srcport = 10002; -//uint8_t dstport_h = 0x27; // Partie haute du port 10001 -//uint8_t dstport_l = 0x11; // Partie basse du port 10001 -//uint8_t dip[] = {10, 0, 0, 17}; // Adresse IP de l'Orange pi zero 3 - ArmDebian -//uint16_t dport = 514; // UDP Port de l'Orange pi zero 3 - ArmDebian -//uint16_t sport = 8; +#include "UDPCommandHandler.h" #if ETHERSHIELD_DEBUG void ethershieldDebug(char *message) { @@ -761,93 +753,95 @@ void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) buf[UDP_CHECKSUM_L_P]=ck& 0xff; enc28j60PacketSend(90,buf); } -// process the answer from the ntp server: -// return 1 on sucessful processing of answer -void client_ntp_process_answer(uint8_t *buf){ - uint32_t time; - // copy time from the transmit time stamp field: - time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); - //for the all paquet, this is where are the data: - //time=((uint32_t)buf[82] << 24) | ((uint32_t)buf[83] << 16) | ((uint32_t)buf[84] << 8) | (uint32_t)buf[85]; - - // Convert NTP timestamp to UNIX timestamp (seconds since 1970) - // by subtracting the seconds between 1900 and 1970. - // Note: This only takes into account the seconds part of the NTP timestamp and ignores fractional seconds. - if (time > 2208988800UL) { - time -= 2208988800UL; - } else { - // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) - time = 0; - - // Create structures for time and date - RTC_TimeTypeDef sTime = {0}; - RTC_DateTypeDef sDate = {0}; - - // Calculate time components - uint32_t seconds = time % 60; - uint32_t minutes = (time / 60) % 60; - uint32_t hours = (time / 3600) % 24; - uint32_t days = time / 86400; // Total number of days since 1970 - - // The start of 1970 was a Thursday (day 4 of the week) - uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday - - // Determine the year - uint32_t year = 1970; - while (days >= 365) { - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - if (days < 366) break; - days -= 366; - } else { - if (days < 365) break; - days -= 365; - } - year++; - } - - // Determine the month and the day of the month - uint32_t month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - month_lengths[1] = 29; // February has 29 days in leap years - } - - uint32_t month = 0; - for (month = 0; month < 12; month++) { - if (days < month_lengths[month]) { - break; - } - days -= month_lengths[month]; - } - month++; // months are 1-12 - uint32_t dayOfMonth = days + 1; // days are 1-31 - -// Configure time - sTime.Hours = hours; - sTime.Minutes = minutes; - sTime.Seconds = seconds; - sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; - sTime.StoreOperation = RTC_STOREOPERATION_RESET; - -// Configure date - sDate.WeekDay = dayOfWeek; - sDate.Month = month; - sDate.Date = dayOfMonth; - sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 - -// Update the RTC - HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); - HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); - - char timeStr[32]; // Adjusted size to hold formatted date and time - sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", - sDate.Date, - sDate.Month, - year, - sTime.Hours, - sTime.Minutes, - sTime.Seconds); - udpLog2("NTP indique la date suivante :", timeStr); - } + +/* + * This function processes the response from an NTP server and updates the RTC of the STM32 microcontroller. + * It extracts the NTP timestamp from the received buffer, converts it to a UNIX timestamp, and then + * updates the RTC with the current date and time. + */ +void client_ntp_process_answer(uint8_t *buf, uint16_t plen) { + uint32_t unix_timestamp; + + // Extract the NTP timestamp (seconds since 1900) + unix_timestamp = ((uint32_t)buf[0x52] << 24) | ((uint32_t)buf[0x53] << 16) | ((uint32_t)buf[0x54] << 8) | (uint32_t)buf[0x55]; + + // Convert the NTP timestamp to UNIX timestamp (seconds since 1970) + // by subtracting the seconds between 1900 and 1970. + if (unix_timestamp > 2208988800UL) { + unix_timestamp -= 2208988800UL; + } else { + // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) + unix_timestamp = 0; + } + + // Create structures for time and date + RTC_TimeTypeDef sTime = {0}; + RTC_DateTypeDef sDate = {0}; + + // Calculate the time components + uint32_t seconds = unix_timestamp % 60; + uint32_t minutes = (unix_timestamp / 60) % 60; + uint32_t hours = (unix_timestamp / 3600) % 24; + uint32_t days = unix_timestamp / 86400; // Total number of days since 1970 + + // The start of 1970 was a Thursday (day 4 of the week) + uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday + + // Determine the year + uint32_t year = 1970; + while (days >= 365) { + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + if (days < 366) break; + days -= 366; + } else { + if (days < 365) break; + days -= 365; + } + year++; + } + + // Determine the month and the day of the month + uint32_t month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + month_lengths[1] = 29; // February has 29 days in leap years + } + + uint32_t month = 0; + for (month = 0; month < 12; month++) { + if (days < month_lengths[month]) { + break; + } + days -= month_lengths[month]; + } + month++; // months are 1-12 + uint32_t dayOfMonth = days + 1; // days are 1-31 + + // Set the time + sTime.Hours = hours; + sTime.Minutes = minutes; + sTime.Seconds = seconds; + sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + sTime.StoreOperation = RTC_STOREOPERATION_RESET; + + // Set the date + sDate.WeekDay = dayOfWeek; + sDate.Month = month; + sDate.Date = dayOfMonth; + sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 + + // Update the RTC + HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); + HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); + + char timeStr[32]; // Adjusted size to hold formatted date and time + sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", + sDate.Date, + sDate.Month, + year, + sTime.Hours, + sTime.Minutes, + sTime.Seconds); + udpLog2("NTP indicates the following date and time:", timeStr); } #endif // NTP_client @@ -913,6 +907,33 @@ void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t * memcpy(&buf[UDP_DATA_P], data, datalen); send_udp_transmit(buf,datalen); } + +// Process UDP packets +void udp_packet_process(uint8_t *buf, uint16_t plen) { + // Get the position of the data in the UDP packet + int data_pos = UDP_DATA_P; // Use the correct constant for your context + uint8_t *data = &buf[data_pos]; + uint16_t data_len = plen - data_pos; + + // Convert the data to a string if necessary + char msg[data_len + 1]; + memcpy(msg, data, data_len); + msg[data_len] = '\0'; + + // Log the received message for debugging + //udpLog2("Received UDP", msg); + + // Iterate through the command table to find and execute the appropriate command + for (CommandMapping *cmd = commandTable; cmd->command != NULL; cmd++) { + if (strncmp(msg, cmd->command, strlen(cmd->command)) == 0) { + cmd->function(); + return; + } + } + + // If no matching command is found + udpLog2("Error", "Unexpected UDP message"); +} #endif // UDP_client #ifdef WOL_client @@ -1420,12 +1441,13 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { } #ifdef NTP_client if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { - client_ntp_process_answer(UDP_DATA_P); + client_ntp_process_answer(buf, plen); + return(UDP_DATA_P); } #endif // NTP_client #ifdef DNS_client if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { - return (UDP_DATA_P); + return (buf); } #endif // NTP_client if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) { @@ -1437,6 +1459,10 @@ uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { ES_PingCallback(); return (0); } + if (buf[IP_PROTO_P] == IP_PROTO_UDP_V) { + udp_packet_process(buf, plen); + return (UDP_DATA_P); + } if (plen < 54 && buf[IP_PROTO_P] != IP_PROTO_TCP_V) { // smaller than the smallest TCP packet and not tcp port return (0); From c9a13aea095318446cccef01428df9c1a1563788 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Wed, 5 Jun 2024 22:10:23 +0200 Subject: [PATCH 14/27] clear the EtherShield of 2 functions, better be in the file ip_arp_udp_tcp and change the name of paquetweb (FR) to ES_ProcessWebPacket (EN) --- inc/EtherShield.h | 11 +-- inc/ip_arp_udp_tcp.h | 15 +++- src/EtherShield.c | 195 +++++++++++-------------------------------- src/ip_arp_udp_tcp.c | 111 ++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 161 deletions(-) diff --git a/inc/EtherShield.h b/inc/EtherShield.h index f1f5bc6..fac0322 100644 --- a/inc/EtherShield.h +++ b/inc/EtherShield.h @@ -21,18 +21,11 @@ #define ETHERSHIELD_H #include "defines.h" - -#define bool _Bool -#define TRUE 1 -#define FALSE 0 - -#include #include "enc28j60.h" -#include "ip_arp_udp_tcp.h" #include "net.h" void ES_FullConnection(); -void paquetweb(); +void ES_ProcessWebPacket(); void ES_enc28j60SpiInit( SPI_HandleTypeDef *hspi ); void ES_enc28j60Init( uint8_t* macaddr); void ES_enc28j60clkout(uint8_t clk); @@ -105,7 +98,6 @@ uint8_t *ES_dnslkup_getip( void ); void ES_dnslkup_set_dnsip(uint8_t *dnsipaddr); void ES_dnslkup_request(uint8_t *buf, uint8_t *hoststr ); uint8_t ES_udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen); -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ); #endif #ifdef DHCP_client @@ -115,7 +107,6 @@ void ES_dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *dnssvrin ); uint8_t ES_check_for_dhcp_answer(uint8_t *buf,uint16_t plen); -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ); #endif #define HTTP_HEADER_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h index 09526d0..75b8739 100644 --- a/inc/ip_arp_udp_tcp.h +++ b/inc/ip_arp_udp_tcp.h @@ -17,6 +17,10 @@ #include "defines.h" +#define bool _Bool +#define TRUE 1 +#define FALSE 0 + void __attribute__((weak)) ES_PingCallback(void); // -- web server functions -- @@ -48,9 +52,7 @@ uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type); uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); -// return 0 to just continue in the packet loop and return the position -// of the tcp data if there is tcp data part -uint16_t packetloop_icmp_tcp(uint8_t *buf,uint16_t plen); + // functions to fill the web pages with data: //extern uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s); uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s); @@ -195,6 +197,13 @@ uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost); void send_wol(uint8_t *buf,uint8_t *wolmac); #endif // WOL_client +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ); +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ); + +// return 0 to just continue in the packet loop and return the position +// of the tcp data if there is tcp data part +uint16_t packetloop_icmp_tcp(uint8_t *buf,uint16_t plen); + uint8_t nextTcpState( uint8_t *buf,uint16_t plen ); uint8_t currentTcpState( ); uint8_t tcpActiveOpen( uint8_t *buf,uint16_t plen, diff --git a/src/EtherShield.c b/src/EtherShield.c index 3c3f1e3..515d875 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -1,47 +1,60 @@ -#include "defines.h" -#include "main.h" -#include "enc28j60.h" -#include "ip_arp_udp_tcp.h" -#include "websrv_help_functions.h" - -#ifdef DNS_client - #include "dnslkup.h" -#endif - -#ifdef DHCP_client - #include "dhcp.h" -#endif +/* + * This file contains the implementation for establishing a full connection using the EtherShield library + * with an ENC28J60 Ethernet module. It includes functions to initialize the connection, allocate IP addresses + * via DHCP, resolve DNS hostnames, and request NTP time updates. Additionally, it processes incoming packets + * for ICMP and TCP protocols. + */ #include "EtherShield.h" #define BUFFER_SIZE 400 -uint8_t hostname[]= "www.google.com"; +uint8_t hostname[] = "www.google.com"; +// Function to initialize and establish a full connection void ES_FullConnection(SPI_HandleTypeDef *hspi) { - enc28j60_set_spi(hspi); - enc28j60Init(macaddrin); - enc28j60Write(ECOCON, 3 & 0x7); - enc28j60PhyWrite(PHLCON,0x3880); - enc28j60PhyWrite(PHLCON,0x3990); - enc28j60PhyWrite(PHLCON,0x3476); - uint8_t bufDHCP[BUFFER_SIZE]; - allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); - client_set_gwip(gwipin); - uint8_t bufDNS[BUFFER_SIZE]; - dnslkup_request(bufDNS, domainName); - udp_client_check_for_dns_answer(bufDNS, plen); - dnslkup_set_dnsip(dnsipaddr); - resolveHostname(bufDNS, BUFFER_SIZE, hostname); - uint8_t bufNTP[BUFFER_SIZE]; - client_ntp_request(bufNTP, ntpip, 123); - udpLog2("ES_FullConnection", "WORKS!"); + // Set the SPI handle for the ENC28J60 + enc28j60_set_spi(hspi); + + // Initialize the ENC28J60 with the MAC address + enc28j60Init(macaddrin); + + // Configure the ENC28J60 clock + enc28j60Write(ECOCON, 3 & 0x7); + + // Configure the ENC28J60 PHY LEDs + enc28j60PhyWrite(PHLCON, 0x3880); + enc28j60PhyWrite(PHLCON, 0x3990); + enc28j60PhyWrite(PHLCON, 0x3476); + + // Allocate IP address via DHCP + uint8_t bufDHCP[BUFFER_SIZE]; + allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); + + // Set the gateway IP for the client + client_set_gwip(gwipin); + + // Resolve the DNS hostname + uint8_t bufDNS[BUFFER_SIZE]; + dnslkup_request(bufDNS, domainName); + udp_client_check_for_dns_answer(bufDNS, plen); + dnslkup_set_dnsip(dnsipaddr); + resolveHostname(bufDNS, BUFFER_SIZE, hostname); + + // Send NTP request to get the current time + uint8_t bufNTP[BUFFER_SIZE]; + client_ntp_request(bufNTP, ntpip, 123); + + // Log the success of the full connection setup + udpLog2("ES_FullConnection", "WORKS!"); } -void paquetweb() { - static uint8_t packet[500]; - packetloop_icmp_tcp(packet, enc28j60PacketReceive(500, packet)); +// Function to process incoming web packets +void ES_ProcessWebPacket() { + static uint8_t packet[500]; + packetloop_icmp_tcp(packet, enc28j60PacketReceive(500, packet)); } + /** * Initialise SPI, separate from main initialisation so that * multiple SPI devices can be used together @@ -355,65 +368,6 @@ void ES_dnslkup_request(uint8_t *buf,uint8_t *hostname) { uint8_t ES_udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen){ return( udp_client_check_for_dns_answer( buf, plen) ); } - - -// Perform all processing to resolve a hostname to IP address. -// Returns 1 for successful Name resolution, 0 otherwise -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ) { - uint16_t dat_p; - int plen = 0; - long lastDnsRequest = HAL_GetTick(); - uint8_t dns_state = DNS_STATE_INIT; - bool gotAddress = FALSE; - uint8_t dnsTries = 3; // After 10 attempts fail gracefully so other action can be carried out - - while( !gotAddress ) { - // handle ping and wait for a tcp packet - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p=packetloop_icmp_tcp(buf,plen); - - // We have a packet - // Check if IP data - if (dat_p == 0) { - if (client_waiting_gw() ) { - // No ARP received for gateway - continue; - } - // It has IP data - if (dns_state==DNS_STATE_INIT) { - dns_state=DNS_STATE_REQUESTED; - lastDnsRequest = HAL_GetTick(); - dnslkup_request(buf,hostname); - continue; - } - if (dns_state!=DNS_STATE_ANSWER){ - // retry every minute if dns-lookup failed: - if (HAL_GetTick() > (lastDnsRequest + 60000L) ){ - if( --dnsTries <= 0 ) - return 0; // Failed to allocate address - - dns_state=DNS_STATE_INIT; - lastDnsRequest = HAL_GetTick(); - } - // don't try to use client before - // we have a result of dns-lookup - - continue; - } - } - else { - if (dns_state==DNS_STATE_REQUESTED && udp_client_check_for_dns_answer( buf, plen ) ){ - dns_state=DNS_STATE_ANSWER; - //client_set_wwwip(dnslkup_getip()); - client_tcp_set_serverip(dnslkup_getip()); - gotAddress = TRUE; - } - } - } - - return 1; -} - #endif // DNS_client #ifdef DHCP_client @@ -429,61 +383,6 @@ uint8_t ES_dhcp_state(void) uint8_t ES_check_for_dhcp_answer(uint8_t *buf,uint16_t plen){ return( check_for_dhcp_answer( buf, plen) ); } - -// Utility functions - -// Perform all processing to get an IP address plus other addresses returned, e.g. gw, dns, dhcp server. -// Returns 1 for successful IP address allocation, 0 otherwise -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ) { - uint16_t dat_p; - int plen = 0; - long lastDhcpRequest = HAL_GetTick(); - uint8_t dhcpState = 0; - bool gotIp = FALSE; - uint8_t dhcpTries = 10; // After 10 attempts fail gracefully so other action can be carried out - - dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); - - while( !gotIp ) { - // handle ping and wait for a tcp packet - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p=packetloop_icmp_tcp(buf,plen); - if(dat_p==0) { - check_for_dhcp_answer( buf, plen); - dhcpState = dhcp_state(); - // we are idle here - if( dhcpState != DHCP_STATE_OK ) { - if (HAL_GetTick() > (lastDhcpRequest + 10000L) ){ - lastDhcpRequest = HAL_GetTick(); - if( --dhcpTries <= 0 ) - return 0; // Failed to allocate address - // send dhcp - dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); - } - } else { - if( !gotIp ) { - gotIp = TRUE; - - //init the ethernet/ip layer: - init_ip_arp_udp_tcp(mymac, myip, myport); - - // Set the Router IP - client_set_gwip(gwip); // e.g internal IP of dsl router - -#ifdef DNS_client - // Set the DNS server IP address if required, or use default - dnslkup_set_dnsip( dnsip ); -#endif - - } - } - } - } - - return 1; - -} - #endif // DHCP_client void ES_enc28j60PowerUp(){ diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index 7ec884e..3b7b689 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -1386,6 +1386,117 @@ uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) } #endif // PING_client +// Utility functions + +// Perform all processing to get an IP address plus other addresses returned, e.g. gw, dns, dhcp server. +// Returns 1 for successful IP address allocation, 0 otherwise +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ) { + uint16_t dat_p; + int plen = 0; + long lastDhcpRequest = HAL_GetTick(); + uint8_t dhcpState = 0; + bool gotIp = FALSE; + uint8_t dhcpTries = 10; // After 10 attempts fail gracefully so other action can be carried out + + dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); + + while( !gotIp ) { + // handle ping and wait for a tcp packet + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p=packetloop_icmp_tcp(buf,plen); + if(dat_p==0) { + check_for_dhcp_answer( buf, plen); + dhcpState = dhcp_state(); + // we are idle here + if( dhcpState != DHCP_STATE_OK ) { + if (HAL_GetTick() > (lastDhcpRequest + 10000L) ){ + lastDhcpRequest = HAL_GetTick(); + if( --dhcpTries <= 0 ) + return 0; // Failed to allocate address + // send dhcp + dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); + } + } else { + if( !gotIp ) { + gotIp = TRUE; + + //init the ethernet/ip layer: + init_ip_arp_udp_tcp(mymac, myip, myport); + + // Set the Router IP + client_set_gwip(gwip); // e.g internal IP of dsl router + +#ifdef DNS_client + // Set the DNS server IP address if required, or use default + dnslkup_set_dnsip( dnsip ); +#endif + + } + } + } + } + + return 1; + +} + +// Perform all processing to resolve a hostname to IP address. +// Returns 1 for successful Name resolution, 0 otherwise +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ) { + uint16_t dat_p; + int plen = 0; + long lastDnsRequest = HAL_GetTick(); + uint8_t dns_state = DNS_STATE_INIT; + bool gotAddress = FALSE; + uint8_t dnsTries = 3; // After 10 attempts fail gracefully so other action can be carried out + + while( !gotAddress ) { + // handle ping and wait for a tcp packet + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p=packetloop_icmp_tcp(buf,plen); + + // We have a packet + // Check if IP data + if (dat_p == 0) { + if (client_waiting_gw() ) { + // No ARP received for gateway + continue; + } + // It has IP data + if (dns_state==DNS_STATE_INIT) { + dns_state=DNS_STATE_REQUESTED; + lastDnsRequest = HAL_GetTick(); + dnslkup_request(buf,hostname); + continue; + } + if (dns_state!=DNS_STATE_ANSWER){ + // retry every minute if dns-lookup failed: + if (HAL_GetTick() > (lastDnsRequest + 60000L) ){ + if( --dnsTries <= 0 ) + return 0; // Failed to allocate address + + dns_state=DNS_STATE_INIT; + lastDnsRequest = HAL_GetTick(); + } + // don't try to use client before + // we have a result of dns-lookup + + continue; + } + } + else { + if (dns_state==DNS_STATE_REQUESTED && udp_client_check_for_dns_answer( buf, plen ) ){ + dns_state=DNS_STATE_ANSWER; + //client_set_wwwip(dnslkup_getip()); + client_tcp_set_serverip(dnslkup_getip()); + gotAddress = TRUE; + } + } + } + + return 1; +} + // return 0 to just continue in the packet loop and return the position // of the tcp/udp data if there is tcp/udp data part uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { From 9658616ccdead537fd2b859479052d71ec038861 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 00:01:06 +0200 Subject: [PATCH 15/27] Update of README, still a lot to do. Clear code on ENC28J60.h abnd ENC28J60.c --- README.md | 133 +++++++++++++++++++++++++++++++++++++++++++------ inc/enc28j60.h | 72 +++++++++++++------------- src/enc28j60.c | 91 +++++---------------------------- 3 files changed, 166 insertions(+), 130 deletions(-) diff --git a/README.md b/README.md index 52c231c..14fd9a9 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,135 @@ -# stm32-enc28j60 +# TOOLS for STM32 +## ENC28J60 +ENC28J60 ethernet module for STM32 MCU -STM32 ENC28J60 ethernet driver, ported for CMake using [stm32-cmake](https://github.com/ObKo/stm32-cmake) and [stm32-base](https://github.com/DVALight/stm32-base) +Search terms for `ENC28J60/HR911105A datasheet` and `enc28j60 schematic` for detail about hardware. -An implementation of ENC28J60 driver for STM32 MCU. Tested only on sending UDP packets and only on MCUs STM32F091xC, STM32F030x6, STM32F103xB and STM32F401VE. +About Cmake, this code was ported using [stm32-cmake](https://github.com/ObKo/stm32-cmake) and [stm32-base](https://github.com/DVALight/stm32-base) -You need to copy `stm32*_hal_conf.h` of your MCU to `inc` folder to make it work. +## UDP Command Hnadler +Domotics ! -If you need to use another STM32 MCU then don't forget to edit file [`inc/stm32includes.h`](inc/stm32includes.h). +## RTC +Active the code on file [`inc/define.h`](inc/defines.h). +Add the code needed in main.c `between USER code begin` and `User code end` of the section RTC created by your STM32cubeIDE, you should find examples files in this repo. -## How to use it +## MCU tested (please share your results for update this list): +- STM32F091xC (v2 Tested only on sending UDP packets) +- STM32F030x6 (v2 Tested only on sending UDP packets) +- STM32F103xB (v2 Tested only on sending UDP packets) +- STM32F401VE (v2 Tested only on sending UDP packets) +- STM32F407 (v3 complete network interface) +- STM32F411 (will be test current 2024 for v3 complete network interface) -* Either just copy it into your project and set up your project to be built with this library. -* Or use the [stm32-cmake](https://github.com/ObKo/stm32-cmake) (see the next section). - - -### News on branch V3 +# News on branch V3 Quick Start for Full Network Connection To quickly set up a complete network connection (DHCP, DNS, Gateway, etc.), use the following function: -`ES_FullConnection();` in the file: `EtherShield.c` +`ES_FullConnection();` from the file: `EtherShield.c` Call this fonction just before the while and ping your device ! and in your while think to use this for treat everything from your network : -` paquetweb();` +` ES_ProcessWebPacket();` + +A lot of code was clear, if any function deseaper please create a Issue + + +# How to use this project + +Copy it into your project and set up your project to be built with this library. +copy all files /inc and /src respectibly to the folder. + +Main.c and Main.h examples should be present. Shared your files and create folder for each MCU for help the community. + +About CMake, see the section Cmake below. + +## Expected code from STM32cubeIDE configuration + +You have to configure via STM32cubeIDE your SPI, automaticaly this code should be create in your main.c + +`void ENC28J60_hspi->Instance_Configuration(void) +{ + SPI_InitTypeDef SPI_InitStructure; + + /* Enable the SPI periph */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_hspi->Instance, ENABLE); + + /* SPI configuration -------------------------------------------------------*/ + SPI_I2S_DeInit(hspi->Instance); + SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_InitStructure.SPI_CRCPolynomial = 7; + SPI_InitStructure.SPI_Mode = SPI_Mode_Master; + SPI_Init(hspi->Instance, &SPI_InitStructure); + + /* Enable hspi->Instance */ + SPI_Cmd(hspi->Instance, ENABLE); +}` + +Suggestion of GPIO Configuration + +`void ENC28J60_GPIO_Configuration(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + /* Enable SCK, MOSI and MISO GPIO clocks */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + /* Enable CS GPIO clock */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_hspi->Instance); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_hspi->Instance); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_hspi->Instance); + + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; + + /* SPI SCK pin configuration */ + GPIO_InitStructure.GPIO_Pin = GPIO_PIN_3; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* SPI MOSI pin configuration */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* SPI MISO pin configuration */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* Configure GPIO PIN for Chip select */ + GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* Deselect : Chip Select high */ + GPIO_SetBits(GPIOA, GPIO_PIN_4); +}` + + +### FILE [`inc/define.h`](inc/defines.h) Hardware SECTION + +If you need to use another STM32 MCU then don't forget to edit file in Hardware section. + + +### FILE [`inc/define.h`](inc/defines.h) Parameters SECTION + +Chose what you want, depend on your needs. + +### FILE [`inc/define.h`](inc/defines.h) CS SECTION +Very important part if you choose CS_Only, you have to specify this correcty. -Options for NSS Output Signal in defines.h that's a simple desactivation and activation on the file enc28j60.h. +### About CMake +Use the [stm32-cmake](https://github.com/ObKo/stm32-cmake) (see the next section). ## CMake diff --git a/inc/enc28j60.h b/inc/enc28j60.h index ddb8025..6b36f17 100644 --- a/inc/enc28j60.h +++ b/inc/enc28j60.h @@ -1,18 +1,19 @@ +/* + * ENC28J60.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #ifndef __ENC28J60_H #define __ENC28J60_H #include "defines.h" -#define Delay HAL_Delay -/* -#pragma GCC push_options -#pragma GCC optimize ("O0") -static inline void uDelay(uint32_t useconds) { - volatile int cycleCount = ? - while (cycleCount--); -} -#pragma GCC pop_options -*/ #ifdef CS_Only #if ETHERNET_CS_DELAY >= 10 # define ETHERNET_CS_DELAY_PROC {volatile uint32_t i=ETHERNET_CS_DELAY; while(i--);} @@ -252,7 +253,6 @@ static inline void uDelay(uint32_t useconds) { #define ENC28J60_BIT_FIELD_CLR 0xA0 #define ENC28J60_SOFT_RESET 0xFF - // The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata // buffer boundaries applied to internal 8K ram // the entire available packet buffer space is allocated @@ -265,40 +265,36 @@ static inline void uDelay(uint32_t useconds) { #define TXSTART_INIT (0x1FFF-0x0600) // stop TX buffer at end of mem #define TXSTOP_INIT 0x1FFF -// + // max frame length which the controller will accept: #define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) //#define MAX_FRAMELEN 600 -//void ENC28J60_SPI1_Configuration(void); -//void ENC28J60_GPIO_Configuration(void); void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new); unsigned char ENC28J60_SendByte(unsigned char dt); -uint8_t enc28j60ReadOp(uint8_t op, uint8_t address); - // functions -extern uint8_t enc28j60ReadOp(uint8_t op, uint8_t address); -extern void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data); -extern void enc28j60ReadBuffer(uint16_t len, uint8_t* data); -extern void enc28j60WriteBuffer(uint16_t len, uint8_t* data); -extern void enc28j60SetBank(uint8_t address); -extern uint8_t enc28j60Read(uint8_t address); -extern void enc28j60Write(uint8_t address, uint8_t data); -extern void enc28j60PhyWrite(uint8_t address, uint16_t data); -extern void enc28j60clkout(uint8_t clk); -extern void enc28j60SpiInit(void); -extern void enc28j60Init(uint8_t* macaddr); -extern void enc28j60PacketSend(uint16_t len, uint8_t* packet); -extern uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet); -extern uint8_t enc28j60getrev(void); -extern uint8_t enc28j60hasRxPkt(void); -extern uint8_t enc28j60linkup(void); -extern void enc28j60EnableBroadcast( void ); -extern void enc28j60DisableBroadcast( void ); -extern void enc28j60EnableMulticast( void ); -extern void enc28j60DisableMulticast( void ); -extern void enc28j60PowerDown(); -extern void enc28j60PowerUp(); +uint8_t enc28j60ReadOp(uint8_t op, uint8_t address); +void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data); +void enc28j60ReadBuffer(uint16_t len, uint8_t* data); +void enc28j60WriteBuffer(uint16_t len, uint8_t* data); +void enc28j60SetBank(uint8_t address); +uint8_t enc28j60Read(uint8_t address); +void enc28j60Write(uint8_t address, uint8_t data); +void enc28j60PhyWrite(uint8_t address, uint16_t data); +void enc28j60clkout(uint8_t clk); +void enc28j60SpiInit(void); +void enc28j60Init(uint8_t* macaddr); +void enc28j60PacketSend(uint16_t len, uint8_t* packet); +uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet); +uint8_t enc28j60getrev(void); +uint8_t enc28j60hasRxPkt(void); +uint8_t enc28j60linkup(void); +void enc28j60EnableBroadcast( void ); +void enc28j60DisableBroadcast( void ); +void enc28j60EnableMulticast( void ); +void enc28j60DisableMulticast( void ); +void enc28j60PowerDown(); +void enc28j60PowerUp(); #endif /* __ENC28J60_H */ diff --git a/src/enc28j60.c b/src/enc28j60.c index ae3d571..85c74ac 100644 --- a/src/enc28j60.c +++ b/src/enc28j60.c @@ -1,3 +1,14 @@ +/* + * ENC28J60.c + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #include "defines.h" #include "enc28j60.h" #include "error_handler.h" @@ -7,83 +18,7 @@ static uint16_t gNextPacketPtr; static uint8_t erxfcon; static SPI_HandleTypeDef *hspi = NULL; -#if 0 -void ENC28J60_hspi->Instance_Configuration(void) -{ - SPI_InitTypeDef SPI_InitStructure; - - /* Enable the SPI periph */ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_hspi->Instance, ENABLE); - - /* SPI configuration -------------------------------------------------------*/ - SPI_I2S_DeInit(hspi->Instance); - SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; - SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; - SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; - SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; - SPI_InitStructure.SPI_CRCPolynomial = 7; - SPI_InitStructure.SPI_Mode = SPI_Mode_Master; - SPI_Init(hspi->Instance, &SPI_InitStructure); - - /* Enable hspi->Instance */ - SPI_Cmd(hspi->Instance, ENABLE); -} - -#endif - -/******************************************************************************* -* Function Name : GPIO_Configuration -* Description : Configures the different GPIO ports. -* Input : None -* Output : None -* Return : None -*******************************************************************************/ -#if 0 -void ENC28J60_GPIO_Configuration(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - - /* Enable SCK, MOSI and MISO GPIO clocks */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - - /* Enable CS GPIO clock */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_hspi->Instance); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_hspi->Instance); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_hspi->Instance); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; - - /* SPI SCK pin configuration */ - GPIO_InitStructure.GPIO_Pin = GPIO_PIN_3; - GPIO_Init(GPIOB, &GPIO_InitStructure); - - /* SPI MOSI pin configuration */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* SPI MISO pin configuration */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Configure GPIO PIN for Chip select */ - GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Deselect : Chip Select high */ - GPIO_SetBits(GPIOA, GPIO_PIN_4); -} -#endif void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new) { @@ -219,7 +154,7 @@ uint16_t enc28j60PhyReadH(uint8_t address) // Set the right address and start the register read operation enc28j60Write(MIREGADR, address); enc28j60Write(MICMD, MICMD_MIIRD); - Delay(15); + HAL_Delay(15); // wait until the PHY read completes while(enc28j60Read(MISTAT) & MISTAT_BUSY); @@ -249,7 +184,7 @@ void enc28j60PhyWrite(uint8_t address, uint16_t data) enc28j60Write(MIWRH, data>>8); // wait until the PHY write completes while(enc28j60Read(MISTAT) & MISTAT_BUSY){ - Delay(15); + HAL_Delay(15); } } /* From 6b7f361112c52317d5dc0b5d06ca1d5c3836ef91 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 01:13:17 +0200 Subject: [PATCH 16/27] DANGEROUS step here. I use AI for clear all functions. A lot of code is delete. 2 commented function are now available --- inc/enc28j60.h | 1 + src/EtherShield.c | 2 +- src/enc28j60.c | 661 +++++++++++++++++++++------------------------- 3 files changed, 306 insertions(+), 358 deletions(-) diff --git a/inc/enc28j60.h b/inc/enc28j60.h index 6b36f17..f16c705 100644 --- a/inc/enc28j60.h +++ b/inc/enc28j60.h @@ -282,6 +282,7 @@ void enc28j60SetBank(uint8_t address); uint8_t enc28j60Read(uint8_t address); void enc28j60Write(uint8_t address, uint8_t data); void enc28j60PhyWrite(uint8_t address, uint16_t data); +static void enc28j60PhyWriteWord(uint8_t address, uint16_t data); void enc28j60clkout(uint8_t clk); void enc28j60SpiInit(void); void enc28j60Init(uint8_t* macaddr); diff --git a/src/EtherShield.c b/src/EtherShield.c index 515d875..1460bf9 100644 --- a/src/EtherShield.c +++ b/src/EtherShield.c @@ -20,7 +20,7 @@ void ES_FullConnection(SPI_HandleTypeDef *hspi) { enc28j60Init(macaddrin); // Configure the ENC28J60 clock - enc28j60Write(ECOCON, 3 & 0x7); + enc28j60clkout(3); // Configure the ENC28J60 PHY LEDs enc28j60PhyWrite(PHLCON, 0x3880); diff --git a/src/enc28j60.c b/src/enc28j60.c index 85c74ac..c518f78 100644 --- a/src/enc28j60.c +++ b/src/enc28j60.c @@ -20,450 +20,397 @@ static SPI_HandleTypeDef *hspi = NULL; -void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new) -{ +void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new) { hspi = hspi_new; } void error (float error_num, char infinite); -unsigned char ENC28J60_SendByte(uint8_t tx) -{ - if (hspi == NULL) - return 0; -// while(SPI_I2S_GetFlagStatus(hspi->Instance, SPI_I2S_FLAG_TXE) == RESET); -// SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_TXE, RESET, 0xffffffff); -/* - //SPI_I2S_SendData(hspi->Instance, dt); - int r; - r = HAL_SPI_Transmit(hspi, &dt, 1, 0xffffffff); - - if (r != HAL_OK) - error(r, 0); - //while(SPI_I2S_GetFlagStatus(hspi->Instance, SPI_I2S_FLAG_RXNE) == RESET); -// SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, 0xffffffff); - - //return SPI_I2S_ReceiveData(hspi->Instance); - r = HAL_SPI_Receive(hspi, &dt, 1, 0xffffffff); +unsigned char ENC28J60_SendByte(uint8_t tx) { + if (hspi == NULL) + return 0; - if (r != HAL_OK) - error(r, 0); -*/ + uint8_t rx = 0; + int r; - uint8_t rx = 0; - int r; + // Use a reasonable timeout value + const uint32_t timeout = 1000; // 1 second timeout - r = HAL_SPI_TransmitReceive(hspi, &tx, &rx, 1, 0xffffffff); + r = HAL_SPI_TransmitReceive(hspi, &tx, &rx, 1, timeout); - if (r != HAL_OK) - ENC28j60_Error_Handler(SPI_ERROR); + if (r != HAL_OK) { + ENC28j60_Error_Handler(SPI_ERROR); + return 0; // Return a default value in case of an error + } - return rx; + return rx; } -uint8_t enc28j60ReadOp(uint8_t op, uint8_t address) -{ - uint8_t temp; - enableChip; - // issue read command - ENC28J60_SendByte(op | (address & ADDR_MASK)); +uint8_t enc28j60ReadOp(uint8_t op, uint8_t address) { + uint8_t temp; + + // Enable the chip (CS low) + enableChip; + + // Issue read command + ENC28J60_SendByte(op | (address & ADDR_MASK)); + + // Read the data + temp = ENC28J60_SendByte(0xFF); + + // If this is a MAC or MII register, read one more byte + if (address & 0x80) temp = ENC28J60_SendByte(0xFF); - if (address & 0x80) - temp = ENC28J60_SendByte(0xFF); - // release CS - disableChip; - return temp; + // Disable the chip (CS high) + disableChip; + + return temp; } -void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data) -{ +void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data) { + // Enable the chip (CS low) enableChip; + + // Issue write command ENC28J60_SendByte(op | (address & ADDR_MASK)); + + // Write the data ENC28J60_SendByte(data); + + // Disable the chip (CS high) disableChip; } void enc28j60PowerDown() { - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); - while(enc28j60Read(ESTAT) & ESTAT_RXBUSY); - while(enc28j60Read(ECON1) & ECON1_TXRTS); - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); + // Disable packet reception + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + + // Wait for any ongoing receive operation to complete + while (enc28j60Read(ESTAT) & ESTAT_RXBUSY); + + // Wait for any ongoing transmission to complete + while (enc28j60Read(ECON1) & ECON1_TXRTS); + + // Enter power save mode + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); } void enc28j60PowerUp() { - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); - while(!enc28j60Read(ESTAT) & ESTAT_CLKRDY); -} + // Exit power save mode + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + // Wait for the clock to become ready + while (!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); +} -void enc28j60ReadBuffer(uint16_t len, uint8_t* data) -{ +void enc28j60ReadBuffer(uint16_t len, uint8_t* data) { + // Enable the chip (CS low) enableChip; + + // Issue read buffer memory command ENC28J60_SendByte(ENC28J60_READ_BUF_MEM); + + // Read data from buffer while (len--) { *data++ = ENC28J60_SendByte(0x00); } + + // Disable the chip (CS high) disableChip; - // Remove next line suggested by user epam - not needed -// *data='\0'; } static uint16_t enc28j60ReadBufferWord() { uint16_t result; - enc28j60ReadBuffer(2, (uint8_t*) &result); + // Read 2 bytes from the buffer and store them in result + enc28j60ReadBuffer(2, (uint8_t*)&result); return result; } - -void enc28j60WriteBuffer(uint16_t len, uint8_t* data) -{ +void enc28j60WriteBuffer(uint16_t len, uint8_t* data) { + // Enable the chip (CS low) enableChip; + + // Issue write buffer memory command ENC28J60_SendByte(ENC28J60_WRITE_BUF_MEM); - while (len--) + + // Write data to buffer + while (len--) { ENC28J60_SendByte(*data++); + } + // Disable the chip (CS high) disableChip; } -void enc28j60SetBank(uint8_t address) -{ - if ((address & BANK_MASK) != Enc28j60Bank) { - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0); - Enc28j60Bank = address & BANK_MASK; - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, Enc28j60Bank>>5); +void enc28j60SetBank(uint8_t address) { + uint8_t bank = address & BANK_MASK; + + if (bank != Enc28j60Bank) { + // Clear the bank select bits in ECON1 + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1 | ECON1_BSEL0); + + // Set the new bank + Enc28j60Bank = bank; + + // Set the bank select bits in ECON1 + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, bank >> 5); } } -uint8_t enc28j60Read(uint8_t address) -{ - // set the bank - enc28j60SetBank(address); - // do the read - return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +uint8_t enc28j60Read(uint8_t address) { + // Set the bank for the given address + enc28j60SetBank(address); + + // Read the value from the specified register + return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); } + void enc28j60WriteWord(uint8_t address, uint16_t data) { - enc28j60Write(address, data & 0xff); + // Write the low byte to the specified address + enc28j60Write(address, data & 0xFF); + + // Write the high byte to the next address enc28j60Write(address + 1, data >> 8); } -// read upper 8 bits -uint16_t enc28j60PhyReadH(uint8_t address) -{ - // Set the right address and start the register read operation - enc28j60Write(MIREGADR, address); - enc28j60Write(MICMD, MICMD_MIIRD); - HAL_Delay(15); - - // wait until the PHY read completes - while(enc28j60Read(MISTAT) & MISTAT_BUSY); - - // reset reading bit - enc28j60Write(MICMD, 0x00); - - return (enc28j60Read(MIRDH)); +// Read upper 8 bits from PHY register +uint16_t enc28j60PhyReadH(uint8_t address) { + // Set the address and start the register read operation + enc28j60Write(MIREGADR, address); + enc28j60Write(MICMD, MICMD_MIIRD); + + // Wait for the read operation to complete + HAL_Delay(15); + + // Wait until the PHY read completes + while (enc28j60Read(MISTAT) & MISTAT_BUSY); + + // Reset the reading bit + enc28j60Write(MICMD, 0x00); + + // Return the read value + return enc28j60Read(MIRDH); } +void enc28j60Write(uint8_t address, uint8_t data) { + // Set the bank for the given address + enc28j60SetBank(address); -void enc28j60Write(uint8_t address, uint8_t data) -{ - // set the bank - enc28j60SetBank(address); - // do the write - enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); + // Write the data to the specified address + enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); } +void enc28j60PhyWrite(uint8_t address, uint16_t data) { + // Set the PHY register address + enc28j60Write(MIREGADR, address); + + // Write the low byte of PHY data + enc28j60Write(MIWRL, data & 0xFF); + + // Write the high byte of PHY data + enc28j60Write(MIWRH, data >> 8); -void enc28j60PhyWrite(uint8_t address, uint16_t data) -{ - // set the PHY register address - enc28j60Write(MIREGADR, address); - // write the PHY data - enc28j60Write(MIWRL, data); - enc28j60Write(MIWRH, data>>8); - // wait until the PHY write completes - while(enc28j60Read(MISTAT) & MISTAT_BUSY){ + // Wait until the PHY write completes + while (enc28j60Read(MISTAT) & MISTAT_BUSY) { HAL_Delay(15); - } + } } -/* -static void enc28j60PhyWriteWord(byte address, word data) { + +static void enc28j60PhyWriteWord(uint8_t address, uint16_t data) { + // Set the PHY register address enc28j60Write(MIREGADR, address); - //enc28j60WriteByte(MIREGADR, address); + + // Write the PHY data enc28j60WriteWord(MIWRL, data); - while (enc28j60ReadByte(MISTAT) & MISTAT_BUSY) - ; -} -*/ -void enc28j60clkout(uint8_t clk) -{ - //setup clkout: 2 is 12.5MHz: - enc28j60Write(ECOCON, clk & 0x7); + + // Wait until the PHY write completes, with a timeout + uint32_t startTick = HAL_GetTick(); + while (enc28j60Read(MISTAT) & MISTAT_BUSY) { + if ((HAL_GetTick() - startTick) > 100) { // 100ms timeout + // Handle timeout (e.g., log an error, reset the PHY, etc.) + return; + } + HAL_Delay(1); + } } -void enc28j60Init( uint8_t* macaddr ) -{ - enableChip; // ss=0 - - // perform system reset - enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - HAL_Delay(50); - // check CLKRDY bit to see if reset is complete - // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. - //while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); - // do bank 0 stuff - // initialize receive buffer - // 16-bit transfers, must write low byte first - // set receive buffer start address - gNextPacketPtr = RXSTART_INIT; - // Rx start - enc28j60WriteWord(ERXSTL, RXSTART_INIT); - // set receive pointer address - enc28j60WriteWord(ERXRDPTL, RXSTART_INIT); - // RX end - enc28j60WriteWord(ERXNDL, RXSTOP_INIT); - // TX start - enc28j60WriteWord(ETXSTL, TXSTART_INIT); - // TX end - enc28j60WriteWord(ETXNDL, TXSTOP_INIT); - // do bank 1 stuff, packet filter: - // For broadcast packets we allow only ARP packtets - // All other packets should be unicast only for our mac (MAADR) - // - // The pattern to match on is therefore - // Type ETH.DST - // ARP BROADCAST - // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 - // in binary these poitions are:11 0000 0011 1111 - // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 - - //enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); - //Change to add ERXFCON_BCEN recommended by epam - //enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); - erxfcon = ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN; - enc28j60Write(ERXFCON, erxfcon ); - enc28j60WriteWord(EPMM0, 0x303f); - enc28j60WriteWord(EPMCSL, 0xf7f9); - // - // do bank 2 stuff - // enable MAC receive - enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); - // bring MAC out of reset - enc28j60Write(MACON2, 0x00); - // enable automatic padding to 60bytes and CRC operations - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); //|MACON3_FULDPX); - // set inter-frame gap (non-back-to-back) - enc28j60WriteWord(MAIPGL, 0x0C12); - // set inter-frame gap (back-to-back) - enc28j60Write(MABBIPG, 0x12); - // Set the maximum packet size which the controller will accept - // Do not send packets longer than MAX_FRAMELEN: - enc28j60WriteWord(MAMXFLL, MAX_FRAMELEN); - // do bank 3 stuff - // write MAC address - // NOTE: MAC address in ENC28J60 is byte-backward - enc28j60Write(MAADR5, macaddr[0]); - enc28j60Write(MAADR4, macaddr[1]); - enc28j60Write(MAADR3, macaddr[2]); - enc28j60Write(MAADR2, macaddr[3]); - enc28j60Write(MAADR1, macaddr[4]); - enc28j60Write(MAADR0, macaddr[5]); - // no loopback of transmitted frames - enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); - // switch to bank 0 - enc28j60SetBank(ECON1); - // enable interrutps - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); - // enable packet reception - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +void enc28j60clkout(uint8_t clk) { + // Set the ECOCON register with the lower 3 bits of clk + // 0: Output clock disabled + // 1: 6.25 MHz + // 2: 12.5 MHz + // 3: 25 MHz + // 4-7: Reserved + enc28j60Write(ECOCON, clk & 0x07); } -// read the revision of the chip: -uint8_t enc28j60getrev(void) -{ - uint8_t rev; - rev=enc28j60Read(EREVID); - // microchip forgot to step the number on the silcon when they - // released the revision B7. 6 is now rev B7. We still have - // to see what they do when they release B8. At the moment - // there is no B8 out yet - if (rev>5) rev++; - return(rev); +void enc28j60Init(uint8_t* macaddr) { + // Enable the chip (CS low) + enableChip; + + // Perform system reset + enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + HAL_Delay(50); + + // Initialize receive buffer + gNextPacketPtr = RXSTART_INIT; + enc28j60WriteWord(ERXSTL, RXSTART_INIT); // Rx start + enc28j60WriteWord(ERXRDPTL, RXSTART_INIT); // Set receive pointer address + enc28j60WriteWord(ERXNDL, RXSTOP_INIT); // Rx end + enc28j60WriteWord(ETXSTL, TXSTART_INIT); // Tx start + enc28j60WriteWord(ETXNDL, TXSTOP_INIT); // Tx end + + // Packet filter: Allow only ARP packets for broadcast, and unicast for our MAC + erxfcon = ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN; + enc28j60Write(ERXFCON, erxfcon); + enc28j60WriteWord(EPMM0, 0x303f); + enc28j60WriteWord(EPMCSL, 0xf7f9); + + // Enable MAC receive + enc28j60Write(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); + // Bring MAC out of reset + enc28j60Write(MACON2, 0x00); + // Enable automatic padding to 60 bytes and CRC operations + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); + + // Set inter-frame gap (non-back-to-back) + enc28j60WriteWord(MAIPGL, 0x0C12); + // Set inter-frame gap (back-to-back) + enc28j60Write(MABBIPG, 0x12); + + // Set the maximum packet size which the controller will accept + enc28j60WriteWord(MAMXFLL, MAX_FRAMELEN); + + // Write MAC address (note: MAC address in ENC28J60 is byte-backward) + enc28j60Write(MAADR5, macaddr[0]); + enc28j60Write(MAADR4, macaddr[1]); + enc28j60Write(MAADR3, macaddr[2]); + enc28j60Write(MAADR2, macaddr[3]); + enc28j60Write(MAADR1, macaddr[4]); + enc28j60Write(MAADR0, macaddr[5]); + + // No loopback of transmitted frames + enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); + + // Switch to bank 0 + enc28j60SetBank(ECON1); + + // Enable interrupts + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); + + // Enable packet reception + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); } -// A number of utility functions to enable/disable broadcast and multicast bits -void enc28j60EnableBroadcast( void ) { - erxfcon |= ERXFCON_BCEN; - enc28j60Write(ERXFCON, erxfcon); +uint8_t enc28j60getrev(void) { + uint8_t rev = enc28j60Read(EREVID); + // Microchip forgot to step the number on the silicon when they released revision B7. + // 6 is now rev B7. If rev is greater than 5, increment it. + if (rev > 5) rev++; + return rev; } -void enc28j60DisableBroadcast( void ) { - erxfcon &= ~ERXFCON_BCEN; - enc28j60Write(ERXFCON, erxfcon); +void enc28j60EnableBroadcast(void) { + erxfcon |= ERXFCON_BCEN; + enc28j60Write(ERXFCON, erxfcon); } -void enc28j60EnableMulticast( void ) { - erxfcon |= ERXFCON_MCEN; - enc28j60Write(ERXFCON, erxfcon); +void enc28j60DisableBroadcast(void) { + erxfcon &= ~ERXFCON_BCEN; + enc28j60Write(ERXFCON, erxfcon); } -void enc28j60DisableMulticast( void ) { - erxfcon &= ~ERXFCON_MCEN; - enc28j60Write(ERXFCON, erxfcon); +void enc28j60EnableMulticast(void) { + erxfcon |= ERXFCON_MCEN; + enc28j60Write(ERXFCON, erxfcon); } +void enc28j60DisableMulticast(void) { + erxfcon &= ~ERXFCON_MCEN; + enc28j60Write(ERXFCON, erxfcon); +} -// link status -uint8_t enc28j60linkup(void) -{ - // bit 10 (= bit 3 in upper reg) - return(enc28j60PhyReadH(PHSTAT2) & 4); +uint8_t enc28j60linkup(void) { + // Check bit 3 in the upper PHSTAT2 register (bit 10 overall) + return (enc28j60PhyReadH(PHSTAT2) & 0x04) ? 1 : 0; } -void enc28j60PacketSend(uint16_t len, uint8_t* packet) -{ - // Check no transmit in progress - while (enc28j60ReadOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) - { - // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. - if( (enc28j60Read(EIR) & EIR_TXERIF) ) { - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); - } - } - - // Set the write pointer to start of transmit buffer area - enc28j60WriteWord(EWRPTL, TXSTART_INIT); - // Set the TXND pointer to correspond to the packet size given - enc28j60WriteWord(ETXNDL, (TXSTART_INIT+len)); - // write per-packet control byte (0x00 means use macon3 settings) - enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); - // copy the packet into the transmit buffer - enc28j60WriteBuffer(len, packet); - // send the contents of the transmit buffer onto the network - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +void enc28j60PacketSend(uint16_t len, uint8_t* packet) { + // Check no transmit in progress + while (enc28j60ReadOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) { // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. -} + if (enc28j60Read(EIR) & EIR_TXERIF) { + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); + } + } -// just probe if there might be a packet -//uint8_t enc28j60hasRxPkt(void) -//{ -// return enc28j60ReadByte(EPKTCNT) > 0; -//} - -// Gets a packet from the network receive buffer, if one is available. -// The packet will by headed by an ethernet header. -// maxlen The maximum acceptable length of a retrieved packet. -// packet Pointer where packet data should be stored. -// Returns: Packet length in bytes if a packet was retrieved, zero otherwise. -uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet) -{ - uint16_t rxstat; - uint16_t len; - // check if a packet has been received and buffered - //if( !(enc28j60Read(EIR) & EIR_PKTIF) ){ - // The above does not work. See Rev. B4 Silicon Errata point 6. - if( enc28j60Read(EPKTCNT) ==0 ){ - return(0); - } - - // Set the read pointer to the start of the received packet - enc28j60WriteWord(ERDPTL, gNextPacketPtr); - //enc28j60Write(ERDPTL, (gNextPacketPtr &0xFF)); - //enc28j60Write(ERDPTH, (gNextPacketPtr)>>8); - // read the next packet pointer - gNextPacketPtr = enc28j60ReadBufferWord(); - //gNextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - //gNextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - // read the packet length (see datasheet page 43) - len = enc28j60ReadBufferWord() - 4; - //len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - //len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - //len-=4; //remove the CRC count - // read the receive status (see datasheet page 43) - rxstat = enc28j60ReadBufferWord(); - //rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - //rxstat |= ((uint16_t)enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0))<<8; - // limit retrieve length - if (len>maxlen-1){ - len=maxlen-1; - } - // check CRC and symbol errors (see datasheet page 44, table 7-3): - // The ERXFCON.CRCEN is set by default. Normally we should not - // need to check this. - if ((rxstat & 0x80)==0){ - // invalid - len=0; - }else{ - // copy the packet from the receive buffer - enc28j60ReadBuffer(len, packet); - } - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - enc28j60WriteWord(ERXRDPTL, gNextPacketPtr ); - //enc28j60Write(ERXRDPTL, (gNextPacketPtr &0xFF)); - //enc28j60Write(ERXRDPTH, (gNextPacketPtr)>>8); - // However, compensate for the errata point 13, rev B4: enver write an even address! - if ((gNextPacketPtr - 1 < RXSTART_INIT) - || (gNextPacketPtr -1 > RXSTOP_INIT)) { - enc28j60WriteWord(ERXRDPTL, RXSTOP_INIT); - //enc28j60Write(ERXRDPTL, (RXSTOP_INIT)&0xFF); - //enc28j60Write(ERXRDPTH, (RXSTOP_INIT)>>8); - } else { - enc28j60WriteWord(ERXRDPTL, (gNextPacketPtr-1)); - //enc28j60Write(ERXRDPTL, (gNextPacketPtr-1)&0xFF); - //enc28j60Write(ERXRDPTH, (gNextPacketPtr-1)>>8); - } - // decrement the packet counter indicate we are done with this packet - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - return(len); + // Set the write pointer to start of the transmit buffer area + enc28j60WriteWord(EWRPTL, TXSTART_INIT); -/* - uint16_t rxstat; - uint16_t len; - // check if a packet has been received and buffered - //if( !(enc28j60Read(EIR) & EIR_PKTIF) ){ - // The above does not work. See Rev. B4 Silicon Errata point 6. - if( enc28j60Read(EPKTCNT) ==0 ){ - return(0); - } + // Set the TXND pointer to correspond to the packet size given + enc28j60WriteWord(ETXNDL, TXSTART_INIT + len); - // Set the read pointer to the start of the received packet - enc28j60WriteWord(ERDPTL, gNextPacketPtr); - // read the next packet pointer - gNextPacketPtr = enc28j60ReadBufferWord(); - // read the packet length (see datasheet page 43) - len = enc28j60ReadBufferWord() - 4; - // read the receive status (see datasheet page 43) - rxstat = enc28j60ReadBufferWord(); - // limit retrieve length - if (len>maxlen-1){ - len=maxlen-1; - } - // check CRC and symbol errors (see datasheet page 44, table 7-3): - // The ERXFCON.CRCEN is set by default. Normally we should not - // need to check this. - if ((rxstat & 0x80)==0){ - // invalid - len=0; - }else{ - // copy the packet from the receive buffer - enc28j60ReadBuffer(len, packet); - } - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out -// enc28j60WriteWord(ERXRDPTL, gNextPacketPtr ); - // However, compensate for the errata point 13, rev B4: enver write an even address! - if ((gNextPacketPtr - 1 < RXSTART_INIT) - || (gNextPacketPtr -1 > RXSTOP_INIT)) { - enc28j60WriteWord(ERXRDPTL, RXSTOP_INIT); - } else { - enc28j60WriteWord(ERXRDPTL, (gNextPacketPtr-1)); - } - // decrement the packet counter indicate we are done with this packet - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - return(len); -*/ + // Write per-packet control byte (0x00 means use macon3 settings) + enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); + + // Copy the packet into the transmit buffer + enc28j60WriteBuffer(len, packet); + + // Send the contents of the transmit buffer onto the network + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +} + +uint8_t enc28j60hasRxPkt(void) { + return enc28j60Read(EPKTCNT) > 0; } +uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet) { + uint16_t rxstat; + uint16_t len; + + // Check if a packet has been received and buffered + if (enc28j60Read(EPKTCNT) == 0) { + return 0; + } + + // Set the read pointer to the start of the received packet + enc28j60WriteWord(ERDPTL, gNextPacketPtr); + + // Read the next packet pointer + gNextPacketPtr = enc28j60ReadBufferWord(); + + // Read the packet length (excluding the CRC length) + len = enc28j60ReadBufferWord() - 4; + + // Read the receive status + rxstat = enc28j60ReadBufferWord(); + + // Limit retrieve length to maxlen - 1 + if (len > maxlen - 1) { + len = maxlen - 1; + } + + // Check CRC and symbol errors + if ((rxstat & 0x80) == 0) { + len = 0; + } else { + // Copy the packet from the receive buffer + enc28j60ReadBuffer(len, packet); + } + + // Move the RX read pointer to the start of the next received packet + if ((gNextPacketPtr - 1 < RXSTART_INIT) || (gNextPacketPtr - 1 > RXSTOP_INIT)) { + enc28j60WriteWord(ERXRDPTL, RXSTOP_INIT); + } else { + enc28j60WriteWord(ERXRDPTL, gNextPacketPtr - 1); + } + + // Decrement the packet counter to indicate we are done with this packet + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + + return len; +} +/* end of enc28j60.c */ From e590b12024b273f497e4c042c68e9029d7da5f9f Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 01:35:58 +0200 Subject: [PATCH 17/27] DANGEROUS step here again for these file dhcp.h and dhcp.c I change a lot of code with gpt4o. Optimisation and more comment. Report any changes I delete ifdef UDP Client for DHCP files. Seems useless. --- inc/dhcp.h | 28 +-- src/dhcp.c | 533 +++++++++++++++++++++++++++-------------------------- 2 files changed, 282 insertions(+), 279 deletions(-) diff --git a/inc/dhcp.h b/inc/dhcp.h index 6bafed9..f1d2a77 100644 --- a/inc/dhcp.h +++ b/inc/dhcp.h @@ -1,25 +1,19 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: - * Copyright: GPL V2 +/* + * ENC28J60.h * - * DHCP look-up functions based on the udp client + * Created on: Jun 4, 2024 + * Author: dtneo * - * Chip type : ATMEGA88/ATMEGA168/ATMEGA328p with ENC28J60 - *********************************************/ -//@{ + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #ifndef DHCP_H #define DHCP_H #include "defines.h" -// to use this you need to enable UDP_client in the file defines.h -#if defined (UDP_client) - -#define DHCP_BOOTREQUEST 1 -#define DHCP_BOOTRESPONSE 2 - extern void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *maskin, uint8_t *gwipin, uint8_t *dhcpsvrin, uint8_t *dnssvrin ); @@ -27,12 +21,8 @@ extern void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, extern uint8_t dhcp_state( void ); void dhcp_send(uint8_t *buf, uint8_t requestType); - uint8_t check_for_dhcp_answer(uint8_t *buf,uint16_t plen); - uint8_t have_dhcpoffer(uint8_t *buf,uint16_t plen); uint8_t have_dhcpack(uint8_t *buf,uint16_t plen); -#endif /* UDP_client */ #endif /* DHCP_H */ -//@} diff --git a/src/dhcp.c b/src/dhcp.c index 36fd79c..c40d8bd 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -1,106 +1,88 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc +/* + * ENC28J60.c * - * Author: Andrew Lindsay - * Rewritten and optimized by Jean-Claude Wippler, http://jeelabs.org/ - * Please visit Jeelabs for tons of cool stuff. + * Created on: Jun 4, 2024 + * Author: dtneo * - * Copyright: GPL V2 - * See http://www.gnu.org/licenses/gpl.html - * - * DHCP look-up functions based on the udp client - * http://www.ietf.org/rfc/rfc2131.txt - *********************************************/ -#include "defines.h" + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #include "dhcp.h" -#include "enc28j60.h" -#include "ip_arp_udp_tcp.h" #include "net.h" -#if defined(UDP_client) - #define DHCP_BOOTREQUEST 1 #define DHCP_BOOTREPLY 2 #define DHCPDISCOVER 0x01 #define DHCPOFFER 0x02 #define DHCPREQUEST 0x03 #define DHCPACK 0x05 -//#define DHCPNACK +// #define DHCPNACK // Uncomment if DHCPNACK is needed -// size 236 -typedef struct dhcpData { - // 0-3 op, htype, hlen, hops - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - // 4-7 xid - uint32_t xid; - // 8-9 secs - uint16_t secs; - // 10-11 flags - uint16_t flags; - // 12-15 ciaddr - uint8_t ciaddr[4]; - // 16-19 yiaddr - uint8_t yiaddr[4]; - // 20-23 siaddr - uint8_t siaddr[4]; - // 24-27 giaddr - uint8_t giaddr[4]; - // 28-43 chaddr(16) - uint8_t chaddr[16]; - // 44-107 sname (64) - uint8_t sname[64]; - // 108-235 file(128) - uint8_t file[128]; - // options - // dont include as its variable size, - // just tag onto end of structure -} __attribute__((packed)) dhcpData; - -static uint8_t dhcptid_l = 0; // a counter for transaction ID -// src port high byte must be a a0 or higher: +// Source port high byte must be a0 or higher: #define DHCPCLIENT_SRC_PORT_H 0xe0 #define DHCP_SRC_PORT 67 #define DHCP_DEST_PORT 68 -static uint8_t dhcpState = DHCP_STATE_INIT; +// DHCP data structure (size 236 bytes) +typedef struct dhcpData { + uint8_t op; // 0-3: op, htype, hlen, hops + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; // 4-7: xid + uint16_t secs; // 8-9: secs + uint16_t flags; // 10-11: flags + uint8_t ciaddr[4]; // 12-15: ciaddr + uint8_t yiaddr[4]; // 16-19: yiaddr + uint8_t siaddr[4]; // 20-23: siaddr + uint8_t giaddr[4]; // 24-27: giaddr + uint8_t chaddr[16]; // 28-43: chaddr + uint8_t sname[64]; // 44-107: sname + uint8_t file[128]; // 108-235: file +} __attribute__((packed)) dhcpData; + +// Static variables for DHCP +static uint8_t dhcptid_l = 0; // Counter for transaction ID +static uint8_t dhcpState = DHCP_STATE_INIT; // DHCP state // Pointers to values we have set or need to set -static uint8_t *macaddr; -uint8_t *dhcpip; -uint8_t *dhcpmask; -uint8_t *dhcpserver; -uint8_t *dnsserver; -uint8_t *gwaddr; - -// For DHCP hostnames we don't need any zero-terminators +static uint8_t *macaddr; // MAC address pointer +uint8_t *dhcpip; // DHCP IP address pointer +uint8_t *dhcpmask; // DHCP subnet mask pointer +uint8_t *dhcpserver; // DHCP server address pointer +uint8_t *dnsserver; // DNS server address pointer +uint8_t *gwaddr; // Gateway address pointer + +// Hostname definitions #define HOSTNAME_LEN DHCP_HOSTNAME_LEN -#define HOSTNAME_SIZE HOSTNAME_LEN + 3 +#define HOSTNAME_SIZE (HOSTNAME_LEN + 3) // Length + 3 for additional characters -static char hostname[HOSTNAME_SIZE]; -static uint8_t haveDhcpAnswer = 0; -static uint8_t dhcp_ansError = 0; -uint32_t currentXid = 0; -uint16_t currentSecs = 0; -static uint32_t leaseStart = 0; -static uint32_t leaseTime = 0; -static uint8_t *bufPtr; +static char hostname[HOSTNAME_SIZE]; // Hostname buffer -static void addToBuf(uint8_t b) { *bufPtr++ = b; } +// DHCP response variables +static uint8_t haveDhcpAnswer = 0; // Flag for DHCP answer +static uint8_t dhcp_ansError = 0; // DHCP answer error flag +uint32_t currentXid = 0; // Current transaction ID +uint16_t currentSecs = 0; // Current seconds since DHCP start +static uint32_t leaseStart = 0; // Lease start time +static uint32_t leaseTime = 0; // Lease duration +static uint8_t *bufPtr; // Buffer pointer + +static void addToBuf(uint8_t b) { + *bufPtr++ = b; +} uint8_t dhcp_state(void) { - // Check lease and request renew if currently OK and time - // leaseStart - start time in millis - // leaseTime - length of lease in millis - // - if (dhcpState == DHCP_STATE_OK && (leaseStart + leaseTime) <= HAL_GetTick()) { - // Calling app needs to detect this and init renewal - dhcpState = DHCP_STATE_RENEW; - } - return (dhcpState); + // Check lease and request renew if currently OK and time + // leaseStart - start time in millis + // leaseTime - length of lease in millis + if (dhcpState == DHCP_STATE_OK && (leaseStart + leaseTime) <= HAL_GetTick()) { + // Calling app needs to detect this and initiate renewal + dhcpState = DHCP_STATE_RENEW; + } + return dhcpState; } // Start request sequence, send DHCPDISCOVER @@ -110,203 +92,234 @@ uint8_t dhcp_state(void) { // All configured void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *maskin, uint8_t *gwipin, uint8_t *dhcpsvrin, uint8_t *dnssvrin) { - macaddr = macaddrin; - dhcpip = ipaddrin; - dhcpmask = maskin; - gwaddr = gwipin; - dhcpserver = dhcpsvrin; - dnsserver = dnssvrin; - /*srand(analogRead(0));*/ srand(0x13); - currentXid = 0x00654321 + rand(); - currentSecs = 0; - int n; - for (n = 0; n < 4; n++) { - dhcpip[n] = 0; - dhcpmask[n] = 0; - gwaddr[n] = 0; - dhcpserver[n] = 0; - dnsserver[n] = 0; - } - // Set a unique hostname, use DHCP_HOSTNAME- plus last octet of mac address - // hostname[8] = 'A' + (macaddr[5] >> 4); - // hostname[9] = 'A' + (macaddr[5] & 0x0F); - for (int i = 0; i < HOSTNAME_LEN; i++) { - hostname[i] = DHCP_HOSTNAME[i]; - } - - hostname[HOSTNAME_LEN] = '-'; - hostname[HOSTNAME_LEN + 1] = 'A' + (macaddr[5] >> 4); - hostname[HOSTNAME_LEN + 2] = 'A' + (macaddr[5] & 0x0F); - hostname[HOSTNAME_LEN + 3] = '\0'; - - // Reception of broadcast packets turned off by default, but - // it has been shown that some routers send responses as - // broadcasts. Enable here and disable later - enc28j60EnableBroadcast(); - dhcp_send(buf, DHCPDISCOVER); - dhcpState = DHCP_STATE_DISCOVER; + macaddr = macaddrin; + dhcpip = ipaddrin; + dhcpmask = maskin; + gwaddr = gwipin; + dhcpserver = dhcpsvrin; + dnsserver = dnssvrin; + + // Initialize random seed and transaction ID + srand(0x13); + currentXid = 0x00654321 + rand(); + currentSecs = 0; + + // Clear IP, mask, gateway, DHCP server, and DNS server addresses + memset(dhcpip, 0, 4); + memset(dhcpmask, 0, 4); + memset(gwaddr, 0, 4); + memset(dhcpserver, 0, 4); + memset(dnsserver, 0, 4); + + // Set a unique hostname, use DHCP_HOSTNAME- plus last octet of MAC address + strncpy(hostname, DHCP_HOSTNAME, HOSTNAME_LEN); + hostname[HOSTNAME_LEN] = '-'; + hostname[HOSTNAME_LEN + 1] = 'A' + (macaddr[5] >> 4); + hostname[HOSTNAME_LEN + 2] = 'A' + (macaddr[5] & 0x0F); + hostname[HOSTNAME_LEN + 3] = '\0'; + + // Enable reception of broadcast packets + enc28j60EnableBroadcast(); + + // Send DHCPDISCOVER packet + dhcp_send(buf, DHCPDISCOVER); + dhcpState = DHCP_STATE_DISCOVER; } void dhcp_request_ip(uint8_t *buf) { - dhcp_send(buf, DHCPREQUEST); - dhcpState = DHCP_STATE_REQUEST; + // Send DHCPREQUEST packet + dhcp_send(buf, DHCPREQUEST); + + // Update the DHCP state to REQUEST + dhcpState = DHCP_STATE_REQUEST; } // Main DHCP message sending function, either DHCPDISCOVER or DHCPREQUEST void dhcp_send(uint8_t *buf, uint8_t requestType) { - int i = 0; - haveDhcpAnswer = 0; - dhcp_ansError = 0; - dhcptid_l++; // increment for next request, finally wrap - // destination IP gets replaced after this call - - memset(buf, 0, 400); // XXX OUCH! - send_udp_prepare(buf, (DHCPCLIENT_SRC_PORT_H << 8) | (dhcptid_l & 0xff), dhcpip, DHCP_DEST_PORT); - - memcpy(buf + ETH_SRC_MAC, macaddr, 6); - memset(buf + ETH_DST_MAC, 0xFF, 6); - buf[IP_TOTLEN_L_P] = 0x82; - buf[IP_PROTO_P] = IP_PROTO_UDP_V; - memset(buf + IP_DST_P, 0xFF, 4); - buf[UDP_DST_PORT_L_P] = DHCP_SRC_PORT; - buf[UDP_SRC_PORT_H_P] = 0; - buf[UDP_SRC_PORT_L_P] = DHCP_DEST_PORT; - - // Build DHCP Packet from buf[UDP_DATA_P] - // Make dhcpPtr start of UDP data buffer - dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; - // 0-3 op, htype, hlen, hops - dhcpPtr->op = DHCP_BOOTREQUEST; - dhcpPtr->htype = 1; - dhcpPtr->hlen = 6; - dhcpPtr->hops = 0; - // 4-7 xid - memcpy(&dhcpPtr->xid, ¤tXid, sizeof(currentXid)); - // 8-9 secs - memcpy(&dhcpPtr->secs, ¤tSecs, sizeof(currentSecs)); - // 16-19 yiaddr - memset(dhcpPtr->yiaddr, 0, 4); - // 28-43 chaddr(16) - memcpy(dhcpPtr->chaddr, macaddr, 6); - - // options defined as option, length, value - bufPtr = buf + UDP_DATA_P + sizeof(dhcpData); - // Magic cookie 99, 130, 83 and 99 - addToBuf(99); - addToBuf(130); - addToBuf(83); - addToBuf(99); - - // Set correct options - // Option 1 - DHCP message type - addToBuf(53); // DHCPDISCOVER, DHCPREQUEST - addToBuf(1); // Length - addToBuf(requestType); // Value - - // Client Identifier Option, this is the client mac address - addToBuf(61); // Client identifier - addToBuf(7); // Length - addToBuf(0x01); // Value - for (i = 0; i < 6; i++) addToBuf(macaddr[i]); - - // Host name Option - addToBuf(12); // Host name - addToBuf(HOSTNAME_SIZE); // Length - for (i = 0; i < HOSTNAME_SIZE; i++) addToBuf(hostname[i]); - - if (requestType == DHCPREQUEST) { - // Request IP address - addToBuf(50); // Requested IP address - addToBuf(4); // Length - for (i = 0; i < 4; i++) addToBuf(dhcpip[i]); - - // Request using server ip address - addToBuf(54); // Server IP address - addToBuf(4); // Length - for (i = 0; i < 4; i++) addToBuf(dhcpserver[i]); - } - - // Additional information in parameter list - minimal list for what we need - addToBuf(55); // Parameter request list - addToBuf(3); // Length - addToBuf(1); // Subnet mask - addToBuf(3); // Route/Gateway - addToBuf(6); // DNS Server - - // payload len should be around 300 - addToBuf(255); // end option - send_udp_transmit(buf, bufPtr - buf - UDP_DATA_P); + haveDhcpAnswer = 0; + dhcp_ansError = 0; + dhcptid_l++; // Increment for next request, finally wrap + + // Clear the buffer + memset(buf, 0, 400); + + // Prepare the UDP packet + send_udp_prepare(buf, (DHCPCLIENT_SRC_PORT_H << 8) | (dhcptid_l & 0xff), dhcpip, DHCP_DEST_PORT); + + // Set Ethernet source and destination MAC addresses + memcpy(buf + ETH_SRC_MAC, macaddr, 6); + memset(buf + ETH_DST_MAC, 0xFF, 6); + + // Set IP total length, protocol, and destination + buf[IP_TOTLEN_L_P] = 0x82; + buf[IP_PROTO_P] = IP_PROTO_UDP_V; + memset(buf + IP_DST_P, 0xFF, 4); + + // Set UDP source and destination ports + buf[UDP_DST_PORT_L_P] = DHCP_SRC_PORT; + buf[UDP_SRC_PORT_H_P] = 0; + buf[UDP_SRC_PORT_L_P] = DHCP_DEST_PORT; + + // Build DHCP packet from buf[UDP_DATA_P] + dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; + + // 0-3: op, htype, hlen, hops + dhcpPtr->op = DHCP_BOOTREQUEST; + dhcpPtr->htype = 1; + dhcpPtr->hlen = 6; + dhcpPtr->hops = 0; + + // 4-7: xid + memcpy(&dhcpPtr->xid, ¤tXid, sizeof(currentXid)); + + // 8-9: secs + memcpy(&dhcpPtr->secs, ¤tSecs, sizeof(currentSecs)); + + // 16-19: yiaddr + memset(dhcpPtr->yiaddr, 0, 4); + + // 28-43: chaddr (16) + memcpy(dhcpPtr->chaddr, macaddr, 6); + + // Options defined as option, length, value + bufPtr = buf + UDP_DATA_P + sizeof(dhcpData); + + // Magic cookie: 99, 130, 83, 99 + const uint8_t magicCookie[] = {99, 130, 83, 99}; + memcpy(bufPtr, magicCookie, sizeof(magicCookie)); + bufPtr += sizeof(magicCookie); + + // Option 1 - DHCP message type + addToBuf(53); // Option: DHCP Message Type + addToBuf(1); // Length: 1 + addToBuf(requestType); // Value: DHCPDISCOVER or DHCPREQUEST + + // Client Identifier Option: client MAC address + addToBuf(61); // Option: Client Identifier + addToBuf(7); // Length: 7 + addToBuf(0x01); // Hardware type: Ethernet + for (int i = 0; i < 6; i++) addToBuf(macaddr[i]); + + // Host name Option + addToBuf(12); // Option: Host Name + addToBuf(HOSTNAME_SIZE); // Length + for (int i = 0; i < HOSTNAME_SIZE; i++) addToBuf(hostname[i]); + + if (requestType == DHCPREQUEST) { + // Request IP address + addToBuf(50); // Option: Requested IP Address + addToBuf(4); // Length: 4 + for (int i = 0; i < 4; i++) addToBuf(dhcpip[i]); + + // Request using server IP address + addToBuf(54); // Option: Server Identifier + addToBuf(4); // Length: 4 + for (int i = 0; i < 4; i++) addToBuf(dhcpserver[i]); + } + + // Additional information in parameter request list + addToBuf(55); // Option: Parameter Request List + addToBuf(3); // Length: 3 + addToBuf(1); // Parameter: Subnet Mask + addToBuf(3); // Parameter: Router + addToBuf(6); // Parameter: DNS Server + + // End option + addToBuf(255); + + // Transmit the UDP packet + send_udp_transmit(buf, bufPtr - buf - UDP_DATA_P); } -// Examine packet, if dhcp then process, if just exit. -// Perform lease expiry checks and initiate renewal -// process the answer from the dhcp server: -// return 1 on sucessful processing of answer. -// We set also the variable haveDhcpAnswer -// Either DHCPOFFER, DHCPACK or DHCPNACK -// Return 0 for nothing processed, 1 for done soemthing +// Examine packet, if DHCP then process, otherwise exit. +// Perform lease expiry checks and initiate renewal. +// Process the answer from the DHCP server: +// Return 1 on successful processing of the answer. +// We also set the variable haveDhcpAnswer to 1 if we process an answer. +// Either DHCPOFFER, DHCPACK, or DHCPNACK. +// Return 0 for nothing processed, 1 for something processed. uint8_t check_for_dhcp_answer(uint8_t *buf, uint16_t plen) { - // Map struct onto payload - dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; - if (plen >= 70 && buf[UDP_SRC_PORT_L_P] == DHCP_SRC_PORT && dhcpPtr->op == DHCP_BOOTREPLY && - !memcmp(&dhcpPtr->xid, ¤tXid, 4)) { - // Check for lease expiry - // uint32_t currentSecs = millis(); - int optionIndex = UDP_DATA_P + sizeof(dhcpData) + 4; - if (buf[optionIndex] == 53) switch (buf[optionIndex + 2]) { - case DHCPOFFER: - return have_dhcpoffer(buf, plen); - case DHCPACK: - return have_dhcpack(buf, plen); - } - } - return 0; + // Map struct onto payload + dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; + + // Check if the packet is a DHCP reply with the correct transaction ID + if (plen >= 70 && + buf[UDP_SRC_PORT_L_P] == DHCP_SRC_PORT && + dhcpPtr->op == DHCP_BOOTREPLY && + !memcmp(&dhcpPtr->xid, ¤tXid, sizeof(currentXid))) { + + // Check for DHCP message type option + int optionIndex = UDP_DATA_P + sizeof(dhcpData) + 4; + if (buf[optionIndex] == 53) { // Option 53 is DHCP message type + uint8_t messageType = buf[optionIndex + 2]; + switch (messageType) { + case DHCPOFFER: + return have_dhcpoffer(buf, plen); + case DHCPACK: + return have_dhcpack(buf, plen); + // You can add more cases here if needed, e.g., DHCPNACK + } + } + } + + return 0; // No DHCP answer processed } uint8_t have_dhcpoffer(uint8_t *buf, uint16_t plen) { - // Map struct onto payload - dhcpData *dhcpPtr = (dhcpData *)((uint8_t *)buf + UDP_DATA_P); - // Offered IP address is in yiaddr - memcpy(dhcpip, dhcpPtr->yiaddr, 4); - // Scan through variable length option list identifying options we want - uint8_t *ptr = (uint8_t *)(dhcpPtr + 1) + 4; - do { - uint8_t option = *ptr++; - uint8_t optionLen = *ptr++; - uint8_t i; - switch (option) { - case 1: - memcpy(dhcpmask, ptr, 4); - break; - case 3: - memcpy(gwaddr, ptr, 4); - break; - case 6: - memcpy(dnsserver, ptr, 4); - break; - case 51: - leaseTime = 0; - for (i = 0; i < 4; i++) leaseTime = (leaseTime + ptr[i]) << 8; - leaseTime *= 1000; // milliseconds - break; - case 54: - memcpy(dhcpserver, ptr, 4); - break; + // Map struct onto payload + dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; + + // Offered IP address is in yiaddr + memcpy(dhcpip, dhcpPtr->yiaddr, 4); + + // Scan through variable length option list identifying options we want + uint8_t *ptr = (uint8_t *)(dhcpPtr + 1) + 4; // Move past fixed DHCP data and magic cookie + + while (ptr < buf + plen) { + uint8_t option = *ptr++; + uint8_t optionLen = *ptr++; + + switch (option) { + case 1: // Subnet Mask + memcpy(dhcpmask, ptr, 4); + break; + case 3: // Router/Gateway + memcpy(gwaddr, ptr, 4); + break; + case 6: // DNS Server + memcpy(dnsserver, ptr, 4); + break; + case 51: // IP Address Lease Time + leaseTime = 0; + for (uint8_t i = 0; i < 4; i++) { + leaseTime = (leaseTime << 8) + ptr[i]; + } + leaseTime *= 1000; // Convert to milliseconds + break; + case 54: // DHCP Server Identifier + memcpy(dhcpserver, ptr, 4); + break; + default: + break; + } + ptr += optionLen; } - ptr += optionLen; - } while (ptr < buf + plen); - dhcp_request_ip(buf); - return 1; + + dhcp_request_ip(buf); + return 1; } uint8_t have_dhcpack(uint8_t *buf, uint16_t plen) { - dhcpState = DHCP_STATE_OK; - leaseStart = HAL_GetTick(); - // Turn off broadcast. Application if it needs it can re-enable it - enc28j60DisableBroadcast(); - return 2; -} + // Set DHCP state to OK + dhcpState = DHCP_STATE_OK; + + // Record the lease start time + leaseStart = HAL_GetTick(); -#endif + // Turn off broadcast. Application can re-enable if needed + enc28j60DisableBroadcast(); + // Return 2 to indicate DHCPACK processed + return 2; +} /* end of dhcp.c */ From 09a7929ff77511912971fd31b45dcb21f67017a7 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 12:05:27 +0200 Subject: [PATCH 18/27] few updates --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 14fd9a9..469e992 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,18 @@ Search terms for `ENC28J60/HR911105A datasheet` and `enc28j60 schematic` for det About Cmake, this code was ported using [stm32-cmake](https://github.com/ObKo/stm32-cmake) and [stm32-base](https://github.com/DVALight/stm32-base) -## UDP Command Hnadler +### UDP Command Hnadler Domotics ! -## RTC +### RTC Active the code on file [`inc/define.h`](inc/defines.h). Add the code needed in main.c `between USER code begin` and `User code end` of the section RTC created by your STM32cubeIDE, you should find examples files in this repo. +### Logs functions +via udp +via html +... + ## MCU tested (please share your results for update this list): - STM32F091xC (v2 Tested only on sending UDP packets) - STM32F030x6 (v2 Tested only on sending UDP packets) @@ -21,7 +26,8 @@ Add the code needed in main.c `between USER code begin` and `User code end` of t - STM32F407 (v3 complete network interface) - STM32F411 (will be test current 2024 for v3 complete network interface) -# News on branch V3 +## News on branch V3 +// Contact of this changes @Dtneo Quick Start for Full Network Connection To quickly set up a complete network connection (DHCP, DNS, Gateway, etc.), use the following function: @@ -31,7 +37,7 @@ Call this fonction just before the while and ping your device ! and in your while think to use this for treat everything from your network : ` ES_ProcessWebPacket();` -A lot of code was clear, if any function deseaper please create a Issue +A lot of code was delete, few functions were added, and a lot of AI comment was generated. If any function deseaper please create a Issue # How to use this project From ece3fe727295e7866c67e6425fe97568ab3eaa3b Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 12:05:27 +0200 Subject: [PATCH 19/27] License --- inc/ip_arp_udp_tcp.h | 21 +++++++++------------ src/ip_arp_udp_tcp.c | 26 +++++++++----------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h index 75b8739..a6c8396 100644 --- a/inc/ip_arp_udp_tcp.h +++ b/inc/ip_arp_udp_tcp.h @@ -1,17 +1,14 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 +/* + * ip_arp_udp_tcp.h * - * IP/ARP/UDP/TCP functions + * Created on: Jun 4, 2024 + * Author: dtneo * - * Chip type : ATMEGA88 with ENC28J60 - *********************************************/ - /********************************************* - * Modified: nuelectronics.com -- Ethershield for Arduino - *********************************************/ -//@{ + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #ifndef IP_ARP_UDP_TCP_H #define IP_ARP_UDP_TCP_H diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index 3b7b689..fd08c2e 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -1,22 +1,14 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * - * Author: Guido Socher - * Copyright: GPL V2 - * See http://www.gnu.org/licenses/gpl.html - * - * IP, Arp, UDP and TCP functions. +/* + * ip_arp_udp_tcp.c * - * The TCP implementation uses some size optimisations which are valid - * only if all data can be sent in one single packet. This is however - * not a big limitation for a microcontroller as you will anyhow use - * small web-pages. The web server must send the entire web page in one - * packet. The client "web browser" as implemented here can also receive - * large pages. + * Created on: Jun 4, 2024 + * Author: dtneo * - * Chip type : ATMEGA88/168/328 with ENC28J60 - *********************************************/ + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #include "defines.h" #include "net.h" #include "enc28j60.h" From e52c6601650f619ff86bc65602b26d72da2219d7 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 12:15:06 +0200 Subject: [PATCH 20/27] 2 files for RTC --- inc/RTC.h | 25 +++++++++++++++++++++++++ src/RTC.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 inc/RTC.h create mode 100644 src/RTC.c diff --git a/inc/RTC.h b/inc/RTC.h new file mode 100644 index 0000000..b9a7b26 --- /dev/null +++ b/inc/RTC.h @@ -0,0 +1,25 @@ +/* + * RTC.h + * + * Created on: Jun 3, 2024 + * Author: dtneo + */ + +#ifndef INC_RTC_H_ +#define INC_RTC_H_ + +#include "main.h" +#include "defines.h" + +typedef struct { + uint8_t day; + uint8_t month; + uint16_t year; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +} DateTime; + +DateTime getRTCDateTime(); + +#endif /* INC_RTC_H_ */ diff --git a/src/RTC.c b/src/RTC.c new file mode 100644 index 0000000..6569d39 --- /dev/null +++ b/src/RTC.c @@ -0,0 +1,45 @@ +/* + * RTC.c + * + * Created on: Jun 3, 2024 + * Author: dtneo + * + * This file contains the implementation for interacting with the Real-Time Clock (RTC) + * on the STM32 microcontroller. It provides a function to obtain the current date and + * time from the RTC and formats it into a human-readable string for logging purposes. + */ + +#include "main.h" +#include "RTC.h" + +// Function to obtain the current date and time from the RTC +DateTime getRTCDateTime() { + DateTime currentDateTime; + RTC_TimeTypeDef sTime; + RTC_DateTypeDef sDate; + + // Get the current time and date + HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); + HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); // Necessary to unlock the time registers + + // Fill the DateTime structure + currentDateTime.hours = sTime.Hours; + currentDateTime.minutes = sTime.Minutes; + currentDateTime.seconds = sTime.Seconds; + currentDateTime.day = sDate.Date; + currentDateTime.month = sDate.Month; + currentDateTime.year = sDate.Year + 1970; // Convert to full year + + // Format the date and time into a string + char timeStr[32]; // Adjusted size to hold formatted date and time + sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d", + currentDateTime.day, + currentDateTime.month, + currentDateTime.year, + currentDateTime.hours, + currentDateTime.minutes, + currentDateTime.seconds); + udpLog2("DateTime_RTC", timeStr); + + return currentDateTime; // Return the filled DateTime structure +} From 1d5e53be37925b4f78eca8282a1ead1fe5b877e8 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Thu, 6 Jun 2024 12:15:06 +0200 Subject: [PATCH 21/27] error --- inc/UDPCommandHandler.h | 38 ++++++++++++++++++++++++++++++++ src/UDPCommandHandler.c | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 inc/UDPCommandHandler.h create mode 100644 src/UDPCommandHandler.c diff --git a/inc/UDPCommandHandler.h b/inc/UDPCommandHandler.h new file mode 100644 index 0000000..d34b1c4 --- /dev/null +++ b/inc/UDPCommandHandler.h @@ -0,0 +1,38 @@ +/* + * UDPCommandHandler.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file declares the necessary structures and functions for handling + * UDP commands received by the STM32 microcontroller. It includes function + * prototypes for various home automation tasks such as sending the STM32 state, + * sending the RTC date and time, turning on a light, activating a relay, and + * sending the temperature. + */ + +#ifndef INC_UDPCOMMANDHANDLER_H_ +#define INC_UDPCOMMANDHANDLER_H_ + +#include "defines.h" + +// Define a function pointer type for command functions +typedef void (*CommandFunction)(); + +// Structure to map commands to their corresponding functions +typedef struct { + const char *command; // Command string + CommandFunction function; // Function to execute for the command +} CommandMapping; + +// Declare the command table as an external variable +extern CommandMapping commandTable[]; + +// Declare command functions here +void sendSTM32State(); // Function to send the STM32 state +void sendRTCDateTime(); // Function to send the RTC date and time +void turnOnLight(); // Function to turn on a light +void activateRelay(); // Function to activate a relay +void sendTemperature(); // Function to send the temperature + +#endif /* INC_UDPCOMMANDHANDLER_H_ */ diff --git a/src/UDPCommandHandler.c b/src/UDPCommandHandler.c new file mode 100644 index 0000000..38544bf --- /dev/null +++ b/src/UDPCommandHandler.c @@ -0,0 +1,49 @@ +/* + * UDPCommandHandler.c + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This source file implements the functions for handling UDP commands received by + * the STM32 microcontroller. It defines a command table that maps specific command + * strings to their corresponding functions. The functions included here perform + * various home automation tasks such as sending the STM32 state, sending the RTC + * date and time, turning on a light, activating a relay, and sending the temperature. + */ + +#include "UDPCommandHandler.h" + +// Define the command table +CommandMapping commandTable[] = { + {"sendSTM32State", sendSTM32State}, + {"sendRTCDateTime", sendRTCDateTime}, + {"turnOnLight", turnOnLight}, + {"activateRelay", activateRelay}, + {"sendTemperature", sendTemperature}, + {NULL, NULL} // End marker +}; + +// Implement the command functions +void sendSTM32State() { + // Code to retrieve and send the STM32 state + // For example, send the state of GPIOs, active peripherals, etc. +} + +void sendRTCDateTime() { + getRTCDateTime(); +} + +void turnOnLight() { + // Code to turn on a light + // For example, activate a GPIO connected to a light +} + +void activateRelay() { + // Code to activate a relay + // For example, activate a GPIO connected to a relay +} + +void sendTemperature() { + // Code to retrieve and send the temperature + // For example, read a temperature sensor and send the value +} From 76d3d8524b7d3a6e51da39c346fcabecd39ef9f0 Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 7 Jun 2024 18:37:01 +0200 Subject: [PATCH 22/27] review of ip_arp_udp_tcp files with GPT4o A lot of change, please inform me if something miss. I add 2 new files for giving a way to manage logs. via UDP or Web. Other idea will be welcome. --- inc/ip_arp_udp_tcp.h | 42 - inc/websrv_help_functions.h | 66 +- src/ip_arp_udp_tcp.c | 2424 ++++++++++++++++------------------- src/websrv_help_functions.c | 297 ++--- 4 files changed, 1262 insertions(+), 1567 deletions(-) diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h index a6c8396..f1e7137 100644 --- a/inc/ip_arp_udp_tcp.h +++ b/inc/ip_arp_udp_tcp.h @@ -81,48 +81,6 @@ void client_tcp_set_serverip(uint8_t *ipaddr); #ifdef TCP_client -// To use the tcp client you need to: -// -// Declare a callback function to get the result (tcp data from the server): -// -// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code;return(close_tcp_session);} -// -// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf -// are invalid. That is: do to use data_start_pos_in_buf and len_of_data -// if statuscode!=0. -// -// This callback gives you access to the TCP data of the first -// packet returned from the server. You should aim to minimize the server -// output such that this will be the only packet. -// -// close_tcp_session=1 means close the session now. close_tcp_session=0 -// read all data and leave it to the other side to close it. -// If you connect to a web server then you want close_tcp_session=0. -// If you connect to a modbus/tcp equipment then you want close_tcp_session=1 -// -// Declare a callback function to be called in order to fill in the -// -// request (tcp data sent to the server): -// uint16_t your_client_tcp_datafill_callback(uint8_t fd){...your code;return(len_of_data_filled_in);} -// -// Now call: -// fd=client_tcp_req(&your_client_tcp_result_callback,&your_client_tcp_datafill_callback,portnumber); -// -// fd is a file descriptor like number that you get back in the fill and result -// function so you know to which call of client_tcp_req this callback belongs. -// -// You can not start different clients (e.g modbus and web) at the -// same time but you can start them one after each other. That is -// when the request has timed out or when the result_callback was -// executed then you can start a new one. The fd makes it still possible to -// distinguish in the callback code the different types you started. -// -// Note that you might never get called back if the other side does -// not answer. A timer would be needed to recongnize such a condition. -// -// We use callback functions because that is the best implementation -// given the fact that we have very little RAM memory. -// uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port); void tcp_client_send_packet(uint8_t *buf,uint16_t dest_port, uint16_t src_port, uint8_t flags, uint8_t max_segment_size, uint8_t clear_seqck, uint16_t next_ack_num, uint16_t dlength, uint8_t *dest_mac, uint8_t *dest_ip); diff --git a/inc/websrv_help_functions.h b/inc/websrv_help_functions.h index fad4b30..17c2c26 100644 --- a/inc/websrv_help_functions.h +++ b/inc/websrv_help_functions.h @@ -1,22 +1,60 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 - *********************************************/ -//@{ +/* + * websrv_help_functions.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #ifndef WEBSRV_HELP_FUNCTIONS_H #define WEBSRV_HELP_FUNCTIONS_H #include "defines.h" -// These functions are documented in websrv_help_functions.c -extern uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key); -extern void urldecode(char *urlbuf); -extern void urlencode(char *str,char *urlbuf); -extern uint8_t parse_ip(uint8_t *bytestr,char *str); -extern void mk_net_str(char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base); +// Function declarations + +/** + * Find the value for a given key in a query string. + * @param str The input query string. + * @param strbuf The buffer to store the value. + * @param maxlen The maximum length of the value to store. + * @param key The key to search for. + * @return Length of the value found. + */ +uint8_t find_key_val(char *str, char *strbuf, uint8_t maxlen, char *key); + +/** + * Decode a URL-encoded string. + * @param urlbuf The URL-encoded string to decode. + */ +void urldecode(char *urlbuf); + +/** + * Encode a string to URL-encoded format. + * @param str The input string to encode. + * @param urlbuf The buffer to store the URL-encoded string. + */ +void urlencode(char *str, char *urlbuf); + +/** + * Parse an IP address string into a byte array. + * @param bytestr The byte array to store the parsed IP address. + * @param str The input IP address string. + * @return 1 if successful, 0 otherwise. + */ +uint8_t parse_ip(uint8_t *bytestr, char *str); +/** + * Create a network string from a byte array. + * @param resultstr The buffer to store the resulting string. + * @param bytestr The input byte array. + * @param len The length of the byte array. + * @param separator The character to use as a separator. + * @param base The numerical base for conversion (e.g., 10 for decimal). + */ +void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base); #endif /* WEBSRV_HELP_FUNCTIONS_H */ -//@} diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c index fd08c2e..2a9153f 100644 --- a/src/ip_arp_udp_tcp.c +++ b/src/ip_arp_udp_tcp.c @@ -14,21 +14,7 @@ #include "enc28j60.h" #include "ip_arp_udp_tcp.h" #include "UDPCommandHandler.h" - -#if ETHERSHIELD_DEBUG -void ethershieldDebug(char *message) { - printf("%s\r\n", message); - udpLog2("ETHERSHIELD_DEBUG", message); -} -#endif - -void udpLog2(char* alerte, char* text) { - static uint8_t udp[500]; - char data[450]; - snprintf(data, 450, "Project %s - %s", alerte, text); - send_udp(udp, data, strlen(data), sport, dip, dport); -} - +#include "websrv_help_functions.h" // Web server port, used when implementing webserver static uint8_t wwwport_l=80; // server port @@ -53,10 +39,6 @@ static uint16_t (*client_tcp_datafill_callback)(uint8_t); #endif #define TCPCLIENT_SRC_PORT_H 11 #if defined (WWW_client) -// WWW_client uses TCP_client -#if ! defined(TCP_client) -#define TCP_client 1 -#endif static uint8_t www_fd=0; static uint8_t browsertype=0; // 0 = get, 1 = post static void (*client_browser_callback)(uint8_t,uint16_t,uint16_t); @@ -83,6 +65,7 @@ static volatile uint8_t waitgwmac=WGW_INITIAL_ARP; uint8_t macaddr[6]; static uint8_t ipaddr[4]; static uint16_t info_data_len=0; +static uint16_t info_hdr_len = 0; static uint8_t seqnum=0xa; // my initial tcp sequence number #define CLIENTMSS 550 @@ -96,654 +79,563 @@ const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on const char ntpreqhdr[] ={0xe3,0,4,0xfa,0,1,0,1,0,0}; #endif + #ifndef DISABLE_IP_STACK +// The checksum function calculates checksums for IP, UDP, and TCP packets. +// For IP and ICMP, the checksum is calculated over the IP header only. +// For UDP and TCP, the checksum includes a pseudo-header using the source and destination IP fields. +uint16_t checksum(uint8_t *buf, uint16_t len, uint8_t type) { + uint32_t sum = 0; + + // Add protocol-specific values for UDP and TCP + if (type == 1) { + sum += IP_PROTO_UDP_V; // Protocol UDP + sum += len - 8; // UDP length (data + header) + } else if (type == 2) { + sum += IP_PROTO_TCP_V; // Protocol TCP + sum += len - 8; // TCP length (data + header) + } -// The Ip checksum is calculated over the ip header only starting -// with the header length field and a total length of 20 bytes -// unitl ip.dst -// You must set the IP checksum field to zero before you start -// the calculation. -// len for ip is 20. -// -// For UDP/TCP we do not make up the required pseudo header. Instead we -// use the ip.src and ip.dst fields of the real packet: -// The udp checksum calculation starts with the ip.src field -// Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len -// In other words the len here is 8 + length over which you actually -// want to calculate the checksum. -// You must set the checksum field to zero before you start -// the calculation. -// The same algorithm is also used for udp and tcp checksums. -// len for udp is: 8 + 8 + data length -// len for tcp is: 4+4 + 20 + option len + data length -// -// For more information on how this algorithm works see: -// http://www.netfor2.com/checksum.html -// http://www.msc.uky.edu/ken/cs471/notes/chap3.htm -// The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html -uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type) -{ - // type 0=ip , icmp - // 1=udp - // 2=tcp - uint32_t sum = 0; - - //if(type==0){ - // // do not add anything, standard IP checksum as described above - // // Usable for ICMP and IP header - //} - if(type==1){ - sum+=IP_PROTO_UDP_V; // protocol udp - // the length here is the length of udp (data+header len) - // =length given to this function - (IP.scr+IP.dst length) - sum+=len-8; // = real udp len - } - if(type==2){ - sum+=IP_PROTO_TCP_V; - // the length here is the length of tcp (data+header len) - // =length given to this function - (IP.scr+IP.dst length) - sum+=len-8; // = real tcp len - } - // build the sum of 16bit words - while(len >1){ - sum += 0xFFFF & (((uint32_t)*buf<<8)|*(buf+1)); - buf+=2; - len-=2; - } - // if there is a byte left then add it (padded with zero) - if (len){ - sum += ((uint32_t)(0xFF & *buf))<<8; - } - // now calculate the sum over the bytes in the sum - // until the result is only 16bit long - while (sum>>16){ - sum = (sum & 0xFFFF)+(sum >> 16); - } - // build 1's complement: - return( (uint16_t) sum ^ 0xFFFF); -} + // Sum up 16-bit words + while (len > 1) { + sum += 0xFFFF & (*buf << 8 | *(buf + 1)); + buf += 2; + len -= 2; + } -#endif + // If there's a leftover byte, add it (padded with zero) + if (len) { + sum += (0xFF & *buf) << 8; + } -// This initializes the web server -// you must call this function once before you use any of the other functions: -void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port) -{ - wwwport_h=(port>>8)&0xff; - wwwport_l=(port&0xff); - memcpy(ipaddr, myip, 4); - memcpy(macaddr, mymac, 6); + // Fold 32-bit sum to 16 bits + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + // Return one's complement of the sum + return (uint16_t)(sum ^ 0xFFFF); } +#endif +// This function initializes the web server. +// You must call this function once before you use any of the other functions. +void init_ip_arp_udp_tcp(uint8_t *mymac, uint8_t *myip, uint16_t port) { + wwwport_h = (port >> 8) & 0xFF; + wwwport_l = port & 0xFF; + memcpy(ipaddr, myip, 4); + memcpy(macaddr, mymac, 6); +} #ifndef DISABLE_IP_STACK -uint8_t check_ip_message_is_from(uint8_t *buf,uint8_t *ip) -{ - return !memcmp(&buf[IP_SRC_P], ip, 4); +// Check if an IP message is from a specified IP address +uint8_t check_ip_message_is_from(uint8_t *buf, uint8_t *ip) { + return memcmp(&buf[IP_SRC_P], ip, 4) == 0; } -uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len){ - // - if (len<41){ - return(0); - } - if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || - buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V){ - return(0); - } - - if (memcmp(&buf[ETH_ARP_DST_IP_P], ipaddr, 4)) { - return 0; - } - - return(1); +// Check if the Ethernet frame is an ARP request for my IP address +uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint16_t len) { + if (len < 41) { + return 0; + } + if (buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V) { + return 0; + } + return memcmp(&buf[ETH_ARP_DST_IP_P], ipaddr, 4) == 0; } -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len){ - //eth+ip+udp header is 42 - if (len<42){ - return(0); - } - if(buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V || - buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V){ - return(0); - } - if (buf[IP_HEADER_LEN_VER_P]!=0x45){ - // must be IP V4 and 20 byte header - return(0); - } - if (memcmp(&buf[IP_DST_P], ipaddr, 4)) { - return 0; - } - return(1); +// Check if the Ethernet frame is an IP packet addressed to my IP address +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf, uint16_t len) { + // eth + ip header is 34 bytes + udp header is 8 bytes + if (len < 42) { + return 0; + } + if (buf[ETH_TYPE_H_P] != ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_IP_L_V) { + return 0; + } + // must be IP V4 and 20 byte header + if (buf[IP_HEADER_LEN_VER_P] != 0x45) { + return 0; + } + return memcmp(&buf[IP_DST_P], ipaddr, 4) == 0; } -// make a return eth header from a received eth packet -void make_eth(uint8_t *buf) -{ - //copy the destination mac from the source and fill my mac into src - memcpy(&buf[ETH_DST_MAC], &buf[ETH_SRC_MAC], 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); +// Make a return Ethernet header from a received Ethernet packet +void make_eth(uint8_t *buf) { + memcpy(&buf[ETH_DST_MAC], &buf[ETH_SRC_MAC], 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); } -// make a new eth header for IP packet -void make_eth_ip_new(uint8_t *buf, uint8_t* dst_mac) -{ - //copy the destination mac from the source and fill my mac into src - memcpy(&buf[ETH_DST_MAC], dst_mac, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; +// Make a new Ethernet header for an IP packet +void make_eth_ip_new(uint8_t *buf, uint8_t *dst_mac) { + memcpy(&buf[ETH_DST_MAC], dst_mac, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; } - -void fill_ip_hdr_checksum(uint8_t *buf) -{ - uint16_t ck; - // clear the 2 byte checksum - buf[IP_CHECKSUM_P]=0; - buf[IP_CHECKSUM_P+1]=0; - buf[IP_FLAGS_P]=0x40; // don't fragment - buf[IP_FLAGS_P+1]=0; // fragement offset - buf[IP_TTL_P]=64; // ttl - // calculate the checksum: - ck=checksum(&buf[IP_P], IP_HEADER_LEN,0); - buf[IP_CHECKSUM_P]=ck>>8; - buf[IP_CHECKSUM_P+1]=ck & 0xff; +// Fill the IP header checksum +void fill_ip_hdr_checksum(uint8_t *buf) { + uint16_t ck; + buf[IP_CHECKSUM_P] = 0; + buf[IP_CHECKSUM_P + 1] = 0; + buf[IP_FLAGS_P] = 0x40; // Don't fragment + buf[IP_FLAGS_P + 1] = 0; // Fragment offset + buf[IP_TTL_P] = 64; // TTL + ck = checksum(&buf[IP_P], IP_HEADER_LEN, 0); + buf[IP_CHECKSUM_P] = ck >> 8; + buf[IP_CHECKSUM_P + 1] = ck & 0xff; } -// is it an arp reply (no len check here, you must first call eth_type_is_arp_and_my_ip) -uint8_t eth_type_is_arp_reply(uint8_t *buf){ - return (buf[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REPLY_L_V); +// Check if it's an ARP reply +uint8_t eth_type_is_arp_reply(uint8_t *buf) { + return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V; } -// is it an arp request (no len check here, you must first call eth_type_is_arp_and_my_ip) -uint8_t eth_type_is_arp_req(uint8_t *buf){ - return (buf[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REQ_L_V); +// Check if it's an ARP request +uint8_t eth_type_is_arp_req(uint8_t *buf) { + return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V; } -// make a return ip header from a received ip packet +// Make a new IP header for a TCP packet static uint16_t ip_identifier = 1; -// make a new ip header for tcp packet - -// make a return ip header from a received ip packet -void make_ip_tcp_new(uint8_t *buf, uint16_t len,uint8_t *dst_ip) -{ - // set ipv4 and header length - buf[ IP_P ] = IP_V4_V | IP_HEADER_LENGTH_V; - - // set TOS to default 0x00 - buf[ IP_TOS_P ] = 0x00; - - // set total length - buf[ IP_TOTLEN_H_P ] = (len >>8)& 0xff; - buf[ IP_TOTLEN_L_P ] = len & 0xff; - - // set packet identification - buf[ IP_ID_H_P ] = (ip_identifier >>8) & 0xff; - buf[ IP_ID_L_P ] = ip_identifier & 0xff; - ip_identifier++; - - // set fragment flags - buf[ IP_FLAGS_H_P ] = 0x00; - buf[ IP_FLAGS_L_P ] = 0x00; - - // set Time To Live - buf[ IP_TTL_P ] = 128; - - // set ip packettype to tcp/udp/icmp... - buf[ IP_PROTO_P ] = IP_PROTO_TCP_V; +void make_ip_tcp_new(uint8_t *buf, uint16_t len, uint8_t *dst_ip) { + buf[IP_P] = IP_V4_V | IP_HEADER_LENGTH_V; + buf[IP_TOS_P] = 0x00; // TOS to default 0x00 + buf[IP_TOTLEN_H_P] = (len >> 8) & 0xff; + buf[IP_TOTLEN_L_P] = len & 0xff; + buf[IP_ID_H_P] = (ip_identifier >> 8) & 0xff; + buf[IP_ID_L_P] = ip_identifier & 0xff; + ip_identifier++; + buf[IP_FLAGS_H_P] = 0x00; // Fragment flags + buf[IP_FLAGS_L_P] = 0x00; + buf[IP_TTL_P] = 128; // Time To Live + buf[IP_PROTO_P] = IP_PROTO_TCP_V; // IP packet type to TCP + memcpy(&buf[IP_DST_P], dst_ip, 4); // Destination IP address + memcpy(&buf[IP_SRC_P], ipaddr, 4); // Source IP address + fill_ip_hdr_checksum(buf); +} - // set source and destination ip address - memcpy(&buf[IP_DST_P], dst_ip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); +// Make a return IP header from a received IP packet +void make_ip(uint8_t *buf) { + memcpy(&buf[IP_DST_P], &buf[IP_SRC_P], 4); + memcpy(&buf[IP_SRC_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); } +// Swap seq and ack number and count ack number up +void step_seq(uint8_t *buf, uint16_t rel_ack_num, uint8_t cp_seq) { + uint8_t i = 4; + uint8_t tseq; -void make_ip(uint8_t *buf) -{ - memcpy(&buf[IP_DST_P], &buf[IP_SRC_P], 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); -} + // Sequence numbers: add the relative ack num to SEQACK + while (i > 0) { + rel_ack_num += buf[TCP_SEQ_H_P + i - 1]; + tseq = buf[TCP_SEQACK_H_P + i - 1]; + buf[TCP_SEQACK_H_P + i - 1] = rel_ack_num & 0xFF; -// swap seq and ack number and count ack number up -void step_seq(uint8_t *buf,uint16_t rel_ack_num,uint8_t cp_seq) -{ - uint8_t i; - uint8_t tseq; - i=4; - // sequence numbers: - // add the rel ack num to SEQACK - while(i>0) { - rel_ack_num=buf[TCP_SEQ_H_P+i-1]+rel_ack_num; - tseq=buf[TCP_SEQACK_H_P+i-1]; - buf[TCP_SEQACK_H_P+i-1]=0xff&rel_ack_num; - if (cp_seq){ - // copy the acknum sent to us into the sequence number - buf[TCP_SEQ_H_P+i-1]=tseq; - }else{ - buf[TCP_SEQ_H_P+i-1]= 0; // some preset vallue + if (cp_seq) { + // Copy the acknum sent to us into the sequence number + buf[TCP_SEQ_H_P + i - 1] = tseq; + } else { + buf[TCP_SEQ_H_P + i - 1] = 0; // Some preset value + } + + rel_ack_num >>= 8; + i--; } - rel_ack_num=rel_ack_num>>8; - i--; - } } -// make a return tcp header from a received tcp packet +// Make a return TCP header from a received TCP packet // rel_ack_num is how much we must step the seq number received from the -// other side. We do not send more than 765 bytes of text (=data) in the tcp packet. +// other side. We do not send more than 765 bytes of text (=data) in the TCP packet. // No mss is included here. // // After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4 -// If cp_seq=0 then an initial sequence number is used (should be use in synack) +// If cp_seq=0 then an initial sequence number is used (should be used in synack) // otherwise it is copied from the packet we received -void make_tcphead(uint8_t *buf,uint16_t rel_ack_num,uint8_t cp_seq) -{ - uint8_t i; - // copy ports: - i=buf[TCP_DST_PORT_H_P]; - buf[TCP_DST_PORT_H_P]=buf[TCP_SRC_PORT_H_P]; - buf[TCP_SRC_PORT_H_P]=i; - // - i=buf[TCP_DST_PORT_L_P]; - buf[TCP_DST_PORT_L_P]=buf[TCP_SRC_PORT_L_P]; - buf[TCP_SRC_PORT_L_P]=i; - step_seq(buf,rel_ack_num,cp_seq); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // no options: - // 20 bytes: - // The tcp header length is only a 4 bit field (the upper 4 bits). - // It is calculated in units of 4 bytes. - // E.g 20 bytes: 20/4=6 => 0x50=header len field - buf[TCP_HEADER_LEN_P]=0x50; +void make_tcphead(uint8_t *buf, uint16_t rel_ack_num, uint8_t cp_seq) { + // Swap source and destination ports + uint8_t temp = buf[TCP_DST_PORT_H_P]; + buf[TCP_DST_PORT_H_P] = buf[TCP_SRC_PORT_H_P]; + buf[TCP_SRC_PORT_H_P] = temp; + + temp = buf[TCP_DST_PORT_L_P]; + buf[TCP_DST_PORT_L_P] = buf[TCP_SRC_PORT_L_P]; + buf[TCP_SRC_PORT_L_P] = temp; + + // Update sequence and acknowledgment numbers + step_seq(buf, rel_ack_num, cp_seq); + + // Zero the checksum + buf[TCP_CHECKSUM_H_P] = 0; + buf[TCP_CHECKSUM_L_P] = 0; + + // No options, header length is 20 bytes (0x50) + buf[TCP_HEADER_LEN_P] = 0x50; } -void make_arp_answer_from_request(uint8_t *buf) -{ - // - make_eth(buf); - buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V; - buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V; - // fill the mac addresses: - memcpy(&buf[ETH_ARP_DST_MAC_P], &buf[ETH_ARP_SRC_MAC_P], 6); - memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); - // fill the ip addresses - memcpy(&buf[ETH_ARP_DST_IP_P], &buf[ETH_ARP_SRC_IP_P], 4); - memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); - // eth+arp is 42 bytes: - enc28j60PacketSend(42,buf); +// Make an ARP reply from a received ARP request +void make_arp_answer_from_request(uint8_t *buf) { + make_eth(buf); + buf[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; + buf[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V; + memcpy(&buf[ETH_ARP_DST_MAC_P], &buf[ETH_ARP_SRC_MAC_P], 6); + memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); + memcpy(&buf[ETH_ARP_DST_IP_P], &buf[ETH_ARP_SRC_IP_P], 4); + memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); + enc28j60PacketSend(42, buf); } -void make_echo_reply_from_request(uint8_t *buf,uint16_t len) -{ - make_eth(buf); - make_ip(buf); - buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V; - // we changed only the icmp.type field from request(=8) to reply(=0). - // we can therefore easily correct the checksum: - if (buf[ICMP_CHECKSUM_P] > (0xff-0x08)){ - buf[ICMP_CHECKSUM_P+1]++; - } - buf[ICMP_CHECKSUM_P]+=0x08; - // - enc28j60PacketSend(len,buf); +// Make an ICMP echo reply from a received echo request +void make_echo_reply_from_request(uint8_t *buf, uint16_t len) { + make_eth(buf); + make_ip(buf); + buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; + if (buf[ICMP_CHECKSUM_P] > (0xff - 0x08)) { + buf[ICMP_CHECKSUM_P + 1]++; + } + buf[ICMP_CHECKSUM_P] += 0x08; + enc28j60PacketSend(len, buf); } -// you can send a max of 220 bytes of data -void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port) -{ - uint16_t ck; - make_eth(buf); - if (datalen>220){ - datalen=220; - } - // total length field in the IP header must be set: - buf[IP_TOTLEN_H_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >>8; - buf[IP_TOTLEN_L_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) & 0xff; - make_ip(buf); - // send to port: - //buf[UDP_DST_PORT_H_P]=port>>8; - //buf[UDP_DST_PORT_L_P]=port & 0xff; - // sent to port of sender and use "port" as own source: - buf[UDP_DST_PORT_H_P]=buf[UDP_SRC_PORT_H_P]; - buf[UDP_DST_PORT_L_P]= buf[UDP_SRC_PORT_L_P]; - buf[UDP_SRC_PORT_H_P]=port>>8; - buf[UDP_SRC_PORT_L_P]=port & 0xff; - // calculte the udp length: - buf[UDP_LEN_H_P]=(UDP_HEADER_LEN+datalen) >> 8; - buf[UDP_LEN_L_P]=(UDP_HEADER_LEN+datalen) & 0xff; - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data: - memcpy(&buf[UDP_DATA_P], data, datalen); - - ck=checksum(&buf[IP_SRC_P], 16 + datalen,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck & 0xff; - enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); +// Make a UDP reply from a received UDP request, sending a max of 220 bytes of data +void make_udp_reply_from_request(uint8_t *buf, char *data, uint16_t datalen, uint16_t port) { + if (datalen > 220) { + datalen = 220; + } + + make_eth(buf); + + // Total length field in the IP header + uint16_t total_len = IP_HEADER_LEN + UDP_HEADER_LEN + datalen; + buf[IP_TOTLEN_H_P] = total_len >> 8; + buf[IP_TOTLEN_L_P] = total_len & 0xff; + + make_ip(buf); + + // Swap source and destination ports + buf[UDP_DST_PORT_H_P] = buf[UDP_SRC_PORT_H_P]; + buf[UDP_DST_PORT_L_P] = buf[UDP_SRC_PORT_L_P]; + buf[UDP_SRC_PORT_H_P] = port >> 8; + buf[UDP_SRC_PORT_L_P] = port & 0xff; + + // Calculate UDP length + uint16_t udp_len = UDP_HEADER_LEN + datalen; + buf[UDP_LEN_H_P] = udp_len >> 8; + buf[UDP_LEN_L_P] = udp_len & 0xff; + + // Zero the checksum + buf[UDP_CHECKSUM_H_P] = 0; + buf[UDP_CHECKSUM_L_P] = 0; + + // Copy data + memcpy(&buf[UDP_DATA_P], data, datalen); + + // Calculate checksum + uint16_t ck = checksum(&buf[IP_SRC_P], 16 + datalen, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); } -// this is for the server not the client: -void make_tcp_synack_from_syn(uint8_t *buf) -{ - uint16_t ck; - make_eth(buf); - // total length field in the IP header must be set: - // 20 bytes IP + 24 bytes (20tcp+4tcp options) - buf[IP_TOTLEN_H_P]=0; - buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4; - make_ip(buf); - buf[TCP_FLAGS_P]=TCP_FLAGS_SYNACK_V; - make_tcphead(buf,1,0); - // put an inital seq number - buf[TCP_SEQ_H_P+0]= 0; - buf[TCP_SEQ_H_P+1]= 0; - // we step only the second byte, this allows us to send packts - // with 255 bytes, 512 or 765 (step by 3) without generating - // overlapping numbers. - buf[TCP_SEQ_H_P+2]= seqnum; - buf[TCP_SEQ_H_P+3]= 0; - // step the inititial seq num by something we will not use - // during this tcp session: - seqnum+=3; - // add an mss options field with MSS to 1280: - // 1280 in hex is 0x500 - buf[TCP_OPTIONS_P]=2; - buf[TCP_OPTIONS_P+1]=4; - buf[TCP_OPTIONS_P+2]=0x05; - buf[TCP_OPTIONS_P+3]=0x0; - // The tcp header length is only a 4 bit field (the upper 4 bits). - // It is calculated in units of 4 bytes. - // E.g 24 bytes: 24/4=6 => 0x60=header len field - buf[TCP_HEADER_LEN_P]=0x60; - // here we must just be sure that the web browser contacting us - // will send only one get packet - buf[TCP_WIN_SIZE]=0x5; // 1400=0x578 - buf[TCP_WIN_SIZE+1]=0x78; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + 4 (one option: mss) - ck=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+4,2); - buf[TCP_CHECKSUM_H_P]=ck>>8; - buf[TCP_CHECKSUM_L_P]=ck& 0xff; - // add 4 for option mss: - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN,buf); +// Make a TCP SYN-ACK response from a received SYN packet +void make_tcp_synack_from_syn(uint8_t *buf) { + uint16_t ck; + make_eth(buf); + + // Total length field in the IP header + buf[IP_TOTLEN_H_P] = 0; + buf[IP_TOTLEN_L_P] = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + 4; + + make_ip(buf); + + buf[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V; + make_tcphead(buf, 1, 0); + + // Initialize sequence number + buf[TCP_SEQ_H_P] = 0; + buf[TCP_SEQ_H_P + 1] = 0; + buf[TCP_SEQ_H_P + 2] = seqnum; + buf[TCP_SEQ_H_P + 3] = 0; + + // Increment the sequence number + seqnum += 3; + + // MSS option field + buf[TCP_OPTIONS_P] = 2; + buf[TCP_OPTIONS_P + 1] = 4; + buf[TCP_OPTIONS_P + 2] = 0x05; + buf[TCP_OPTIONS_P + 3] = 0x00; + + // TCP header length + buf[TCP_HEADER_LEN_P] = 0x60; + + // Window size + buf[TCP_WIN_SIZE] = 0x05; + buf[TCP_WIN_SIZE + 1] = 0x78; + + // Calculate the checksum + ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2); + buf[TCP_CHECKSUM_H_P] = ck >> 8; + buf[TCP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + 4 + ETH_HEADER_LEN, buf); } -// do some basic length calculations and store the result in static variables -uint16_t get_tcp_data_len(uint8_t *buf) -{ - int16_t i; - i=(((int16_t)buf[IP_TOTLEN_H_P])<<8)|(buf[IP_TOTLEN_L_P]&0xff); - i-=IP_HEADER_LEN; - i-=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; - if (i<=0){ - i=0; - } - return((uint16_t)i); +// Calculate the length of TCP data and store the result in static variables +uint16_t get_tcp_data_len(uint8_t *buf) { + int16_t total_len = ((int16_t)buf[IP_TOTLEN_H_P] << 8) | (buf[IP_TOTLEN_L_P] & 0xff); + int16_t header_len = IP_HEADER_LEN + ((buf[TCP_HEADER_LEN_P] >> 4) * 4); // Calculate header length in bytes + int16_t data_len = total_len - header_len; + + return (data_len > 0) ? (uint16_t)data_len : 0; } -// get a pointer to the start of tcp data in buf +// Get a pointer to the start of TCP data in buf // Returns 0 if there is no data // You must call init_len_info once before calling this function -// Not used? -/* - uint16_t get_tcp_data_pointer(void) -{ - if (info_data_len){ - return((uint16_t)TCP_SRC_PORT_H_P+info_hdr_len); - }else{ - return(0); - } +uint16_t get_tcp_data_pointer(void) { + if (info_data_len) { + return ((uint16_t)TCP_SRC_PORT_H_P + info_hdr_len); + } else { + return 0; + } } -*/ - -// do some basic length calculations and store the result in static varibales -// Not used? -/* -void init_len_info(uint8_t *buf) -{ - info_data_len=(((int16_t)buf[IP_TOTLEN_H_P])<<8)|(buf[IP_TOTLEN_L_P]&0xff); - info_data_len-=IP_HEADER_LEN; - info_hdr_len=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; - info_data_len-=info_hdr_len; - if (info_data_len<=0){ - info_data_len=0; - } +// Do some basic length calculations and store the result in static variables +void init_len_info(uint8_t *buf) { + info_data_len = (((int16_t)buf[IP_TOTLEN_H_P]) << 8) | (buf[IP_TOTLEN_L_P] & 0xff); + info_data_len -= IP_HEADER_LEN; + info_hdr_len = (buf[TCP_HEADER_LEN_P] >> 4) * 4; // Generate length in bytes + info_data_len -= info_hdr_len; + if (info_data_len <= 0) { + info_data_len = 0; + } } -*/ -// fill a binary string of len data into the tcp packet -uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len) -{ - // fill in tcp data at position pos - // - // with no options the data starts after the checksum + 2 more bytes (urgent ptr) - memcpy(&buf[TCP_CHECKSUM_L_P+3], s, len); - return(pos); +// Fill a binary string of len data into the TCP packet +uint16_t fill_tcp_data_len(uint8_t *buf, uint16_t pos, const char *s, uint16_t len) { + // Fill in TCP data at position pos + memcpy(&buf[TCP_CHECKSUM_L_P + 3 + pos], s, len); + return pos + len; } -// fill in tcp data at position pos. pos=0 means start of -// tcp data. Returns the position at which the string after -// this string could be filled. -uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s) -{ - return(fill_tcp_data_len(buf,pos,(char*)s,strlen(s))); +// Fill in TCP data at position pos. pos=0 means start of TCP data. +// Returns the position at which the string after this string could be filled. +uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const char *s) { + return fill_tcp_data_len(buf, pos, s, strlen(s)); } -// Make just an ack packet with no tcp data inside -// This will modify the eth/ip/tcp header -void make_tcp_ack_from_any(uint8_t *buf,int16_t datlentoack,uint8_t addflags) -{ - uint16_t j; - make_eth(buf); - // fill the header: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|addflags; - if (addflags==TCP_FLAGS_RST_V){ - make_tcphead(buf,datlentoack,1); - } else { - if (datlentoack==0){ - // if there is no data then we must still acknoledge one packet - datlentoack = 1; +// Make just an ACK packet with no TCP data inside +// This will modify the ETH/IP/TCP header +void make_tcp_ack_from_any(uint8_t *buf, int16_t datlentoack, uint8_t addflags) { + uint16_t len; + + make_eth(buf); + + // Fill the header + buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | addflags; + + if (addflags == TCP_FLAGS_RST_V) { + make_tcphead(buf, datlentoack, 1); + } else { + if (datlentoack == 0) { + // If there is no data, then we must still acknowledge one packet + datlentoack = 1; + } + make_tcphead(buf, datlentoack, 1); // No options } - make_tcphead(buf,datlentoack,1); // no options - } - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN; - buf[IP_TOTLEN_H_P]=j >> 8; - buf[IP_TOTLEN_L_P]=j & 0xff; - make_ip(buf); - // use a low window size otherwise we have to have - // timers and can not just react on every packet. - buf[TCP_WIN_SIZE]=0x4; // 1024=0x400 - buf[TCP_WIN_SIZE+1]=0x0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN,buf); -} + // Total length field in the IP header must be set + len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN; + buf[IP_TOTLEN_H_P] = len >> 8; + buf[IP_TOTLEN_L_P] = len & 0xff; + + make_ip(buf); -// dlen is the amount of tcp data (http data) we send in this packet + // Use a low window size, otherwise we have to have timers and cannot just react on every packet + buf[TCP_WIN_SIZE] = 0x4; // 1024 = 0x400 + buf[TCP_WIN_SIZE + 1] = 0x0; + + // Calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len + len = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN, 2); + buf[TCP_CHECKSUM_H_P] = len >> 8; + buf[TCP_CHECKSUM_L_P] = len & 0xff; + + enc28j60PacketSend(IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN, buf); +} + +// dlen is the amount of TCP data (HTTP data) we send in this packet // You can use this function only immediately after make_tcp_ack_from_any // This is because this function will NOT modify the eth/ip/tcp header except for // length and checksum // You must set TCP_FLAGS before calling this -void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen) -{ - uint16_t j; - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) + len of data - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - buf[IP_TOTLEN_H_P]=j>>8; - buf[IP_TOTLEN_L_P]=j& 0xff; - fill_ip_hdr_checksum(buf); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); -} +void make_tcp_ack_with_data_noflags(uint8_t *buf, uint16_t dlen) { + uint16_t total_len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; + + // Set the total length field in the IP header + buf[IP_TOTLEN_H_P] = total_len >> 8; + buf[IP_TOTLEN_L_P] = total_len & 0xff; + + // Fill in the IP header checksum + fill_ip_hdr_checksum(buf); + // Zero the TCP checksum field + buf[TCP_CHECKSUM_H_P] = 0; + buf[TCP_CHECKSUM_L_P] = 0; + + // Calculate the checksum, len = 8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len + uint16_t checksum_len = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2); + buf[TCP_CHECKSUM_H_P] = checksum_len >> 8; + buf[TCP_CHECKSUM_L_P] = checksum_len & 0xff; + + // Send the packet + enc28j60PacketSend(total_len + ETH_HEADER_LEN, buf); +} -// you must have called init_len_info at some time before calling this function -// dlen is the amount of tcp data (http data) we send in this packet +// You must have called init_len_info at some time before calling this function +// dlen is the amount of TCP data (HTTP data) we send in this packet // You can use this function only immediately after make_tcp_ack_from_any // This is because this function will NOT modify the eth/ip/tcp header except for // length and checksum -// Used? -void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen) -{ - uint16_t j; - // fill the header: - // This code requires that we send only one data packet - // because we keep no state information. We must therefore set - // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V; - - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) + len of data - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - buf[IP_TOTLEN_H_P]=j>>8; - buf[IP_TOTLEN_L_P]=j& 0xff; - fill_ip_hdr_checksum(buf); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); -} +void make_tcp_ack_with_data(uint8_t *buf, uint16_t dlen) { + uint16_t total_len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; + // Fill the header with appropriate flags + buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V; -// you must have initialized info_data_len at some time before calling this function -// -// This info_data_len initialisation is done automatically if you call -// packetloop_icmp_tcp(buf,enc28j60PacketReceive(BUFFER_SIZE, buf)); -// and test the return value for non zero. + // Set the total length field in the IP header + buf[IP_TOTLEN_H_P] = total_len >> 8; + buf[IP_TOTLEN_L_P] = total_len & 0xff; + + // Fill in the IP header checksum + fill_ip_hdr_checksum(buf); + + // Zero the TCP checksum field + buf[TCP_CHECKSUM_H_P] = 0; + buf[TCP_CHECKSUM_L_P] = 0; + + // Calculate the checksum, len = 8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len + uint16_t checksum_len = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2); + buf[TCP_CHECKSUM_H_P] = checksum_len >> 8; + buf[TCP_CHECKSUM_L_P] = checksum_len & 0xff; + + // Send the packet + enc28j60PacketSend(total_len + ETH_HEADER_LEN, buf); +} + +// You must have initialized info_data_len at some time before calling this function +// This info_data_len initialization is done automatically if you call +// packetloop_icmp_tcp(buf, enc28j60PacketReceive(BUFFER_SIZE, buf)); +// and test the return value for non-zero. // -// dlen is the amount of tcp data (http data) we send in this packet +// dlen is the amount of TCP data (HTTP data) we send in this packet // You can use this function only immediately after make_tcp_ack_from_any // This is because this function will NOT modify the eth/ip/tcp header except for // length and checksum -void www_server_reply(uint8_t *buf,uint16_t dlen) -{ - make_tcp_ack_from_any(buf,info_data_len,0); // send ack for http get - // fill the header: - // This code requires that we send only one data packet - // because we keep no state information. We must therefore set - // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V; - make_tcp_ack_with_data_noflags(buf,dlen); // send data -} +void www_server_reply(uint8_t *buf, uint16_t dlen) { + // Send ACK for HTTP GET + make_tcp_ack_from_any(buf, info_data_len, 0); -#if defined (NTP_client) || defined (WOL_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -// fill buffer with a prog-mem string - CHANGED TO NON PROGMEM! -void fill_buf_p(uint8_t *buf,uint16_t len, const char *s) -{ - // fill in tcp data at position pos - // - // with no options the data starts after the checksum + 2 more bytes (urgent ptr) - memcpy(buf, s, len); + // Fill the header with appropriate flags + buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V; + // Send data + make_tcp_ack_with_data_noflags(buf, dlen); +} + +#if defined(NTP_client) || defined(WOL_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) +// Fills the buffer with a string of specified length +void fill_buf_p(uint8_t *buf, uint16_t len, const char *s) { + memcpy(buf, s, len); } #endif #ifdef PING_client -// icmp echo, matchpat is a pattern that has to be sent back by the -// host answering the ping. -// The ping is sent to destip and mac gwmacaddr -void client_icmp_request(uint8_t *buf,uint8_t *destip) -{ - uint16_t ck; - // - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); // gw mac in local lan or host mac - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=0x82; // TUX Code has 0x54, here has 0x82 - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], destip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREQUEST_V; - buf[ICMP_TYPE_P+1]=0; // code - // zero the checksum - buf[ICMP_CHECKSUM_H_P]=0; - buf[ICMP_CHECKSUM_L_P]=0; - // a possibly unique id of this host: - buf[ICMP_IDENT_H_P]=5; // some number - buf[ICMP_IDENT_L_P]=ipaddr[3]; // last byte of my IP - // - buf[ICMP_IDENT_L_P+1]=0; // seq number, high byte - buf[ICMP_IDENT_L_P+2]=1; // seq number, low byte, we send only 1 ping at a time - // copy the data: - memset(&buf[ICMP_DATA_P], PINGPATTERN, 56); - // - ck=checksum(&buf[ICMP_TYPE_P], 56+8,0); - buf[ICMP_CHECKSUM_H_P]=ck>>8; - buf[ICMP_CHECKSUM_L_P]=ck& 0xff; - enc28j60PacketSend(98,buf); +// Sends an ICMP echo request (ping) to the specified destination IP using the gateway MAC address. +void client_icmp_request(uint8_t *buf, uint8_t *destip) { + // Fill Ethernet header + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); // Gateway MAC address in local LAN or host MAC address + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + + // Fill IP header + fill_buf_p(&buf[IP_P], 9, iphdr); + buf[IP_TOTLEN_L_P] = 0x82; // Total length + buf[IP_PROTO_P] = IP_PROTO_ICMP_V; // ICMP protocol + memcpy(&buf[IP_DST_P], destip, 4); + memcpy(&buf[IP_SRC_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + // Fill ICMP header and data + buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; + buf[ICMP_TYPE_P + 1] = 0; // Code + buf[ICMP_CHECKSUM_H_P] = 0; // Zero the checksum + buf[ICMP_CHECKSUM_L_P] = 0; + buf[ICMP_IDENT_H_P] = 5; // Some number for identification + buf[ICMP_IDENT_L_P] = ipaddr[3]; // Last byte of my IP for identification + buf[ICMP_IDENT_L_P + 1] = 0; // Sequence number, high byte + buf[ICMP_IDENT_L_P + 2] = 1; // Sequence number, low byte + + // Copy the data pattern + memset(&buf[ICMP_DATA_P], PINGPATTERN, 56); + + // Calculate checksum + uint16_t ck = checksum(&buf[ICMP_TYPE_P], 56 + 8, 0); + buf[ICMP_CHECKSUM_H_P] = ck >> 8; + buf[ICMP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(98, buf); } #endif // PING_client -void __attribute__((weak)) ES_PingCallback(void) -{ -} +void __attribute__((weak)) ES_PingCallback(void) {} #ifdef NTP_client -// ntp udp packet +// Sends an NTP UDP packet // See http://tools.ietf.org/html/rfc958 for details -void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) -{ - uint16_t ck; - // - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=0x4c; - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], ntpip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[UDP_DST_PORT_H_P]=0; - buf[UDP_DST_PORT_L_P]=0x7b; // ntp=123 - buf[UDP_SRC_PORT_H_P]=10; - buf[UDP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port - buf[UDP_LEN_H_P]=0; - buf[UDP_LEN_L_P]=56; // fixed len - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data: - // most fields are zero, here we zero everything and fill later - memset(&buf[UDP_DATA_P], 0, 48); - fill_buf_p(&buf[UDP_DATA_P],10,ntpreqhdr); - // - ck=checksum(&buf[IP_SRC_P], 16 + 48,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck& 0xff; - enc28j60PacketSend(90,buf); +void client_ntp_request(uint8_t *buf, uint8_t *ntpip, uint8_t srcport) { + // Fill Ethernet header + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + + // Fill IP header + fill_buf_p(&buf[IP_P], 9, iphdr); + buf[IP_TOTLEN_L_P] = 0x4c; // Total length + buf[IP_PROTO_P] = IP_PROTO_UDP_V; // UDP protocol + memcpy(&buf[IP_DST_P], ntpip, 4); + memcpy(&buf[IP_SRC_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + // Fill UDP header + buf[UDP_DST_PORT_H_P] = 0; + buf[UDP_DST_PORT_L_P] = 0x7b; // NTP port = 123 + buf[UDP_SRC_PORT_H_P] = 10; + buf[UDP_SRC_PORT_L_P] = srcport; // Lower 8 bits of source port + buf[UDP_LEN_H_P] = 0; + buf[UDP_LEN_L_P] = 56; // Fixed length + buf[UDP_CHECKSUM_H_P] = 0; // Zero the checksum + buf[UDP_CHECKSUM_L_P] = 0; + + // Fill NTP data + memset(&buf[UDP_DATA_P], 0, 48); + fill_buf_p(&buf[UDP_DATA_P], 10, ntpreqhdr); + + // Calculate and fill UDP checksum + uint16_t ck = checksum(&buf[IP_SRC_P], 16 + 48, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(90, buf); } /* @@ -838,84 +730,61 @@ void client_ntp_process_answer(uint8_t *buf, uint16_t plen) { #endif // NTP_client #ifdef UDP_client -// -------------------- send a spontanious UDP packet to a server -// There are two ways of using this: -// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], -// you send the packet by calling send_udp_transmit -// -// 2) You just allocate a large enough buffer for you data and you call send_udp and nothing else -// needs to be done. -// -// send_udp sends via gwip, you must call client_set_gwip at startup -void send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport) -{ - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); +// Prepare a spontaneous UDP packet to a server +void send_udp_prepare(uint8_t *buf, uint16_t sport, uint8_t *dip, uint16_t dport) { + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P], 9, iphdr); + + buf[IP_TOTLEN_H_P] = 0; + buf[IP_PROTO_P] = IP_PROTO_UDP_V; + memcpy(&buf[IP_DST_P], dip, 4); + memcpy(&buf[IP_SRC_P], ipaddr, 4); + + buf[UDP_DST_PORT_H_P] = (dport >> 8); + buf[UDP_DST_PORT_L_P] = dport & 0xff; + buf[UDP_SRC_PORT_H_P] = (sport >> 8); + buf[UDP_SRC_PORT_L_P] = sport & 0xff; + buf[UDP_LEN_H_P] = 0; + buf[UDP_CHECKSUM_H_P] = 0; + buf[UDP_CHECKSUM_L_P] = 0; +} - // total length field in the IP header must be set: - buf[IP_TOTLEN_H_P]=0; - // done in transmit: buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen; - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], dip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); +// Transmit a prepared UDP packet +void send_udp_transmit(uint8_t *buf, uint16_t datalen) { + buf[IP_TOTLEN_H_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) >> 8; + buf[IP_TOTLEN_L_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) & 0xff; + fill_ip_hdr_checksum(buf); - // done in transmit: fill_ip_hdr_checksum(buf); - buf[UDP_DST_PORT_H_P]=(dport>>8); - buf[UDP_DST_PORT_L_P]=0xff&dport; - buf[UDP_SRC_PORT_H_P]=(sport>>8); - buf[UDP_SRC_PORT_L_P]=sport&0xff; - buf[UDP_LEN_H_P]=0; - // done in transmit: buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen; - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data: - // now starting with the first byte at buf[UDP_DATA_P] -} + buf[UDP_LEN_H_P] = (UDP_HEADER_LEN + datalen) >> 8; + buf[UDP_LEN_L_P] = (UDP_HEADER_LEN + datalen) & 0xff; -void send_udp_transmit(uint8_t *buf,uint16_t datalen) -{ - uint16_t ck; - buf[IP_TOTLEN_H_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >> 8; - buf[IP_TOTLEN_L_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) & 0xff; - fill_ip_hdr_checksum(buf); - //buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen; - buf[UDP_LEN_H_P]=(UDP_HEADER_LEN+datalen) >>8; - buf[UDP_LEN_L_P]=(UDP_HEADER_LEN+datalen) & 0xff; + uint16_t ck = checksum(&buf[IP_SRC_P], 16 + datalen, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; - // - ck=checksum(&buf[IP_SRC_P], 16 + datalen,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck& 0xff; - enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); + enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); } -void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport) -{ - send_udp_prepare(buf,sport, dip, dport); - memcpy(&buf[UDP_DATA_P], data, datalen); - send_udp_transmit(buf,datalen); +// Send a UDP packet +void send_udp(uint8_t *buf, char *data, uint16_t datalen, uint16_t sport, uint8_t *dip, uint16_t dport) { + send_udp_prepare(buf, sport, dip, dport); + memcpy(&buf[UDP_DATA_P], data, datalen); + send_udp_transmit(buf, datalen); } // Process UDP packets void udp_packet_process(uint8_t *buf, uint16_t plen) { - // Get the position of the data in the UDP packet - int data_pos = UDP_DATA_P; // Use the correct constant for your context + uint16_t data_pos = UDP_DATA_P; uint8_t *data = &buf[data_pos]; uint16_t data_len = plen - data_pos; - // Convert the data to a string if necessary char msg[data_len + 1]; memcpy(msg, data, data_len); msg[data_len] = '\0'; - // Log the received message for debugging - //udpLog2("Received UDP", msg); - - // Iterate through the command table to find and execute the appropriate command for (CommandMapping *cmd = commandTable; cmd->command != NULL; cmd++) { if (strncmp(msg, cmd->command, strlen(cmd->command)) == 0) { cmd->function(); @@ -923,7 +792,6 @@ void udp_packet_process(uint8_t *buf, uint16_t plen) { } } - // If no matching command is found udpLog2("Error", "Unexpected UDP message"); } #endif // UDP_client @@ -975,801 +843,655 @@ void send_wol(uint8_t *buf,uint8_t *wolmac) #endif // WOL_client #if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -// make a arp request -void client_arp_whohas(uint8_t *buf,uint8_t *ip_we_search) -{ - memset(&buf[ETH_DST_MAC], 0xFF, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; - fill_buf_p(&buf[ETH_ARP_P],8,arpreqhdr); - - memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); - memset(&buf[ETH_ARP_DST_MAC_P], 0, 6); - - memcpy(&buf[ETH_ARP_DST_IP_P], ip_we_search, 4); - memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); - - waitgwmac|=WGW_ACCEPT_ARP_REPLY; - - // 0x2a=42=len of packet - enc28j60PacketSend(0x2a,buf); +// Make an ARP request +void client_arp_whohas(uint8_t *buf, uint8_t *ip_we_search) { + // Prepare Ethernet header + memset(&buf[ETH_DST_MAC], 0xFF, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; + + // Prepare ARP request header + fill_buf_p(&buf[ETH_ARP_P], 8, arpreqhdr); + memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); + memset(&buf[ETH_ARP_DST_MAC_P], 0, 6); + memcpy(&buf[ETH_ARP_DST_IP_P], ip_we_search, 4); + memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); + + // Set the flag to accept ARP reply + waitgwmac |= WGW_ACCEPT_ARP_REPLY; + + // Send the packet + enc28j60PacketSend(0x2a, buf); // 0x2a = 42 = length of the packet } -uint8_t client_waiting_gw(void) -{ - if (waitgwmac & WGW_HAVE_GW_MAC){ - return(0); - } - return(1); +// Check if the client is waiting for the gateway MAC address +uint8_t client_waiting_gw(void) { + return (waitgwmac & WGW_HAVE_GW_MAC) ? 0 : 1; } -// store the mac addr from an arp reply -// no len check here, you must first call eth_type_is_arp_and_my_ip -uint8_t client_store_gw_mac(uint8_t *buf) -{ - if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4)) { +// Store the MAC address from an ARP reply +uint8_t client_store_gw_mac(uint8_t *buf) { + if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4) == 0) { + memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); + return 1; + } return 0; - } - memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); - return 1; } +// Refresh the gateway ARP entry void client_gw_arp_refresh(void) { - if (waitgwmac & WGW_HAVE_GW_MAC){ - waitgwmac|=WGW_REFRESHING; - } + if (waitgwmac & WGW_HAVE_GW_MAC) { + waitgwmac |= WGW_REFRESHING; + } } -// Used? -/* -void client_set_wwwip(uint8_t *wwwipaddr) -{ - uint8_t i=0; - while(i<4){ - wwwip[i]=wwwipaddr[i]; - i++; - } -} -*/ +//void client_set_wwwip(uint8_t *wwwipaddr) { +// memcpy(wwwip, wwwipaddr, 4); +//} void client_set_gwip(uint8_t *gwipaddr) { - waitgwmac=WGW_INITIAL_ARP; // causes an arp request in the packet loop - memcpy(gwip, gwipaddr, 4); + waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop + memcpy(gwip, gwipaddr, 4); } -#endif +#endif // defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) void client_tcp_set_serverip(uint8_t *ipaddr) { - memcpy(tcpsrvip, ipaddr, 4); + memcpy(tcpsrvip, ipaddr, 4); } #if defined (TCP_client) // Make a tcp syn packet -void client_syn(uint8_t *buf,uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) +void client_syn(uint8_t *buf, uint8_t srcport, uint8_t dstport_h, uint8_t dstport_l) { - uint16_t ck; - // -- make the main part of the eth/IP/tcp header: - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=44; // good for syn - buf[IP_PROTO_P]=IP_PROTO_TCP_V; - memcpy(&buf[IP_DST_P], tcpsrvip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); + // -- make the main part of the eth/IP/tcp header: + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P], 9, iphdr); + + buf[IP_TOTLEN_L_P] = 44; // good for syn + buf[IP_PROTO_P] = IP_PROTO_TCP_V; + memcpy(&buf[IP_DST_P], tcpsrvip, 4); + memcpy(&buf[IP_SRC_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + buf[TCP_DST_PORT_H_P] = dstport_h; + buf[TCP_DST_PORT_L_P] = dstport_l; + buf[TCP_SRC_PORT_H_P] = TCPCLIENT_SRC_PORT_H; + buf[TCP_SRC_PORT_L_P] = srcport; // lower 8 bit of src port + // zero out sequence number and acknowledgement number + memset(&buf[TCP_SEQ_H_P], 0, 8); + + // -- header ready + // put initial seq number + buf[TCP_SEQ_H_P+2] = seqnum; + seqnum += 3; // step the initial seq num by something we will not use during this tcp session + + buf[TCP_HEADER_LEN_P] = 0x60; // 0x60=24 len: (0x60>>4) * 4 + buf[TCP_FLAGS_P] = TCP_FLAGS_SYN_V; + + // use a low window size otherwise we have to have timers and can not just react on every packet. + buf[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 768 = 0x300, initial window + buf[TCP_WIN_SIZE+1] = 0x0; + + // zero the checksum + buf[TCP_CHECKSUM_H_P] = 0; + buf[TCP_CHECKSUM_L_P] = 0; + + // urgent pointer + buf[TCP_CHECKSUM_L_P+1] = 0; + buf[TCP_CHECKSUM_L_P+2] = 0; + + // MSS=768, must be more than 50% of the window size we use + buf[TCP_OPTIONS_P] = 2; + buf[TCP_OPTIONS_P+1] = 4; + buf[TCP_OPTIONS_P+2] = (CLIENTMSS >> 8); + buf[TCP_OPTIONS_P+3] = CLIENTMSS & 0xff; + + uint16_t ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2); + buf[TCP_CHECKSUM_H_P] = ck >> 8; + buf[TCP_CHECKSUM_L_P] = ck & 0xff; + + // 4 is the tcp mss option: + enc28j60PacketSend(IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4, buf); - buf[TCP_DST_PORT_H_P]=dstport_h; - buf[TCP_DST_PORT_L_P]=dstport_l; - buf[TCP_SRC_PORT_H_P]=TCPCLIENT_SRC_PORT_H; - buf[TCP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port - // zero out sequence number and acknowledgement number - memset(&buf[TCP_SEQ_H_P], 0, 8); - // -- header ready - // put inital seq number - // we step only the second byte, this allows us to send packts - // with 255 bytes 512 (if we step the initial seqnum by 2) - // or 765 (step by 3) - buf[TCP_SEQ_H_P+2]= seqnum; - // step the inititial seq num by something we will not use - // during this tcp session: - seqnum+=3; - buf[TCP_HEADER_LEN_P]=0x60; // 0x60=24 len: (0x60>>4) * 4 - buf[TCP_FLAGS_P]=TCP_FLAGS_SYN_V; - // use a low window size otherwise we have to have - // timers and can not just react on every packet. - buf[TCP_WIN_SIZE]=0x4; // 1024=0x400, 768 = 0x300, initial window - buf[TCP_WIN_SIZE+1]=0x0; - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // urgent pointer - buf[TCP_CHECKSUM_L_P+1]=0; - buf[TCP_CHECKSUM_L_P+2]=0; - // MSS=768, must be more than 50% of the window size we use - // 768 in hex is 0x300 - buf[TCP_OPTIONS_P]=2; - buf[TCP_OPTIONS_P+1]=4; - buf[TCP_OPTIONS_P+2]=(CLIENTMSS>>8); - buf[TCP_OPTIONS_P+3]=CLIENTMSS & 0xff; - ck=checksum(&buf[IP_SRC_P], 8 +TCP_HEADER_LEN_PLAIN+4,2); - buf[TCP_CHECKSUM_H_P]=ck>>8; - buf[TCP_CHECKSUM_L_P]=ck& 0xff; - // 4 is the tcp mss option: - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4,buf); #if ETHERSHIELD_DEBUG - ethershieldDebug( "Sent TCP Syn\n"); + ethershieldDebug("Sent TCP Syn\n"); #endif } -// Make a tcp data packet with next sequence number -// XXXXXXXXXXX -uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort ) +uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort) { - uint16_t ck; - // -- make the main part of the eth/IP/tcp header: - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=40; - buf[IP_PROTO_P]=IP_PROTO_TCP_V; - memcpy(&buf[IP_DST_P], tcpsrvip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[TCP_DST_PORT_H_P]=tcp_client_port_h; - buf[TCP_DST_PORT_L_P]=tcp_client_port_l; - buf[TCP_SRC_PORT_H_P]= (srcPort >> 8); - buf[TCP_SRC_PORT_L_P]= srcPort & 0x00ff; - // zero out sequence number and acknowledgement number - memset(&buf[TCP_SEQ_H_P], 0, 8); - // -- header ready - // put inital seq number - // we step only the second byte, this allows us to send packts - // with 255 bytes 512 (if we step the initial seqnum by 2) - // or 765 (step by 3) - buf[TCP_SEQ_H_P+2]= seqnum; - // step the inititial seq num by something we will not use - // during this tcp session: - seqnum+=3; - buf[TCP_HEADER_LEN_P]=0x60; // 0x60=24 len: (0x60>>4) * 4 - buf[TCP_FLAGS_P]=TCP_FLAGS_PUSH_V; - // use a low window size otherwise we have to have - // timers and can not just react on every packet. - buf[TCP_WIN_SIZE]=0x4; // 1024=0x400, 768 = 0x300, initial window - buf[TCP_WIN_SIZE+1]=0x0; - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // urgent pointer - buf[TCP_CHECKSUM_L_P+1]=0; - buf[TCP_CHECKSUM_L_P+2]=0; - // MSS=768, must be more than 50% of the window size we use - // 768 in hex is 0x300 - buf[TCP_OPTIONS_P]=2; - buf[TCP_OPTIONS_P+1]=4; - buf[TCP_OPTIONS_P+2]=(CLIENTMSS>>8); - buf[TCP_OPTIONS_P+3]=CLIENTMSS & 0xff; - ck=checksum(&buf[IP_SRC_P], 8 +TCP_HEADER_LEN_PLAIN+4,2); - buf[TCP_CHECKSUM_H_P]=ck>>8; - buf[TCP_CHECKSUM_L_P]=ck& 0xff; - // 4 is the tcp mss option: -// enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4,buf); - return IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4; + // -- make the main part of the eth/IP/tcp header: + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P], 9, iphdr); + + buf[IP_TOTLEN_L_P] = 40; + buf[IP_PROTO_P] = IP_PROTO_TCP_V; + memcpy(&buf[IP_DST_P], tcpsrvip, 4); + memcpy(&buf[IP_SRC_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + buf[TCP_DST_PORT_H_P] = tcp_client_port_h; + buf[TCP_DST_PORT_L_P] = tcp_client_port_l; + buf[TCP_SRC_PORT_H_P] = srcPort >> 8; + buf[TCP_SRC_PORT_L_P] = srcPort & 0xff; + + // zero out sequence number and acknowledgement number + memset(&buf[TCP_SEQ_H_P], 0, 8); + + // put initial seq number + buf[TCP_SEQ_H_P + 2] = seqnum; + seqnum += 3; // step the initial seq num by something we will not use during this tcp session + + buf[TCP_HEADER_LEN_P] = 0x60; // 0x60=24 len: (0x60>>4) * 4 + buf[TCP_FLAGS_P] = TCP_FLAGS_PUSH_V; + + buf[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 768 = 0x300, initial window + buf[TCP_WIN_SIZE + 1] = 0x0; + + // zero the checksum + buf[TCP_CHECKSUM_H_P] = 0; + buf[TCP_CHECKSUM_L_P] = 0; + + // urgent pointer + buf[TCP_CHECKSUM_L_P + 1] = 0; + buf[TCP_CHECKSUM_L_P + 2] = 0; + + // MSS=768, must be more than 50% of the window size we use + buf[TCP_OPTIONS_P] = 2; + buf[TCP_OPTIONS_P + 1] = 4; + buf[TCP_OPTIONS_P + 2] = CLIENTMSS >> 8; + buf[TCP_OPTIONS_P + 3] = CLIENTMSS & 0xff; + + uint16_t ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2); + buf[TCP_CHECKSUM_H_P] = ck >> 8; + buf[TCP_CHECKSUM_L_P] = ck & 0xff; + + return IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4; } -// Send the data -void send_tcp_data(uint8_t *buf,uint16_t dlen) +void send_tcp_data(uint8_t *buf, uint16_t dlen) { - uint16_t j; - // fill the header: - // This code requires that we send only one data packet - // because we keep no state information. We must therefore set - // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_PUSH_V; - - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) + len of data - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - buf[IP_TOTLEN_H_P]=j>>8; - buf[IP_TOTLEN_L_P]=j& 0xff; - fill_ip_hdr_checksum(buf); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); -} + // fill the header: + buf[TCP_FLAGS_P] = TCP_FLAGS_PUSH_V; + // total length field in the IP header must be set: + uint16_t total_len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; + buf[IP_TOTLEN_H_P] = total_len >> 8; + buf[IP_TOTLEN_L_P] = total_len & 0xff; -// This is how to use the tcp client: -// -// Declare a callback function to get the result (tcp data from the server): -// -// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code;return(close_tcp_session);} -// -// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf -// are invalid. That is: do to use data_start_pos_in_buf and len_of_data -// if statuscode!=0. -// -// This callback gives you access to the TCP data of the first -// packet returned from the server. You should aim to minimize the server -// output such that this will be the only packet. -// -// close_tcp_session=1 means close the session now. close_tcp_session=0 -// read all data and leave it to the other side to close it. -// If you connect to a web server then you want close_tcp_session=0. -// If you connect to a modbus/tcp equipment then you want close_tcp_session=1 -// -// Declare a callback function to be called in order to fill in the -// request (tcp data sent to the server): -// uint16_t your_client_tcp_datafill_callback(uint8_t fd){...your code;return(len_of_data_filled_in);} -// -// Now call: -// fd=client_tcp_req(&your_client_tcp_result_callback,&your_client_tcp_datafill_callback,portnumber); -// -// fd is a file descriptor like number that you get back in the fill and result -// function so you know to which call of client_tcp_req this callback belongs. -// -// You can not start different clients (e.g modbus and web) at the -// same time but you can start them one after each other. That is -// when the request has timed out or when the result_callback was -// executed then you can start a new one. The fd makes it still possible to -// distinguish in the callback code the different types you started. -// -// Note that you might never get called back if the other side does -// not answer. A timer would be needed to recongnize such a condition. -// -// We use callback functions because that saves memory and a uC is very -// limited in memory -// -uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port) + fill_ip_hdr_checksum(buf); + + // zero the checksum + buf[TCP_CHECKSUM_H_P] = 0; + buf[TCP_CHECKSUM_L_P] = 0; + + // calculate the checksum + uint16_t ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2); + buf[TCP_CHECKSUM_H_P] = ck >> 8; + buf[TCP_CHECKSUM_L_P] = ck & 0xff; + + enc28j60PacketSend(total_len + ETH_HEADER_LEN, buf); +} + +/** + * @brief Requests a TCP connection to a server. + * + * This function sets up the necessary callback functions for handling the TCP request and response. + * + * @param result_callback Pointer to the callback function for processing the server response. + * @param datafill_callback Pointer to the callback function for filling the request data. + * @param port The port number of the server to connect to. + * @return A file descriptor for the TCP session. + */ +uint8_t client_tcp_req( + uint8_t (*result_callback)(uint8_t fd, uint8_t statuscode, uint16_t data_start_pos_in_buf, uint16_t len_of_data), + uint16_t (*datafill_callback)(uint8_t fd), + uint16_t port) { - client_tcp_result_callback=result_callback; - client_tcp_datafill_callback=datafill_callback; - tcp_client_port_h=(port>>8) & 0xff; - tcp_client_port_l=(port & 0xff); - tcp_client_state=1; - tcp_fd++; - if (tcp_fd>7){ - tcp_fd=0; - } - return(tcp_fd); + client_tcp_result_callback = result_callback; + client_tcp_datafill_callback = datafill_callback; + tcp_client_port_h = (port >> 8) & 0xff; + tcp_client_port_l = port & 0xff; + tcp_client_state = 1; + + if (++tcp_fd > 7) { + tcp_fd = 0; + } + + return tcp_fd; } #endif // TCP_client -#if defined (WWW_client) -uint16_t www_client_internal_datafill_callback(uint8_t fd){ - char strbuf[5]; - uint16_t len=0; - if (fd==www_fd){ - if (browsertype==0){ - // GET - len=fill_tcp_data(bufptr,0,"GET "); - len=fill_tcp_data(bufptr,len,client_urlbuf); - if( client_urlbuf_var ) - len=fill_tcp_data(bufptr,len,client_urlbuf_var); - // I would prefer http/1.0 but there is a funny - // bug in some apache webservers which causes - // them to send two packets (fragmented PDU) - // if we don't use HTTP/1.1 + Connection: close - len=fill_tcp_data(bufptr,len," HTTP/1.1\r\nHost: "); - len=fill_tcp_data(bufptr,len,client_hoststr); - len=fill_tcp_data(bufptr,len,"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n"); - }else{ - // POST - if( client_method ) { - len=fill_tcp_data(bufptr,0, client_method ); - len=fill_tcp_data(bufptr,len, " "); - } else { - len=fill_tcp_data(bufptr,0, "POST "); - } - len=fill_tcp_data(bufptr,len,client_urlbuf); - len=fill_tcp_data(bufptr,len," HTTP/1.1\r\nHost: "); - len=fill_tcp_data(bufptr,len,client_hoststr); - if (client_additionalheaderline){ - len=fill_tcp_data(bufptr,len,"\r\n"); - len=fill_tcp_data(bufptr,len,client_additionalheaderline); - } - len=fill_tcp_data(bufptr,len,"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n"); - len=fill_tcp_data(bufptr,len,"Content-Length: "); - //itoa(strlen(client_postval),strbuf,10); - sprintf(strbuf, "%d", strlen(client_postval)); - len=fill_tcp_data(bufptr,len,strbuf); - len=fill_tcp_data(bufptr,len,"\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"); - len=fill_tcp_data(bufptr,len,client_postval); +#if defined (WWW_client) +/** + * @brief Internal data fill callback for the WWW client. + * + * This function fills the buffer with the appropriate HTTP GET or POST request. + * + * @param fd The file descriptor. + * @return The length of the data filled. + */ +uint16_t www_client_internal_datafill_callback(uint8_t fd) { + char strbuf[6]; // Adjusted to ensure it can hold up to 5 digits and the null terminator + uint16_t len = 0; + + if (fd != www_fd) { + return 0; } - return(len); - } - return(0); -} -uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data){ - if (fd!=www_fd){ - (*client_browser_callback)(4,0,0); - return(0); - } - if (statuscode==0 && len_of_data>12){ - // we might have a http status code - if (client_browser_callback){ - if (strncmp("200",(char *)&(bufptr[datapos+9]),3)==0){ - (*client_browser_callback)(0,((uint16_t)TCP_SRC_PORT_H_P+(bufptr[TCP_HEADER_LEN_P]>>4)*4),len_of_data); - }else{ - (*client_browser_callback)(1,((uint16_t)TCP_SRC_PORT_H_P+(bufptr[TCP_HEADER_LEN_P]>>4)*4),len_of_data); - } + if (browsertype == 0) { + // GET Request + len = fill_tcp_data(bufptr, len, "GET "); + len = fill_tcp_data(bufptr, len, client_urlbuf); + if (client_urlbuf_var) { + len = fill_tcp_data(bufptr, len, client_urlbuf_var); + } + len = fill_tcp_data(bufptr, len, " HTTP/1.1\r\nHost: "); + len = fill_tcp_data(bufptr, len, client_hoststr); + len = fill_tcp_data(bufptr, len, "\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n"); + } else { + // POST Request + len = fill_tcp_data(bufptr, len, client_method ? client_method : "POST "); + len = fill_tcp_data(bufptr, len, client_urlbuf); + len = fill_tcp_data(bufptr, len, " HTTP/1.1\r\nHost: "); + len = fill_tcp_data(bufptr, len, client_hoststr); + + if (client_additionalheaderline) { + len = fill_tcp_data(bufptr, len, "\r\n"); + len = fill_tcp_data(bufptr, len, client_additionalheaderline); + } + + len = fill_tcp_data(bufptr, len, "\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n"); + len = fill_tcp_data(bufptr, len, "Content-Length: "); + snprintf(strbuf, sizeof(strbuf), "%zu", strlen(client_postval)); + len = fill_tcp_data(bufptr, len, strbuf); + len = fill_tcp_data(bufptr, len, "\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"); + len = fill_tcp_data(bufptr, len, client_postval); } - } - return(0); + + return len; } +/** + * @brief Internal result callback for the WWW client. + * + * This function processes the result of the HTTP request. + * + * @param fd The file descriptor. + * @param statuscode The status code. + * @param datapos The position of the data in the buffer. + * @param len_of_data The length of the data. + * @return 0 Always returns 0. + */ +uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data) { + if (fd != www_fd) { + client_browser_callback(4, 0, 0); + return 0; + } + + if (statuscode == 0 && len_of_data > 12 && client_browser_callback) { + uint16_t offset = TCP_SRC_PORT_H_P + ((bufptr[TCP_HEADER_LEN_P] >> 4) * 4); + uint8_t *status_code_pos = &bufptr[datapos + 9]; -// call this function externally like this: -// -// Declare a callback function: void browserresult(uint8_t statuscode){...your code} -// client_browser_url(PSTR("/cgi-bin/checkip"),NULL,PSTR("tuxgraphics.org"),&browserresult); -// urlbuf_varpart is a pointer to a string buffer that contains the second -// non constant part of the url. You must keep this buffer allocated until the -// callback function is executed or until you can be sure that the server side -// has timed out. -// hoststr is the name of the host. This is needed because many sites host several -// sites on the same physical machine with only one IP address. The web server needs -// to know to which site you want to go. -// additionalheaderline must be set to NULL if not used. -// statuscode is zero if the answer from the web server is 200 OK (e.g HTTP/1.1 200 OK) -// -// -void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)) -{ - client_urlbuf=urlbuf; - client_urlbuf_var=urlbuf_varpart; - client_hoststr=hoststr; - browsertype=0; - client_browser_callback=callback; - www_fd=client_tcp_req(&www_client_internal_result_callback,&www_client_internal_datafill_callback,80); + if (strncmp("200", (char *)status_code_pos, 3) == 0) { + client_browser_callback(0, offset, len_of_data); + } else { + client_browser_callback(1, offset, len_of_data); + } + } + + return 0; } -// client web browser using http POST operation: -// additionalheaderline must be set to NULL if not used. -// method set to NULL if using default POST alternative is PUT -// postval is a string buffer which can only be de-allocated by the caller -// when the post operation was really done (e.g when callback was executed). -// postval must be urlencoded. -void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval, void (*callback)(uint8_t,uint16_t)) -{ - client_urlbuf=urlbuf; - client_hoststr=hoststr; - client_additionalheaderline=additionalheaderline; - client_method=method; - client_postval=postval; - browsertype=1; - client_browser_callback=(void (*)(uint8_t,uint16_t,uint16_t))callback; - www_fd=client_tcp_req(&www_client_internal_result_callback,&www_client_internal_datafill_callback,80); +/** + * @brief Sends an HTTP GET request. + * + * @param urlbuf URL buffer. + * @param urlbuf_varpart Variable part of the URL. + * @param hoststr Hostname string. + * @param callback Callback function to handle the result. + */ +void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t, uint16_t, uint16_t)) { + client_urlbuf = urlbuf; + client_urlbuf_var = urlbuf_varpart; + client_hoststr = hoststr; + browsertype = 0; + client_browser_callback = callback; + www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); +} + +/** + * @brief Sends an HTTP POST request. + * + * @param urlbuf URL buffer. + * @param hoststr Hostname string. + * @param additionalheaderline Additional header line, set to NULL if not used. + * @param method HTTP method, set to NULL for default POST, alternative is PUT. + * @param postval URL-encoded string buffer for the POST data. + * @param callback Callback function to handle the result. + */ +void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval, void (*callback)(uint8_t, uint16_t)) { + client_urlbuf = urlbuf; + client_hoststr = hoststr; + client_additionalheaderline = additionalheaderline; + client_method = method; + client_postval = postval; + browsertype = 1; + client_browser_callback = (void (*)(uint8_t, uint16_t, uint16_t))callback; + www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); } #endif // WWW_client -void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) -{ - icmp_callback=callback; +/** + * @brief Register a callback function for receiving ping replies. + * + * @param callback The callback function to be called when a ping reply is received. + */ +void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) { + icmp_callback = callback; } #ifdef PING_client -// loop over this to check if we get a ping reply: -uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) -{ - if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V){ - if (buf[ICMP_DATA_P]== PINGPATTERN){ - if (check_ip_message_is_from(buf,ip_monitoredhost)){ - return(1); - // ping reply is from monitored host and ping was from us - } +/** + * @brief Check for an ICMP ping reply packet. + * + * This function should be called in a loop to check if a ping reply has been received. + * + * @param buf The buffer containing the packet data. + * @param ip_monitoredhost The IP address of the monitored host. + * @return 1 if the ping reply is from the monitored host, 0 otherwise. + */ +uint8_t packetloop_icmp_checkreply(uint8_t *buf, uint8_t *ip_monitoredhost) { + if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && + buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREPLY_V && + buf[ICMP_DATA_P] == PINGPATTERN && + check_ip_message_is_from(buf, ip_monitoredhost)) { + return 1; } - } - return(0); + return 0; } #endif // PING_client -// Utility functions - -// Perform all processing to get an IP address plus other addresses returned, e.g. gw, dns, dhcp server. -// Returns 1 for successful IP address allocation, 0 otherwise -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ) { - uint16_t dat_p; - int plen = 0; - long lastDhcpRequest = HAL_GetTick(); - uint8_t dhcpState = 0; - bool gotIp = FALSE; - uint8_t dhcpTries = 10; // After 10 attempts fail gracefully so other action can be carried out - - dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); - - while( !gotIp ) { - // handle ping and wait for a tcp packet - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p=packetloop_icmp_tcp(buf,plen); - if(dat_p==0) { - check_for_dhcp_answer( buf, plen); - dhcpState = dhcp_state(); - // we are idle here - if( dhcpState != DHCP_STATE_OK ) { - if (HAL_GetTick() > (lastDhcpRequest + 10000L) ){ - lastDhcpRequest = HAL_GetTick(); - if( --dhcpTries <= 0 ) - return 0; // Failed to allocate address - // send dhcp - dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); - } - } else { - if( !gotIp ) { - gotIp = TRUE; - - //init the ethernet/ip layer: - init_ip_arp_udp_tcp(mymac, myip, myport); - - // Set the Router IP - client_set_gwip(gwip); // e.g internal IP of dsl router +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip) { + uint16_t dat_p; + int plen = 0; + long lastDhcpRequest = HAL_GetTick(); + uint8_t dhcpState = 0; + bool gotIp = FALSE; + uint8_t dhcpTries = 10; + + dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); + + while (!gotIp) { + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p = packetloop_icmp_tcp(buf, plen); + + if (dat_p == 0) { + check_for_dhcp_answer(buf, plen); + dhcpState = dhcp_state(); + + if (dhcpState != DHCP_STATE_OK) { + if (HAL_GetTick() > (lastDhcpRequest + 10000L)) { + lastDhcpRequest = HAL_GetTick(); + if (--dhcpTries <= 0) { + return 0; + } + dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); + } + } else if (!gotIp) { + gotIp = TRUE; + + init_ip_arp_udp_tcp(mymac, myip, myport); + client_set_gwip(gwip); #ifdef DNS_client - // Set the DNS server IP address if required, or use default - dnslkup_set_dnsip( dnsip ); + dnslkup_set_dnsip(dnsip); #endif - + } } - } } - } - - return 1; + return 1; } // Perform all processing to resolve a hostname to IP address. -// Returns 1 for successful Name resolution, 0 otherwise -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ) { - uint16_t dat_p; - int plen = 0; - long lastDnsRequest = HAL_GetTick(); - uint8_t dns_state = DNS_STATE_INIT; - bool gotAddress = FALSE; - uint8_t dnsTries = 3; // After 10 attempts fail gracefully so other action can be carried out - - while( !gotAddress ) { - // handle ping and wait for a tcp packet - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p=packetloop_icmp_tcp(buf,plen); - - // We have a packet - // Check if IP data - if (dat_p == 0) { - if (client_waiting_gw() ) { - // No ARP received for gateway - continue; - } - // It has IP data - if (dns_state==DNS_STATE_INIT) { - dns_state=DNS_STATE_REQUESTED; - lastDnsRequest = HAL_GetTick(); - dnslkup_request(buf,hostname); - continue; - } - if (dns_state!=DNS_STATE_ANSWER){ - // retry every minute if dns-lookup failed: - if (HAL_GetTick() > (lastDnsRequest + 60000L) ){ - if( --dnsTries <= 0 ) - return 0; // Failed to allocate address - - dns_state=DNS_STATE_INIT; - lastDnsRequest = HAL_GetTick(); +// Returns 1 for successful name resolution, 0 otherwise +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname) { + uint16_t dat_p; + int plen = 0; + long lastDnsRequest = HAL_GetTick(); + uint8_t dns_state = DNS_STATE_INIT; + bool gotAddress = FALSE; + uint8_t dnsTries = 3; // After 3 attempts fail gracefully + + while (!gotAddress) { + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p = packetloop_icmp_tcp(buf, plen); + + if (dat_p == 0) { + if (client_waiting_gw()) { + continue; + } + + if (dns_state == DNS_STATE_INIT) { + dns_state = DNS_STATE_REQUESTED; + lastDnsRequest = HAL_GetTick(); + dnslkup_request(buf, hostname); + continue; + } + + if (dns_state != DNS_STATE_ANSWER) { + if (HAL_GetTick() > (lastDnsRequest + 60000L)) { + if (--dnsTries <= 0) { + return 0; // Failed to resolve address + } + dns_state = DNS_STATE_INIT; + lastDnsRequest = HAL_GetTick(); + } + continue; + } + } else { + if (dns_state == DNS_STATE_REQUESTED && udp_client_check_for_dns_answer(buf, plen)) { + dns_state = DNS_STATE_ANSWER; + client_tcp_set_serverip(dnslkup_getip()); + gotAddress = TRUE; + } } - // don't try to use client before - // we have a result of dns-lookup - - continue; - } } - else { - if (dns_state==DNS_STATE_REQUESTED && udp_client_check_for_dns_answer( buf, plen ) ){ - dns_state=DNS_STATE_ANSWER; - //client_set_wwwip(dnslkup_getip()); - client_tcp_set_serverip(dnslkup_getip()); - gotAddress = TRUE; - } - } - } - return 1; + return 1; } -// return 0 to just continue in the packet loop and return the position +// Return 0 to just continue in the packet loop and return the position // of the tcp/udp data if there is tcp/udp data part -uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { - uint16_t len; - #if defined(TCP_client) - uint8_t send_fin = 0; - uint16_t tcpstart; - uint16_t save_len; - #endif - - //plen will be unequal to zero if there is a valid - // packet (without crc error): - #if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) - if (plen == 0) { - if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && delaycnt == 0 && enc28j60linkup()) { - client_arp_whohas(buf, gwip); - } - delaycnt++; - #if defined(TCP_client) - if (tcp_client_state == 1 && (waitgwmac & WGW_HAVE_GW_MAC)) { // send a syn - tcp_client_state = 2; - tcpclient_src_port_l++; // allocate a new port - // we encode our 3 bit fd into the src port this - // way we get it back in every message that comes - // from the server: - client_syn(buf, ((tcp_fd << 5) | (0x1f & tcpclient_src_port_l)), tcp_client_port_h, tcp_client_port_l); - } - #endif - return (0); - } - #endif // NTP_client||UDP_client||TCP_client||PING_client - // arp is broadcast if unknown but a host may also - // verify the mac address by sending it to - // a unicast address. - if (eth_type_is_arp_and_my_ip(buf, plen)) { - if (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V) { - // is it an arp request - make_arp_answer_from_request(buf); - } - #if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) - if (waitgwmac & WGW_ACCEPT_ARP_REPLY && (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V)) { - // is it an arp reply - if (client_store_gw_mac(buf)) { - waitgwmac = WGW_HAVE_GW_MAC; - } - } - #endif // NTP_client||UDP_client||TCP_client||PING_client - return (0); - } - // check if ip packets are for us: - if (eth_type_is_ip_and_my_ip(buf, plen) == 0) { - return (0); - } - #ifdef NTP_client - if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { - client_ntp_process_answer(buf, plen); - return(UDP_DATA_P); - } - #endif // NTP_client - #ifdef DNS_client - if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { - return (buf); - } - #endif // NTP_client - if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) { - if (icmp_callback) { - ( * icmp_callback)( & (buf[IP_SRC_P])); - } - // a ping packet, let's send pong - make_echo_reply_from_request(buf, plen); - ES_PingCallback(); - return (0); - } - if (buf[IP_PROTO_P] == IP_PROTO_UDP_V) { - udp_packet_process(buf, plen); - return (UDP_DATA_P); - } - if (plen < 54 && buf[IP_PROTO_P] != IP_PROTO_TCP_V) { - // smaller than the smallest TCP packet and not tcp port - return (0); - } - #if defined(TCP_client) - // a message for the tcp client, client_state is zero if client was never used - if (buf[TCP_DST_PORT_H_P] == TCPCLIENT_SRC_PORT_H) { - #if defined(WWW_client) - // workaround to pass pointer to www_client_internal.. - bufptr = buf; - #endif // WWW_client - if (check_ip_message_is_from(buf, tcpsrvip) == 0) { - return (0); - } - // if we get a reset: - if (buf[TCP_FLAGS_P] & TCP_FLAGS_RST_V) { - if (client_tcp_result_callback) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("RST: Calling tcp client callback\n"); - #endif - // parameters in client_tcp_result_callback: fd, status, buf_start, len - ( * client_tcp_result_callback)((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 3, 0, 0); - } - tcp_client_state = 5; - return (0); - } +uint16_t packetloop_icmp_tcp(uint8_t *buf, uint16_t plen) { + uint16_t len; +#if defined(TCP_client) + uint8_t send_fin = 0; + uint16_t tcpstart; + uint16_t save_len; +#endif - // Determine what to do with packed depending on state - len = get_tcp_data_len(buf); - if (tcp_client_state == 2) { - if ((buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Got SYNACK\n"); - #endif - // synack, answer with ack - make_tcp_ack_from_any(buf, 0, 0); - buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V; - - // Make a tcp message with data. When calling this function we must - // still have a valid tcp-ack in the buffer. In other words - // you have just called make_tcp_ack_from_any(buf,0). - if (client_tcp_datafill_callback) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Datafill Callback\n"); - #endif - // in this case it is src port because the above - // make_tcp_ack_from_any swaps the dst and src port: - len = ( * client_tcp_datafill_callback)((buf[TCP_SRC_PORT_L_P] >> 5) & 0x7); - } else { - // this is just to prevent a crash - len = 0; - } - tcp_client_state = 3; - make_tcp_ack_with_data_noflags(buf, len); - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send ACK\n"); - #endif - return (0); - } else { - // reset only if we have sent a syn and don't get syn-ack back. - // If we connect to a non listen port then we get a RST - // which will be handeled above. In other words there is - // normally no danger for an endless loop. - tcp_client_state = 1; // retry - // do not inform application layer as we retry. - len++; - if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { - // if packet was an ack then do not step the ack number - len = 0; + // Handle empty packets and ARP requests + if (plen == 0) { +#if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) + if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && delaycnt == 0 && enc28j60linkup()) { + client_arp_whohas(buf, gwip); } - // refuse and reset the connection - make_tcp_ack_from_any(buf, len, TCP_FLAGS_RST_V); - return (0); - } - } - // in tcp_client_state==3 we will normally first get an empty - // ack-packet and then a ack-packet with data. - if (tcp_client_state == 4) { //&& len>0){ - // our first real data packet - #if ETHERSHIELD_DEBUG - ethershieldDebug( "First Data Packet\n"); - #endif - // Removed this as there is no code to handle state 4. Only 1st packet will be available. - //tcp_client_state=4; - // return the data we received - if (client_tcp_result_callback) { - tcpstart = TCP_DATA_START; // TCP_DATA_START is a formula - // out of buffer bounds check, needed in case of fragmented IP packets - if (tcpstart > plen - 8) { - tcpstart = plen - 8; // dummy but save + delaycnt++; +#if defined(TCP_client) + if (tcp_client_state == 1 && (waitgwmac & WGW_HAVE_GW_MAC)) { // Send a SYN + tcp_client_state = 2; + tcpclient_src_port_l++; // Allocate a new port + client_syn(buf, ((tcp_fd << 5) | (0x1f & tcpclient_src_port_l)), tcp_client_port_h, tcp_client_port_l); } - save_len = len; - if (tcpstart + len > plen) { - save_len = plen - tcpstart; - } - #if ETHERSHIELD_DEBUG - ethershieldDebug("Calling Result callback\n"); - #endif - send_fin = ( * client_tcp_result_callback)((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 0, tcpstart, save_len); - - } - if (send_fin) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send FIN\n"); - #endif - make_tcp_ack_from_any(buf, len, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; - return (0); - } - make_tcp_ack_from_any(buf, len, 0); - return (0); +#endif +#endif + return 0; } - if (tcp_client_state == 3) { // && len>0){ - // our first real data packet - #if ETHERSHIELD_DEBUG - ethershieldDebug( "First Data Packet\n"); - #endif - // Removed this as there is no code to handle state 4. Only 1st packet will be available. - tcp_client_state = 4; - // return the data we received - if (client_tcp_result_callback) { - tcpstart = TCP_DATA_START; // TCP_DATA_START is a formula - // out of buffer bounds check, needed in case of fragmented IP packets - if (tcpstart > plen - 8) { - tcpstart = plen - 8; // dummy but save + + // Handle ARP packets + if (eth_type_is_arp_and_my_ip(buf, plen)) { + if (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V) { + make_arp_answer_from_request(buf); } - save_len = len; - if (tcpstart + len > plen) { - save_len = plen - tcpstart; +#if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) + if (waitgwmac & WGW_ACCEPT_ARP_REPLY && buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V) { + if (client_store_gw_mac(buf)) { + waitgwmac = WGW_HAVE_GW_MAC; + } } - #if ETHERSHIELD_DEBUG - ethershieldDebug("Calling Result callback\n"); - #endif - send_fin = ( * client_tcp_result_callback)((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 0, tcpstart, save_len); - - } - if (send_fin) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send FIN\n"); - #endif - make_tcp_ack_from_any(buf, len, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; - return (0); - } - make_tcp_ack_from_any(buf, len, 0); - return (0); - } - if (tcp_client_state == 5) { - // no more ack - #if ETHERSHIELD_DEBUG - ethershieldDebug("No more ACK\n"); - #endif - return (0); +#endif + return 0; } - if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Terminated\n"); - #endif - make_tcp_ack_from_any(buf, len + 1, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; // connection terminated - return (0); + + // Check if IP packets are for us + if (!eth_type_is_ip_and_my_ip(buf, plen)) { + return 0; } - // ack all data (the web page may be long): - // if we just get a fragment then len will be zero - // and we ack only once we have the full packet - if (len > 0) { - make_tcp_ack_from_any(buf, len, 0); - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send ACK\n"); - #endif + + // Handle ICMP echo requests + if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) { + if (icmp_callback) { + icmp_callback(&buf[IP_SRC_P]); + } + make_echo_reply_from_request(buf, plen); + ES_PingCallback(); + return 0; } - return (0); - } - #endif // WWW_client||TCP_client - // tcp port web server start - if (buf[TCP_DST_PORT_H_P] == wwwport_h && buf[TCP_DST_PORT_L_P] == wwwport_l) { - if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) { - make_tcp_synack_from_syn(buf); - // make_tcp_synack_from_syn does already send the syn,ack - return (0); + + // Handle UDP packets + if (buf[IP_PROTO_P] == IP_PROTO_UDP_V) { +#ifdef NTP_client + if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { + client_ntp_process_answer(buf, plen); + return UDP_DATA_P; + } +#endif +#ifdef DNS_client + if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { + return UDP_DATA_P; + } +#endif + udp_packet_process(buf, plen); + return UDP_DATA_P; } - if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { - info_data_len = get_tcp_data_len(buf); - // we can possibly have no data, just ack: - // Here we misuse plen for something else to save a variable. - // plen is now the position of start of the tcp user data. - if (info_data_len == 0) { - if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { - // finack, answer with ack - make_tcp_ack_from_any(buf, 0, 0); + + // Handle TCP packets + if (plen >= 54 && buf[IP_PROTO_P] == IP_PROTO_TCP_V) { +#if defined(TCP_client) + if (buf[TCP_DST_PORT_H_P] == TCPCLIENT_SRC_PORT_H) { +#if defined(WWW_client) + bufptr = buf; +#endif + if (!check_ip_message_is_from(buf, tcpsrvip)) { + return 0; + } + + if (buf[TCP_FLAGS_P] & TCP_FLAGS_RST_V) { + if (client_tcp_result_callback) { + client_tcp_result_callback((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 3, 0, 0); + } + tcp_client_state = 5; + return 0; + } + + len = get_tcp_data_len(buf); + + if (tcp_client_state == 2) { + if ((buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)) { + make_tcp_ack_from_any(buf, 0, 0); + buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V; + + if (client_tcp_datafill_callback) { + len = client_tcp_datafill_callback((buf[TCP_SRC_PORT_L_P] >> 5) & 0x7); + } else { + len = 0; + } + tcp_client_state = 3; + make_tcp_ack_with_data_noflags(buf, len); + return 0; + } else { + tcp_client_state = 1; + len++; + if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { + len = 0; + } + make_tcp_ack_from_any(buf, len, TCP_FLAGS_RST_V); + return 0; + } + } + + if (tcp_client_state == 3 || tcp_client_state == 4) { + tcp_client_state = 4; + if (client_tcp_result_callback) { + tcpstart = TCP_DATA_START; + if (tcpstart > plen - 8) { + tcpstart = plen - 8; + } + save_len = len; + if (tcpstart + len > plen) { + save_len = plen - tcpstart; + } + send_fin = client_tcp_result_callback((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 0, tcpstart, save_len); + } + if (send_fin) { + make_tcp_ack_from_any(buf, len, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); + tcp_client_state = 5; + return 0; + } + make_tcp_ack_from_any(buf, len, 0); + return 0; + } + + if (tcp_client_state == 5) { + return 0; + } + + if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { + make_tcp_ack_from_any(buf, len + 1, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); + tcp_client_state = 5; + return 0; + } + + if (len > 0) { + make_tcp_ack_from_any(buf, len, 0); + } + return 0; + } +#endif + + if (buf[TCP_DST_PORT_H_P] == wwwport_h && buf[TCP_DST_PORT_L_P] == wwwport_l) { + if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) { + make_tcp_synack_from_syn(buf); + return 0; + } + if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { + info_data_len = get_tcp_data_len(buf); + if (info_data_len == 0) { + if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { + make_tcp_ack_from_any(buf, 0, 0); + } + return 0; + } + len = TCP_DATA_START; + if (len > plen - 8) { + return 0; + } + return len; + } } - // just an ack with no data, wait for next packet - return (0); - } - // Here we misuse len for something else to save a variable - len = TCP_DATA_START; // TCP_DATA_START is a formula - // check for data corruption - if (len > plen - 8) { - return (0); - } - return (len); } - } - return (0); + return 0; } #endif diff --git a/src/websrv_help_functions.c b/src/websrv_help_functions.c index 227b744..5cdaeb2 100644 --- a/src/websrv_help_functions.c +++ b/src/websrv_help_functions.c @@ -1,11 +1,14 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 +/* + * websrv_help_functions.C * - * Some common utilities needed for IP and web applications - *********************************************/ + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #include "defines.h" #include "websrv_help_functions.h" @@ -16,184 +19,158 @@ // The returned value is stored in strbuf. You must allocate // enough storage for strbuf, maxlen is the size of strbuf. // I.e the value it is declated with: strbuf[5]-> maxlen=5 -uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key) -{ - uint8_t found=0; - uint8_t i=0; - char *kp; - kp=key; - while(*str && *str!=' ' && *str!='\n' && found==0){ - if (*str == *kp){ - kp++; - if (*kp == '\0'){ - str++; - kp=key; - if (*str == '='){ - found=1; - } - } - }else{ - kp=key; - } - str++; +uint8_t find_key_val(char *str, char *strbuf, uint8_t maxlen, char *key) { + uint8_t found = 0; + uint8_t i = 0; + char *kp = key; + + // Find the key in the string + while (*str && *str != ' ' && *str != '\n' && !found) { + if (*str == *kp) { + kp++; + if (*kp == '\0' && *(str + 1) == '=') { + found = 1; + str += 2; // Move past '=' + } + } else { + kp = key; } - if (found==1){ - // copy the value to a buffer and terminate it with '\0' - while(*str && *str!=' ' && *str!='\n' && *str!='&' && i= '0' && c <='9'){ - return((unsigned char)c - '0'); - } - if (c >= 'a' && c <='f'){ - return((unsigned char)c - 'a' + 10); - } - if (c >= 'A' && c <='F'){ - return((unsigned char)c - 'A' + 10); - } - return(0); +// Convert a single hex digit character to its integer value +unsigned char h2int(char c) { + if (c >= '0' && c <= '9') { + return (unsigned char)(c - '0'); + } else if (c >= 'a' && c <= 'f') { + return (unsigned char)(c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + return (unsigned char)(c - 'A' + 10); + } else { + return 0; + } } -// decode a url string e.g "hello%20joe" or "hello+joe" becomes "hello joe" -void urldecode(char *urlbuf) -{ - char c; - char *dst; - dst=urlbuf; - while ((c = *urlbuf)) { - if (c == '+') c = ' '; - if (c == '%') { - urlbuf++; - c = *urlbuf; - urlbuf++; - c = (h2int(c) << 4) | h2int(*urlbuf); - } - *dst = c; - dst++; - urlbuf++; + +// Decode a URL string, e.g., "hello%20joe" or "hello+joe" becomes "hello joe" +void urldecode(char *urlbuf) { + char c; + char *dst = urlbuf; + + while ((c = *urlbuf)) { + if (c == '+') { + c = ' '; + } else if (c == '%') { + urlbuf++; + char high = *urlbuf++; + char low = *urlbuf; + c = (h2int(high) << 4) | h2int(low); } - *dst = '\0'; + *dst++ = c; + urlbuf++; + } + *dst = '\0'; } - #endif // FROMDECODE_websrv_help #ifdef URLENCODE_websrv_help +// Convert a single character to a 2-digit hex string +// A terminating '\0' is added +void int2h(char c, char *hstr) { + // Convert the lower 4 bits + hstr[1] = (c & 0xF) + '0'; + if ((c & 0xF) > 9) { + hstr[1] = (c & 0xF) - 10 + 'a'; + } -// convert a single character to a 2 digit hex str -// a terminating '\0' is added -void int2h(char c, char *hstr) -{ - hstr[1]=(c & 0xf)+'0'; - if ((c & 0xf) >9){ - hstr[1]=(c & 0xf) - 10 + 'a'; - } - c=(c>>4)&0xf; - hstr[0]=c+'0'; - if (c > 9){ - hstr[0]=c - 10 + 'a'; - } - hstr[2]='\0'; + // Convert the upper 4 bits + c = (c >> 4) & 0xF; + hstr[0] = c + '0'; + if (c > 9) { + hstr[0] = c - 10 + 'a'; + } + + // Null-terminate the string + hstr[2] = '\0'; } -// there must be enoug space in urlbuf. In the worst case that is -// 3 times the length of str -void urlencode(char *str,char *urlbuf) -{ - char c; - while ((c = *str)) { - if (c == ' '||isalnum((unsigned char)c)){ - if (c == ' '){ - c = '+'; - } - *urlbuf=c; - str++; - urlbuf++; - continue; - } - *urlbuf='%'; - urlbuf++; - int2h(c,urlbuf); - urlbuf++; - urlbuf++; - str++; +// There must be enough space in urlbuf. In the worst case, that is +// 3 times the length of str. +void urlencode(char *str, char *urlbuf) { + char c; + while ((c = *str)) { + if (c == ' ' || isalnum((unsigned char)c)) { + if (c == ' ') { + c = '+'; + } + *urlbuf++ = c; + } else { + *urlbuf++ = '%'; + int2h(c, urlbuf); + urlbuf += 2; } - *urlbuf='\0'; + str++; + } + *urlbuf = '\0'; } - #endif // URLENCODE_websrv_help +// Parse a string and extract the IP to bytestr +uint8_t parse_ip(uint8_t *bytestr, char *str) { + char *sptr = NULL; + uint8_t i = 0; -// parse a string an extract the IP to bytestr -uint8_t parse_ip(uint8_t *bytestr,char *str) -{ - char *sptr; - uint8_t i=0; - sptr=NULL; - while(i<4){ - bytestr[i]=0; - i++; - } - i=0; - while(*str && i<4){ - // if a number then start - if (sptr==NULL && isdigit((unsigned char)*str)){ - sptr=str; - } - if (*str == '.'){ - *str ='\0'; - bytestr[i]=(atoi(sptr)&0xff); - i++; - sptr=NULL; - } - str++; + // Initialize bytestr to zero + for (i = 0; i < 4; i++) { + bytestr[i] = 0; + } + + i = 0; + while (*str && i < 4) { + if (sptr == NULL && isdigit((unsigned char)*str)) { + sptr = str; } - *str ='\0'; - if (i==3){ - bytestr[i]=(atoi(sptr)&0xff); - return(0); + if (*str == '.') { + *str = '\0'; + bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); + i++; + sptr = NULL; } - return(1); + str++; + } + + if (i == 3 && sptr != NULL) { + bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); + return 0; + } + return 1; } -// take a byte string and convert it to a human readable display string (base is 10 for ip and 16 for mac addr), len is 4 for IP addr and 6 for mac. -void mk_net_str(char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base) -{ - uint8_t i=0; - uint8_t j=0; +// Convert a byte string to a human-readable display string +// (base is 10 for IP and 16 for MAC address), len is 4 for IP address and 6 for MAC. +void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base) { + uint8_t i, j = 0; + + for (i = 0; i < len; i++) { if (base == 10) { - while(i Date: Fri, 7 Jun 2024 18:37:01 +0200 Subject: [PATCH 23/27] udplogs --- inc/LogManager.h | 18 ++++++++++++ src/LogManager.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 inc/LogManager.h create mode 100644 src/LogManager.c diff --git a/inc/LogManager.h b/inc/LogManager.h new file mode 100644 index 0000000..d7ff4f5 --- /dev/null +++ b/inc/LogManager.h @@ -0,0 +1,18 @@ +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include "defines.h" + +// Enumeration for log types +typedef enum { + LOG_UDP, + LOG_WEB, + LOG_UART + // Add more log types as needed +} LogType; + +// Function prototypes +void logMessage(LogType type, const char *message); +void initLogManager(LogType type); + +#endif /* LOGMANAGER_H */ diff --git a/src/LogManager.c b/src/LogManager.c new file mode 100644 index 0000000..5a73f6b --- /dev/null +++ b/src/LogManager.c @@ -0,0 +1,75 @@ +#include "LogManager.h" +#include "EtherShield.h" // Assuming this is where udplog2 is declared + + +#if ETHERSHIELD_DEBUG +void ethershieldDebug(char *message) { + printf("%s\r\n", message); + udpLog2("ETHERSHIELD_DEBUG", message); +} +#endif + +void udpLog2(char* alerte, char* text) { + static uint8_t udp[500]; + char data[450]; + snprintf(data, 450, "Project %s - %s", alerte, text); + send_udp(udp, data, strlen(data), sport, dip, dport); +} + +#define MAX_LOGS 50 +#define LOG_LENGTH 80 + +char logs[MAX_LOGS][LOG_LENGTH]; +int log_index = 0; + +// Fonction pour ajouter un log +void add_log(const char *log) { + strncpy(logs[log_index], log, LOG_LENGTH); + log_index = (log_index + 1) % MAX_LOGS; +} + +// Fonction pour récupérer les logs en format HTML +void get_logs(char *buffer, size_t size) { + size_t offset = 0; + for (int i = 0; i < MAX_LOGS; i++) { + int index = (log_index + i) % MAX_LOGS; + offset += snprintf(buffer + offset, size - offset, "%s\n", logs[index]); + if (offset >= size) break; + } +} + +// Function to log messages based on log type +void logMessage(LogType type, const char *message) { + switch (type) { + case LOG_UDP: + udplog2("Log", message); + break; + case LOG_WEB: + // Implement web logging + break; + case LOG_UART: + // Implement UART logging + break; + // Add more cases as needed + default: + break; + } +} + +// Function to initialize the log manager for a specific log type +void initLogManager(LogType type) { + switch (type) { + case LOG_UDP: + // Initialize UDP logging if needed + break; + case LOG_WEB: + // Initialize web logging if needed + break; + case LOG_UART: + // Initialize UART logging if needed + break; + // Add more cases as needed + default: + break; + } +} From 1b285a50b1e3770b710b3449f535d346071f6aae Mon Sep 17 00:00:00 2001 From: DtNeo Date: Mon, 26 Aug 2024 21:10:28 +0200 Subject: [PATCH 24/27] This commit is specific for timers, 2 files (timers.c and timers.h). Timer4 is use for renew the network connection. --- inc/timers.h | 23 +++++++++++++++++++ src/timers.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 inc/timers.h create mode 100644 src/timers.c diff --git a/inc/timers.h b/inc/timers.h new file mode 100644 index 0000000..466c627 --- /dev/null +++ b/inc/timers.h @@ -0,0 +1,23 @@ +/* + * timers.h + * + * Created on: May 30, 2024 + * Author: dtneo + */ + +#ifndef TIMERS_H +#define TIMERS_H + +#include "main.h" + +extern TIM_HandleTypeDef htim4; + + +extern uint8_t TIM4Flag; // Flag for renew the networt + +void timerLog(TIM_HandleTypeDef *htim); +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); +void generalTimersInit(); + + +#endif // TIMERS_H diff --git a/src/timers.c b/src/timers.c new file mode 100644 index 0000000..d7f2074 --- /dev/null +++ b/src/timers.c @@ -0,0 +1,63 @@ +/* + * timers.c + * + * Created on: Aug 7, 2024 + * Author: dtneo + */ + +#include "timers.h" +#include +#include "LogManager.h" + +// Counter for the number of timer interrupts +static uint32_t int_counterTIM4 = 0; + +_Bool isInitializationDone = false; + +const char* getTimerName(TIM_TypeDef *instance) { + if (instance == TIM4) return "Timer 4"; + return "Unknown Timer"; +} + +void timerLog(TIM_HandleTypeDef *htim) { + const char* timerName = getTimerName(htim->Instance); + char logMessage[128]; + sprintf(logMessage, "%s, Counter=%lu, Period=%lu", timerName, (unsigned long)__HAL_TIM_GET_COUNTER(htim), (unsigned long)htim->Init.Period); + udpLog2("Initialisation_Timers", logMessage); +} + +void timerInit(TIM_HandleTypeDef *htim) { + HAL_TIM_Base_Start_IT(htim); + HAL_TIM_Base_Stop_IT(htim); + __HAL_TIM_SET_COUNTER(htim, 0); + timerLog(htim); +} + +void generalTimersInit() { + TIM4Flag = 0; + + timerInit(&htim4); + + isInitializationDone = true; + + // Timers initialisation is done, now active them needed permanently. + HAL_TIM_Base_Start_IT(&htim4); +} + +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { + + if (!isInitializationDone) { + return; // no - the initialization of timer is not done - we skip this function + } + + if (htim->Instance == TIM4) { + HAL_TIM_Base_Stop_IT(htim); + int_counterTIM4++; + if (int_counterTIM4 >= 10) // + { + TIM4Flag = 1; + int_counterTIM4 = 0; + } + HAL_TIM_Base_Start_IT(htim); + } +} From 95993a4e4bdced4160d302192a9de1391924cd2a Mon Sep 17 00:00:00 2001 From: DtNeo Date: Mon, 26 Aug 2024 21:00:11 +0200 Subject: [PATCH 25/27] shouldn't be this big commit with changes inside files. I changed folders name ./src -> ./Src && ./insc -> ./Inc --- {inc => Inc}/EtherShield.h | 31 +- {inc => Inc}/LogManager.h | 5 +- {inc => Inc}/RTC.h | 3 +- Inc/defines.h | 86 ++ {inc => Inc}/dhcp.h | 5 +- {inc => Inc}/dnslkup.h | 28 +- {inc => Inc}/enc28j60.h | 14 +- {inc => Inc}/error_handler.h | 0 Inc/ip_arp_udp_tcp.h | 58 + Inc/net.h | 260 +++++ Inc/ntp.h | 20 + Inc/packet.h | 29 + Inc/tcp.h | 70 ++ {inc => Inc}/websrv_help_functions.h | 14 +- README.md | 2 +- {src => Src}/EtherShield.c | 63 +- {src => Src}/LogManager.c | 16 +- {src => Src}/RTC.c | 3 +- {src => Src}/dhcp.c | 60 +- {src => Src}/dnslkup.c | 84 +- {src => Src}/enc28j60.c | 0 {src => Src}/error_handler.c | 0 Src/ip_arp_udp_tcp.c | 338 ++++++ Src/net.c | 187 ++++ Src/ntp.c | 147 +++ Src/packet.c | 229 ++++ Src/tcp.c | 700 ++++++++++++ {src => Src}/timers.c | 1 + Src/websrv_help_functions.c | 365 +++++++ inc/UDPCommandHandler.h | 38 - inc/ip_arp_udp_tcp.h | 173 --- inc/net.h | 193 ---- inc/stm32includes.h | 8 - inc/timers.h | 3 - src/UDPCommandHandler.c | 49 - src/ip_arp_udp_tcp.c | 1498 -------------------------- src/websrv_help_functions.c | 176 --- 37 files changed, 2720 insertions(+), 2236 deletions(-) rename {inc => Inc}/EtherShield.h (88%) rename {inc => Inc}/LogManager.h (71%) rename {inc => Inc}/RTC.h (93%) create mode 100644 Inc/defines.h rename {inc => Inc}/dhcp.h (72%) rename {inc => Inc}/dnslkup.h (66%) rename {inc => Inc}/enc28j60.h (96%) rename {inc => Inc}/error_handler.h (100%) create mode 100644 Inc/ip_arp_udp_tcp.h create mode 100644 Inc/net.h create mode 100644 Inc/ntp.h create mode 100644 Inc/packet.h create mode 100644 Inc/tcp.h rename {inc => Inc}/websrv_help_functions.h (72%) rename {src => Src}/EtherShield.c (87%) rename {src => Src}/LogManager.c (84%) rename {src => Src}/RTC.c (98%) rename {src => Src}/dhcp.c (85%) rename {src => Src}/dnslkup.c (72%) rename {src => Src}/enc28j60.c (100%) rename {src => Src}/error_handler.c (100%) create mode 100644 Src/ip_arp_udp_tcp.c create mode 100644 Src/net.c create mode 100644 Src/ntp.c create mode 100644 Src/packet.c create mode 100644 Src/tcp.c rename {src => Src}/timers.c (98%) create mode 100644 Src/websrv_help_functions.c delete mode 100644 inc/UDPCommandHandler.h delete mode 100644 inc/ip_arp_udp_tcp.h delete mode 100644 inc/net.h delete mode 100644 inc/stm32includes.h delete mode 100644 src/UDPCommandHandler.c delete mode 100644 src/ip_arp_udp_tcp.c delete mode 100644 src/websrv_help_functions.c diff --git a/inc/EtherShield.h b/Inc/EtherShield.h similarity index 88% rename from inc/EtherShield.h rename to Inc/EtherShield.h index fac0322..a7480db 100644 --- a/inc/EtherShield.h +++ b/Inc/EtherShield.h @@ -1,31 +1,27 @@ /* - EHTERSHIELD_H library for Arduino etherShield - Copyright (c) 2008 Xing Yu. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + * EtherShield.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ #ifndef ETHERSHIELD_H #define ETHERSHIELD_H +#include "main.h" #include "defines.h" #include "enc28j60.h" #include "net.h" void ES_FullConnection(); +void ES_RenewCo(); void ES_ProcessWebPacket(); +void ES_ProcessWebPacketFilter(); +void ES_init_network(); void ES_enc28j60SpiInit( SPI_HandleTypeDef *hspi ); void ES_enc28j60Init( uint8_t* macaddr); void ES_enc28j60clkout(uint8_t clk); @@ -71,7 +67,6 @@ uint16_t ES_packetloop_icmp_tcp(uint8_t *buf,uint16_t plen); // functions to fill the web pages with data: //uint16_t ES_fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s); uint16_t ES_fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s); -uint16_t ES_fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len ); // send data from the web server to the client: void ES_www_server_reply(uint8_t *buf,uint16_t dlen); diff --git a/inc/LogManager.h b/Inc/LogManager.h similarity index 71% rename from inc/LogManager.h rename to Inc/LogManager.h index d7ff4f5..28eb8d1 100644 --- a/inc/LogManager.h +++ b/Inc/LogManager.h @@ -1,7 +1,7 @@ #ifndef LOGMANAGER_H #define LOGMANAGER_H -#include "defines.h" +#include "main.h" // Enumeration for log types typedef enum { @@ -12,6 +12,9 @@ typedef enum { } LogType; // Function prototypes +void ethershieldDebug(char *message); +void udpLog2(char* alerte, char* text); +void add_log(const char *log); void logMessage(LogType type, const char *message); void initLogManager(LogType type); diff --git a/inc/RTC.h b/Inc/RTC.h similarity index 93% rename from inc/RTC.h rename to Inc/RTC.h index b9a7b26..64fd1a1 100644 --- a/inc/RTC.h +++ b/Inc/RTC.h @@ -9,7 +9,8 @@ #define INC_RTC_H_ #include "main.h" -#include "defines.h" +//#include "defines.h" + typedef struct { uint8_t day; diff --git a/Inc/defines.h b/Inc/defines.h new file mode 100644 index 0000000..64a922a --- /dev/null +++ b/Inc/defines.h @@ -0,0 +1,86 @@ +/* + * defines.h + * + * Created on: May 30, 2024 + * Author: dtneo + */ + +#ifndef INC_DEFINES_H_ +#define INC_DEFINESE_H_ + +#include "main.h" + +// Comment only one of this two. Depend of your SPI configuration. +// CS_only is simple (no option from SPI configuration). +// NSS is better. configuration details in enc28J60.h +//#define CS_Only +#define NSS_OutputSignal + +// # CS SECTION +#ifdef CS_Only +#define ETHERNET_LED_GPIO GPIOC +#define ETHERNET_LED_PIN GPIO_PIN_13 +#define ETHERNET_CS_GPIO GPIOA +#define ETHERNET_CS_PIN GPIO_PIN_4 +#define ETHERNET_CS_DELAY 10 // Start with 2, then 1 and 0. 1000 is an other step. if it work try 10, could be Unstable but fast. +#endif + +// # Parameters SECTION +// Comment or not depend on your need below : +#define ETHERSHIELD_DEBUG 1 +#define NTP_client +#define DNS_client +#define DHCP_client +#define HOSTNAME "STM32_ENC28J60" +#define DHCP_HOSTNAME_LEN 14 // here the len of the HOSTNAME define just before +#define DHCP_HOSTNAME "STM32_ENC28J60" +#define PING_client +//#define PINGPATTERN 0x42 (element needed in net.c - is it very usefull ? +#define TCP_client +#define WWW_client //uncoment next one too +#define WWW_USER_AGENT "MyUserAgent" +#define FROMDECODE_websrv_help +#define URLENCODE_websrv_help + + + +//extern volatile uint8_t dma_isr_called; + + +// Define this in defines.c file +extern uint8_t macaddrin[6]; +extern uint8_t ipaddrin[4]; +extern uint8_t maskin[4]; +extern uint8_t gwipin[4]; +extern uint8_t dhcpsvrin[4]; +extern uint8_t dnssvrin[4]; +extern uint8_t dnsipaddr[4]; +extern uint8_t ntpip[4]; +extern char domainName[]; +extern uint8_t dest_ip[4]; +extern uint16_t dest_port; +extern uint16_t srcport; +extern uint8_t dstport_h; +extern uint8_t dstport_l; +extern uint8_t dip[]; +extern uint16_t dport; +extern uint16_t sport; + +// Define the structure for command mapping +typedef void (*CommandFunction)(); +typedef void (*CommandFunctionWithArg)(char*); + +typedef struct { + const char *command; + CommandFunction function; + CommandFunctionWithArg function_with_arg; +} CommandMapping; + +// Declare the command table +extern CommandMapping commandTable[]; + +// Declare command functions here +void sendSTM32State(); // Function to send the STM32 state +void sendRTCDateTime(); // Function to send the RTC date and time + +#endif /* INC_DEFINES_H_ */ diff --git a/inc/dhcp.h b/Inc/dhcp.h similarity index 72% rename from inc/dhcp.h rename to Inc/dhcp.h index f1d2a77..aeea350 100644 --- a/inc/dhcp.h +++ b/Inc/dhcp.h @@ -12,9 +12,10 @@ #ifndef DHCP_H #define DHCP_H +#include "main.h" #include "defines.h" -extern void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, +extern void dhcp_start(uint8_t *buf, uint8_t *macaddrDCPin, uint8_t *ipaddrin, uint8_t *maskin, uint8_t *gwipin, uint8_t *dhcpsvrin, uint8_t *dnssvrin ); @@ -25,4 +26,6 @@ uint8_t check_for_dhcp_answer(uint8_t *buf,uint16_t plen); uint8_t have_dhcpoffer(uint8_t *buf,uint16_t plen); uint8_t have_dhcpack(uint8_t *buf,uint16_t plen); +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ); + #endif /* DHCP_H */ diff --git a/inc/dnslkup.h b/Inc/dnslkup.h similarity index 66% rename from inc/dnslkup.h rename to Inc/dnslkup.h index 861f4f9..5d45a94 100644 --- a/inc/dnslkup.h +++ b/Inc/dnslkup.h @@ -1,21 +1,19 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 +/* + * dnslkup.h * - * DNS look-up functions based on the udp client + * Created on: Jun 4, 2024 + * Author: dtneo * - * Chip type : ATMEGA88/ATMEGA168/ATMEGA328p with ENC28J60 - *********************************************/ -//@{ + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #ifndef DNSLKUP_H #define DNSLKUP_H -#include "defines.h" - -// to use this you need to enable UDP_client in the file defines.h -#if defined (UDP_client) +#include "main.h" +//#include "defines.h" // look-up a hostname (you should check client_waiting_gw() before calling this function): extern void dnslkup_request(uint8_t *buf, uint8_t *hostname); @@ -35,6 +33,6 @@ extern uint8_t *dnslkup_getip(void); // set DNS server to be used for lookups. extern void dnslkup_set_dnsip(uint8_t *dnsipaddr); -#endif /* UDP_client */ +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ); + #endif /* DNSLKUP_H */ -//@} diff --git a/inc/enc28j60.h b/Inc/enc28j60.h similarity index 96% rename from inc/enc28j60.h rename to Inc/enc28j60.h index f16c705..41fcd4e 100644 --- a/inc/enc28j60.h +++ b/Inc/enc28j60.h @@ -12,7 +12,7 @@ #ifndef __ENC28J60_H #define __ENC28J60_H -#include "defines.h" +#include "main.h" #ifdef CS_Only #if ETHERNET_CS_DELAY >= 10 @@ -31,7 +31,7 @@ #ifdef NSS_OutputSignal #define disableChip __HAL_SPI_DISABLE(hspi); -#define enableChip __HAL_SPI_ENABLE(hspi); +#define enableChip; #endif // ENC28J60 Control Registers @@ -258,16 +258,16 @@ // the entire available packet buffer space is allocated // // start with RX buf at 0/ -#define RXSTART_INIT 0x0 +#define RXSTART_INIT 0x0 // RX buffer end -#define RXSTOP_INIT (0x1FFF-0x0600-1) +#define RXSTOP_INIT (0x1FFF-0x0600-1) // start TX buffer at 0x1FFF-0x0600, place for one full ethernet frame (~1500 bytes) -#define TXSTART_INIT (0x1FFF-0x0600) +#define TXSTART_INIT (0x1FFF-0x0600) // stop TX buffer at end of mem -#define TXSTOP_INIT 0x1FFF +#define TXSTOP_INIT 0x1FFF // max frame length which the controller will accept: -#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) +#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) //#define MAX_FRAMELEN 600 void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new); diff --git a/inc/error_handler.h b/Inc/error_handler.h similarity index 100% rename from inc/error_handler.h rename to Inc/error_handler.h diff --git a/Inc/ip_arp_udp_tcp.h b/Inc/ip_arp_udp_tcp.h new file mode 100644 index 0000000..6281c72 --- /dev/null +++ b/Inc/ip_arp_udp_tcp.h @@ -0,0 +1,58 @@ +/* + * ip_arp_udp_tcp.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#ifndef IP_ARP_UDP_TCP_H +#define IP_ARP_UDP_TCP_H + +#include "main.h" + +// -- web server functions -- +// you must call this function once before you use any of the other server functions: +void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port); +// for a UDP server: +uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len); +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); +void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); + +void make_arp_answer_from_request(uint8_t *buf); +void init_len_info(uint8_t *buf); + +void fill_buf_p(uint8_t *buf, uint16_t len, const char *s); +void fill_ip_hdr_checksum(uint8_t *buf); +uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type); + +// -- client functions -- +#if defined (WWW_client) || defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) +uint8_t client_store_gw_mac(uint8_t *buf); +void client_set_gwip(uint8_t *gwipaddr); +void client_gw_arp_refresh(void); +void client_arp_whohas(uint8_t *buf,uint8_t *gwipaddr); +uint8_t client_waiting_gw(void); // 1 no GW mac yet, 0 have a gw mac + +//#if defined (WWW_client) || defined (TCP_client) +//#define client_set_wwwip client_tcp_set_serverip +// set the ip of the next tcp session that we open to a server +//#endif +#endif // WWW_client TCP_client etc + +#ifdef TCP_client +void tcp_client_send_packet(uint8_t *buf,uint16_t dest_port, uint16_t src_port, uint8_t flags, uint8_t max_segment_size, + uint8_t clear_seqck, uint16_t next_ack_num, uint16_t dlength, uint8_t *dest_mac, uint8_t *dest_ip); +uint16_t tcp_get_dlength ( uint8_t *buf ); + +#endif // TCP_client + +void send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport); +void send_udp_transmit(uint8_t *buf,uint16_t datalen); +void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport); +#endif /* IP_ARP_UDP_TCP_H */ +//@} diff --git a/Inc/net.h b/Inc/net.h new file mode 100644 index 0000000..d429915 --- /dev/null +++ b/Inc/net.h @@ -0,0 +1,260 @@ +/* + * net.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#ifndef NET_H +#define NET_H + +#include +#include +#include +#include +#include + + +#include "main.h" + +#if defined(STM32F0) +/*# if STM32F091xC +# include "stm32f091xc.h" +# endif*/ +# include "stm32f0xx.h" +# include "stm32f0xx_hal_def.h" +# include "stm32f0xx_hal_spi.h" +#elif defined(STM32F1) +//# include "stm32f103xb.h" +# include "stm32f1xx.h" +# include "stm32f1xx_hal_def.h" +# include "stm32f1xx_hal_spi.h" +#elif defined(STM32F4) +# include "stm32f4xx.h" +# include "stm32f4xx_hal.h" +# include "stm32f4xx_hal_gpio.h" +# include "stm32f4xx_hal_def.h" +# include "stm32f4xx_hal_spi.h" +#endif + + +// Ethernet Header +#define ETH_HEADER_LEN 14 +#define ETHTYPE_ARP_H_V 0x08 +#define ETHTYPE_ARP_L_V 0x06 +#define ETHTYPE_IP_H_V 0x08 +#define ETHTYPE_IP_L_V 0x00 +#define ETH_TYPE_H_P 12 +#define ETH_TYPE_L_P 13 +#define ETH_DST_MAC 0 +#define ETH_SRC_MAC 6 +// ARP Header +#define ETH_ARP_OPCODE_REPLY_H_V 0x0 +#define ETH_ARP_OPCODE_REPLY_L_V 0x02 +#define ETH_ARP_OPCODE_REQ_H_V 0x0 +#define ETH_ARP_OPCODE_REQ_L_V 0x01 +#define ETH_ARP_P 0xe +#define ETH_ARP_DST_IP_P 0x26 +#define ETH_ARP_OPCODE_H_P 0x14 +#define ETH_ARP_OPCODE_L_P 0x15 +#define ETH_ARP_SRC_MAC_P 0x16 +#define ETH_ARP_SRC_IP_P 0x1c +#define ETH_ARP_DST_MAC_P 0x20 +#define ETH_ARP_DST_IP_P 0x26 +// IP Header +#define IP_HEADER_LEN 20 +#define IP_PROTO_ICMP_V 0x01 +#define IP_PROTO_TCP 0x06 // Protocol number for TCP +#define IP_PROTO_TCP_V 0x06 +#define IP_PROTO_UDP_V 0x11 +#define IP_V4_V 0x40 +#define IP_HEADER_LENGTH_V 0x05 +#define IP_P 0x0E +#define IP_HEADER_VER_LEN_P 0x0E +#define IP_TOS_P 0x0F +#define IP_TOTLEN_H_P 0x10 +#define IP_TOTLEN_L_P 0x11 +#define IP_ID_H_P 0x12 +#define IP_ID_L_P 0x13 +#define IP_FLAGS_H_P 0x14 +#define IP_FLAGS_L_P 0x15 +#define IP_TTL_P 0x16 +#define IP_PROTO_P 0x17 +#define IP_CHECKSUM_H_P 0x18 +#define IP_CHECKSUM_L_P 0x19 +#define IP_SRC_IP_P 0x1A +#define IP_DST_IP_P 0x1E +#define IP_HEADER_LEN_VER_P 0xe +// ICMP Header +#define ICMP_TYPE_ECHOREPLY_V 0 +#define ICMP_TYPE_ECHOREQUEST_V 8 +#define ICMP_TYPE_P 0x22 +#define ICMP_CHECKSUM_H_P 0x24 +#define ICMP_CHECKSUM_L_P 0x25 +#define ICMP_IDENT_H_P 0x26 +#define ICMP_IDENT_L_P 0x27 +#define ICMP_DATA_P 0x2a +// UDP Header +#define UDP_HEADER_LEN 8 +#define UDP_SRC_PORT_H_P 0x22 +#define UDP_SRC_PORT_L_P 0x23 +#define UDP_DST_PORT_H_P 0x24 +#define UDP_DST_PORT_L_P 0x25 +#define UDP_LEN_H_P 0x26 +#define UDP_LEN_L_P 0x27 +#define UDP_CHECKSUM_H_P 0x28 +#define UDP_CHECKSUM_L_P 0x29 +#define UDP_DATA_P 0x2a +// TCP Header +#define TCP_STATE_CLOSED 0 +#define TCP_STATE_SYN_SENT 1 +#define TCP_STATE_SYN_RECEIVED 2 +#define TCP_STATE_ESTABLISHED 3 +#define TCP_STATE_FIN_WAIT_1 4 +#define TCP_STATE_FIN_WAIT_2 5 +#define TCP_STATE_CLOSE_WAIT 6 +#define TCP_STATE_CLOSING 7 +#define TCP_STATE_LAST_ACK 8 +#define TCP_STATE_TIME_WAIT 9 +#define TCP_FLAGS_FIN_V 0x01 +#define TCP_FLAGS_SYN_V 0x02 +#define TCP_FLAGS_RST_V 0x04 +#define TCP_FLAGS_PUSH_V 0x08 +#define TCP_FLAGS_ACK_V 0x10 +#define TCP_FLAGS_SYNACK_V 0x12 +#define TCP_FLAGS_PSHACK_V 0x18 +#define TCP_SRC_PORT_H_P 0x22 +#define TCP_SRC_PORT_L_P 0x23 +#define TCP_DST_PORT_H_P 0x24 +#define TCP_DST_PORT_L_P 0x25 +#define TCP_SEQ_P 0x26 +#define TCP_SEQ_H_P 0x26 +#define TCP_SEQ_L_P (TCP_SEQ_H_P + 1) +#define TCP_SEQACK_P 0x2a +#define TCP_SEQACK_H_P 0x2a +#define TCP_FLAGS_P 0x2f +#define TCP_WINDOWSIZE_H_P 0x30 +#define TCP_WINDOWSIZE_L_P 0x31 +#define TCP_CHECKSUM_H_P 0x32 +#define TCP_CHECKSUM_L_P 0x33 +#define TCP_URGENT_PTR_H_P 0x34 +#define TCP_URGENT_PTR_L_P 0x35 +#define TCP_OPTIONS_P 0x36 +#define TCP_DATA_P 0x36 +#define TCP_ACK_H_P 0x2C +#define TCP_ACK_L_P (TCP_ACK_H_P + 1) +#define TCP_HEADER_LEN_PLAIN 20 +#define TCP_HEADER_LEN_P 0x2e +#define TCP_WIN_SIZE 0x30 +#define TCP_LEN_H_P 0x2C // Position of TCP length high byte +#define TCP_LEN_L_P 0x2D // Position of TCP length low byte +// DNS +#define DNSCLIENT_SRC_PORT_H 0xe0 + + +//TCP personalyse ? +#define TCPCLIENT_SRC_PORT_H 11 +#define WGW_INITIAL_ARP 1 +#define WGW_HAVE_GW_MAC 2 +#define WGW_REFRESHING 4 +#define WGW_ACCEPT_ARP_REPLY 8 +#define CLIENTMSS 550 +#define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) + +// DNS States for access in applications +#define DNS_STATE_INIT 0 +#define DNS_STATE_REQUESTED 1 +#define DNS_STATE_ANSWER 2 +// DHCP States for access in applications +#define DHCP_STATE_INIT 0 +#define DHCP_STATE_DISCOVER 1 +#define DHCP_STATE_OFFER 2 +#define DHCP_STATE_REQUEST 3 +#define DHCP_STATE_ACK 4 +#define DHCP_STATE_OK 5 +#define DHCP_STATE_RENEW 6 + +#define bool _Bool +#define true 1 +#define false 0 + +#define PINGPATTERN 0x42 + +extern uint16_t len; +extern uint16_t plen; + +// Web server port, used when implementing webserver +extern uint8_t wwwport_l; // server port +extern uint8_t wwwport_h; // Note: never use same as TCPCLIENT_SRC_PORT_H + +extern uint8_t www_fd; +extern uint8_t browsertype; // 0 = get, 1 = post +extern char *client_additionalheaderline; +extern char *client_method; // for POST or PUT, no trailing space needed +extern char *client_urlbuf; +extern char *client_hoststr; +extern char *client_postval; +extern char *client_urlbuf_var; +extern uint8_t *bufptr; // ugly workaround for backward compatibility + +// just lower byte, the upper byte is TCPCLIENT_SRC_PORT_H: +extern uint8_t tcpclient_src_port_l; +extern uint8_t tcp_fd; // a file descriptor, will be encoded into the port +extern uint8_t tcp_client_state; +// TCP client Destination port +extern uint8_t tcp_client_port_h; +extern uint8_t tcp_client_port_l; + +#define MAX_TCP_CONNECTIONS 10 + +typedef struct { + uint8_t state; + uint32_t seq_num; + uint32_t ack_num; + uint8_t remote_ip[4]; + uint16_t remote_port; + uint16_t local_port; +} tcp_connection_t; + +extern tcp_connection_t tcp_connections[MAX_TCP_CONNECTIONS]; + + +extern int16_t delaycnt; +extern uint8_t gwip[4]; +extern uint8_t gwmacaddr[6]; +extern uint8_t tcpsrvip[4]; +extern uint8_t macaddr[6]; +extern uint8_t ipaddr[4]; +extern uint16_t info_data_len; +extern uint16_t info_hdr_len; +extern uint8_t seqnum; +extern const char arpreqhdr[]; +extern const char iphdr[]; +extern const char ntpreqhdr[]; + +extern uint8_t waitgwmac; + + +extern void (*icmp_callback)(uint8_t *ip); + + +// TCP functions on packet.c +extern uint8_t send_fin; +extern uint16_t tcpstart; +extern uint16_t save_len; + + +void client_icmp_request(uint8_t *buf,uint8_t *destip); +void register_ping_rec_callback(void (*callback)(uint8_t *srcip)); +uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost); +void make_echo_reply_from_request(uint8_t *buf,uint16_t len); +void __attribute__((weak)) pingCallback(void); + +void send_wol(uint8_t *buf,uint8_t *wolmac); + + +#endif diff --git a/Inc/ntp.h b/Inc/ntp.h new file mode 100644 index 0000000..b5d9143 --- /dev/null +++ b/Inc/ntp.h @@ -0,0 +1,20 @@ +/* + * ntp.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ +#ifndef NTP_H +#define NTP_H + +#include "main.h" + +void client_ntp_request(uint8_t *buf, uint8_t *ntpip, uint8_t srcport); +void client_ntp_process_answer(uint8_t *buf, uint16_t plen); + + +#endif diff --git a/Inc/packet.h b/Inc/packet.h new file mode 100644 index 0000000..1ff3b97 --- /dev/null +++ b/Inc/packet.h @@ -0,0 +1,29 @@ +/* + * packet.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#ifndef PACKET_H +#define PACKET_H + +#include "main.h" +#include "defines.h" +#include "net.h" + + + +void handle_AllPacket(uint8_t *buf, uint16_t len); + +void handle_icmp_packet(uint8_t *buf, uint16_t len); +void handle_tcp_response(uint8_t *buf); +void loop(); +void handle_tcp_packet(uint8_t *buf, uint16_t len); +void handle_udp_packet(uint8_t *buf, uint16_t len); +void handle_http_request(uint8_t *buf, uint16_t data_offset, uint16_t data_len, tcp_connection_t *tcp_conn); +#endif diff --git a/Inc/tcp.h b/Inc/tcp.h new file mode 100644 index 0000000..9716f8c --- /dev/null +++ b/Inc/tcp.h @@ -0,0 +1,70 @@ +/* + * tcp.h + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#ifndef TCP_H +#define TCP_H + +#include "main.h" +//#include "defines.h" +#include "net.h" + +// Callback function types for handling TCP results and data fill +typedef uint8_t (*tcp_result_callback_t)(uint8_t fd, uint8_t statuscode, uint16_t data_start_pos_in_buf, uint16_t len_of_data); +typedef uint16_t (*tcp_datafill_callback_t)(uint8_t fd); + +// Static variables to store the callbacks +extern tcp_result_callback_t client_tcp_result_callback; +extern tcp_datafill_callback_t client_tcp_datafill_callback; + +//-------------------------------------- STEP1 +void client_tcp_set_serverip(uint8_t *ipaddr); +void initialize_tcp_sequence_number(); +void init_tcp_connections(); +tcp_connection_t* find_free_connection(); +tcp_connection_t* find_connection(uint8_t *ip, uint16_t port); +//-------------------------------------- STEP2 +void tcp_open_connection(uint8_t *src_ip, uint16_t src_port, uint8_t *dst_ip, uint16_t dst_port); +void make_tcphead(uint8_t *buf, uint16_t len, uint8_t mss, uint8_t win_size); +static uint16_t TCP_htons(uint16_t hostshort); +void make_ip_tcp_new(uint8_t *buf, uint16_t len, uint8_t *dst_ip); +void create_tcp_synack_packet(uint8_t *buf, tcp_connection_t *tcp_conn); +void create_tcp_ack_packet(uint8_t *buf, tcp_connection_t *tcp_conn, uint8_t mss, uint8_t win_size); +void create_tcp_ack_with_data_packet(uint8_t *buf,uint16_t len); +//void tcp_close_connection(tcp_connection_t *conn); +//-------------------------------------- STEP3 +uint16_t get_tcp_data_len(uint8_t *buf); +uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len); +uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort); +void send_tcp_data(uint8_t *buf, uint16_t dlen); +void send_data_packet(uint8_t *buf, uint16_t srcPort, const uint8_t *data, uint16_t len); +void send_tcp_fin_packet(uint8_t *buf); +void tcp_send_packet(uint32_t seq_num, uint32_t ack_num); +//-------------------------------------- STEP4 +uint16_t prepare_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len); +void transmit_tcp_data(uint8_t *buf, uint16_t dlen); +//-------------------------------------- STEP6 +void tcp_handle_retransmissions(); +void tcp_state_machine(uint8_t *buf, uint16_t len); +//-------------------------------------- OTHERS +void client_syn(uint8_t *buf, uint8_t srcport, uint8_t dstport_h, uint8_t dstport_l); +void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen); + +uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port); + +void fill_ip_tcp_checksum(uint8_t *buf, uint16_t len); +void fill_tcp_ip_hdr_checksum(uint8_t *buf); +uint16_t calculate_tcp_checksum(uint8_t *buf, uint16_t len); +void send_tcp_syn_packet(uint8_t *buf, uint16_t srcPort, uint16_t dstPort, uint8_t *dst_ip); + +void log_ip_header(uint8_t *buf); +void log_tcp_header(uint8_t *buf); + +#endif diff --git a/inc/websrv_help_functions.h b/Inc/websrv_help_functions.h similarity index 72% rename from inc/websrv_help_functions.h rename to Inc/websrv_help_functions.h index 17c2c26..60daf16 100644 --- a/inc/websrv_help_functions.h +++ b/Inc/websrv_help_functions.h @@ -12,7 +12,17 @@ #ifndef WEBSRV_HELP_FUNCTIONS_H #define WEBSRV_HELP_FUNCTIONS_H -#include "defines.h" +#include "main.h" +#include "net.h" + + +uint16_t send_logs_page(uint8_t *buf); +uint16_t send_test_page(uint8_t *buf); +void www_server_reply(uint8_t *buf, uint16_t dlen, tcp_connection_t *tcp_conn); +uint16_t www_client_internal_datafill_callback(uint8_t fd); +uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data); +void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); +void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval,void (*callback)(uint8_t,uint16_t)); // Function declarations @@ -57,4 +67,6 @@ uint8_t parse_ip(uint8_t *bytestr, char *str); */ void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base); +void get_logs(char *buffer, size_t size); + #endif /* WEBSRV_HELP_FUNCTIONS_H */ diff --git a/README.md b/README.md index 469e992..8c8518d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ via html - STM32F103xB (v2 Tested only on sending UDP packets) - STM32F401VE (v2 Tested only on sending UDP packets) - STM32F407 (v3 complete network interface) -- STM32F411 (will be test current 2024 for v3 complete network interface) +- STM32F446 (v3 complete network interface) ## News on branch V3 // Contact of this changes @Dtneo diff --git a/src/EtherShield.c b/Src/EtherShield.c similarity index 87% rename from src/EtherShield.c rename to Src/EtherShield.c index 1460bf9..209fd76 100644 --- a/src/EtherShield.c +++ b/Src/EtherShield.c @@ -9,6 +9,14 @@ #define BUFFER_SIZE 400 +#define DHCP_BUFFER_SIZE 300 +#define DNS_BUFFER_SIZE 200 +#define NTP_BUFFER_SIZE 150 + +uint8_t bufDHCP[DHCP_BUFFER_SIZE]; +uint8_t bufDNS[DNS_BUFFER_SIZE]; +uint8_t bufNTP[NTP_BUFFER_SIZE]; + uint8_t hostname[] = "www.google.com"; // Function to initialize and establish a full connection @@ -28,33 +36,74 @@ void ES_FullConnection(SPI_HandleTypeDef *hspi) { enc28j60PhyWrite(PHLCON, 0x3476); // Allocate IP address via DHCP - uint8_t bufDHCP[BUFFER_SIZE]; allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); // Set the gateway IP for the client client_set_gwip(gwipin); // Resolve the DNS hostname - uint8_t bufDNS[BUFFER_SIZE]; dnslkup_request(bufDNS, domainName); udp_client_check_for_dns_answer(bufDNS, plen); dnslkup_set_dnsip(dnsipaddr); resolveHostname(bufDNS, BUFFER_SIZE, hostname); // Send NTP request to get the current time - uint8_t bufNTP[BUFFER_SIZE]; client_ntp_request(bufNTP, ntpip, 123); + + // Log the success of the full connection setup udpLog2("ES_FullConnection", "WORKS!"); + +} + +// Function to initialize and establish a full connection +void ES_RenewCo() { + // Allocate IP address via DHCP + allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); + + // Set the gateway IP for the client + client_set_gwip(gwipin); + + // Resolve the DNS hostname + dnslkup_request(bufDNS, domainName); + udp_client_check_for_dns_answer(bufDNS, plen); + dnslkup_set_dnsip(dnsipaddr); + resolveHostname(bufDNS, BUFFER_SIZE, hostname); + + // Send NTP request to get the current time + client_ntp_request(bufNTP, ntpip, 123); + + // Log the success of the full connection setup + udpLog2("ES_RenewCo", "WORKS!"); } // Function to process incoming web packets void ES_ProcessWebPacket() { static uint8_t packet[500]; - packetloop_icmp_tcp(packet, enc28j60PacketReceive(500, packet)); + handle_AllPacket(packet, enc28j60PacketReceive(500, packet)); } +// Function to process incoming web packets +void ES_ProcessWebPacketFilter() { +// enc28j60SetBank(ERXFCON); +// write_control_register(0x18, 0b10100001); +// enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); +// enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_PKTIF); + static uint8_t packet[500]; + uint16_t len = enc28j60PacketReceive(500, packet); + if (len > 0) { + handle_AllPacket(packet, len); + } + handle_AllPacket(packet, enc28j60PacketReceive(500, packet)); +} + + +void ES_init_network() { + init_ip_arp_udp_tcp(macaddrin, ipaddrin, 80); + initialize_tcp_sequence_number(); + init_tcp_connections(); +} /** * Initialise SPI, separate from main initialisation so that * multiple SPI devices can be used together @@ -223,10 +272,6 @@ uint16_t ES_fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s){ return fill_tcp_data(buf,pos, s); } -uint16_t ES_fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len ){ - return fill_tcp_data_len(buf,pos, s, len); -} - void ES_www_server_reply(uint8_t *buf,uint16_t dlen) { www_server_reply(buf,dlen); } @@ -305,11 +350,9 @@ uint8_t ES_packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) { } #endif // PING_client -#ifdef WOL_client void ES_send_wol(uint8_t *buf,uint8_t *wolmac) { send_wol(buf,wolmac); } -#endif // WOL_client #ifdef FROMDECODE_websrv_help diff --git a/src/LogManager.c b/Src/LogManager.c similarity index 84% rename from src/LogManager.c rename to Src/LogManager.c index 5a73f6b..3d98aaf 100644 --- a/src/LogManager.c +++ b/Src/LogManager.c @@ -29,14 +29,14 @@ void add_log(const char *log) { } // Fonction pour récupérer les logs en format HTML -void get_logs(char *buffer, size_t size) { - size_t offset = 0; - for (int i = 0; i < MAX_LOGS; i++) { - int index = (log_index + i) % MAX_LOGS; - offset += snprintf(buffer + offset, size - offset, "%s\n", logs[index]); - if (offset >= size) break; - } -} +//void get_logs(char *buffer, size_t size) { +// size_t offset = 0; +// for (int i = 0; i < MAX_LOGS; i++) { +// int index = (log_index + i) % MAX_LOGS; +// offset += snprintf(buffer + offset, size - offset, "%s\n", logs[index]); +// if (offset >= size) break; +// } +//} // Function to log messages based on log type void logMessage(LogType type, const char *message) { diff --git a/src/RTC.c b/Src/RTC.c similarity index 98% rename from src/RTC.c rename to Src/RTC.c index 6569d39..d895051 100644 --- a/src/RTC.c +++ b/Src/RTC.c @@ -1,4 +1,4 @@ -/* + /* * RTC.c * * Created on: Jun 3, 2024 @@ -9,7 +9,6 @@ * time from the RTC and formats it into a human-readable string for logging purposes. */ -#include "main.h" #include "RTC.h" // Function to obtain the current date and time from the RTC diff --git a/src/dhcp.c b/Src/dhcp.c similarity index 85% rename from src/dhcp.c rename to Src/dhcp.c index c40d8bd..c71aa16 100644 --- a/src/dhcp.c +++ b/Src/dhcp.c @@ -48,7 +48,7 @@ static uint8_t dhcptid_l = 0; // Counter for transaction ID static uint8_t dhcpState = DHCP_STATE_INIT; // DHCP state // Pointers to values we have set or need to set -static uint8_t *macaddr; // MAC address pointer +static uint8_t *macaddrDHCP; // MAC address pointer uint8_t *dhcpip; // DHCP IP address pointer uint8_t *dhcpmask; // DHCP subnet mask pointer uint8_t *dhcpserver; // DHCP server address pointer @@ -90,9 +90,9 @@ uint8_t dhcp_state(void) { // Send DHCPREQUEST // Wait for DHCPACK // All configured -void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *maskin, +void dhcp_start(uint8_t *buf, uint8_t *macaddrDHCPin, uint8_t *ipaddrin, uint8_t *maskin, uint8_t *gwipin, uint8_t *dhcpsvrin, uint8_t *dnssvrin) { - macaddr = macaddrin; + macaddrDHCP = macaddrDHCPin; dhcpip = ipaddrin; dhcpmask = maskin; gwaddr = gwipin; @@ -114,8 +114,8 @@ void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *ma // Set a unique hostname, use DHCP_HOSTNAME- plus last octet of MAC address strncpy(hostname, DHCP_HOSTNAME, HOSTNAME_LEN); hostname[HOSTNAME_LEN] = '-'; - hostname[HOSTNAME_LEN + 1] = 'A' + (macaddr[5] >> 4); - hostname[HOSTNAME_LEN + 2] = 'A' + (macaddr[5] & 0x0F); + hostname[HOSTNAME_LEN + 1] = 'A' + (macaddrDHCP[5] >> 4); + hostname[HOSTNAME_LEN + 2] = 'A' + (macaddrDHCP[5] & 0x0F); hostname[HOSTNAME_LEN + 3] = '\0'; // Enable reception of broadcast packets @@ -147,13 +147,13 @@ void dhcp_send(uint8_t *buf, uint8_t requestType) { send_udp_prepare(buf, (DHCPCLIENT_SRC_PORT_H << 8) | (dhcptid_l & 0xff), dhcpip, DHCP_DEST_PORT); // Set Ethernet source and destination MAC addresses - memcpy(buf + ETH_SRC_MAC, macaddr, 6); + memcpy(buf + ETH_SRC_MAC, macaddrDHCP, 6); memset(buf + ETH_DST_MAC, 0xFF, 6); // Set IP total length, protocol, and destination buf[IP_TOTLEN_L_P] = 0x82; buf[IP_PROTO_P] = IP_PROTO_UDP_V; - memset(buf + IP_DST_P, 0xFF, 4); + memset(buf + IP_DST_IP_P, 0xFF, 4); // Set UDP source and destination ports buf[UDP_DST_PORT_L_P] = DHCP_SRC_PORT; @@ -179,7 +179,7 @@ void dhcp_send(uint8_t *buf, uint8_t requestType) { memset(dhcpPtr->yiaddr, 0, 4); // 28-43: chaddr (16) - memcpy(dhcpPtr->chaddr, macaddr, 6); + memcpy(dhcpPtr->chaddr, macaddrDHCP, 6); // Options defined as option, length, value bufPtr = buf + UDP_DATA_P + sizeof(dhcpData); @@ -198,7 +198,7 @@ void dhcp_send(uint8_t *buf, uint8_t requestType) { addToBuf(61); // Option: Client Identifier addToBuf(7); // Length: 7 addToBuf(0x01); // Hardware type: Ethernet - for (int i = 0; i < 6; i++) addToBuf(macaddr[i]); + for (int i = 0; i < 6; i++) addToBuf(macaddrDHCP[i]); // Host name Option addToBuf(12); // Option: Host Name @@ -322,4 +322,46 @@ uint8_t have_dhcpack(uint8_t *buf, uint16_t plen) { // Return 2 to indicate DHCPACK processed return 2; } + +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip) { + uint16_t dat_p; + int plen = 0; + long lastDhcpRequest = HAL_GetTick(); + uint8_t dhcpState = 0; + bool gotIp = false; + uint8_t dhcpTries = 10; + + dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); + + while (!gotIp) { + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p = handle_AllPacket(buf, plen); + + if (dat_p == 0) { + check_for_dhcp_answer(buf, plen); + dhcpState = dhcp_state(); + + if (dhcpState != DHCP_STATE_OK) { + if (HAL_GetTick() > (lastDhcpRequest + 10000L)) { + lastDhcpRequest = HAL_GetTick(); + if (--dhcpTries <= 0) { + return 0; + } + dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); + } + } else if (!gotIp) { + gotIp = true; + + init_ip_arp_udp_tcp(mymac, myip, myport); + client_set_gwip(gwip); + +#ifdef DNS_client + dnslkup_set_dnsip(dnsip); +#endif + } + } + } + + return 1; +} /* end of dhcp.c */ diff --git a/src/dnslkup.c b/Src/dnslkup.c similarity index 72% rename from src/dnslkup.c rename to Src/dnslkup.c index 29437f3..0086859 100644 --- a/src/dnslkup.c +++ b/Src/dnslkup.c @@ -1,40 +1,35 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc +/* + * dnslkup.C * - * Author: Guido Socher - * Copyright: GPL V2 - * See http://www.gnu.org/licenses/gpl.html + * Created on: Jun 4, 2024 + * Author: dtneo * - * DNS look-up functions based on the udp client - * - *********************************************/ + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + #include "defines.h" #include "net.h" #include "ip_arp_udp_tcp.h" -#if defined (UDP_client) static uint8_t dnstid_l=0; // a counter for transaction ID // src port high byte must be a a0 or higher: -#define DNSCLIENT_SRC_PORT_H 0xe0 uint8_t dnsip[]={8,8,8,8}; // the google public DNS. To be used if DNS server is not set by user. static uint8_t haveDNSanswer=0; static uint8_t dns_answerip[4]; static uint8_t dns_ansError=0; -uint8_t dnslkup_haveanswer(void) -{ +uint8_t dnslkup_haveanswer(void) { return(haveDNSanswer); } -uint8_t dnslkup_get_error_info(void) -{ +uint8_t dnslkup_get_error_info(void) { return(dns_ansError); } -uint8_t *dnslkup_getip() -{ +uint8_t *dnslkup_getip() { return(dns_answerip); } @@ -43,8 +38,7 @@ uint8_t *dnslkup_getip() // and http://www.ietf.org/rfc/rfc1035.txt // //void dnslkup_request(uint8_t *buf,const prog_char *progmem_hostname) -void dnslkup_request(uint8_t *buf, uint8_t *domainName) -{ +void dnslkup_request(uint8_t *buf, uint8_t *domainName) { uint8_t i,lenpos,lencnt; char c; haveDNSanswer=0; @@ -103,7 +97,7 @@ void dnslkup_request(uint8_t *buf, uint8_t *domainName) // process the answer from the dns server: // return 1 on sucessful processing of answer. // We set also the variable haveDNSanswer -uint8_t udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen){ +uint8_t udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen) { uint8_t j,i; if (plen<70){ return(0); @@ -184,8 +178,7 @@ uint8_t udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen){ // set DNS server to be used for lookups. // defaults to Google DNS server if not called. -void dnslkup_set_dnsip(uint8_t *dnsipaddr) -{ +void dnslkup_set_dnsip(uint8_t *dnsipaddr) { uint8_t i=0; while(i<4){ dnsip[i]=dnsipaddr[i]; @@ -193,7 +186,50 @@ void dnslkup_set_dnsip(uint8_t *dnsipaddr) } } -#endif +// Perform all processing to resolve a hostname to IP address. +// Returns 1 for successful name resolution, 0 otherwise +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname) { + uint16_t dat_p; + int plen = 0; + long lastDnsRequest = HAL_GetTick(); + uint8_t dns_state = DNS_STATE_INIT; + bool gotAddress = false; + uint8_t dnsTries = 3; // After 3 attempts fail gracefully + + while (!gotAddress) { + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p = handle_AllPacket(buf, plen); + + if (dat_p == 0) { + if (client_waiting_gw()) { + continue; + } -/* end of dnslkup.c */ + if (dns_state == DNS_STATE_INIT) { + dns_state = DNS_STATE_REQUESTED; + lastDnsRequest = HAL_GetTick(); + dnslkup_request(buf, hostname); + continue; + } + if (dns_state != DNS_STATE_ANSWER) { + if (HAL_GetTick() > (lastDnsRequest + 60000L)) { + if (--dnsTries <= 0) { + return 0; // Failed to resolve address + } + dns_state = DNS_STATE_INIT; + lastDnsRequest = HAL_GetTick(); + } + continue; + } + } else { + if (dns_state == DNS_STATE_REQUESTED && udp_client_check_for_dns_answer(buf, plen)) { + dns_state = DNS_STATE_ANSWER; + client_tcp_set_serverip(dnslkup_getip()); + gotAddress = true; + } + } + } + + return 1; +} diff --git a/src/enc28j60.c b/Src/enc28j60.c similarity index 100% rename from src/enc28j60.c rename to Src/enc28j60.c diff --git a/src/error_handler.c b/Src/error_handler.c similarity index 100% rename from src/error_handler.c rename to Src/error_handler.c diff --git a/Src/ip_arp_udp_tcp.c b/Src/ip_arp_udp_tcp.c new file mode 100644 index 0000000..af2b9e2 --- /dev/null +++ b/Src/ip_arp_udp_tcp.c @@ -0,0 +1,338 @@ +/* + * ip_arp_udp_tcp.c + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#include "defines.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" +#include "websrv_help_functions.h" + +// The checksum function calculates checksums for IP, UDP, and TCP packets. +// For IP and ICMP, the checksum is calculated over the IP header only. +// For UDP and TCP, the checksum includes a pseudo-header using the source and destination IP fields. +uint16_t checksum(uint8_t *buf, uint16_t len, uint8_t type) { + uint32_t sum = 0; + // Ajout des valeurs spécifiques au protocole pour UDP et TCP + if (type == 1) { + sum += IP_PROTO_UDP_V; // Protocole UDP + sum += len - 8; // Longueur UDP (données + en-tête) + } else if (type == 2) { + sum += IP_PROTO_TCP_V; // Protocole TCP + sum += len - 8; // Longueur TCP (données + en-tête) + } + + // Sommation des mots de 16 bits + while (len > 1) { + sum += 0xFFFF & (*buf << 8 | *(buf + 1)); + buf += 2; + len -= 2; + } + + // Si un octet reste, l'ajouter (complété par zéro) + if (len) { + sum += (0xFF & *buf) << 8; + } + + // Réduction de la somme de 32 bits à 16 bits + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + // Retourner le complément à un de la somme + return (uint16_t)(sum ^ 0xFFFF); +} +// This function initializes the web server. +// You must call this function once before you use any of the other functions. +void init_ip_arp_udp_tcp(uint8_t *mymac, uint8_t *myip, uint16_t port) { + wwwport_h = (port >> 8) & 0xFF; + wwwport_l = port & 0xFF; + memcpy(ipaddr, myip, 4); + memcpy(macaddr, mymac, 6); +} + +// Check if an IP message is from a specified IP address +uint8_t check_ip_message_is_from(uint8_t *buf, uint8_t *ip) { + return memcmp(&buf[IP_SRC_IP_P], ip, 4) == 0; +} + +// Check if the Ethernet frame is an ARP request for my IP address +uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint16_t len) { + if (len < 41) { + return 0; + } + if (buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V) { + return 0; + } + return memcmp(&buf[ETH_ARP_DST_IP_P], ipaddr, 4) == 0; +} + +// Check if the Ethernet frame is an IP packet addressed to my IP address +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf, uint16_t len) { + // eth + ip header is 34 bytes + udp header is 8 bytes + if (len < 42) { + return 0; + } + if (buf[ETH_TYPE_H_P] != ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_IP_L_V) { + return 0; + } + // must be IP V4 and 20 byte header + if (buf[IP_HEADER_LEN_VER_P] != 0x45) { + return 0; + } + return memcmp(&buf[IP_DST_IP_P], ipaddr, 4) == 0; +} + +// Make a return Ethernet header from a received Ethernet packet +void make_eth(uint8_t *buf) { + memcpy(&buf[ETH_DST_MAC], &buf[ETH_SRC_MAC], 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); +} + +// Make a new Ethernet header for an IP packet +void make_eth_ip_new(uint8_t *buf, uint8_t *dst_mac) { + memcpy(&buf[ETH_DST_MAC], dst_mac, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; +} + +// Function to calculate IP header checksum +void fill_ip_hdr_checksum(uint8_t *buf) { + uint16_t ck; + + // Zero out IP checksum field + buf[0x18] = 0; + buf[0x19] = 0; + + // Calculate IP header checksum + ck = calculate_tcp_checksum(&buf[0x0E], 20); // IP_P = 0x0E, IP_HEADER_LEN = 20 + + // Set IP header checksum + buf[0x18] = ck >> 8; + buf[0x19] = ck & 0xFF; +} + + +// Check if it's an ARP reply +uint8_t eth_type_is_arp_reply(uint8_t *buf) { + return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V; +} + +// Check if it's an ARP request +uint8_t eth_type_is_arp_req(uint8_t *buf) { + return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V; +} + +// Make a return IP header from a received IP packet +void make_ip(uint8_t *buf) { + memcpy(&buf[IP_DST_IP_P], &buf[IP_SRC_IP_P], 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); +} + +// Swap seq and ack number and count ack number up +void step_seq(uint8_t *buf, uint16_t rel_ack_num, uint8_t cp_seq) { + uint8_t i = 4; + uint8_t tseq; + + // Sequence numbers: add the relative ack num to SEQACK + while (i > 0) { + rel_ack_num += buf[TCP_SEQ_H_P + i - 1]; + tseq = buf[TCP_SEQACK_H_P + i - 1]; + buf[TCP_SEQACK_H_P + i - 1] = rel_ack_num & 0xFF; + + if (cp_seq) { + // Copy the acknum sent to us into the sequence number + buf[TCP_SEQ_H_P + i - 1] = tseq; + } else { + buf[TCP_SEQ_H_P + i - 1] = 0; // Some preset value + } + + rel_ack_num >>= 8; + i--; + } +} + + +// Make an ARP reply from a received ARP request +void make_arp_answer_from_request(uint8_t *buf) { + make_eth(buf); + buf[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; + buf[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V; + memcpy(&buf[ETH_ARP_DST_MAC_P], &buf[ETH_ARP_SRC_MAC_P], 6); + memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); + memcpy(&buf[ETH_ARP_DST_IP_P], &buf[ETH_ARP_SRC_IP_P], 4); + memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); + enc28j60PacketSend(42, buf); +} + +// Make a UDP reply from a received UDP request, sending a max of 220 bytes of data +void make_udp_reply_from_request(uint8_t *buf, char *data, uint16_t datalen, uint16_t port) { + if (datalen > 220) { + datalen = 220; + } + + make_eth(buf); + + // Total length field in the IP header + uint16_t total_len = IP_HEADER_LEN + UDP_HEADER_LEN + datalen; + buf[IP_TOTLEN_H_P] = total_len >> 8; + buf[IP_TOTLEN_L_P] = total_len & 0xff; + + make_ip(buf); + + // Swap source and destination ports + buf[UDP_DST_PORT_H_P] = buf[UDP_SRC_PORT_H_P]; + buf[UDP_DST_PORT_L_P] = buf[UDP_SRC_PORT_L_P]; + buf[UDP_SRC_PORT_H_P] = port >> 8; + buf[UDP_SRC_PORT_L_P] = port & 0xff; + + // Calculate UDP length + uint16_t udp_len = UDP_HEADER_LEN + datalen; + buf[UDP_LEN_H_P] = udp_len >> 8; + buf[UDP_LEN_L_P] = udp_len & 0xff; + + // Zero the checksum + buf[UDP_CHECKSUM_H_P] = 0; + buf[UDP_CHECKSUM_L_P] = 0; + + // Copy data + memcpy(&buf[UDP_DATA_P], data, datalen); + + // Calculate checksum + uint16_t ck = checksum(&buf[IP_SRC_IP_P], 16 + datalen, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); +} + + +// Do some basic length calculations and store the result in static variables +void init_len_info(uint8_t *buf) { + info_data_len = (((int16_t)buf[IP_TOTLEN_H_P]) << 8) | (buf[IP_TOTLEN_L_P] & 0xff); + info_data_len -= IP_HEADER_LEN; + info_hdr_len = (buf[TCP_HEADER_LEN_P] >> 4) * 4; // Generate length in bytes + info_data_len -= info_hdr_len; + if (info_data_len <= 0) { + info_data_len = 0; + } +} + +#if defined(NTP_client) || defined(WOL_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) +// Fills the buffer with a string of specified length +void fill_buf_p(uint8_t *buf, uint16_t len, const char *s) { + memcpy(buf, s, len); +} + +// Prepare a spontaneous UDP packet to a server +void send_udp_prepare(uint8_t *buf, uint16_t sport, uint8_t *dip, uint16_t dport) { + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P], 9, iphdr); + + buf[IP_TOTLEN_H_P] = 0; + buf[IP_PROTO_P] = IP_PROTO_UDP_V; + memcpy(&buf[IP_DST_IP_P], dip, 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + + buf[UDP_DST_PORT_H_P] = (dport >> 8); + buf[UDP_DST_PORT_L_P] = dport & 0xff; + buf[UDP_SRC_PORT_H_P] = (sport >> 8); + buf[UDP_SRC_PORT_L_P] = sport & 0xff; + buf[UDP_LEN_H_P] = 0; + buf[UDP_CHECKSUM_H_P] = 0; + buf[UDP_CHECKSUM_L_P] = 0; +} + +// Transmit a prepared UDP packet +void send_udp_transmit(uint8_t *buf, uint16_t datalen) { + buf[IP_TOTLEN_H_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) >> 8; + buf[IP_TOTLEN_L_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) & 0xff; + fill_ip_hdr_checksum(buf); + + buf[UDP_LEN_H_P] = (UDP_HEADER_LEN + datalen) >> 8; + buf[UDP_LEN_L_P] = (UDP_HEADER_LEN + datalen) & 0xff; + + uint16_t ck = checksum(&buf[IP_SRC_IP_P], 16 + datalen, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); +} + +// Send a UDP packet +void send_udp(uint8_t *buf, char *data, uint16_t datalen, uint16_t sport, uint8_t *dip, uint16_t dport) { + send_udp_prepare(buf, sport, dip, dport); + memcpy(&buf[UDP_DATA_P], data, datalen); + send_udp_transmit(buf, datalen); +} + +#if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) +// Make an ARP request +void client_arp_whohas(uint8_t *buf, uint8_t *ip_we_search) { + // Prepare Ethernet header + memset(&buf[ETH_DST_MAC], 0xFF, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; + + // Prepare ARP request header + fill_buf_p(&buf[ETH_ARP_P], 8, arpreqhdr); + memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); + memset(&buf[ETH_ARP_DST_MAC_P], 0, 6); + memcpy(&buf[ETH_ARP_DST_IP_P], ip_we_search, 4); + memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); + + // Set the flag to accept ARP reply + waitgwmac |= WGW_ACCEPT_ARP_REPLY; + + // Send the packet + enc28j60PacketSend(0x2a, buf); // 0x2a = 42 = length of the packet +} + +// Check if the client is waiting for the gateway MAC address +uint8_t client_waiting_gw(void) { + return (waitgwmac & WGW_HAVE_GW_MAC) ? 0 : 1; +} + +// Store the MAC address from an ARP reply +uint8_t client_store_gw_mac(uint8_t *buf) { + if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4) == 0) { + memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); + return 1; + } + return 0; +} + +// Refresh the gateway ARP entry +void client_gw_arp_refresh(void) { + if (waitgwmac & WGW_HAVE_GW_MAC) { + waitgwmac |= WGW_REFRESHING; + } +} + +//void client_set_wwwip(uint8_t *wwwipaddr) { +// memcpy(wwwip, wwwipaddr, 4); +//} + +void client_set_gwip(uint8_t *gwipaddr) +{ + waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop + memcpy(gwip, gwipaddr, 4); +} +#endif // defined (NTP_client) || defined (UDP_client) || defined (TCP_client) + +#endif + +/* end of ip_arp_udp.c */ diff --git a/Src/net.c b/Src/net.c new file mode 100644 index 0000000..e52ae0c --- /dev/null +++ b/Src/net.c @@ -0,0 +1,187 @@ +/* + * net.C + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#include "net.h" + +uint16_t len; +uint16_t plen; + +// Web server port, used when implementing webserver +uint8_t wwwport_l=80; // server port +uint8_t wwwport_h=0; // Note: never use same as TCPCLIENT_SRC_PORT_H + +uint8_t www_fd=0; +uint8_t browsertype=0; // 0 = get, 1 = post +char *client_additionalheaderline; +char *client_method; // for POST or PUT, no trailing space needed +char *client_urlbuf; +char *client_hoststr; +char *client_postval; +char *client_urlbuf_var; +uint8_t *bufptr=0; // ugly workaround for backward compatibility + +// just lower byte, the upper byte is TCPCLIENT_SRC_PORT_H: +uint8_t tcpclient_src_port_l=1; +uint8_t tcp_fd=0; // a file descriptor, will be encoded into the port +uint8_t tcp_client_state=0; +// TCP client Destination port +uint8_t tcp_client_port_h=0; +uint8_t tcp_client_port_l=0; + +int16_t delaycnt=0; +uint8_t gwip[4]; +uint8_t gwmacaddr[6]; +uint8_t tcpsrvip[4]; +uint8_t macaddr[6]; +uint8_t ipaddr[4]; +uint16_t info_data_len=0; +uint16_t info_hdr_len = 0; +uint8_t seqnum=0xa; // my initial tcp sequence number +const char arpreqhdr[] ={0,1,8,0,6,4,0,1}; +const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on ip, 0x20 is ttl (time to live) +const char ntpreqhdr[] ={0xe3,0,4,0xfa,0,1,0,1,0,0}; + +uint8_t waitgwmac=WGW_INITIAL_ARP; + +void (*icmp_callback)(uint8_t *ip) = NULL; + + + +// TCP functions on packet.c +uint8_t send_fin = 0; +uint16_t tcpstart; +uint16_t save_len; + + +// Sends an ICMP echo request (ping) to the specified destination IP using the gateway MAC address. +void client_icmp_request(uint8_t *buf, uint8_t *destip) { + // Fill Ethernet header + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); // Gateway MAC address in local LAN or host MAC address + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + + // Fill IP header + fill_buf_p(&buf[IP_P], 9, iphdr); + buf[IP_TOTLEN_L_P] = 0x82; // Total length + buf[IP_PROTO_P] = IP_PROTO_ICMP_V; // ICMP protocol + memcpy(&buf[IP_DST_IP_P], destip, 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + // Fill ICMP header and data + buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; + buf[ICMP_TYPE_P + 1] = 0; // Code + buf[ICMP_CHECKSUM_H_P] = 0; // Zero the checksum + buf[ICMP_CHECKSUM_L_P] = 0; + buf[ICMP_IDENT_H_P] = 5; // Some number for identification + buf[ICMP_IDENT_L_P] = ipaddr[3]; // Last byte of my IP for identification + buf[ICMP_IDENT_L_P + 1] = 0; // Sequence number, high byte + buf[ICMP_IDENT_L_P + 2] = 1; // Sequence number, low byte + + // Copy the data pattern + memset(&buf[ICMP_DATA_P], PINGPATTERN, 56); + + // Calculate checksum + uint16_t ck = checksum(&buf[ICMP_TYPE_P], 56 + 8, 0); + buf[ICMP_CHECKSUM_H_P] = ck >> 8; + buf[ICMP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(98, buf); +} + +/** + * @brief Register a callback function for receiving ping replies. + * + * @param callback The callback function to be called when a ping reply is received. + */ +void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) { + icmp_callback = callback; +} + +/** + * @brief Check for an ICMP ping reply packet. + * + * This function should be called in a loop to check if a ping reply has been received. + * + * @param buf The buffer containing the packet data. + * @param ip_monitoredhost The IP address of the monitored host. + * @return 1 if the ping reply is from the monitored host, 0 otherwise. + */ +uint8_t packetloop_icmp_checkreply(uint8_t *buf, uint8_t *ip_monitoredhost) { + if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && + buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREPLY_V && + buf[ICMP_DATA_P] == PINGPATTERN && + check_ip_message_is_from(buf, ip_monitoredhost)) { + return 1; + } + return 0; +} + +// Make an ICMP echo reply from a received echo request +void make_echo_reply_from_request(uint8_t *buf, uint16_t len) { + make_eth(buf); + make_ip(buf); + buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; + if (buf[ICMP_CHECKSUM_H_P] > (0xff - 0x08)) { + buf[ICMP_CHECKSUM_H_P + 1]++; + } + buf[ICMP_CHECKSUM_H_P] += 0x08; + enc28j60PacketSend(len, buf); +} + +void __attribute__((weak)) pingCallback(void) {} + + +// A WOL (Wake on Lan) packet is a UDP packet to the broadcast +// address and UDP port 9. The data part contains 6x FF followed by +// 16 times the mac address of the host to wake-up +void send_wol(uint8_t *buf,uint8_t *wolmac) +{ + uint16_t ck; + // + memset(&buf[ETH_DST_MAC], 0xff, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P],9,iphdr); + + buf[IP_TOTLEN_L_P]=0x82; + buf[IP_PROTO_P]=IP_PROTO_UDP_V; + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + memset(&buf[IP_DST_IP_P], 0xff, 4); + fill_ip_hdr_checksum(buf); + buf[UDP_DST_PORT_H_P]=0; + buf[UDP_DST_PORT_L_P]=0x9; // wol=normally 9 + buf[UDP_SRC_PORT_H_P]=10; + buf[UDP_SRC_PORT_L_P]=0x42; // source port does not matter + buf[UDP_LEN_H_P]=0; + buf[UDP_LEN_L_P]=110; // fixed len + // zero the checksum + buf[UDP_CHECKSUM_H_P]=0; + buf[UDP_CHECKSUM_L_P]=0; + // copy the data (102 bytes): + + // first mac - 0xFFs + memset(&buf[UDP_DATA_P], 0xff, 6); + // next 16 macs - wolmac repeated + // TODO: may need testing + for (unsigned i = 1; i <= 16; i++) { + memcpy(&buf[UDP_DATA_P + i*6], wolmac, 6); + } + + ck=checksum(&buf[IP_SRC_IP_P], 118,1); + buf[UDP_CHECKSUM_H_P]=ck>>8; + buf[UDP_CHECKSUM_L_P]=ck& 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+102,buf); +} diff --git a/Src/ntp.c b/Src/ntp.c new file mode 100644 index 0000000..ad387a7 --- /dev/null +++ b/Src/ntp.c @@ -0,0 +1,147 @@ +/* + * packet.C + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ +#include "ntp.h" +#include "packet.h" +#include "tcp.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" +#include "websrv_help_functions.h" + +// Sends an NTP UDP packet +// See http://tools.ietf.org/html/rfc958 for details +void client_ntp_request(uint8_t *buf, uint8_t *ntpip, uint8_t srcport) { + // Fill Ethernet header + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + + // Fill IP header + fill_buf_p(&buf[IP_P], 9, iphdr); + buf[IP_TOTLEN_L_P] = 0x4c; // Total length + buf[IP_PROTO_P] = IP_PROTO_UDP_V; // UDP protocol + memcpy(&buf[IP_DST_IP_P], ntpip, 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + // Fill UDP header + buf[UDP_DST_PORT_H_P] = 0; + buf[UDP_DST_PORT_L_P] = 0x7b; // NTP port = 123 + buf[UDP_SRC_PORT_H_P] = 10; + buf[UDP_SRC_PORT_L_P] = srcport; // Lower 8 bits of source port + buf[UDP_LEN_H_P] = 0; + buf[UDP_LEN_L_P] = 56; // Fixed length + buf[UDP_CHECKSUM_H_P] = 0; // Zero the checksum + buf[UDP_CHECKSUM_L_P] = 0; + + // Fill NTP data + memset(&buf[UDP_DATA_P], 0, 48); + fill_buf_p(&buf[UDP_DATA_P], 10, ntpreqhdr); + + // Calculate and fill UDP checksum + uint16_t ck = checksum(&buf[IP_SRC_IP_P], 16 + 48, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(90, buf); +} + +/* + * This function processes the response from an NTP server and updates the RTC of the STM32 microcontroller. + * It extracts the NTP timestamp from the received buffer, converts it to a UNIX timestamp, and then + * updates the RTC with the current date and time. + */ +void client_ntp_process_answer(uint8_t *buf, uint16_t plen) { + uint32_t unix_timestamp; + + // Extract the NTP timestamp (seconds since 1900) + unix_timestamp = ((uint32_t)buf[0x52] << 24) | ((uint32_t)buf[0x53] << 16) | ((uint32_t)buf[0x54] << 8) | (uint32_t)buf[0x55]; + + // Convert the NTP timestamp to UNIX timestamp (seconds since 1970) + // by subtracting the seconds between 1900 and 1970. + if (unix_timestamp > 2208988800UL) { + unix_timestamp -= 2208988800UL; + } else { + // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) + unix_timestamp = 0; + } + + // Create structures for time and date + RTC_TimeTypeDef sTime = {0}; + RTC_DateTypeDef sDate = {0}; + + // Calculate the time components + uint32_t seconds = unix_timestamp % 60; + uint32_t minutes = (unix_timestamp / 60) % 60; + uint32_t hours = (unix_timestamp / 3600) % 24; + uint32_t days = unix_timestamp / 86400; // Total number of days since 1970 + + // The start of 1970 was a Thursday (day 4 of the week) + uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday + + // Determine the year + uint32_t year = 1970; + while (days >= 365) { + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + if (days < 366) break; + days -= 366; + } else { + if (days < 365) break; + days -= 365; + } + year++; + } + + // Determine the month and the day of the month + uint32_t month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + month_lengths[1] = 29; // February has 29 days in leap years + } + + uint32_t month = 0; + for (month = 0; month < 12; month++) { + if (days < month_lengths[month]) { + break; + } + days -= month_lengths[month]; + } + month++; // months are 1-12 + uint32_t dayOfMonth = days + 1; // days are 1-31 + + // Set the time + sTime.Hours = hours; + sTime.Minutes = minutes; + sTime.Seconds = seconds; + sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + sTime.StoreOperation = RTC_STOREOPERATION_RESET; + + // Set the date + sDate.WeekDay = dayOfWeek; + sDate.Month = month; + sDate.Date = dayOfMonth; + sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 + + // Update the RTC + HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); + HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); + + char timeStr[32]; // Adjusted size to hold formatted date and time + sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", + sDate.Date, + sDate.Month, + year, + sTime.Hours, + sTime.Minutes, + sTime.Seconds); + udpLog2("NTP indicates the following date and time:", timeStr); +} diff --git a/Src/packet.c b/Src/packet.c new file mode 100644 index 0000000..f9eaf64 --- /dev/null +++ b/Src/packet.c @@ -0,0 +1,229 @@ +/* + * packet.C + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ +#include "packet.h" + +// Function to process all incoming packets +void handle_AllPacket(uint8_t *buf, uint16_t plen) { + // Check if the packet is an ARP packet + if (buf[ETH_TYPE_H_P] == ETHTYPE_ARP_H_V && buf[ETH_TYPE_L_P] == ETHTYPE_ARP_L_V) { + // Handle ARP packet + if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && delaycnt == 0 && enc28j60linkup()) { + client_arp_whohas(buf, gwip); // Send ARP request for gateway IP + } + delaycnt++; + if (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V) { + make_arp_answer_from_request(buf); // Respond to ARP request + } + if (waitgwmac & WGW_ACCEPT_ARP_REPLY && buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V) { + if (client_store_gw_mac(buf)) { + waitgwmac = WGW_HAVE_GW_MAC; // Store gateway MAC address + } + } + return; + } + + // Check if the packet is an IP packet addressed to us + if (!eth_type_is_ip_and_my_ip(buf, plen)) { + return; + } + + // Check Ethernet type + if (buf[ETH_TYPE_H_P] == ETHTYPE_IP_H_V && buf[ETH_TYPE_L_P] == ETHTYPE_IP_L_V) { + // It's an IP packet, check the IP protocol + uint8_t ip_proto = buf[IP_PROTO_P]; + + if (ip_proto == IP_PROTO_ICMP_V) { + // Handle ICMP packet + handle_icmp_packet(buf, plen); + } else if (ip_proto == IP_PROTO_TCP_V) { + // Handle TCP packet + handle_tcp_packet(buf, plen); + } else if (ip_proto == IP_PROTO_UDP_V) { + // Handle UDP packet + handle_udp_packet(buf, plen); + } + } +} + +// Function to handle ICMP packets +void handle_icmp_packet(uint8_t *buf, uint16_t plen) { + // Process ICMP packet (e.g., reply to ping) + if (icmp_callback) { + icmp_callback(&buf[IP_SRC_IP_P]); + } + make_echo_reply_from_request(buf, plen); + pingCallback(); +} + +// Déclarations Globales +static uint8_t tcp_state = TCP_STATE_CLOSED; + +void handle_tcp_response(uint8_t *buf) { + uint8_t connection_id = 0; // Assuming single connection for simplicity + tcp_connection_t *conn = &tcp_connections[connection_id]; + + if (conn->state == TCP_STATE_SYN_SENT && (buf[33] & TCP_FLAGS_SYN_V) && (buf[33] & TCP_FLAGS_ACK_V)) { + conn->ack_num = ((uint32_t)buf[24] << 24) | ((uint32_t)buf[25] << 16) | ((uint32_t)buf[26] << 8) | buf[27]; + conn->seq_num++; + conn->state = TCP_STATE_ESTABLISHED; + udpLog2("TCP_client", "Connection Established"); + + // Send HTTP GET request + const char *http_get = "GET / HTTP/1.1\r\nHost: 192.168.1.1\r\n\r\n"; + uint8_t buf[200]; + memcpy(buf + 38, http_get, strlen(http_get)); + send_tcp_ack_packet(buf, conn, conn->remote_ip, strlen(http_get)); + } +} + +void loop() { + uint8_t buf[60]; + // Simulate receiving a TCP response packet (this should be replaced with actual packet receiving code) + buf[33] = TCP_FLAGS_SYN_V | TCP_FLAGS_ACK_V; + buf[24] = 0; buf[25] = 0; buf[26] = 0; buf[27] = 2; // ACK number + handle_tcp_response(buf); +} + + +// Function to handle TCP packets +void handle_tcp_packet(uint8_t *buf, uint16_t plen) { + uint8_t flags = buf[TCP_FLAGS_P]; + uint32_t seq_num = (buf[TCP_SEQ_H_P] << 24) | (buf[TCP_SEQ_H_P + 1] << 16) | (buf[TCP_SEQ_H_P + 2] << 8) | buf[TCP_SEQ_H_P + 3]; + uint32_t ack_num = (buf[TCP_ACK_H_P] << 24) | (buf[TCP_ACK_H_P + 1] << 16) | (buf[TCP_ACK_H_P + 2] << 8) | buf[TCP_ACK_H_P + 3]; + uint8_t src_ip[4] = { buf[IP_SRC_IP_P], buf[IP_SRC_IP_P + 1], buf[IP_SRC_IP_P + 2], buf[IP_SRC_IP_P + 3] }; + uint16_t src_port = (buf[TCP_SRC_PORT_H_P] << 8) | buf[TCP_SRC_PORT_L_P]; + + char log_buffer[100]; + snprintf(log_buffer, sizeof(log_buffer), "TCP packet received: SEQ=%u, ACK=%u, FLAGS=%02X", seq_num, ack_num, flags); + udpLog2("TCP", log_buffer); + + tcp_connection_t *tcp_conn = find_connection(src_ip, src_port); + + if (tcp_conn == NULL) { + if (flags & TCP_FLAGS_SYN_V) { + tcp_conn = find_free_connection(); + if (tcp_conn != NULL) { + tcp_conn->state = TCP_STATE_SYN_RECEIVED; + tcp_conn->seq_num = seq_num; + tcp_conn->ack_num = ack_num; + memcpy(tcp_conn->remote_ip, src_ip, 4); + tcp_conn->remote_port = src_port; + create_tcp_synack_packet(buf, tcp_conn); + udpLog2("TCP", "SYN received, SYN-ACK sent"); + } else { + udpLog2("TCP", "No free connection slot available"); + } + } + return; + } + + switch (tcp_conn->state) { + case TCP_STATE_SYN_RECEIVED: + if (flags & TCP_FLAGS_ACK_V) { + tcp_conn->state = TCP_STATE_ESTABLISHED; + udpLog2("TCP", "Connection established"); + } + break; + + case TCP_STATE_ESTABLISHED: + if (flags & TCP_FLAGS_FIN_V) { + tcp_conn->state = TCP_STATE_CLOSED; + create_tcp_ack_packet(buf, tcp_conn, 0, 0); + udpLog2("TCP", "FIN received, ACK sent, connection closed"); + } else if (flags & TCP_FLAGS_ACK_V) { + if (flags & TCP_FLAGS_PUSH_V) { + handle_http_request(buf, TCP_DATA_P, plen - TCP_DATA_P, tcp_conn); + } else { + create_tcp_ack_packet(buf, tcp_conn, 0, 0); + udpLog2("TCP", "ACK received, no data to process"); + } + } + break; + + default: + udpLog2("TCP", "Unknown state, resetting connection"); + tcp_conn->state = TCP_STATE_CLOSED; + create_tcp_ack_packet(buf, tcp_conn, 0, TCP_FLAGS_RST_V); + break; + } +} + + +// Function to handle UDP packets +void handle_udp_packet(uint8_t *buf, uint16_t plen) { + if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { + client_ntp_process_answer(buf, plen); + return; + } + + if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { + return; + } + + uint16_t data_pos = UDP_DATA_P; + uint8_t *data = &buf[data_pos]; + uint16_t data_len = plen - data_pos; + + char msg[data_len + 1]; + memcpy(msg, data, data_len); + msg[data_len] = '\0'; + + // Vérifier si le message reçu correspond à une commande dans la table des commandes + for (CommandMapping *cmd = commandTable; cmd->command != NULL; cmd++) { + if (strncmp(msg, cmd->command, strlen(cmd->command)) == 0) { + udpLog2("handle_udp_packet", cmd->command); // Log de la commande reconnue + if (cmd->function_with_arg != NULL) { + cmd->function_with_arg(msg); // Passer la commande entière pour traitement + } else if (cmd->function != NULL) { + cmd->function(); + } + return; + } + } + + udpLog2("Error", "Unexpected UDP message"); +} + + +// Function to handle HTTP requests +void handle_http_request(uint8_t *buf, uint16_t data_offset, uint16_t data_len, tcp_connection_t *tcp_conn) { + // Extract the HTTP request from the TCP payload + char *http_request = (char *)(buf + data_offset); + udpLog2("HTTP", "HTTP request received"); + + // Log the received HTTP request + char request_log[100]; + snprintf(request_log, sizeof(request_log), "HTTP Request: %.50s", http_request); + udpLog2("HTTP", request_log); + + // Check if the HTTP request is a GET request for "/logs" + if (strncmp(http_request, "GET /logs", 9) == 0) { + uint16_t response_len = send_logs_page(buf); + www_server_reply(buf, response_len, tcp_conn); + udpLog2("HTTP", "Logs page sent"); + } else if (strncmp(http_request, "GET /test", 9) == 0) { + uint16_t response_len = send_test_page(buf); + www_server_reply(buf, response_len, tcp_conn); + udpLog2("HTTP", "Test page sent"); + } else { + // Handle other HTTP requests or send a 404 Not Found response + const char *not_found_response = "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 13\r\n" + "Connection: close\r\n" + "\r\n" + "404 Not Found"; + uint16_t response_len = strlen(not_found_response); + memcpy(buf + TCP_DATA_P, not_found_response, response_len); + www_server_reply(buf, response_len, tcp_conn); + udpLog2("HTTP", "404 Not Found response sent"); + } +} diff --git a/Src/tcp.c b/Src/tcp.c new file mode 100644 index 0000000..f74af47 --- /dev/null +++ b/Src/tcp.c @@ -0,0 +1,700 @@ +/* + * tcp.C + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ +#include "defines.h" +#include "tcp.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" +#include "websrv_help_functions.h" + +//-------------------------------------- STEP1 +// Initialisation by ES_init_network() for this 2 next function +void client_tcp_set_serverip(uint8_t *ipaddr) { + memcpy(tcpsrvip, ipaddr, 4); +} + +void initialize_tcp_sequence_number() { + seqnum = random(); // Assurez-vous d'avoir une fonction pour générer un nombre aléatoire +} + +// Définition de la variable globale +tcp_connection_t tcp_connections[MAX_TCP_CONNECTIONS]; + +// Function to initialize all TCP connections to the CLOSED state +void init_tcp_connections() { + for (int i = 0; i < MAX_TCP_CONNECTIONS; i++) { + tcp_connections[i].state = TCP_STATE_CLOSED; + tcp_connections[i].seq_num = 0; + tcp_connections[i].ack_num = 0; + memset(tcp_connections[i].remote_ip, 0, sizeof(tcp_connections[i].remote_ip)); + tcp_connections[i].remote_port = 0; + tcp_connections[i].local_port = 0; + } +} +void initiate_tcp_connection(uint8_t connection_id, uint8_t *remote_ip, uint16_t remote_port, uint16_t local_port) { + tcp_connections[connection_id].state = TCP_STATE_SYN_SENT; + tcp_connections[connection_id].local_port = local_port; + tcp_connections[connection_id].remote_port = remote_port; + memcpy(tcp_connections[connection_id].remote_ip, remote_ip, 4); + tcp_connections[connection_id].seq_num = 1; + tcp_connections[connection_id].ack_num = 0; + + uint8_t buf[60]; + send_tcp_syn_packet(buf, local_port, remote_port, remote_ip); +} + + +// Function to find a free TCP connection (one that is in the CLOSED state) +tcp_connection_t* find_free_connection() { + for (int i = 0; i < MAX_TCP_CONNECTIONS; i++) { + if (tcp_connections[i].state == TCP_STATE_CLOSED) { + return &tcp_connections[i]; // Return the first free connection found + } + } + return NULL; // No free connection found +} + +// Function to find an existing TCP connection based on remote IP and port +tcp_connection_t* find_connection(uint8_t *ip, uint16_t port) { + for (int i = 0; i < MAX_TCP_CONNECTIONS; i++) { + if (tcp_connections[i].state != TCP_STATE_CLOSED && + memcmp(tcp_connections[i].remote_ip, ip, sizeof(tcp_connections[i].remote_ip)) == 0 && + tcp_connections[i].remote_port == port) { + return &tcp_connections[i]; // Return the matching connection + } + } + return NULL; // No matching connection found +} + +//-------------------------------------- STEP2 +void make_tcphead(uint8_t *buf, uint16_t len, uint8_t mss, uint8_t win_size) { + uint16_t ck; + + // Set IP length + buf[0x10] = len >> 8; // IP_TOTLEN_H_P = 0x10 + buf[0x11] = len & 0xff; // IP_TOTLEN_L_P = 0x11 + + // Set TCP header length + buf[0x2C] = (20 / 4) << 4; // TCP_HEADER_LEN_P = 0x2C, TCP_HEADER_LEN_PLAIN = 20 + + // Set MSS option if present + if (mss) { + buf[0x32] = 0x02; + buf[0x33] = 0x04; + buf[0x34] = (mss >> 8) & 0xFF; + buf[0x35] = mss & 0xFF; + } + + // Zero out checksum fields + buf[0x2E] = 0; // TCP_CHECKSUM_H_P = 0x2E + buf[0x2F] = 0; // TCP_CHECKSUM_L_P = 0x2F + + // Calculate and set TCP checksum + ck = calculate_tcp_checksum(&buf[0x1A], len - 20); // IP_SRC_IP_P = 0x1A, TCP_HEADER_LEN_PLAIN = 20 + buf[0x2E] = ck >> 8; // TCP_CHECKSUM_H_P = 0x2E + buf[0x2F] = ck & 0xff; // TCP_CHECKSUM_L_P = 0x2F + + // Calculate and set IP checksum + fill_tcp_ip_hdr_checksum(buf); +} + +// htons function: convert host byte order to network byte order (short) +static uint16_t TCP_htons(uint16_t hostshort) { + return ((hostshort << 8) & 0xFF00) | ((hostshort >> 8) & 0x00FF); +} + +static uint16_t ip_identifier = 1; + +// Helper function to create a new IP + TCP packet +void make_ip_tcp_new(uint8_t *buf, uint16_t len, uint8_t *dst_ip) { + static uint16_t ip_identifier = 1; + + buf[0] = IP_V4_V | IP_HEADER_LENGTH_V; + buf[1] = 0x00; + buf[2] = (len >> 8) & 0xFF; + buf[3] = len & 0xFF; + buf[4] = (ip_identifier >> 8) & 0xFF; + buf[5] = ip_identifier & 0xFF; + ip_identifier++; + buf[6] = 0x00; + buf[7] = 0x00; + buf[8] = 128; + buf[9] = IP_PROTO_TCP_V; + memcpy(&buf[12], ipaddr, 4); + memcpy(&buf[16], dst_ip, 4); + fill_ip_hdr_checksum(buf); +} + +void create_tcp_synack_packet(uint8_t *buf, tcp_connection_t *tcp_conn) { + memset(buf + 0x22, 0, 20); // TCP_SRC_PORT_H_P = 0x22, TCP_HEADER_LEN_PLAIN = 20 + buf[0x2F] = 0x12; // TCP_FLAGS_P = 0x2F, TCP_FLAGS_SYNACK_V + + buf[0x22] = (tcp_conn->local_port >> 8) & 0xFF; // TCP_SRC_PORT_H_P = 0x22 + buf[0x23] = tcp_conn->local_port & 0xFF; // TCP_SRC_PORT_L_P = 0x23 + buf[0x24] = (tcp_conn->remote_port >> 8) & 0xFF; // TCP_DST_PORT_H_P = 0x24 + buf[0x25] = tcp_conn->remote_port & 0xFF; // TCP_DST_PORT_L_P = 0x25 + + uint32_t seq_num = tcp_conn->seq_num; + uint32_t ack_num = tcp_conn->ack_num + 1; + buf[0x26] = (seq_num >> 24) & 0xFF; // TCP_SEQ_P = 0x26 + buf[0x27] = (seq_num >> 16) & 0xFF; + buf[0x28] = (seq_num >> 8) & 0xFF; + buf[0x29] = seq_num & 0xFF; + buf[0x2A] = (ack_num >> 24) & 0xFF; // TCP_SEQACK_P = 0x2A + buf[0x2B] = (ack_num >> 16) & 0xFF; + buf[0x2C] = (ack_num >> 8) & 0xFF; + buf[0x2D] = ack_num & 0xFF; + + buf[0x30] = 0x05; // TCP_WINDOWSIZE_H_P = 0x30 + buf[0x31] = 0xB4; // TCP_WINDOWSIZE_L_P = 0x31, 1460 bytes + + // Calculate and set checksums + fill_ip_tcp_checksum(buf, 20 + 20); // IP_HEADER_LEN = 20, TCP_HEADER_LEN_PLAIN = 20 + + // Update connection state + tcp_conn->state = TCP_STATE_SYN_RECEIVED; + tcp_conn->seq_num += 1; +} + +// Function to create and send a TCP ACK packet +void create_tcp_ack_packet(uint8_t *buf, tcp_connection_t *tcp_conn, uint8_t mss, uint8_t win_size) { + uint16_t len = 20 + (mss ? 4 : 0); // TCP_HEADER_LEN_PLAIN = 20 + + // Configure the TCP header with necessary information + buf[0x2F] = 0x10; // TCP_FLAGS_ACK_V + + // Set source and destination ports + buf[0x22] = tcp_conn->local_port >> 8; // TCP_SRC_PORT_H_P = 0x22 + buf[0x23] = tcp_conn->local_port & 0xFF; // TCP_SRC_PORT_L_P = 0x23 + buf[0x24] = tcp_conn->remote_port >> 8; // TCP_DST_PORT_H_P = 0x24 + buf[0x25] = tcp_conn->remote_port & 0xFF; // TCP_DST_PORT_L_P = 0x25 + + // Set sequence and acknowledgment numbers + buf[0x26] = tcp_conn->seq_num >> 24; // TCP_SEQ_H_P = 0x26 + buf[0x27] = tcp_conn->seq_num >> 16; + buf[0x28] = tcp_conn->seq_num >> 8; + buf[0x29] = tcp_conn->seq_num & 0xFF; + + buf[0x2A] = tcp_conn->ack_num >> 24; // TCP_ACK_H_P = 0x2A + buf[0x2B] = tcp_conn->ack_num >> 16; + buf[0x2C] = tcp_conn->ack_num >> 8; + buf[0x2D] = tcp_conn->ack_num & 0xFF; + + // Set window size + buf[0x30] = win_size >> 8; // TCP_WINDOWSIZE_H_P = 0x30 + buf[0x31] = win_size & 0xFF; // TCP_WINDOWSIZE_L_P = 0x31 + + // Zero out the checksum fields + buf[0x32] = 0; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = 0; // TCP_CHECKSUM_L_P = 0x33 + + // Fill MSS option if necessary + if (mss) { + buf[0x34] = mss; // TCP_OPTIONS_P = 0x34 + } + + // Calculate TCP checksum + uint16_t checksum = calculate_tcp_checksum(buf, len); + buf[0x32] = checksum >> 8; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = checksum & 0xFF; // TCP_CHECKSUM_L_P = 0x33 + + // Calculate IP checksum + fill_tcp_ip_hdr_checksum(buf); + + // Send the packet + enc28j60PacketSend(20 + len + 14, buf); // IP_HEADER_LEN = 20, ETH_HEADER_LEN = 14 + + udpLog2("TCP", "ACK packet sent"); +} + +// Function to create and send a TCP ACK packet with data +void create_tcp_ack_with_data_packet(uint8_t *buf, uint16_t len) { + uint16_t total_len = 20 + 20 + len; // IP_HEADER_LEN = 20, TCP_HEADER_LEN_PLAIN = 20 + + // Update the total length field in the IP header + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P = 0x10 + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P = 0x11 + + // Update the TCP header length field + buf[0x2E] = (20 / 4) << 4; // TCP_HEADER_LEN_P = 0x2E, TCP_HEADER_LEN_PLAIN = 20 + + // Calculate and set the TCP checksum + buf[0x32] = 0; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = 0; // TCP_CHECKSUM_L_P = 0x33 + uint16_t tcp_ck = calculate_tcp_checksum(&buf[0x1A], 8 + 20 + len); // IP_SRC_IP_P = 0x1A, TCP_HEADER_LEN_PLAIN = 20 + buf[0x32] = tcp_ck >> 8; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = tcp_ck & 0xff; // TCP_CHECKSUM_L_P = 0x33 + + // Update the TCP flags to include ACK and PUSH + buf[0x2F] = 0x18; // TCP_FLAGS_ACK_V = 0x10, TCP_FLAGS_PUSH_V = 0x08 + + // Calculate and set the IP header checksum + buf[0x18] = 0; // IP_CHECKSUM_H_P = 0x18 + buf[0x19] = 0; // IP_CHECKSUM_L_P = 0x19 + uint16_t ip_ck = calculate_tcp_checksum(&buf[0x0E], 20); // IP_P = 0x0E, IP_HEADER_LEN = 20 + buf[0x18] = ip_ck >> 8; // IP_CHECKSUM_H_P = 0x18 + buf[0x19] = ip_ck & 0xff; // IP_CHECKSUM_L_P = 0x19 + + // Log the IP and TCP headers + log_ip_header(buf); + log_tcp_header(buf); + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // ETH_HEADER_LEN = 14 +} +//-------------------------------------- STEP3 +// Function to calculate the length of TCP data +uint16_t get_tcp_data_len(uint8_t *buf) { + int16_t total_len = ((int16_t)buf[0x10] << 8) | (buf[0x11] & 0xff); // IP_TOTLEN_H_P, IP_TOTLEN_L_P + int16_t header_len = 20 + ((buf[0x2E] >> 4) * 4); // IP_HEADER_LEN, TCP_HEADER_LEN_P (0x2E) + int16_t data_len = total_len - header_len; + + // Return the data length if it is greater than 0, otherwise return 0 + return (data_len > 0) ? (uint16_t)data_len : 0; +} + +// Function to fill TCP data into the buffer +uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len) { + memcpy(&buf[0x35 + 3 + pos], data, len); // TCP_CHECKSUM_L_P (0x35) + return pos + len; // Return the new position after the data +} + + +// Function to build TCP data packet +uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort) { + udpLog2("TCP_client", "Entering build_tcp_data"); + + // Build Ethernet header + memcpy(&buf[0x00], gwmacaddr, 6); // ETH_DST_MAC + memcpy(&buf[0x06], macaddr, 6); // ETH_SRC_MAC + buf[0x0C] = 0x08; // ETH_TYPE_H_P (ETHTYPE_IP_H_V) + buf[0x0D] = 0x00; // ETH_TYPE_L_P (ETHTYPE_IP_L_V) + + // Build IP header + fill_buf_p(&buf[0x0E], 9, iphdr); // IP_P + buf[0x10] = 0x00; // IP_TOTLEN_H_P + buf[0x11] = 40; // IP_TOTLEN_L_P (IP total length field) + buf[0x17] = 0x06; // IP_PROTO_P (IP_PROTO_TCP_V) + memcpy(&buf[0x1E], tcpsrvip, 4); // IP_DST_IP_P + memcpy(&buf[0x22], ipaddr, 4); // IP_SRC_IP_P + fill_ip_hdr_checksum(buf); + + // Build TCP header + buf[0x24] = tcp_client_port_h; // TCP_DST_PORT_H_P + buf[0x25] = tcp_client_port_l; // TCP_DST_PORT_L_P + buf[0x26] = srcPort >> 8; // TCP_SRC_PORT_H_P + buf[0x27] = srcPort & 0xff; // TCP_SRC_PORT_L_P + + // Zero out sequence number and acknowledgement number + memset(&buf[0x28], 0, 8); // TCP_SEQ_H_P + + // Set initial sequence number + buf[0x2A] = seqnum; // TCP_SEQ_H_P + 2 + seqnum += 3; + + // Set TCP header length and flags + buf[0x2E] = 0x60; // TCP_HEADER_LEN_P (Header length (5 words) and reserved bits) + buf[0x2F] = 0x08; // TCP_FLAGS_P (TCP_FLAGS_PUSH_V) + + // Set TCP window size + buf[0x30] = 0x4; // TCP_WIN_SIZE + buf[0x31] = 0x0; // TCP_WIN_SIZE + 1 + + // Zero out TCP checksum and urgent pointer + buf[0x34] = 0; // TCP_CHECKSUM_H_P + buf[0x35] = 0; // TCP_CHECKSUM_L_P + buf[0x36] = 0; // TCP_CHECKSUM_L_P + 1 + buf[0x37] = 0; // TCP_CHECKSUM_L_P + 2 + + // Set MSS option + buf[0x38] = 2; // TCP_OPTIONS_P + buf[0x39] = 4; // TCP_OPTIONS_P + 1 + buf[0x3A] = CLIENTMSS >> 8; // TCP_OPTIONS_P + 2 + buf[0x3B] = CLIENTMSS & 0xff; // TCP_OPTIONS_P + 3 + + // Calculate and set TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + 4, 2); // IP_SRC_IP_P + 8 + TCP_HEADER_LEN_PLAIN + 4 + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P + + udpLog2("TCP_client", "Built TCP data"); + + // Return the total length of the packet + return 20 + 20 + 14 + 4; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4 +} + +// Function to send TCP data +void send_tcp_data(uint8_t *buf, uint16_t dlen) { + udpLog2("TCP_client", "Entering send_tcp_data"); + + // Set TCP flags to PUSH + buf[0x2F] = 0x08; // TCP_FLAGS_P (TCP_FLAGS_PUSH_V) + + // Calculate total length of the IP packet (IP header + TCP header + data length) + uint16_t total_len = 20 + 20 + dlen; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P + + // Fill in the IP header checksum + fill_ip_hdr_checksum(buf); + + // Set TCP checksum to zero before calculation + buf[0x34] = 0; // TCP_CHECKSUM_H_P + buf[0x35] = 0; // TCP_CHECKSUM_L_P + + // Calculate TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + dlen, 2); // IP_SRC_IP_P + 8 + TCP_HEADER_LEN_PLAIN + dlen + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // total_len + ETH_HEADER_LEN + + udpLog2("TCP_client", "Sent TCP data"); +} + +// Function to build and send TCP data packet +void send_data_packet(uint8_t *buf, uint16_t srcPort, const uint8_t *data, uint16_t len) { + // Build TCP data packet + uint16_t pos = build_tcp_data(buf, srcPort); + + // Fill TCP data into the packet + pos = fill_tcp_data(buf, pos, data, len); + + // Send the TCP data packet + send_tcp_data(buf, pos - (20 + 20 + 14)); // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN +} + +// Function to send a FIN packet to close the TCP connection +void send_tcp_fin_packet(uint8_t *buf) { + // Set TCP flags to ACK and FIN + buf[0x2F] = 0x11; // TCP_FLAGS_P offset (TCP_FLAGS_ACK_V | TCP_FLAGS_FIN_V) + + // Set TCP checksum to zero before calculation + buf[0x34] = 0; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0; // TCP_CHECKSUM_L_P offset + + // Calculate TCP checksum + uint16_t tcp_ck = checksum(&buf[0x1E], 8 + 20, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + buf[0x34] = tcp_ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = tcp_ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Set IP checksum to zero before calculation + buf[0x18] = 0; // IP_CHECKSUM_H_P offset + buf[0x19] = 0; // IP_CHECKSUM_L_P offset + + // Calculate IP checksum + uint16_t ip_ck = checksum(&buf[0x0E], 20, 0); // IP_P offset + IP_HEADER_LEN + buf[0x18] = ip_ck >> 8; // IP_CHECKSUM_H_P offset + buf[0x19] = ip_ck & 0xff; // IP_CHECKSUM_L_P offset + + // Log sending FIN packet + char log_buffer[100]; + sprintf(log_buffer, "Sending FIN packet. TCP Checksum: %04x", tcp_ck); + udpLog2("TCP", log_buffer); + + // Send the FIN packet + enc28j60PacketSend(20 + 20 + 14, buf); // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN +} + +//-------------------------------------- STEP4 +// Function to prepare TCP data at a specified position and return the new position +uint16_t prepare_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len) { + // Copy data to the specified position in the buffer + memcpy(&buf[0x37 + pos], data, len); // TCP_CHECKSUM_L_P + 3 offset + + // Return the new position after adding the data + return pos + len; +} + +// Function to transmit prepared TCP data +void transmit_tcp_data(uint8_t *buf, uint16_t dlen) { + udpLog2("TCP_client", "Entering transmit_tcp_data"); + + // Set TCP flags to indicate PUSH operation + buf[0x2F] = 0x18; // TCP_FLAGS_P offset (TCP_FLAGS_PUSH_V) + + // Calculate total length of the IP packet (IP header + TCP header + data length) + uint16_t total_len = 20 + 20 + dlen; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + data length + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P offset + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P offset + + // Fill in the IP header checksum + fill_ip_hdr_checksum(buf); + + // Set TCP checksum to zero before calculation + buf[0x34] = 0; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0; // TCP_CHECKSUM_L_P offset + + // Calculate TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + dlen, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + data length + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // total length + ETH_HEADER_LEN + + udpLog2("TCP_client", "Sent TCP data"); +} + +//-------------------------------------- OTHERS + +// Function to create and send a TCP SYN packet +void client_syn(uint8_t *buf, uint8_t srcport, uint8_t dstport_h, uint8_t dstport_l) { + udpLog2("TCP_client", "Entering client_syn"); + + // Copy MAC addresses to the buffer + memcpy(&buf[0x00], gwmacaddr, 6); // ETH_DST_MAC offset + memcpy(&buf[0x06], macaddr, 6); // ETH_SRC_MAC offset + + // Set Ethernet type to IP + buf[0x0C] = 0x08; // ETH_TYPE_H_P offset (ETHTYPE_IP_H_V) + buf[0x0D] = 0x00; // ETH_TYPE_L_P offset (ETHTYPE_IP_L_V) + + // Fill IP header + fill_buf_p(&buf[0x0E], 9, iphdr); // IP_P offset + + // Set IP total length for SYN packet + buf[0x10] = 0x00; // IP_TOTLEN_H_P offset + buf[0x11] = 44; // IP_TOTLEN_L_P offset (20 bytes IP header + 24 bytes TCP header) + buf[0x17] = 0x06; // IP_PROTO_P offset (IP_PROTO_TCP_V) + + // Set source and destination IP addresses + memcpy(&buf[0x1A], tcpsrvip, 4); // IP_DST_IP_P offset + memcpy(&buf[0x1E], ipaddr, 4); // IP_SRC_IP_P offset + + // Calculate and set IP header checksum + fill_ip_hdr_checksum(buf); + + // Set TCP source and destination ports + buf[0x22] = dstport_h; // TCP_DST_PORT_H_P offset + buf[0x23] = dstport_l; // TCP_DST_PORT_L_P offset + buf[0x20] = TCPCLIENT_SRC_PORT_H; // TCP_SRC_PORT_H_P offset + buf[0x21] = srcport; // TCP_SRC_PORT_L_P offset + + // Zero out sequence number and acknowledgment number + memset(&buf[0x26], 0, 4); // TCP_SEQ_H_P offset + memset(&buf[0x2A], 0, 4); // TCP_ACK_H_P offset + + // Set initial sequence number + buf[0x28] = seqnum >> 8; // TCP_SEQ_H_P offset + 2 + buf[0x29] = seqnum & 0xff; // TCP_SEQ_H_P offset + 3 + seqnum += 3; // Increment sequence number for next use + + // Set TCP header length and flags + buf[0x2E] = 0x60; // TCP_HEADER_LEN_P offset (24 bytes TCP header) + buf[0x2F] = 0x02; // TCP_FLAGS_P offset (TCP_FLAGS_SYN_V) + + // Set TCP window size + buf[0x30] = 0x04; // TCP_WIN_SIZE offset + buf[0x31] = 0x00; // TCP_WIN_SIZE offset + 1 + + // Zero out TCP checksum + buf[0x34] = 0x00; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0x00; // TCP_CHECKSUM_L_P offset + + // Set urgent pointer + buf[0x36] = 0x00; // TCP_CHECKSUM_L_P offset + 1 + buf[0x37] = 0x00; // TCP_CHECKSUM_L_P offset + 2 + + // Set TCP options for MSS (Maximum Segment Size) + buf[0x38] = 0x02; // TCP_OPTIONS_P offset (Option kind = MSS) + buf[0x39] = 0x04; // TCP_OPTIONS_P offset + 1 (Option length) + buf[0x3A] = (CLIENTMSS >> 8); // TCP_OPTIONS_P offset + 2 (MSS value high byte) + buf[0x3B] = CLIENTMSS & 0xff; // TCP_OPTIONS_P offset + 3 (MSS value low byte) + + // Calculate and set TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + 4, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + 4 + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Send the SYN packet + enc28j60PacketSend(20 + 20 + 14 + 4, buf); // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4 + + udpLog2("TCP_client", "Sent TCP Syn"); +} + +// Function to create and send a TCP ACK packet with data, without setting TCP flags +void make_tcp_ack_with_data_noflags(uint8_t *buf, uint16_t dlen) { + uint16_t total_len = 20 + 20 + dlen; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + data length + + // Set the total length field in the IP header + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P offset + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P offset + + // Calculate and set the IP header checksum + fill_ip_hdr_checksum(buf); + + // Zero out the TCP checksum field + buf[0x34] = 0; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0; // TCP_CHECKSUM_L_P offset + + // Calculate the TCP checksum + uint16_t tcp_ck = checksum(&buf[0x1E], 8 + 20 + dlen, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + data length + buf[0x34] = tcp_ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = tcp_ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // total length + ETH_HEADER_LEN + + udpLog2("TCP_client", "Sent TCP ACK with data (no flags)"); +} + + +// Function to initiate a TCP request +uint8_t client_tcp_req( + uint8_t (*result_callback)(uint8_t fd, uint8_t statuscode, uint16_t data_start_pos_in_buf, uint16_t len_of_data), + uint16_t (*datafill_callback)(uint8_t fd), + uint16_t port) { + + udpLog2("TCP_client", "Entering client_tcp_req"); + + // Set the callback functions + client_tcp_result_callback = result_callback; + client_tcp_datafill_callback = datafill_callback; + + // Set the destination port + tcp_client_port_h = (port >> 8) & 0xff; + tcp_client_port_l = port & 0xff; + + // Initialize TCP client state + tcp_client_state = 1; + + // Increment and wrap around the file descriptor + if (++tcp_fd > 7) { + tcp_fd = 0; + } + + udpLog2("TCP_client", "TCP Request initialized"); + + return tcp_fd; +} + + +void fill_ip_tcp_checksum(uint8_t *buf, uint16_t len) { + // IP header length + uint8_t ip_header_len = 0x14; // 20 bytes + + // IP checksum offsets + uint8_t ip_checksum_h_p = 0x18; + uint8_t ip_checksum_l_p = 0x19; + + // TCP checksum offsets + uint8_t tcp_checksum_h_p = 0x2E; + uint8_t tcp_checksum_l_p = 0x2F; + + // Source IP and destination IP offsets + uint8_t ip_src_ip_p = 0x1A; + uint8_t ip_dst_ip_p = 0x1E; + + // Zero out IP checksum field + buf[ip_checksum_h_p] = 0; + buf[ip_checksum_l_p] = 0; + + // Calculate IP header checksum + uint16_t ip_ck = calculate_tcp_checksum(&buf[0x0E], ip_header_len); // Starting at IP header + + // Set IP header checksum + buf[ip_checksum_h_p] = ip_ck >> 8; + buf[ip_checksum_l_p] = ip_ck & 0xFF; + + // Zero out TCP checksum field + buf[tcp_checksum_h_p] = 0; + buf[tcp_checksum_l_p] = 0; + + // Calculate TCP checksum + uint16_t tcp_ck = calculate_tcp_checksum(&buf[ip_src_ip_p], len); // Starting at source IP + + // Set TCP checksum + buf[tcp_checksum_h_p] = tcp_ck >> 8; + buf[tcp_checksum_l_p] = tcp_ck & 0xFF; +} + +void fill_tcp_ip_hdr_checksum(uint8_t *buf) { + uint16_t ck; + + // Zero out IP checksum field + buf[0x18] = 0; + buf[0x19] = 0; + + // Calculate IP header checksum + ck = calculate_tcp_checksum(&buf[0x0E], 20); // IP_P = 0x0E, IP_HEADER_LEN = 20 + + // Set IP header checksum + buf[0x18] = ck >> 8; + buf[0x19] = ck & 0xff; +} + +uint16_t calculate_tcp_checksum(uint8_t *buf, uint16_t len) { + uint32_t sum = 0; + + // Sum up 2-byte values from the buffer + for (int i = 0; i < len; i += 2) { + sum += (buf[i] << 8) | buf[i + 1]; + } + + // Add carry bits to the lower 16 bits + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + // One's complement + return ~sum; +} + +void log_ip_header(uint8_t *buf) { + char log_buffer[100]; + udpLog2("IP Header", "-----------------"); + sprintf(log_buffer, "IP Version/IHL: 0x%02x", buf[IP_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Type of Service: 0x%02x", buf[IP_TOS_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Total Length: %d", (buf[IP_TOTLEN_H_P] << 8) | buf[IP_TOTLEN_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "ID: 0x%04x", (buf[IP_ID_H_P] << 8) | buf[IP_ID_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Flags/Fragment Offset: 0x%04x", (buf[IP_FLAGS_H_P] << 8) | buf[IP_FLAGS_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "TTL: %d", buf[IP_TTL_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Protocol: %d", buf[IP_PROTO_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Header Checksum: 0x%04x", (buf[IP_CHECKSUM_H_P] << 8) | buf[IP_CHECKSUM_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Source IP: %d.%d.%d.%d", buf[IP_SRC_IP_P], buf[IP_SRC_IP_P + 1], buf[IP_SRC_IP_P + 2], buf[IP_SRC_IP_P + 3]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Destination IP: %d.%d.%d.%d", buf[IP_DST_IP_P], buf[IP_DST_IP_P + 1], buf[IP_DST_IP_P + 2], buf[IP_DST_IP_P + 3]); + udpLog2("IP Header", log_buffer); +} + + +void log_tcp_header(uint8_t *buf) { + char log_buffer[100]; + udpLog2("TCP Header", "-----------------"); + sprintf(log_buffer, "Source Port: %d", (buf[TCP_SRC_PORT_H_P] << 8) | buf[TCP_SRC_PORT_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Destination Port: %d", (buf[TCP_DST_PORT_H_P] << 8) | buf[TCP_DST_PORT_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Sequence Number: 0x%08x", (buf[TCP_SEQ_H_P] << 24) | (buf[TCP_SEQ_H_P + 1] << 16) | (buf[TCP_SEQ_H_P + 2] << 8) | buf[TCP_SEQ_H_P + 3]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Acknowledgment Number: 0x%08x", (buf[TCP_ACK_H_P] << 24) | (buf[TCP_ACK_H_P + 1] << 16) | (buf[TCP_ACK_H_P + 2] << 8) | buf[TCP_ACK_H_P + 3]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Data Offset: %d", (buf[TCP_HEADER_LEN_P] >> 4) * 4); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Flags: 0x%02x", buf[TCP_FLAGS_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Window Size: %d", (buf[TCP_WINDOWSIZE_H_P] << 8) | buf[TCP_WINDOWSIZE_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Checksum: 0x%04x", (buf[TCP_CHECKSUM_H_P] << 8) | buf[TCP_CHECKSUM_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Urgent Pointer: %d", (buf[TCP_URGENT_PTR_H_P] << 8) | buf[TCP_URGENT_PTR_L_P]); + udpLog2("TCP Header", log_buffer); +} diff --git a/src/timers.c b/Src/timers.c similarity index 98% rename from src/timers.c rename to Src/timers.c index d7f2074..0ad8534 100644 --- a/src/timers.c +++ b/Src/timers.c @@ -7,6 +7,7 @@ #include "timers.h" #include +#include #include "LogManager.h" // Counter for the number of timer interrupts diff --git a/Src/websrv_help_functions.c b/Src/websrv_help_functions.c new file mode 100644 index 0000000..95cc8a0 --- /dev/null +++ b/Src/websrv_help_functions.c @@ -0,0 +1,365 @@ +/* + * websrv_help_functions.C + * + * Created on: Jun 4, 2024 + * Author: dtneo + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#include "defines.h" +#include "websrv_help_functions.h" +#include "tcp.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" + +#ifdef FROMDECODE_websrv_help + +typedef void (*browser_callback_t)(uint8_t, uint16_t, uint16_t); +static browser_callback_t client_browser_callback = NULL; + +// Function to send the logs page +uint16_t send_logs_page(uint8_t *buf) { + const char *logs_page = "Logs

System Logs

Log content goes here

"; + uint16_t len = strlen(logs_page); + memcpy(buf + TCP_DATA_P, logs_page, len); + return len; +} + +// Function to send a test page +uint16_t send_test_page(uint8_t *buf) { + const char *test_page = "Test

Test Page

Test content goes here

"; + uint16_t len = strlen(test_page); + memcpy(buf + TCP_DATA_P, test_page, len); + return len; +} + +// Function to send an HTTP response +void www_server_reply(uint8_t *buf, uint16_t dlen, tcp_connection_t *tcp_conn) { + uint16_t ck; + uint8_t i; + + // Setup the TCP header + buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V; // FIN, ACK, and PSH flags + buf[TCP_SEQACK_P] = (tcp_conn->ack_num >> 24) & 0xFF; + buf[TCP_SEQACK_P + 1] = (tcp_conn->ack_num >> 16) & 0xFF; + buf[TCP_SEQACK_P + 2] = (tcp_conn->ack_num >> 8) & 0xFF; + buf[TCP_SEQACK_P + 3] = tcp_conn->ack_num & 0xFF; + tcp_conn->seq_num += dlen; + buf[TCP_SEQ_P] = (tcp_conn->seq_num >> 24) & 0xFF; + buf[TCP_SEQ_P + 1] = (tcp_conn->seq_num >> 16) & 0xFF; + buf[TCP_SEQ_P + 2] = (tcp_conn->seq_num >> 8) & 0xFF; + buf[TCP_SEQ_P + 3] = tcp_conn->seq_num & 0xFF; + + // TCP header length is 20 bytes + buf[TCP_HEADER_LEN_P] = 0x50; + + // Set the window size + buf[TCP_WINDOWSIZE_H_P] = 0x05; + buf[TCP_WINDOWSIZE_L_P] = 0xB4; + + // Calculate and set the checksum + ck = 0; + // Pseudo header part 1: source and destination IPs + for (i = 0; i < 4; i++) { + ck += buf[IP_SRC_IP_P + i]; + ck += buf[IP_DST_IP_P + i]; + } + // Pseudo header part 2: protocol and TCP length + ck += IP_PROTO_TCP_V + (TCP_HEADER_LEN_PLAIN + dlen); + // TCP header and data + for (i = IP_HEADER_LEN + ETH_HEADER_LEN; i < IP_HEADER_LEN + ETH_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; i += 2) { + ck += (uint16_t)((buf[i] << 8) | buf[i + 1]); + } + while (ck >> 16) { + ck = (ck & 0xFFFF) + (ck >> 16); + } + ck = ~ck; + buf[TCP_CHECKSUM_H_P] = ck >> 8; + buf[TCP_CHECKSUM_L_P] = ck & 0xFF; + + // Send the packet + enc28j60PacketSend(IP_HEADER_LEN + ETH_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen, buf); +} + +uint16_t www_client_internal_datafill_callback(uint8_t fd) { + if (fd != www_fd) { + return 0; + } + + char strbuf[6]; + uint16_t len = 0; + + if (browsertype == 0) { + // GET Request + len = fill_tcp_data(bufptr, len, (uint8_t *)"GET ", strlen("GET ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_urlbuf, strlen(client_urlbuf)); + if (client_urlbuf_var) { + len = fill_tcp_data(bufptr, len, (uint8_t *)client_urlbuf_var, strlen(client_urlbuf_var)); + } + len = fill_tcp_data(bufptr, len, (uint8_t *)" HTTP/1.1\r\nHost: ", strlen(" HTTP/1.1\r\nHost: ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_hoststr, strlen(client_hoststr)); + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n", strlen("\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n")); + } else { + // POST Request + len = fill_tcp_data(bufptr, len, (uint8_t *)(client_method ? client_method : "POST "), strlen(client_method ? client_method : "POST ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_urlbuf, strlen(client_urlbuf)); + len = fill_tcp_data(bufptr, len, (uint8_t *)" HTTP/1.1\r\nHost: ", strlen(" HTTP/1.1\r\nHost: ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_hoststr, strlen(client_hoststr)); + + if (client_additionalheaderline) { + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\n", strlen("\r\n")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_additionalheaderline, strlen(client_additionalheaderline)); + } + + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n", strlen("\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n")); + len = fill_tcp_data(bufptr, len, (uint8_t *)"Content-Length: ", strlen("Content-Length: ")); + snprintf(strbuf, sizeof(strbuf), "%zu", strlen(client_postval)); + len = fill_tcp_data(bufptr, len, (uint8_t *)strbuf, strlen(strbuf)); + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n", strlen("\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_postval, strlen(client_postval)); + } + + // Logging the final request for debugging + udpLog2("WWW_client", "Generated HTTP request:"); + udpLog2("WWW_client", (char *)bufptr); + + return len; +} + + +/** + * @brief Internal result callback for the WWW client. + * + * This function processes the result of the HTTP request. + * + * @param fd The file descriptor. + * @param statuscode The status code. + * @param datapos The position of the data in the buffer. + * @param len_of_data The length of the data. + * @return 0 Always returns 0. + */ +uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data) { + char log_msg[50]; + + // Log the entry into the function + udpLog2("WWW_client", "Entering www_client_internal_result_callback"); + + // Log the parameters received + snprintf(log_msg, sizeof(log_msg), "FD: %d, Status: %d, DataPos: %d, Len: %d", fd, statuscode, datapos, len_of_data); + udpLog2("WWW_client", log_msg); + + if (fd != www_fd) { + udpLog2("WWW_client", "File descriptor mismatch"); + client_browser_callback(4, 0, 0); + return 0; + } + + if (statuscode == 0 && len_of_data > 12 && client_browser_callback) { + uint16_t offset = TCP_SRC_PORT_H_P + ((bufptr[TCP_HEADER_LEN_P] >> 4) * 4); + uint8_t *status_code_pos = &bufptr[datapos + 9]; + + // Log the status code + snprintf(log_msg, sizeof(log_msg), "Status Code: %.3s", status_code_pos); + udpLog2("WWW_client", log_msg); + + if (strncmp("200", (char *)status_code_pos, 3) == 0) { + udpLog2("WWW_client", "HTTP 200 OK"); + client_browser_callback(0, offset, len_of_data); + } else { + udpLog2("WWW_client", "HTTP Error"); + client_browser_callback(1, offset, len_of_data); + } + } else { + udpLog2("WWW_client", "Invalid status or data length"); + } + + udpLog2("WWW_client", "Exiting www_client_internal_result_callback"); + return 0; +} + + +void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t, uint16_t, uint16_t)) { + client_urlbuf = urlbuf; + client_urlbuf_var = urlbuf_varpart; + client_hoststr = hoststr; + browsertype = 0; + client_browser_callback = callback; + www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); +} + +void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval, void (*callback)(uint8_t, uint16_t)) { + client_urlbuf = urlbuf; + client_hoststr = hoststr; + client_additionalheaderline = additionalheaderline; + client_method = method; + client_postval = postval; + browsertype = 1; + client_browser_callback = (void (*)(uint8_t, uint16_t, uint16_t))callback; + www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); +} + +// search for a string of the form key=value in +// a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\r\n +// +// The returned value is stored in strbuf. You must allocate +// enough storage for strbuf, maxlen is the size of strbuf. +// I.e the value it is declated with: strbuf[5]-> maxlen=5 +uint8_t find_key_val(char *str, char *strbuf, uint8_t maxlen, char *key) { + uint8_t found = 0; + uint8_t i = 0; + char *kp = key; + + // Find the key in the string + while (*str && *str != ' ' && *str != '\n' && !found) { + if (*str == *kp) { + kp++; + if (*kp == '\0' && *(str + 1) == '=') { + found = 1; + str += 2; // Move past '=' + } + } else { + kp = key; + } + str++; + } + + // Copy the value to the buffer if key is found + if (found) { + while (*str && *str != ' ' && *str != '\n' && *str != '&' && i < maxlen - 1) { + strbuf[i++] = *str++; + } + strbuf[i] = '\0'; + } + + // Return the length of the value + return i; +} + +// Convert a single hex digit character to its integer value +unsigned char h2int(char c) { + if (c >= '0' && c <= '9') { + return (unsigned char)(c - '0'); + } else if (c >= 'a' && c <= 'f') { + return (unsigned char)(c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + return (unsigned char)(c - 'A' + 10); + } else { + return 0; + } +} + + +// Decode a URL string, e.g., "hello%20joe" or "hello+joe" becomes "hello joe" +void urldecode(char *urlbuf) { + char c; + char *dst = urlbuf; + + while ((c = *urlbuf)) { + if (c == '+') { + c = ' '; + } else if (c == '%') { + urlbuf++; + char high = *urlbuf++; + char low = *urlbuf; + c = (h2int(high) << 4) | h2int(low); + } + *dst++ = c; + urlbuf++; + } + *dst = '\0'; +} +#endif // FROMDECODE_websrv_help + +#ifdef URLENCODE_websrv_help +// Convert a single character to a 2-digit hex string +// A terminating '\0' is added +void int2h(char c, char *hstr) { + // Convert the lower 4 bits + hstr[1] = (c & 0xF) + '0'; + if ((c & 0xF) > 9) { + hstr[1] = (c & 0xF) - 10 + 'a'; + } + + // Convert the upper 4 bits + c = (c >> 4) & 0xF; + hstr[0] = c + '0'; + if (c > 9) { + hstr[0] = c - 10 + 'a'; + } + + // Null-terminate the string + hstr[2] = '\0'; +} + +// There must be enough space in urlbuf. In the worst case, that is +// 3 times the length of str. +void urlencode(char *str, char *urlbuf) { + char c; + while ((c = *str)) { + if (c == ' ' || isalnum((unsigned char)c)) { + if (c == ' ') { + c = '+'; + } + *urlbuf++ = c; + } else { + *urlbuf++ = '%'; + int2h(c, urlbuf); + urlbuf += 2; + } + str++; + } + *urlbuf = '\0'; +} +#endif // URLENCODE_websrv_help +// Parse a string and extract the IP to bytestr +uint8_t parse_ip(uint8_t *bytestr, char *str) { + char *sptr = NULL; + uint8_t i = 0; + + // Initialize bytestr to zero + for (i = 0; i < 4; i++) { + bytestr[i] = 0; + } + + i = 0; + while (*str && i < 4) { + if (sptr == NULL && isdigit((unsigned char)*str)) { + sptr = str; + } + if (*str == '.') { + *str = '\0'; + bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); + i++; + sptr = NULL; + } + str++; + } + + if (i == 3 && sptr != NULL) { + bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); + return 0; + } + return 1; +} + +// Convert a byte string to a human-readable display string +// (base is 10 for IP and 16 for MAC address), len is 4 for IP address and 6 for MAC. +void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base) { + uint8_t i, j = 0; + + for (i = 0; i < len; i++) { + if (base == 10) { + j += sprintf(&resultstr[j], "%d", bytestr[i]); + } else if (base == 16) { + j += sprintf(&resultstr[j], "%02x", bytestr[i]); + } + resultstr[j++] = separator; + } + resultstr[j - 1] = '\0'; // Replace the last separator with null terminator +} + +// end of websrv_help_functions.c diff --git a/inc/UDPCommandHandler.h b/inc/UDPCommandHandler.h deleted file mode 100644 index d34b1c4..0000000 --- a/inc/UDPCommandHandler.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * UDPCommandHandler.h - * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This header file declares the necessary structures and functions for handling - * UDP commands received by the STM32 microcontroller. It includes function - * prototypes for various home automation tasks such as sending the STM32 state, - * sending the RTC date and time, turning on a light, activating a relay, and - * sending the temperature. - */ - -#ifndef INC_UDPCOMMANDHANDLER_H_ -#define INC_UDPCOMMANDHANDLER_H_ - -#include "defines.h" - -// Define a function pointer type for command functions -typedef void (*CommandFunction)(); - -// Structure to map commands to their corresponding functions -typedef struct { - const char *command; // Command string - CommandFunction function; // Function to execute for the command -} CommandMapping; - -// Declare the command table as an external variable -extern CommandMapping commandTable[]; - -// Declare command functions here -void sendSTM32State(); // Function to send the STM32 state -void sendRTCDateTime(); // Function to send the RTC date and time -void turnOnLight(); // Function to turn on a light -void activateRelay(); // Function to activate a relay -void sendTemperature(); // Function to send the temperature - -#endif /* INC_UDPCOMMANDHANDLER_H_ */ diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h deleted file mode 100644 index f1e7137..0000000 --- a/inc/ip_arp_udp_tcp.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * ip_arp_udp_tcp.h - * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. - */ - -#ifndef IP_ARP_UDP_TCP_H -#define IP_ARP_UDP_TCP_H - -#include "defines.h" - -#define bool _Bool -#define TRUE 1 -#define FALSE 0 - -void __attribute__((weak)) ES_PingCallback(void); - -// -- web server functions -- -// you must call this function once before you use any of the other server functions: -void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port); -// for a UDP server: -uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len); -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); -uint8_t eth_type_is_arp_reply(uint8_t *buf); -uint8_t eth_type_is_arp_req(uint8_t *buf); - -void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); -void make_echo_reply_from_request(uint8_t *buf,uint16_t len); - -void make_arp_answer_from_request(uint8_t *buf); -void make_tcp_synack_from_syn(uint8_t *buf); -void init_len_info(uint8_t *buf); -uint16_t get_tcp_data_pointer(void); - -void make_tcp_ack_from_any(uint8_t *buf,int16_t datlentoack,uint8_t addflags); -void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen); -void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen); - -//extern void fill_buf_p(uint8_t *buf,uint16_t len, const prog_char *progmem_s); -void fill_ip_hdr_checksum(uint8_t *buf); -uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type); - -// for a UDP server: -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); -void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); - - -// functions to fill the web pages with data: -//extern uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s); -uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s); -uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len); -// send data from the web server to the client: -void www_server_reply(uint8_t *buf,uint16_t dlen); - -// -- client functions -- -#if defined (WWW_client) || defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -uint8_t client_store_gw_mac(uint8_t *buf); - -void client_set_gwip(uint8_t *gwipaddr); -// do a continues refresh until found: -void client_gw_arp_refresh(void); -// do an arp request once (call this function only if enc28j60PacketReceive returned zero: -void client_arp_whohas(uint8_t *buf,uint8_t *gwipaddr); -uint8_t client_waiting_gw(void); // 1 no GW mac yet, 0 have a gw mac - -uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort ); -void send_tcp_data(uint8_t *buf,uint16_t dlen ); -#endif // WWW_client TCP_client etc - - -//#if defined (WWW_client) || defined (TCP_client) -//#define client_set_wwwip client_tcp_set_serverip -// set the ip of the next tcp session that we open to a server -void client_tcp_set_serverip(uint8_t *ipaddr); -//#endif - - -#ifdef TCP_client -uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port); -void tcp_client_send_packet(uint8_t *buf,uint16_t dest_port, uint16_t src_port, uint8_t flags, uint8_t max_segment_size, - uint8_t clear_seqck, uint16_t next_ack_num, uint16_t dlength, uint8_t *dest_mac, uint8_t *dest_ip); -uint16_t tcp_get_dlength ( uint8_t *buf ); - -#endif // TCP_client - -#define HTTP_HEADER_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) - -#ifdef WWW_client - -// ----- http get -void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); - -// The callback is a reference to a function which must look like this: -// void browserresult_callback(uint8_t statuscode,uint16_t datapos) -// statuscode=0 means a good webpage was received, with http code 200 OK -// statuscode=1 an http error was received -// statuscode=2 means the other side in not a web server and in this case datapos is also zero -// ----- http post -// client web browser using http POST operation: -// additionalheaderline must be set to NULL if not used. -// postval is a string buffer which can only be de-allocated by the caller -// when the post operation was really done (e.g when callback was executed). -// postval must be urlencoded. - -void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval,void (*callback)(uint8_t,uint16_t)); - -// The callback is a reference to a function which must look like this: -// void browserresult_callback(uint8_t statuscode,uint16_t datapos) -// statuscode=0 means a good webpage was received, with http code 200 OK -// statuscode=1 an http error was received -// statuscode=2 means the other side in not a web server and in this case datapos is also zero -#endif // WWW_client - -#ifdef NTP_client -void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport); -void client_ntp_process_answer(uint8_t *buf, uint16_t plen); -#endif - -#ifdef UDP_client -// There are two ways of using this UDP client: -// -// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], -// you send the packet by calling send_udp_transmit -// -// 2) You just allocate a large enough buffer for you data and you call send_udp and nothing else -// needs to be done. -// -void send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport); -void send_udp_transmit(uint8_t *buf,uint16_t datalen); - -// send_udp sends via gwip, you must call client_set_gwip at startup, datalen must be less than 220 bytes -void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport); -void udp_packet_process(uint8_t *buf, uint16_t plen); -#endif // UDP_client - - -// you can find out who ping-ed you if you want: -void register_ping_rec_callback(void (*callback)(uint8_t *srcip)); - -#ifdef PING_client -void client_icmp_request(uint8_t *buf,uint8_t *destip); -// you must loop over this function to check if there was a ping reply: -uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost); -#endif // PING_client - -#ifdef WOL_client -void send_wol(uint8_t *buf,uint8_t *wolmac); -#endif // WOL_client - -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ); -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ); - -// return 0 to just continue in the packet loop and return the position -// of the tcp data if there is tcp data part -uint16_t packetloop_icmp_tcp(uint8_t *buf,uint16_t plen); - -uint8_t nextTcpState( uint8_t *buf,uint16_t plen ); -uint8_t currentTcpState( ); -uint8_t tcpActiveOpen( uint8_t *buf,uint16_t plen, - uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data), - uint16_t (*datafill_callback)(uint8_t fd), - uint16_t port); - -void tcpPassiveOpen( uint8_t *buf,uint16_t plen ); -void tcpClose( uint8_t *buf,uint16_t plen ); - -#endif /* IP_ARP_UDP_TCP_H */ -//@} diff --git a/inc/net.h b/inc/net.h deleted file mode 100644 index 30447ab..0000000 --- a/inc/net.h +++ /dev/null @@ -1,193 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 - * - * Based on the net.h file from the AVRlib library by Pascal Stang. - * For AVRlib See http://www.procyonengineering.com/ - * Used with explicit permission of Pascal Stang. - * - * Chip type : ATMEGA88 with ENC28J60 - *********************************************/ - -// notation: _P = position of a field -// _V = value of a field - -//@{ - -#ifndef NET_H -#define NET_H - -#include "defines.h" - -// ******* ETH ******* -#define ETH_HEADER_LEN 14 -// values of certain bytes: -#define ETHTYPE_ARP_H_V 0x08 -#define ETHTYPE_ARP_L_V 0x06 -#define ETHTYPE_IP_H_V 0x08 -#define ETHTYPE_IP_L_V 0x00 -// byte positions in the ethernet frame: -// -// Ethernet type field (2bytes): -#define ETH_TYPE_H_P 12 -#define ETH_TYPE_L_P 13 -// -#define ETH_DST_MAC 0 -#define ETH_SRC_MAC 6 - - -// ******* ARP ******* -#define ETH_ARP_OPCODE_REPLY_H_V 0x0 -#define ETH_ARP_OPCODE_REPLY_L_V 0x02 -#define ETH_ARP_OPCODE_REQ_H_V 0x0 -#define ETH_ARP_OPCODE_REQ_L_V 0x01 -// start of arp header: -#define ETH_ARP_P 0xe -// -#define ETHTYPE_ARP_L_V 0x06 -// arp.dst.ip -#define ETH_ARP_DST_IP_P 0x26 -// arp.opcode -#define ETH_ARP_OPCODE_H_P 0x14 -#define ETH_ARP_OPCODE_L_P 0x15 -// arp.src.mac -#define ETH_ARP_SRC_MAC_P 0x16 -#define ETH_ARP_SRC_IP_P 0x1c -#define ETH_ARP_DST_MAC_P 0x20 -#define ETH_ARP_DST_IP_P 0x26 - -// ******* IP ******* -#define IP_HEADER_LEN 20 - -#define IP_PROTO_ICMP_V 0x01 -#define IP_PROTO_TCP_V 0x06 -#define IP_PROTO_UDP_V 0x11 -#define IP_V4_V 0x40 -#define IP_HEADER_LENGTH_V 0x05 - -#define IP_P 0x0E -#define IP_HEADER_VER_LEN_P 0x0E -#define IP_TOS_P 0x0F -#define IP_TOTLEN_H_P 0x10 -#define IP_TOTLEN_L_P 0x11 -#define IP_ID_H_P 0x12 -#define IP_ID_L_P 0x13 -#define IP_FLAGS_P 0x14 -#define IP_FLAGS_H_P 0x14 -#define IP_FLAGS_L_P 0x15 -#define IP_TTL_P 0x16 -#define IP_PROTO_P 0x17 -#define IP_CHECKSUM_P 0x18 -#define IP_CHECKSUM_H_P 0x18 -#define IP_CHECKSUM_L_P 0x19 -#define IP_SRC_IP_P 0x1A -#define IP_DST_IP_P 0x1E - -#define IP_SRC_P 0x1a -#define IP_DST_P 0x1e -#define IP_HEADER_LEN_VER_P 0xe - -// ip.src -/* -#define IP_SRC_P 0x1a -#define IP_DST_P 0x1e -#define IP_P 0xe -#define IP_HEADER_LEN_VER_P 0xe -#define IP_CHECKSUM_P 0x18 -#define IP_TTL_P 0x16 -#define IP_FLAGS_P 0x14 -#define IP_TOTLEN_H_P 0x10 -#define IP_TOTLEN_L_P 0x11 - -#define IP_PROTO_P 0x17 - -#define IP_PROTO_ICMP_V 1 -#define IP_PROTO_TCP_V 6 -// 17=0x11 -#define IP_PROTO_UDP_V 17 -#define IP_V4_V 0x40 -*/ -// ******* ICMP ******* -#define ICMP_TYPE_ECHOREPLY_V 0 -#define ICMP_TYPE_ECHOREQUEST_V 8 -// -#define ICMP_TYPE_P 0x22 -#define ICMP_CHECKSUM_P 0x24 -#define ICMP_CHECKSUM_H_P 0x24 -#define ICMP_CHECKSUM_L_P 0x25 -#define ICMP_IDENT_H_P 0x26 -#define ICMP_IDENT_L_P 0x27 -#define ICMP_DATA_P 0x2a - -// ******* UDP ******* -#define UDP_HEADER_LEN 8 -// -#define UDP_SRC_PORT_H_P 0x22 -#define UDP_SRC_PORT_L_P 0x23 -#define UDP_DST_PORT_H_P 0x24 -#define UDP_DST_PORT_L_P 0x25 -// -#define UDP_LEN_H_P 0x26 -#define UDP_LEN_L_P 0x27 -#define UDP_CHECKSUM_H_P 0x28 -#define UDP_CHECKSUM_L_P 0x29 -#define UDP_DATA_P 0x2a - -// ******* TCP ******* -#define TCP_FLAGS_FIN_V 0x01 -#define TCP_FLAGS_SYN_V 0x02 -#define TCP_FLAGS_RST_V 0x04 -#define TCP_FLAGS_PUSH_V 0x08 -#define TCP_FLAGS_ACK_V 0x10 -#define TCP_FLAGS_SYNACK_V 0x12 -#define TCP_FLAGS_PSHACK_V 0x18 - -#define TCP_SRC_PORT_H_P 0x22 -#define TCP_SRC_PORT_L_P 0x23 -#define TCP_DST_PORT_H_P 0x24 -#define TCP_DST_PORT_L_P 0x25 -#define TCP_SEQ_P 0x26 // the tcp seq number is 4 bytes 0x26-0x29 -#define TCP_SEQ_H_P 0x26 -#define TCP_SEQACK_P 0x2a -#define TCP_SEQACK_H_P 0x2a -// flags: SYN=2 -#define TCP_FLAGS_P 0x2f -//#define TCP_FLAG_P 0x2f -#define TCP_WINDOWSIZE_H_P 0x30 // 2 bytes -#define TCP_WINDOWSIZE_L_P 0x31 -#define TCP_CHECKSUM_H_P 0x32 -#define TCP_CHECKSUM_L_P 0x33 -#define TCP_URGENT_PTR_H_P 0x34 // 2 bytes -#define TCP_URGENT_PTR_L_P 0x35 -#define TCP_OPTIONS_P 0x36 -#define TCP_DATA_P 0x36 -// plain len without the options: -#define TCP_HEADER_LEN_PLAIN 20 -#define TCP_HEADER_LEN_P 0x2e -#define TCP_WIN_SIZE 0x30 -#define TCP_CHECKSUM_H_P 0x32 -#define TCP_CHECKSUM_L_P 0x33 -#define TCP_OPTIONS_P 0x36 -// -// - -// DNS States for access in applications -#define DNS_STATE_INIT 0 -#define DNS_STATE_REQUESTED 1 -#define DNS_STATE_ANSWER 2 - -// DHCP States for access in applications -#define DHCP_STATE_INIT 0 -#define DHCP_STATE_DISCOVER 1 -#define DHCP_STATE_OFFER 2 -#define DHCP_STATE_REQUEST 3 -#define DHCP_STATE_ACK 4 -#define DHCP_STATE_OK 5 -#define DHCP_STATE_RENEW 6 - - -#endif -//@} - diff --git a/inc/stm32includes.h b/inc/stm32includes.h deleted file mode 100644 index ba95bd7..0000000 --- a/inc/stm32includes.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __STM32INCLUDES_H -#define __STM32INCLUDES_H - - - - - -#endif diff --git a/inc/timers.h b/inc/timers.h index 466c627..663de9b 100644 --- a/inc/timers.h +++ b/inc/timers.h @@ -12,9 +12,6 @@ extern TIM_HandleTypeDef htim4; - -extern uint8_t TIM4Flag; // Flag for renew the networt - void timerLog(TIM_HandleTypeDef *htim); void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); void generalTimersInit(); diff --git a/src/UDPCommandHandler.c b/src/UDPCommandHandler.c deleted file mode 100644 index 38544bf..0000000 --- a/src/UDPCommandHandler.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * UDPCommandHandler.c - * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This source file implements the functions for handling UDP commands received by - * the STM32 microcontroller. It defines a command table that maps specific command - * strings to their corresponding functions. The functions included here perform - * various home automation tasks such as sending the STM32 state, sending the RTC - * date and time, turning on a light, activating a relay, and sending the temperature. - */ - -#include "UDPCommandHandler.h" - -// Define the command table -CommandMapping commandTable[] = { - {"sendSTM32State", sendSTM32State}, - {"sendRTCDateTime", sendRTCDateTime}, - {"turnOnLight", turnOnLight}, - {"activateRelay", activateRelay}, - {"sendTemperature", sendTemperature}, - {NULL, NULL} // End marker -}; - -// Implement the command functions -void sendSTM32State() { - // Code to retrieve and send the STM32 state - // For example, send the state of GPIOs, active peripherals, etc. -} - -void sendRTCDateTime() { - getRTCDateTime(); -} - -void turnOnLight() { - // Code to turn on a light - // For example, activate a GPIO connected to a light -} - -void activateRelay() { - // Code to activate a relay - // For example, activate a GPIO connected to a relay -} - -void sendTemperature() { - // Code to retrieve and send the temperature - // For example, read a temperature sensor and send the value -} diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c deleted file mode 100644 index 2a9153f..0000000 --- a/src/ip_arp_udp_tcp.c +++ /dev/null @@ -1,1498 +0,0 @@ -/* - * ip_arp_udp_tcp.c - * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. - */ - -#include "defines.h" -#include "net.h" -#include "enc28j60.h" -#include "ip_arp_udp_tcp.h" -#include "UDPCommandHandler.h" -#include "websrv_help_functions.h" - -// Web server port, used when implementing webserver -static uint8_t wwwport_l=80; // server port -static uint8_t wwwport_h=0; // Note: never use same as TCPCLIENT_SRC_PORT_H - -#if defined (WWW_client) || defined (TCP_client) - -// just lower byte, the upper byte is TCPCLIENT_SRC_PORT_H: -static uint8_t tcpclient_src_port_l=1; -static uint8_t tcp_fd=0; // a file descriptor, will be encoded into the port -static uint8_t tcp_client_state=0; -// TCP client Destination port -static uint8_t tcp_client_port_h=0; -static uint8_t tcp_client_port_l=0; -// This function will be called if we ever get a result back from the -// TCP connection to the sever: -// close_connection= your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code} -// statuscode=0 means the buffer has valid data -static uint8_t (*client_tcp_result_callback)(uint8_t,uint8_t,uint16_t,uint16_t); -// len_of_data_filled_in=your_client_tcp_datafill_callback(uint8_t fd){...your code} -static uint16_t (*client_tcp_datafill_callback)(uint8_t); -#endif -#define TCPCLIENT_SRC_PORT_H 11 -#if defined (WWW_client) -static uint8_t www_fd=0; -static uint8_t browsertype=0; // 0 = get, 1 = post -static void (*client_browser_callback)(uint8_t,uint16_t,uint16_t); -static char *client_additionalheaderline; -static char *client_method; // for POST or PUT, no trailing space needed -static char *client_urlbuf; -static char *client_hoststr; -static char *client_postval; -static char *client_urlbuf_var; -static uint8_t *bufptr=0; // ugly workaround for backward compatibility -#endif -static void (*icmp_callback)(uint8_t *ip); -// 0=wait, 1=first req no anser, 2=have gwmac, 4=refeshing but have gw mac, 8=accept an arp reply -#define WGW_INITIAL_ARP 1 -#define WGW_HAVE_GW_MAC 2 -#define WGW_REFRESHING 4 -#define WGW_ACCEPT_ARP_REPLY 8 -static int16_t delaycnt=0; -static uint8_t gwip[4]; -static uint8_t gwmacaddr[6]; -static uint8_t tcpsrvip[4]; -static volatile uint8_t waitgwmac=WGW_INITIAL_ARP; - -uint8_t macaddr[6]; -static uint8_t ipaddr[4]; -static uint16_t info_data_len=0; -static uint16_t info_hdr_len = 0; -static uint8_t seqnum=0xa; // my initial tcp sequence number - -#define CLIENTMSS 550 -#define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) - -const char arpreqhdr[] ={0,1,8,0,6,4,0,1}; -#if defined (NTP_client) || defined (WOL_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on ip, 0x20 is ttl (time to live) -#endif -#ifdef NTP_client -const char ntpreqhdr[] ={0xe3,0,4,0xfa,0,1,0,1,0,0}; -#endif - - -#ifndef DISABLE_IP_STACK -// The checksum function calculates checksums for IP, UDP, and TCP packets. -// For IP and ICMP, the checksum is calculated over the IP header only. -// For UDP and TCP, the checksum includes a pseudo-header using the source and destination IP fields. -uint16_t checksum(uint8_t *buf, uint16_t len, uint8_t type) { - uint32_t sum = 0; - - // Add protocol-specific values for UDP and TCP - if (type == 1) { - sum += IP_PROTO_UDP_V; // Protocol UDP - sum += len - 8; // UDP length (data + header) - } else if (type == 2) { - sum += IP_PROTO_TCP_V; // Protocol TCP - sum += len - 8; // TCP length (data + header) - } - - // Sum up 16-bit words - while (len > 1) { - sum += 0xFFFF & (*buf << 8 | *(buf + 1)); - buf += 2; - len -= 2; - } - - // If there's a leftover byte, add it (padded with zero) - if (len) { - sum += (0xFF & *buf) << 8; - } - - // Fold 32-bit sum to 16 bits - while (sum >> 16) { - sum = (sum & 0xFFFF) + (sum >> 16); - } - - // Return one's complement of the sum - return (uint16_t)(sum ^ 0xFFFF); -} -#endif - -// This function initializes the web server. -// You must call this function once before you use any of the other functions. -void init_ip_arp_udp_tcp(uint8_t *mymac, uint8_t *myip, uint16_t port) { - wwwport_h = (port >> 8) & 0xFF; - wwwport_l = port & 0xFF; - memcpy(ipaddr, myip, 4); - memcpy(macaddr, mymac, 6); -} -#ifndef DISABLE_IP_STACK - -// Check if an IP message is from a specified IP address -uint8_t check_ip_message_is_from(uint8_t *buf, uint8_t *ip) { - return memcmp(&buf[IP_SRC_P], ip, 4) == 0; -} - -// Check if the Ethernet frame is an ARP request for my IP address -uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint16_t len) { - if (len < 41) { - return 0; - } - if (buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V) { - return 0; - } - return memcmp(&buf[ETH_ARP_DST_IP_P], ipaddr, 4) == 0; -} - -// Check if the Ethernet frame is an IP packet addressed to my IP address -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf, uint16_t len) { - // eth + ip header is 34 bytes + udp header is 8 bytes - if (len < 42) { - return 0; - } - if (buf[ETH_TYPE_H_P] != ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_IP_L_V) { - return 0; - } - // must be IP V4 and 20 byte header - if (buf[IP_HEADER_LEN_VER_P] != 0x45) { - return 0; - } - return memcmp(&buf[IP_DST_P], ipaddr, 4) == 0; -} - -// Make a return Ethernet header from a received Ethernet packet -void make_eth(uint8_t *buf) { - memcpy(&buf[ETH_DST_MAC], &buf[ETH_SRC_MAC], 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); -} - -// Make a new Ethernet header for an IP packet -void make_eth_ip_new(uint8_t *buf, uint8_t *dst_mac) { - memcpy(&buf[ETH_DST_MAC], dst_mac, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; -} - -// Fill the IP header checksum -void fill_ip_hdr_checksum(uint8_t *buf) { - uint16_t ck; - buf[IP_CHECKSUM_P] = 0; - buf[IP_CHECKSUM_P + 1] = 0; - buf[IP_FLAGS_P] = 0x40; // Don't fragment - buf[IP_FLAGS_P + 1] = 0; // Fragment offset - buf[IP_TTL_P] = 64; // TTL - ck = checksum(&buf[IP_P], IP_HEADER_LEN, 0); - buf[IP_CHECKSUM_P] = ck >> 8; - buf[IP_CHECKSUM_P + 1] = ck & 0xff; -} - -// Check if it's an ARP reply -uint8_t eth_type_is_arp_reply(uint8_t *buf) { - return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V; -} - -// Check if it's an ARP request -uint8_t eth_type_is_arp_req(uint8_t *buf) { - return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V; -} - -// Make a new IP header for a TCP packet -static uint16_t ip_identifier = 1; - -void make_ip_tcp_new(uint8_t *buf, uint16_t len, uint8_t *dst_ip) { - buf[IP_P] = IP_V4_V | IP_HEADER_LENGTH_V; - buf[IP_TOS_P] = 0x00; // TOS to default 0x00 - buf[IP_TOTLEN_H_P] = (len >> 8) & 0xff; - buf[IP_TOTLEN_L_P] = len & 0xff; - buf[IP_ID_H_P] = (ip_identifier >> 8) & 0xff; - buf[IP_ID_L_P] = ip_identifier & 0xff; - ip_identifier++; - buf[IP_FLAGS_H_P] = 0x00; // Fragment flags - buf[IP_FLAGS_L_P] = 0x00; - buf[IP_TTL_P] = 128; // Time To Live - buf[IP_PROTO_P] = IP_PROTO_TCP_V; // IP packet type to TCP - memcpy(&buf[IP_DST_P], dst_ip, 4); // Destination IP address - memcpy(&buf[IP_SRC_P], ipaddr, 4); // Source IP address - fill_ip_hdr_checksum(buf); -} - -// Make a return IP header from a received IP packet -void make_ip(uint8_t *buf) { - memcpy(&buf[IP_DST_P], &buf[IP_SRC_P], 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); -} - -// Swap seq and ack number and count ack number up -void step_seq(uint8_t *buf, uint16_t rel_ack_num, uint8_t cp_seq) { - uint8_t i = 4; - uint8_t tseq; - - // Sequence numbers: add the relative ack num to SEQACK - while (i > 0) { - rel_ack_num += buf[TCP_SEQ_H_P + i - 1]; - tseq = buf[TCP_SEQACK_H_P + i - 1]; - buf[TCP_SEQACK_H_P + i - 1] = rel_ack_num & 0xFF; - - if (cp_seq) { - // Copy the acknum sent to us into the sequence number - buf[TCP_SEQ_H_P + i - 1] = tseq; - } else { - buf[TCP_SEQ_H_P + i - 1] = 0; // Some preset value - } - - rel_ack_num >>= 8; - i--; - } -} - -// Make a return TCP header from a received TCP packet -// rel_ack_num is how much we must step the seq number received from the -// other side. We do not send more than 765 bytes of text (=data) in the TCP packet. -// No mss is included here. -// -// After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4 -// If cp_seq=0 then an initial sequence number is used (should be used in synack) -// otherwise it is copied from the packet we received -void make_tcphead(uint8_t *buf, uint16_t rel_ack_num, uint8_t cp_seq) { - // Swap source and destination ports - uint8_t temp = buf[TCP_DST_PORT_H_P]; - buf[TCP_DST_PORT_H_P] = buf[TCP_SRC_PORT_H_P]; - buf[TCP_SRC_PORT_H_P] = temp; - - temp = buf[TCP_DST_PORT_L_P]; - buf[TCP_DST_PORT_L_P] = buf[TCP_SRC_PORT_L_P]; - buf[TCP_SRC_PORT_L_P] = temp; - - // Update sequence and acknowledgment numbers - step_seq(buf, rel_ack_num, cp_seq); - - // Zero the checksum - buf[TCP_CHECKSUM_H_P] = 0; - buf[TCP_CHECKSUM_L_P] = 0; - - // No options, header length is 20 bytes (0x50) - buf[TCP_HEADER_LEN_P] = 0x50; -} - -// Make an ARP reply from a received ARP request -void make_arp_answer_from_request(uint8_t *buf) { - make_eth(buf); - buf[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; - buf[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V; - memcpy(&buf[ETH_ARP_DST_MAC_P], &buf[ETH_ARP_SRC_MAC_P], 6); - memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); - memcpy(&buf[ETH_ARP_DST_IP_P], &buf[ETH_ARP_SRC_IP_P], 4); - memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); - enc28j60PacketSend(42, buf); -} - -// Make an ICMP echo reply from a received echo request -void make_echo_reply_from_request(uint8_t *buf, uint16_t len) { - make_eth(buf); - make_ip(buf); - buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; - if (buf[ICMP_CHECKSUM_P] > (0xff - 0x08)) { - buf[ICMP_CHECKSUM_P + 1]++; - } - buf[ICMP_CHECKSUM_P] += 0x08; - enc28j60PacketSend(len, buf); -} - -// Make a UDP reply from a received UDP request, sending a max of 220 bytes of data -void make_udp_reply_from_request(uint8_t *buf, char *data, uint16_t datalen, uint16_t port) { - if (datalen > 220) { - datalen = 220; - } - - make_eth(buf); - - // Total length field in the IP header - uint16_t total_len = IP_HEADER_LEN + UDP_HEADER_LEN + datalen; - buf[IP_TOTLEN_H_P] = total_len >> 8; - buf[IP_TOTLEN_L_P] = total_len & 0xff; - - make_ip(buf); - - // Swap source and destination ports - buf[UDP_DST_PORT_H_P] = buf[UDP_SRC_PORT_H_P]; - buf[UDP_DST_PORT_L_P] = buf[UDP_SRC_PORT_L_P]; - buf[UDP_SRC_PORT_H_P] = port >> 8; - buf[UDP_SRC_PORT_L_P] = port & 0xff; - - // Calculate UDP length - uint16_t udp_len = UDP_HEADER_LEN + datalen; - buf[UDP_LEN_H_P] = udp_len >> 8; - buf[UDP_LEN_L_P] = udp_len & 0xff; - - // Zero the checksum - buf[UDP_CHECKSUM_H_P] = 0; - buf[UDP_CHECKSUM_L_P] = 0; - - // Copy data - memcpy(&buf[UDP_DATA_P], data, datalen); - - // Calculate checksum - uint16_t ck = checksum(&buf[IP_SRC_P], 16 + datalen, 1); - buf[UDP_CHECKSUM_H_P] = ck >> 8; - buf[UDP_CHECKSUM_L_P] = ck & 0xff; - - enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); -} - -// Make a TCP SYN-ACK response from a received SYN packet -void make_tcp_synack_from_syn(uint8_t *buf) { - uint16_t ck; - make_eth(buf); - - // Total length field in the IP header - buf[IP_TOTLEN_H_P] = 0; - buf[IP_TOTLEN_L_P] = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + 4; - - make_ip(buf); - - buf[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V; - make_tcphead(buf, 1, 0); - - // Initialize sequence number - buf[TCP_SEQ_H_P] = 0; - buf[TCP_SEQ_H_P + 1] = 0; - buf[TCP_SEQ_H_P + 2] = seqnum; - buf[TCP_SEQ_H_P + 3] = 0; - - // Increment the sequence number - seqnum += 3; - - // MSS option field - buf[TCP_OPTIONS_P] = 2; - buf[TCP_OPTIONS_P + 1] = 4; - buf[TCP_OPTIONS_P + 2] = 0x05; - buf[TCP_OPTIONS_P + 3] = 0x00; - - // TCP header length - buf[TCP_HEADER_LEN_P] = 0x60; - - // Window size - buf[TCP_WIN_SIZE] = 0x05; - buf[TCP_WIN_SIZE + 1] = 0x78; - - // Calculate the checksum - ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2); - buf[TCP_CHECKSUM_H_P] = ck >> 8; - buf[TCP_CHECKSUM_L_P] = ck & 0xff; - - // Send the packet - enc28j60PacketSend(IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + 4 + ETH_HEADER_LEN, buf); -} - -// Calculate the length of TCP data and store the result in static variables -uint16_t get_tcp_data_len(uint8_t *buf) { - int16_t total_len = ((int16_t)buf[IP_TOTLEN_H_P] << 8) | (buf[IP_TOTLEN_L_P] & 0xff); - int16_t header_len = IP_HEADER_LEN + ((buf[TCP_HEADER_LEN_P] >> 4) * 4); // Calculate header length in bytes - int16_t data_len = total_len - header_len; - - return (data_len > 0) ? (uint16_t)data_len : 0; -} - -// Get a pointer to the start of TCP data in buf -// Returns 0 if there is no data -// You must call init_len_info once before calling this function -uint16_t get_tcp_data_pointer(void) { - if (info_data_len) { - return ((uint16_t)TCP_SRC_PORT_H_P + info_hdr_len); - } else { - return 0; - } -} - -// Do some basic length calculations and store the result in static variables -void init_len_info(uint8_t *buf) { - info_data_len = (((int16_t)buf[IP_TOTLEN_H_P]) << 8) | (buf[IP_TOTLEN_L_P] & 0xff); - info_data_len -= IP_HEADER_LEN; - info_hdr_len = (buf[TCP_HEADER_LEN_P] >> 4) * 4; // Generate length in bytes - info_data_len -= info_hdr_len; - if (info_data_len <= 0) { - info_data_len = 0; - } -} - -// Fill a binary string of len data into the TCP packet -uint16_t fill_tcp_data_len(uint8_t *buf, uint16_t pos, const char *s, uint16_t len) { - // Fill in TCP data at position pos - memcpy(&buf[TCP_CHECKSUM_L_P + 3 + pos], s, len); - return pos + len; -} - -// Fill in TCP data at position pos. pos=0 means start of TCP data. -// Returns the position at which the string after this string could be filled. -uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const char *s) { - return fill_tcp_data_len(buf, pos, s, strlen(s)); -} - -// Make just an ACK packet with no TCP data inside -// This will modify the ETH/IP/TCP header -void make_tcp_ack_from_any(uint8_t *buf, int16_t datlentoack, uint8_t addflags) { - uint16_t len; - - make_eth(buf); - - // Fill the header - buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | addflags; - - if (addflags == TCP_FLAGS_RST_V) { - make_tcphead(buf, datlentoack, 1); - } else { - if (datlentoack == 0) { - // If there is no data, then we must still acknowledge one packet - datlentoack = 1; - } - make_tcphead(buf, datlentoack, 1); // No options - } - - // Total length field in the IP header must be set - len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN; - buf[IP_TOTLEN_H_P] = len >> 8; - buf[IP_TOTLEN_L_P] = len & 0xff; - - make_ip(buf); - - // Use a low window size, otherwise we have to have timers and cannot just react on every packet - buf[TCP_WIN_SIZE] = 0x4; // 1024 = 0x400 - buf[TCP_WIN_SIZE + 1] = 0x0; - - // Calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - len = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN, 2); - buf[TCP_CHECKSUM_H_P] = len >> 8; - buf[TCP_CHECKSUM_L_P] = len & 0xff; - - enc28j60PacketSend(IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN, buf); -} - -// dlen is the amount of TCP data (HTTP data) we send in this packet -// You can use this function only immediately after make_tcp_ack_from_any -// This is because this function will NOT modify the eth/ip/tcp header except for -// length and checksum -// You must set TCP_FLAGS before calling this -void make_tcp_ack_with_data_noflags(uint8_t *buf, uint16_t dlen) { - uint16_t total_len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; - - // Set the total length field in the IP header - buf[IP_TOTLEN_H_P] = total_len >> 8; - buf[IP_TOTLEN_L_P] = total_len & 0xff; - - // Fill in the IP header checksum - fill_ip_hdr_checksum(buf); - - // Zero the TCP checksum field - buf[TCP_CHECKSUM_H_P] = 0; - buf[TCP_CHECKSUM_L_P] = 0; - - // Calculate the checksum, len = 8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - uint16_t checksum_len = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2); - buf[TCP_CHECKSUM_H_P] = checksum_len >> 8; - buf[TCP_CHECKSUM_L_P] = checksum_len & 0xff; - - // Send the packet - enc28j60PacketSend(total_len + ETH_HEADER_LEN, buf); -} - -// You must have called init_len_info at some time before calling this function -// dlen is the amount of TCP data (HTTP data) we send in this packet -// You can use this function only immediately after make_tcp_ack_from_any -// This is because this function will NOT modify the eth/ip/tcp header except for -// length and checksum -void make_tcp_ack_with_data(uint8_t *buf, uint16_t dlen) { - uint16_t total_len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; - - // Fill the header with appropriate flags - buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V; - - // Set the total length field in the IP header - buf[IP_TOTLEN_H_P] = total_len >> 8; - buf[IP_TOTLEN_L_P] = total_len & 0xff; - - // Fill in the IP header checksum - fill_ip_hdr_checksum(buf); - - // Zero the TCP checksum field - buf[TCP_CHECKSUM_H_P] = 0; - buf[TCP_CHECKSUM_L_P] = 0; - - // Calculate the checksum, len = 8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - uint16_t checksum_len = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2); - buf[TCP_CHECKSUM_H_P] = checksum_len >> 8; - buf[TCP_CHECKSUM_L_P] = checksum_len & 0xff; - - // Send the packet - enc28j60PacketSend(total_len + ETH_HEADER_LEN, buf); -} - -// You must have initialized info_data_len at some time before calling this function -// This info_data_len initialization is done automatically if you call -// packetloop_icmp_tcp(buf, enc28j60PacketReceive(BUFFER_SIZE, buf)); -// and test the return value for non-zero. -// -// dlen is the amount of TCP data (HTTP data) we send in this packet -// You can use this function only immediately after make_tcp_ack_from_any -// This is because this function will NOT modify the eth/ip/tcp header except for -// length and checksum -void www_server_reply(uint8_t *buf, uint16_t dlen) { - // Send ACK for HTTP GET - make_tcp_ack_from_any(buf, info_data_len, 0); - - // Fill the header with appropriate flags - buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V; - - // Send data - make_tcp_ack_with_data_noflags(buf, dlen); -} - -#if defined(NTP_client) || defined(WOL_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) -// Fills the buffer with a string of specified length -void fill_buf_p(uint8_t *buf, uint16_t len, const char *s) { - memcpy(buf, s, len); -} -#endif - -#ifdef PING_client -// Sends an ICMP echo request (ping) to the specified destination IP using the gateway MAC address. -void client_icmp_request(uint8_t *buf, uint8_t *destip) { - // Fill Ethernet header - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); // Gateway MAC address in local LAN or host MAC address - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - - // Fill IP header - fill_buf_p(&buf[IP_P], 9, iphdr); - buf[IP_TOTLEN_L_P] = 0x82; // Total length - buf[IP_PROTO_P] = IP_PROTO_ICMP_V; // ICMP protocol - memcpy(&buf[IP_DST_P], destip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - // Fill ICMP header and data - buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; - buf[ICMP_TYPE_P + 1] = 0; // Code - buf[ICMP_CHECKSUM_H_P] = 0; // Zero the checksum - buf[ICMP_CHECKSUM_L_P] = 0; - buf[ICMP_IDENT_H_P] = 5; // Some number for identification - buf[ICMP_IDENT_L_P] = ipaddr[3]; // Last byte of my IP for identification - buf[ICMP_IDENT_L_P + 1] = 0; // Sequence number, high byte - buf[ICMP_IDENT_L_P + 2] = 1; // Sequence number, low byte - - // Copy the data pattern - memset(&buf[ICMP_DATA_P], PINGPATTERN, 56); - - // Calculate checksum - uint16_t ck = checksum(&buf[ICMP_TYPE_P], 56 + 8, 0); - buf[ICMP_CHECKSUM_H_P] = ck >> 8; - buf[ICMP_CHECKSUM_L_P] = ck & 0xff; - - // Send the packet - enc28j60PacketSend(98, buf); -} -#endif // PING_client - -void __attribute__((weak)) ES_PingCallback(void) {} - -#ifdef NTP_client -// Sends an NTP UDP packet -// See http://tools.ietf.org/html/rfc958 for details -void client_ntp_request(uint8_t *buf, uint8_t *ntpip, uint8_t srcport) { - // Fill Ethernet header - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - - // Fill IP header - fill_buf_p(&buf[IP_P], 9, iphdr); - buf[IP_TOTLEN_L_P] = 0x4c; // Total length - buf[IP_PROTO_P] = IP_PROTO_UDP_V; // UDP protocol - memcpy(&buf[IP_DST_P], ntpip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - // Fill UDP header - buf[UDP_DST_PORT_H_P] = 0; - buf[UDP_DST_PORT_L_P] = 0x7b; // NTP port = 123 - buf[UDP_SRC_PORT_H_P] = 10; - buf[UDP_SRC_PORT_L_P] = srcport; // Lower 8 bits of source port - buf[UDP_LEN_H_P] = 0; - buf[UDP_LEN_L_P] = 56; // Fixed length - buf[UDP_CHECKSUM_H_P] = 0; // Zero the checksum - buf[UDP_CHECKSUM_L_P] = 0; - - // Fill NTP data - memset(&buf[UDP_DATA_P], 0, 48); - fill_buf_p(&buf[UDP_DATA_P], 10, ntpreqhdr); - - // Calculate and fill UDP checksum - uint16_t ck = checksum(&buf[IP_SRC_P], 16 + 48, 1); - buf[UDP_CHECKSUM_H_P] = ck >> 8; - buf[UDP_CHECKSUM_L_P] = ck & 0xff; - - // Send the packet - enc28j60PacketSend(90, buf); -} - -/* - * This function processes the response from an NTP server and updates the RTC of the STM32 microcontroller. - * It extracts the NTP timestamp from the received buffer, converts it to a UNIX timestamp, and then - * updates the RTC with the current date and time. - */ -void client_ntp_process_answer(uint8_t *buf, uint16_t plen) { - uint32_t unix_timestamp; - - // Extract the NTP timestamp (seconds since 1900) - unix_timestamp = ((uint32_t)buf[0x52] << 24) | ((uint32_t)buf[0x53] << 16) | ((uint32_t)buf[0x54] << 8) | (uint32_t)buf[0x55]; - - // Convert the NTP timestamp to UNIX timestamp (seconds since 1970) - // by subtracting the seconds between 1900 and 1970. - if (unix_timestamp > 2208988800UL) { - unix_timestamp -= 2208988800UL; - } else { - // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) - unix_timestamp = 0; - } - - // Create structures for time and date - RTC_TimeTypeDef sTime = {0}; - RTC_DateTypeDef sDate = {0}; - - // Calculate the time components - uint32_t seconds = unix_timestamp % 60; - uint32_t minutes = (unix_timestamp / 60) % 60; - uint32_t hours = (unix_timestamp / 3600) % 24; - uint32_t days = unix_timestamp / 86400; // Total number of days since 1970 - - // The start of 1970 was a Thursday (day 4 of the week) - uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday - - // Determine the year - uint32_t year = 1970; - while (days >= 365) { - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - if (days < 366) break; - days -= 366; - } else { - if (days < 365) break; - days -= 365; - } - year++; - } - - // Determine the month and the day of the month - uint32_t month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { - month_lengths[1] = 29; // February has 29 days in leap years - } - - uint32_t month = 0; - for (month = 0; month < 12; month++) { - if (days < month_lengths[month]) { - break; - } - days -= month_lengths[month]; - } - month++; // months are 1-12 - uint32_t dayOfMonth = days + 1; // days are 1-31 - - // Set the time - sTime.Hours = hours; - sTime.Minutes = minutes; - sTime.Seconds = seconds; - sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; - sTime.StoreOperation = RTC_STOREOPERATION_RESET; - - // Set the date - sDate.WeekDay = dayOfWeek; - sDate.Month = month; - sDate.Date = dayOfMonth; - sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 - - // Update the RTC - HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); - HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); - - char timeStr[32]; // Adjusted size to hold formatted date and time - sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", - sDate.Date, - sDate.Month, - year, - sTime.Hours, - sTime.Minutes, - sTime.Seconds); - udpLog2("NTP indicates the following date and time:", timeStr); -} -#endif // NTP_client - -#ifdef UDP_client -// Prepare a spontaneous UDP packet to a server -void send_udp_prepare(uint8_t *buf, uint16_t sport, uint8_t *dip, uint16_t dport) { - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P], 9, iphdr); - - buf[IP_TOTLEN_H_P] = 0; - buf[IP_PROTO_P] = IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], dip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - - buf[UDP_DST_PORT_H_P] = (dport >> 8); - buf[UDP_DST_PORT_L_P] = dport & 0xff; - buf[UDP_SRC_PORT_H_P] = (sport >> 8); - buf[UDP_SRC_PORT_L_P] = sport & 0xff; - buf[UDP_LEN_H_P] = 0; - buf[UDP_CHECKSUM_H_P] = 0; - buf[UDP_CHECKSUM_L_P] = 0; -} - -// Transmit a prepared UDP packet -void send_udp_transmit(uint8_t *buf, uint16_t datalen) { - buf[IP_TOTLEN_H_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) >> 8; - buf[IP_TOTLEN_L_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) & 0xff; - fill_ip_hdr_checksum(buf); - - buf[UDP_LEN_H_P] = (UDP_HEADER_LEN + datalen) >> 8; - buf[UDP_LEN_L_P] = (UDP_HEADER_LEN + datalen) & 0xff; - - uint16_t ck = checksum(&buf[IP_SRC_P], 16 + datalen, 1); - buf[UDP_CHECKSUM_H_P] = ck >> 8; - buf[UDP_CHECKSUM_L_P] = ck & 0xff; - - enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); -} - -// Send a UDP packet -void send_udp(uint8_t *buf, char *data, uint16_t datalen, uint16_t sport, uint8_t *dip, uint16_t dport) { - send_udp_prepare(buf, sport, dip, dport); - memcpy(&buf[UDP_DATA_P], data, datalen); - send_udp_transmit(buf, datalen); -} - -// Process UDP packets -void udp_packet_process(uint8_t *buf, uint16_t plen) { - uint16_t data_pos = UDP_DATA_P; - uint8_t *data = &buf[data_pos]; - uint16_t data_len = plen - data_pos; - - char msg[data_len + 1]; - memcpy(msg, data, data_len); - msg[data_len] = '\0'; - - for (CommandMapping *cmd = commandTable; cmd->command != NULL; cmd++) { - if (strncmp(msg, cmd->command, strlen(cmd->command)) == 0) { - cmd->function(); - return; - } - } - - udpLog2("Error", "Unexpected UDP message"); -} -#endif // UDP_client - -#ifdef WOL_client -// A WOL (Wake on Lan) packet is a UDP packet to the broadcast -// address and UDP port 9. The data part contains 6x FF followed by -// 16 times the mac address of the host to wake-up -void send_wol(uint8_t *buf,uint8_t *wolmac) -{ - uint16_t ck; - // - memset(&buf[ETH_DST_MAC], 0xff, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=0x82; - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_SRC_P], ipaddr, 4); - memset(&buf[IP_DST_P], 0xff, 4); - fill_ip_hdr_checksum(buf); - buf[UDP_DST_PORT_H_P]=0; - buf[UDP_DST_PORT_L_P]=0x9; // wol=normally 9 - buf[UDP_SRC_PORT_H_P]=10; - buf[UDP_SRC_PORT_L_P]=0x42; // source port does not matter - buf[UDP_LEN_H_P]=0; - buf[UDP_LEN_L_P]=110; // fixed len - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data (102 bytes): - - // first mac - 0xFFs - memset(&buf[UDP_DATA_P], 0xff, 6); - // next 16 macs - wolmac repeated - // TODO: may need testing - for (unsigned i = 1; i <= 16; i++) { - memcpy(&buf[UDP_DATA_P + i*6], wolmac, 6); - } - - ck=checksum(&buf[IP_SRC_P], 118,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck& 0xff; - - enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+102,buf); -} -#endif // WOL_client - -#if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -// Make an ARP request -void client_arp_whohas(uint8_t *buf, uint8_t *ip_we_search) { - // Prepare Ethernet header - memset(&buf[ETH_DST_MAC], 0xFF, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; - - // Prepare ARP request header - fill_buf_p(&buf[ETH_ARP_P], 8, arpreqhdr); - memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); - memset(&buf[ETH_ARP_DST_MAC_P], 0, 6); - memcpy(&buf[ETH_ARP_DST_IP_P], ip_we_search, 4); - memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); - - // Set the flag to accept ARP reply - waitgwmac |= WGW_ACCEPT_ARP_REPLY; - - // Send the packet - enc28j60PacketSend(0x2a, buf); // 0x2a = 42 = length of the packet -} - -// Check if the client is waiting for the gateway MAC address -uint8_t client_waiting_gw(void) { - return (waitgwmac & WGW_HAVE_GW_MAC) ? 0 : 1; -} - -// Store the MAC address from an ARP reply -uint8_t client_store_gw_mac(uint8_t *buf) { - if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4) == 0) { - memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); - return 1; - } - return 0; -} - -// Refresh the gateway ARP entry -void client_gw_arp_refresh(void) { - if (waitgwmac & WGW_HAVE_GW_MAC) { - waitgwmac |= WGW_REFRESHING; - } -} - -//void client_set_wwwip(uint8_t *wwwipaddr) { -// memcpy(wwwip, wwwipaddr, 4); -//} - -void client_set_gwip(uint8_t *gwipaddr) -{ - waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop - memcpy(gwip, gwipaddr, 4); -} -#endif // defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) - -void client_tcp_set_serverip(uint8_t *ipaddr) -{ - memcpy(tcpsrvip, ipaddr, 4); -} - -#if defined (TCP_client) -// Make a tcp syn packet -void client_syn(uint8_t *buf, uint8_t srcport, uint8_t dstport_h, uint8_t dstport_l) -{ - // -- make the main part of the eth/IP/tcp header: - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P], 9, iphdr); - - buf[IP_TOTLEN_L_P] = 44; // good for syn - buf[IP_PROTO_P] = IP_PROTO_TCP_V; - memcpy(&buf[IP_DST_P], tcpsrvip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[TCP_DST_PORT_H_P] = dstport_h; - buf[TCP_DST_PORT_L_P] = dstport_l; - buf[TCP_SRC_PORT_H_P] = TCPCLIENT_SRC_PORT_H; - buf[TCP_SRC_PORT_L_P] = srcport; // lower 8 bit of src port - // zero out sequence number and acknowledgement number - memset(&buf[TCP_SEQ_H_P], 0, 8); - - // -- header ready - // put initial seq number - buf[TCP_SEQ_H_P+2] = seqnum; - seqnum += 3; // step the initial seq num by something we will not use during this tcp session - - buf[TCP_HEADER_LEN_P] = 0x60; // 0x60=24 len: (0x60>>4) * 4 - buf[TCP_FLAGS_P] = TCP_FLAGS_SYN_V; - - // use a low window size otherwise we have to have timers and can not just react on every packet. - buf[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 768 = 0x300, initial window - buf[TCP_WIN_SIZE+1] = 0x0; - - // zero the checksum - buf[TCP_CHECKSUM_H_P] = 0; - buf[TCP_CHECKSUM_L_P] = 0; - - // urgent pointer - buf[TCP_CHECKSUM_L_P+1] = 0; - buf[TCP_CHECKSUM_L_P+2] = 0; - - // MSS=768, must be more than 50% of the window size we use - buf[TCP_OPTIONS_P] = 2; - buf[TCP_OPTIONS_P+1] = 4; - buf[TCP_OPTIONS_P+2] = (CLIENTMSS >> 8); - buf[TCP_OPTIONS_P+3] = CLIENTMSS & 0xff; - - uint16_t ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2); - buf[TCP_CHECKSUM_H_P] = ck >> 8; - buf[TCP_CHECKSUM_L_P] = ck & 0xff; - - // 4 is the tcp mss option: - enc28j60PacketSend(IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4, buf); - -#if ETHERSHIELD_DEBUG - ethershieldDebug("Sent TCP Syn\n"); -#endif -} - -uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort) -{ - // -- make the main part of the eth/IP/tcp header: - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P], 9, iphdr); - - buf[IP_TOTLEN_L_P] = 40; - buf[IP_PROTO_P] = IP_PROTO_TCP_V; - memcpy(&buf[IP_DST_P], tcpsrvip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[TCP_DST_PORT_H_P] = tcp_client_port_h; - buf[TCP_DST_PORT_L_P] = tcp_client_port_l; - buf[TCP_SRC_PORT_H_P] = srcPort >> 8; - buf[TCP_SRC_PORT_L_P] = srcPort & 0xff; - - // zero out sequence number and acknowledgement number - memset(&buf[TCP_SEQ_H_P], 0, 8); - - // put initial seq number - buf[TCP_SEQ_H_P + 2] = seqnum; - seqnum += 3; // step the initial seq num by something we will not use during this tcp session - - buf[TCP_HEADER_LEN_P] = 0x60; // 0x60=24 len: (0x60>>4) * 4 - buf[TCP_FLAGS_P] = TCP_FLAGS_PUSH_V; - - buf[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 768 = 0x300, initial window - buf[TCP_WIN_SIZE + 1] = 0x0; - - // zero the checksum - buf[TCP_CHECKSUM_H_P] = 0; - buf[TCP_CHECKSUM_L_P] = 0; - - // urgent pointer - buf[TCP_CHECKSUM_L_P + 1] = 0; - buf[TCP_CHECKSUM_L_P + 2] = 0; - - // MSS=768, must be more than 50% of the window size we use - buf[TCP_OPTIONS_P] = 2; - buf[TCP_OPTIONS_P + 1] = 4; - buf[TCP_OPTIONS_P + 2] = CLIENTMSS >> 8; - buf[TCP_OPTIONS_P + 3] = CLIENTMSS & 0xff; - - uint16_t ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + 4, 2); - buf[TCP_CHECKSUM_H_P] = ck >> 8; - buf[TCP_CHECKSUM_L_P] = ck & 0xff; - - return IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4; -} - -void send_tcp_data(uint8_t *buf, uint16_t dlen) -{ - // fill the header: - buf[TCP_FLAGS_P] = TCP_FLAGS_PUSH_V; - - // total length field in the IP header must be set: - uint16_t total_len = IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; - buf[IP_TOTLEN_H_P] = total_len >> 8; - buf[IP_TOTLEN_L_P] = total_len & 0xff; - - fill_ip_hdr_checksum(buf); - - // zero the checksum - buf[TCP_CHECKSUM_H_P] = 0; - buf[TCP_CHECKSUM_L_P] = 0; - - // calculate the checksum - uint16_t ck = checksum(&buf[IP_SRC_P], 8 + TCP_HEADER_LEN_PLAIN + dlen, 2); - buf[TCP_CHECKSUM_H_P] = ck >> 8; - buf[TCP_CHECKSUM_L_P] = ck & 0xff; - - enc28j60PacketSend(total_len + ETH_HEADER_LEN, buf); -} - -/** - * @brief Requests a TCP connection to a server. - * - * This function sets up the necessary callback functions for handling the TCP request and response. - * - * @param result_callback Pointer to the callback function for processing the server response. - * @param datafill_callback Pointer to the callback function for filling the request data. - * @param port The port number of the server to connect to. - * @return A file descriptor for the TCP session. - */ -uint8_t client_tcp_req( - uint8_t (*result_callback)(uint8_t fd, uint8_t statuscode, uint16_t data_start_pos_in_buf, uint16_t len_of_data), - uint16_t (*datafill_callback)(uint8_t fd), - uint16_t port) -{ - client_tcp_result_callback = result_callback; - client_tcp_datafill_callback = datafill_callback; - tcp_client_port_h = (port >> 8) & 0xff; - tcp_client_port_l = port & 0xff; - tcp_client_state = 1; - - if (++tcp_fd > 7) { - tcp_fd = 0; - } - - return tcp_fd; -} -#endif // TCP_client - -#if defined (WWW_client) -/** - * @brief Internal data fill callback for the WWW client. - * - * This function fills the buffer with the appropriate HTTP GET or POST request. - * - * @param fd The file descriptor. - * @return The length of the data filled. - */ -uint16_t www_client_internal_datafill_callback(uint8_t fd) { - char strbuf[6]; // Adjusted to ensure it can hold up to 5 digits and the null terminator - uint16_t len = 0; - - if (fd != www_fd) { - return 0; - } - - if (browsertype == 0) { - // GET Request - len = fill_tcp_data(bufptr, len, "GET "); - len = fill_tcp_data(bufptr, len, client_urlbuf); - if (client_urlbuf_var) { - len = fill_tcp_data(bufptr, len, client_urlbuf_var); - } - len = fill_tcp_data(bufptr, len, " HTTP/1.1\r\nHost: "); - len = fill_tcp_data(bufptr, len, client_hoststr); - len = fill_tcp_data(bufptr, len, "\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n"); - } else { - // POST Request - len = fill_tcp_data(bufptr, len, client_method ? client_method : "POST "); - len = fill_tcp_data(bufptr, len, client_urlbuf); - len = fill_tcp_data(bufptr, len, " HTTP/1.1\r\nHost: "); - len = fill_tcp_data(bufptr, len, client_hoststr); - - if (client_additionalheaderline) { - len = fill_tcp_data(bufptr, len, "\r\n"); - len = fill_tcp_data(bufptr, len, client_additionalheaderline); - } - - len = fill_tcp_data(bufptr, len, "\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n"); - len = fill_tcp_data(bufptr, len, "Content-Length: "); - snprintf(strbuf, sizeof(strbuf), "%zu", strlen(client_postval)); - len = fill_tcp_data(bufptr, len, strbuf); - len = fill_tcp_data(bufptr, len, "\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"); - len = fill_tcp_data(bufptr, len, client_postval); - } - - return len; -} - -/** - * @brief Internal result callback for the WWW client. - * - * This function processes the result of the HTTP request. - * - * @param fd The file descriptor. - * @param statuscode The status code. - * @param datapos The position of the data in the buffer. - * @param len_of_data The length of the data. - * @return 0 Always returns 0. - */ -uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data) { - if (fd != www_fd) { - client_browser_callback(4, 0, 0); - return 0; - } - - if (statuscode == 0 && len_of_data > 12 && client_browser_callback) { - uint16_t offset = TCP_SRC_PORT_H_P + ((bufptr[TCP_HEADER_LEN_P] >> 4) * 4); - uint8_t *status_code_pos = &bufptr[datapos + 9]; - - if (strncmp("200", (char *)status_code_pos, 3) == 0) { - client_browser_callback(0, offset, len_of_data); - } else { - client_browser_callback(1, offset, len_of_data); - } - } - - return 0; -} - -/** - * @brief Sends an HTTP GET request. - * - * @param urlbuf URL buffer. - * @param urlbuf_varpart Variable part of the URL. - * @param hoststr Hostname string. - * @param callback Callback function to handle the result. - */ -void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t, uint16_t, uint16_t)) { - client_urlbuf = urlbuf; - client_urlbuf_var = urlbuf_varpart; - client_hoststr = hoststr; - browsertype = 0; - client_browser_callback = callback; - www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); -} - -/** - * @brief Sends an HTTP POST request. - * - * @param urlbuf URL buffer. - * @param hoststr Hostname string. - * @param additionalheaderline Additional header line, set to NULL if not used. - * @param method HTTP method, set to NULL for default POST, alternative is PUT. - * @param postval URL-encoded string buffer for the POST data. - * @param callback Callback function to handle the result. - */ -void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval, void (*callback)(uint8_t, uint16_t)) { - client_urlbuf = urlbuf; - client_hoststr = hoststr; - client_additionalheaderline = additionalheaderline; - client_method = method; - client_postval = postval; - browsertype = 1; - client_browser_callback = (void (*)(uint8_t, uint16_t, uint16_t))callback; - www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); -} -#endif // WWW_client - -/** - * @brief Register a callback function for receiving ping replies. - * - * @param callback The callback function to be called when a ping reply is received. - */ -void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) { - icmp_callback = callback; -} - -#ifdef PING_client -/** - * @brief Check for an ICMP ping reply packet. - * - * This function should be called in a loop to check if a ping reply has been received. - * - * @param buf The buffer containing the packet data. - * @param ip_monitoredhost The IP address of the monitored host. - * @return 1 if the ping reply is from the monitored host, 0 otherwise. - */ -uint8_t packetloop_icmp_checkreply(uint8_t *buf, uint8_t *ip_monitoredhost) { - if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && - buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREPLY_V && - buf[ICMP_DATA_P] == PINGPATTERN && - check_ip_message_is_from(buf, ip_monitoredhost)) { - return 1; - } - return 0; -} -#endif // PING_client - -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip) { - uint16_t dat_p; - int plen = 0; - long lastDhcpRequest = HAL_GetTick(); - uint8_t dhcpState = 0; - bool gotIp = FALSE; - uint8_t dhcpTries = 10; - - dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); - - while (!gotIp) { - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p = packetloop_icmp_tcp(buf, plen); - - if (dat_p == 0) { - check_for_dhcp_answer(buf, plen); - dhcpState = dhcp_state(); - - if (dhcpState != DHCP_STATE_OK) { - if (HAL_GetTick() > (lastDhcpRequest + 10000L)) { - lastDhcpRequest = HAL_GetTick(); - if (--dhcpTries <= 0) { - return 0; - } - dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); - } - } else if (!gotIp) { - gotIp = TRUE; - - init_ip_arp_udp_tcp(mymac, myip, myport); - client_set_gwip(gwip); - -#ifdef DNS_client - dnslkup_set_dnsip(dnsip); -#endif - } - } - } - - return 1; -} - -// Perform all processing to resolve a hostname to IP address. -// Returns 1 for successful name resolution, 0 otherwise -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname) { - uint16_t dat_p; - int plen = 0; - long lastDnsRequest = HAL_GetTick(); - uint8_t dns_state = DNS_STATE_INIT; - bool gotAddress = FALSE; - uint8_t dnsTries = 3; // After 3 attempts fail gracefully - - while (!gotAddress) { - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p = packetloop_icmp_tcp(buf, plen); - - if (dat_p == 0) { - if (client_waiting_gw()) { - continue; - } - - if (dns_state == DNS_STATE_INIT) { - dns_state = DNS_STATE_REQUESTED; - lastDnsRequest = HAL_GetTick(); - dnslkup_request(buf, hostname); - continue; - } - - if (dns_state != DNS_STATE_ANSWER) { - if (HAL_GetTick() > (lastDnsRequest + 60000L)) { - if (--dnsTries <= 0) { - return 0; // Failed to resolve address - } - dns_state = DNS_STATE_INIT; - lastDnsRequest = HAL_GetTick(); - } - continue; - } - } else { - if (dns_state == DNS_STATE_REQUESTED && udp_client_check_for_dns_answer(buf, plen)) { - dns_state = DNS_STATE_ANSWER; - client_tcp_set_serverip(dnslkup_getip()); - gotAddress = TRUE; - } - } - } - - return 1; -} - -// Return 0 to just continue in the packet loop and return the position -// of the tcp/udp data if there is tcp/udp data part -uint16_t packetloop_icmp_tcp(uint8_t *buf, uint16_t plen) { - uint16_t len; -#if defined(TCP_client) - uint8_t send_fin = 0; - uint16_t tcpstart; - uint16_t save_len; -#endif - - // Handle empty packets and ARP requests - if (plen == 0) { -#if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) - if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && delaycnt == 0 && enc28j60linkup()) { - client_arp_whohas(buf, gwip); - } - delaycnt++; -#if defined(TCP_client) - if (tcp_client_state == 1 && (waitgwmac & WGW_HAVE_GW_MAC)) { // Send a SYN - tcp_client_state = 2; - tcpclient_src_port_l++; // Allocate a new port - client_syn(buf, ((tcp_fd << 5) | (0x1f & tcpclient_src_port_l)), tcp_client_port_h, tcp_client_port_l); - } -#endif -#endif - return 0; - } - - // Handle ARP packets - if (eth_type_is_arp_and_my_ip(buf, plen)) { - if (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V) { - make_arp_answer_from_request(buf); - } -#if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) - if (waitgwmac & WGW_ACCEPT_ARP_REPLY && buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V) { - if (client_store_gw_mac(buf)) { - waitgwmac = WGW_HAVE_GW_MAC; - } - } -#endif - return 0; - } - - // Check if IP packets are for us - if (!eth_type_is_ip_and_my_ip(buf, plen)) { - return 0; - } - - // Handle ICMP echo requests - if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) { - if (icmp_callback) { - icmp_callback(&buf[IP_SRC_P]); - } - make_echo_reply_from_request(buf, plen); - ES_PingCallback(); - return 0; - } - - // Handle UDP packets - if (buf[IP_PROTO_P] == IP_PROTO_UDP_V) { -#ifdef NTP_client - if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { - client_ntp_process_answer(buf, plen); - return UDP_DATA_P; - } -#endif -#ifdef DNS_client - if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { - return UDP_DATA_P; - } -#endif - udp_packet_process(buf, plen); - return UDP_DATA_P; - } - - // Handle TCP packets - if (plen >= 54 && buf[IP_PROTO_P] == IP_PROTO_TCP_V) { -#if defined(TCP_client) - if (buf[TCP_DST_PORT_H_P] == TCPCLIENT_SRC_PORT_H) { -#if defined(WWW_client) - bufptr = buf; -#endif - if (!check_ip_message_is_from(buf, tcpsrvip)) { - return 0; - } - - if (buf[TCP_FLAGS_P] & TCP_FLAGS_RST_V) { - if (client_tcp_result_callback) { - client_tcp_result_callback((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 3, 0, 0); - } - tcp_client_state = 5; - return 0; - } - - len = get_tcp_data_len(buf); - - if (tcp_client_state == 2) { - if ((buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)) { - make_tcp_ack_from_any(buf, 0, 0); - buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V; - - if (client_tcp_datafill_callback) { - len = client_tcp_datafill_callback((buf[TCP_SRC_PORT_L_P] >> 5) & 0x7); - } else { - len = 0; - } - tcp_client_state = 3; - make_tcp_ack_with_data_noflags(buf, len); - return 0; - } else { - tcp_client_state = 1; - len++; - if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { - len = 0; - } - make_tcp_ack_from_any(buf, len, TCP_FLAGS_RST_V); - return 0; - } - } - - if (tcp_client_state == 3 || tcp_client_state == 4) { - tcp_client_state = 4; - if (client_tcp_result_callback) { - tcpstart = TCP_DATA_START; - if (tcpstart > plen - 8) { - tcpstart = plen - 8; - } - save_len = len; - if (tcpstart + len > plen) { - save_len = plen - tcpstart; - } - send_fin = client_tcp_result_callback((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 0, tcpstart, save_len); - } - if (send_fin) { - make_tcp_ack_from_any(buf, len, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; - return 0; - } - make_tcp_ack_from_any(buf, len, 0); - return 0; - } - - if (tcp_client_state == 5) { - return 0; - } - - if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { - make_tcp_ack_from_any(buf, len + 1, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; - return 0; - } - - if (len > 0) { - make_tcp_ack_from_any(buf, len, 0); - } - return 0; - } -#endif - - if (buf[TCP_DST_PORT_H_P] == wwwport_h && buf[TCP_DST_PORT_L_P] == wwwport_l) { - if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) { - make_tcp_synack_from_syn(buf); - return 0; - } - if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { - info_data_len = get_tcp_data_len(buf); - if (info_data_len == 0) { - if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { - make_tcp_ack_from_any(buf, 0, 0); - } - return 0; - } - len = TCP_DATA_START; - if (len > plen - 8) { - return 0; - } - return len; - } - } - } - return 0; -} -#endif - -/* end of ip_arp_udp.c */ diff --git a/src/websrv_help_functions.c b/src/websrv_help_functions.c deleted file mode 100644 index 5cdaeb2..0000000 --- a/src/websrv_help_functions.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * websrv_help_functions.C - * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. - */ - -#include "defines.h" -#include "websrv_help_functions.h" - -#ifdef FROMDECODE_websrv_help -// search for a string of the form key=value in -// a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\r\n -// -// The returned value is stored in strbuf. You must allocate -// enough storage for strbuf, maxlen is the size of strbuf. -// I.e the value it is declated with: strbuf[5]-> maxlen=5 -uint8_t find_key_val(char *str, char *strbuf, uint8_t maxlen, char *key) { - uint8_t found = 0; - uint8_t i = 0; - char *kp = key; - - // Find the key in the string - while (*str && *str != ' ' && *str != '\n' && !found) { - if (*str == *kp) { - kp++; - if (*kp == '\0' && *(str + 1) == '=') { - found = 1; - str += 2; // Move past '=' - } - } else { - kp = key; - } - str++; - } - - // Copy the value to the buffer if key is found - if (found) { - while (*str && *str != ' ' && *str != '\n' && *str != '&' && i < maxlen - 1) { - strbuf[i++] = *str++; - } - strbuf[i] = '\0'; - } - - // Return the length of the value - return i; -} - -// Convert a single hex digit character to its integer value -unsigned char h2int(char c) { - if (c >= '0' && c <= '9') { - return (unsigned char)(c - '0'); - } else if (c >= 'a' && c <= 'f') { - return (unsigned char)(c - 'a' + 10); - } else if (c >= 'A' && c <= 'F') { - return (unsigned char)(c - 'A' + 10); - } else { - return 0; - } -} - - -// Decode a URL string, e.g., "hello%20joe" or "hello+joe" becomes "hello joe" -void urldecode(char *urlbuf) { - char c; - char *dst = urlbuf; - - while ((c = *urlbuf)) { - if (c == '+') { - c = ' '; - } else if (c == '%') { - urlbuf++; - char high = *urlbuf++; - char low = *urlbuf; - c = (h2int(high) << 4) | h2int(low); - } - *dst++ = c; - urlbuf++; - } - *dst = '\0'; -} -#endif // FROMDECODE_websrv_help - -#ifdef URLENCODE_websrv_help -// Convert a single character to a 2-digit hex string -// A terminating '\0' is added -void int2h(char c, char *hstr) { - // Convert the lower 4 bits - hstr[1] = (c & 0xF) + '0'; - if ((c & 0xF) > 9) { - hstr[1] = (c & 0xF) - 10 + 'a'; - } - - // Convert the upper 4 bits - c = (c >> 4) & 0xF; - hstr[0] = c + '0'; - if (c > 9) { - hstr[0] = c - 10 + 'a'; - } - - // Null-terminate the string - hstr[2] = '\0'; -} - -// There must be enough space in urlbuf. In the worst case, that is -// 3 times the length of str. -void urlencode(char *str, char *urlbuf) { - char c; - while ((c = *str)) { - if (c == ' ' || isalnum((unsigned char)c)) { - if (c == ' ') { - c = '+'; - } - *urlbuf++ = c; - } else { - *urlbuf++ = '%'; - int2h(c, urlbuf); - urlbuf += 2; - } - str++; - } - *urlbuf = '\0'; -} -#endif // URLENCODE_websrv_help -// Parse a string and extract the IP to bytestr -uint8_t parse_ip(uint8_t *bytestr, char *str) { - char *sptr = NULL; - uint8_t i = 0; - - // Initialize bytestr to zero - for (i = 0; i < 4; i++) { - bytestr[i] = 0; - } - - i = 0; - while (*str && i < 4) { - if (sptr == NULL && isdigit((unsigned char)*str)) { - sptr = str; - } - if (*str == '.') { - *str = '\0'; - bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); - i++; - sptr = NULL; - } - str++; - } - - if (i == 3 && sptr != NULL) { - bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); - return 0; - } - return 1; -} - -// Convert a byte string to a human-readable display string -// (base is 10 for IP and 16 for MAC address), len is 4 for IP address and 6 for MAC. -void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base) { - uint8_t i, j = 0; - - for (i = 0; i < len; i++) { - if (base == 10) { - j += sprintf(&resultstr[j], "%d", bytestr[i]); - } else if (base == 16) { - j += sprintf(&resultstr[j], "%02x", bytestr[i]); - } - resultstr[j++] = separator; - } - resultstr[j - 1] = '\0'; // Replace the last separator with null terminator -} - -// end of websrv_help_functions.c From e621bf7a22593a5201fd7566c347efba6860bd4f Mon Sep 17 00:00:00 2001 From: DtNeo Date: Fri, 30 Aug 2024 21:41:34 +0200 Subject: [PATCH 26/27] Head of files --- Inc/EtherShield.h | 13 ++++++------- Inc/LogManager.h | 10 ++++++++++ Inc/RTC.h | 10 ++++++---- Inc/defines.h | 11 +++++++---- Inc/dhcp.h | 15 ++++++++------- Inc/dnslkup.h | 14 +++++++------- Inc/enc28j60.h | 14 +++++++------- Inc/error_handler.h | 7 ++++++- Inc/ip_arp_udp_tcp.h | 14 +++++++------- Inc/net.h | 14 +++++++------- Inc/ntp.h | 15 +++++++-------- Inc/packet.h | 14 +++++++------- Inc/tcp.h | 6 ++---- Inc/timers.h | 27 +++++++++++++++++++++++++++ Inc/websrv_help_functions.h | 6 ++---- Src/EtherShield.c | 13 +++++++++---- Src/LogManager.c | 9 +++++++++ Src/RTC.c | 7 +++---- Src/dhcp.c | 14 +++++++------- Src/dnslkup.c | 14 +++++++------- Src/enc28j60.c | 15 ++++++++------- Src/error_handler.c | 7 ++++++- Src/ip_arp_udp_tcp.c | 15 ++++++++------- Src/net.c | 15 +++++++-------- Src/ntp.c | 16 ++++++++-------- Src/packet.c | 15 ++++++++------- Src/tcp.c | 13 ++++++------- Src/timers.c | 12 ++++++++---- Src/websrv_help_functions.c | 6 ++---- inc/timers.h | 20 -------------------- 30 files changed, 212 insertions(+), 169 deletions(-) create mode 100644 Inc/timers.h delete mode 100644 inc/timers.h diff --git a/Inc/EtherShield.h b/Inc/EtherShield.h index a7480db..3b96e3c 100644 --- a/Inc/EtherShield.h +++ b/Inc/EtherShield.h @@ -1,12 +1,11 @@ -/* - * EtherShield.h +/** + * @file EtherShield.h + * @brief Header file containing definitions and macros for interfacing with the ENC28J60 Ethernet controller. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file includes control register definitions, chip enable/disable macros, and configurations for delays + * and chip select (CS) handling. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #ifndef ETHERSHIELD_H diff --git a/Inc/LogManager.h b/Inc/LogManager.h index 28eb8d1..297dcc1 100644 --- a/Inc/LogManager.h +++ b/Inc/LogManager.h @@ -1,3 +1,13 @@ +/* + * @file logmanager.c + * @brief Source file + * + * This header file defines the interface for the Log Manager module, which handles logging + * operations for different types of logs such as UDP, Web, and UART. It includes an enumeration + * for log types, function prototypes for logging messages, initializing the log manager, and + * adding logs to the system. + */ + #ifndef LOGMANAGER_H #define LOGMANAGER_H diff --git a/Inc/RTC.h b/Inc/RTC.h index 64fd1a1..aeca4a7 100644 --- a/Inc/RTC.h +++ b/Inc/RTC.h @@ -1,8 +1,10 @@ -/* - * RTC.h +/** + * @file RTC.h + * @brief Real-Time Clock (RTC) interaction functions for STM32 microcontroller. * - * Created on: Jun 3, 2024 - * Author: dtneo + * This header file provides the declaration of the function used to obtain the current + * date and time from the RTC of the STM32 microcontroller. The function formats the + * date and time into a human-readable string for logging purposes. */ #ifndef INC_RTC_H_ diff --git a/Inc/defines.h b/Inc/defines.h index 64a922a..32d444c 100644 --- a/Inc/defines.h +++ b/Inc/defines.h @@ -1,8 +1,11 @@ -/* - * defines.h +/** + * @file defines.h + * @brief Header file containing global definitions and macros for configuring the STM32 and ENC28J60 Ethernet module. * - * Created on: May 30, 2024 - * Author: dtneo + * This file includes configurations for SPI settings, conditional compilation flags for enabling various + * networking clients (e.g., NTP, DNS, DHCP), and external declarations of global variables and function prototypes. + * + * @note For more information, refer to the `license.md` file located at the root of the project. */ #ifndef INC_DEFINES_H_ diff --git a/Inc/dhcp.h b/Inc/dhcp.h index aeea350..1a0845d 100644 --- a/Inc/dhcp.h +++ b/Inc/dhcp.h @@ -1,14 +1,15 @@ -/* - * ENC28J60.h +/** + * @file dhcp.h + * @brief Header file for DHCP client function prototypes and definitions. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file provides the function prototypes and necessary definitions for implementing DHCP client functionality. + * It includes declarations for initializing the DHCP process, sending requests, and handling DHCP responses + * such as DHCPOFFER and DHCPACK. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ + #ifndef DHCP_H #define DHCP_H diff --git a/Inc/dnslkup.h b/Inc/dnslkup.h index 5d45a94..88ea847 100644 --- a/Inc/dnslkup.h +++ b/Inc/dnslkup.h @@ -1,12 +1,12 @@ -/* - * dnslkup.h +/** + * @file dnslkup.h + * @brief Header file for DNS lookup function prototypes and related definitions. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file provides the function prototypes for DNS client operations, such as sending DNS requests, + * processing DNS responses, and resolving hostnames to IP addresses. It also includes functions + * for setting the DNS server and checking for errors during DNS lookups. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #ifndef DNSLKUP_H diff --git a/Inc/enc28j60.h b/Inc/enc28j60.h index 41fcd4e..aa8edda 100644 --- a/Inc/enc28j60.h +++ b/Inc/enc28j60.h @@ -1,12 +1,12 @@ -/* - * ENC28J60.h +/** + * @file ENC28J60.h + * @brief Header file containing definitions, macros, and function prototypes for interfacing with the ENC28J60 Ethernet controller. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file includes control register definitions, chip enable/disable macros, configurations for delays, + * chip select (CS) handling, and various SPI operation codes. It also provides function prototypes + * for initializing and managing the ENC28J60 Ethernet controller. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #ifndef __ENC28J60_H diff --git a/Inc/error_handler.h b/Inc/error_handler.h index 06cc560..78f1f16 100644 --- a/Inc/error_handler.h +++ b/Inc/error_handler.h @@ -1,3 +1,8 @@ +/** +* @file error_handler.h + * @brief Header file + */ + #ifndef __ERROR_HANDLER_H #define __ERROR_HANDLER_H @@ -7,4 +12,4 @@ enum ENC28j60_Error { void __attribute__((weak)) ENC28j60_Error_Handler(enum ENC28j60_Error error); -#endif \ No newline at end of file +#endif diff --git a/Inc/ip_arp_udp_tcp.h b/Inc/ip_arp_udp_tcp.h index 6281c72..d560626 100644 --- a/Inc/ip_arp_udp_tcp.h +++ b/Inc/ip_arp_udp_tcp.h @@ -1,12 +1,12 @@ -/* - * ip_arp_udp_tcp.h +/** + * @file ip_arp_udp_tcp.h + * @brief Header file containing function prototypes and definitions for IP, ARP, UDP, and TCP protocols. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file includes function declarations for initializing and managing network communications using + * the ENC28J60 Ethernet controller. It provides functions for handling ARP, UDP, and TCP packets, + * as well as client-side networking tasks such as ARP requests and gateway management. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #ifndef IP_ARP_UDP_TCP_H diff --git a/Inc/net.h b/Inc/net.h index d429915..8981ae3 100644 --- a/Inc/net.h +++ b/Inc/net.h @@ -1,12 +1,12 @@ -/* - * net.h +/** + * @file net.h + * @brief Header file for network-related definitions and macros for ENC28J60 Ethernet controller. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file contains the definitions and macros necessary for interacting with the ENC28J60 Ethernet controller, + * including structures and constants for handling Ethernet, IP, TCP, UDP, and DNS protocols. It also defines + * various states for DHCP and DNS clients, as well as structures for managing TCP connections. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note The file is configured to support different STM32 series, including STM32F0, STM32F1, and STM32F4. */ #ifndef NET_H diff --git a/Inc/ntp.h b/Inc/ntp.h index b5d9143..a22cc6e 100644 --- a/Inc/ntp.h +++ b/Inc/ntp.h @@ -1,13 +1,12 @@ -/* - * ntp.h +/** + * @file ntp.h + * @brief NTP (Network Time Protocol) functions for the STM32 using ENC28J60 Ethernet controller. * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * This header file provides the necessary function declarations for sending NTP requests and processing + * NTP responses. It facilitates synchronization of the STM32 microcontroller's Real-Time Clock (RTC) + * with a remote NTP server. */ + #ifndef NTP_H #define NTP_H diff --git a/Inc/packet.h b/Inc/packet.h index 1ff3b97..92bfd64 100644 --- a/Inc/packet.h +++ b/Inc/packet.h @@ -1,12 +1,12 @@ -/* - * packet.h +/** + * @file packet.h + * @brief Header file containing function prototypes for processing Ethernet packets, including TCP, UDP, ICMP, and HTTP protocols. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file declares functions for handling various types of network packets, including Ethernet, TCP, UDP, + * ICMP, and HTTP. It provides the necessary prototypes for managing packet processing, handling protocol-specific + * tasks, and maintaining network communication states. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #ifndef PACKET_H diff --git a/Inc/tcp.h b/Inc/tcp.h index 9716f8c..7ba03ba 100644 --- a/Inc/tcp.h +++ b/Inc/tcp.h @@ -1,8 +1,6 @@ /* - * tcp.h - * - * Created on: Jun 4, 2024 - * Author: dtneo + * @file tcp.h + * @brief Header file * * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. * It includes control register definitions, chip enable/disable macros, and configurations for delays and diff --git a/Inc/timers.h b/Inc/timers.h new file mode 100644 index 0000000..4812027 --- /dev/null +++ b/Inc/timers.h @@ -0,0 +1,27 @@ +/** + * @file timers.h + * @brief Header file for timer-related functions and declarations for the STM32 platform. + * + * This file contains the declarations of functions for initializing, logging, and handling + * timer interrupts, specifically for the TIM4 timer. It also includes the necessary external + * references to the TIM handle and function prototypes. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + * + * @created May 30, 2024 + * @author dtneo + */ + +#ifndef TIMERS_H +#define TIMERS_H + +#include "main.h" + +extern TIM_HandleTypeDef htim4; + +void timerLog(TIM_HandleTypeDef *htim); +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); +void generalTimersInit(); + + +#endif // TIMERS_H diff --git a/Inc/websrv_help_functions.h b/Inc/websrv_help_functions.h index 60daf16..64e6f5c 100644 --- a/Inc/websrv_help_functions.h +++ b/Inc/websrv_help_functions.h @@ -1,8 +1,6 @@ /* - * websrv_help_functions.h - * - * Created on: Jun 4, 2024 - * Author: dtneo + * @file websrv_help_functions.h + * @brief Header file * * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. * It includes control register definitions, chip enable/disable macros, and configurations for delays and diff --git a/Src/EtherShield.c b/Src/EtherShield.c index 209fd76..38ca795 100644 --- a/Src/EtherShield.c +++ b/Src/EtherShield.c @@ -1,8 +1,13 @@ -/* - * This file contains the implementation for establishing a full connection using the EtherShield library - * with an ENC28J60 Ethernet module. It includes functions to initialize the connection, allocate IP addresses - * via DHCP, resolve DNS hostnames, and request NTP time updates. Additionally, it processes incoming packets +/** + * @file EtherShield.c + * @brief Implementation of functions for establishing a full connection using the EtherShield + * library with the ENC28J60 Ethernet module. + * + * This file contains functions to initialize the connection, allocate IP addresses via DHCP, + * resolve DNS hostnames, and request NTP time updates. It also processes incoming packets * for ICMP and TCP protocols. + * + * @note For more information, refer to the `license.md` file located at the root of the project. */ #include "EtherShield.h" diff --git a/Src/LogManager.c b/Src/LogManager.c index 3d98aaf..fe7c1aa 100644 --- a/Src/LogManager.c +++ b/Src/LogManager.c @@ -1,3 +1,12 @@ +/* + * @file logmanager.c + * @brief Source file + * + * This source file implements the Log Manager module, which handles logging operations + * for different types of logs such as UDP, Web, and UART. It includes functions to log + * messages, initialize the log manager, and store logs in a circular buffer. + */ + #include "LogManager.h" #include "EtherShield.h" // Assuming this is where udplog2 is declared diff --git a/Src/RTC.c b/Src/RTC.c index d895051..f6ba394 100644 --- a/Src/RTC.c +++ b/Src/RTC.c @@ -1,8 +1,7 @@ - /* - * RTC.c +/* + * @file RTC.c + * @brief Source file * - * Created on: Jun 3, 2024 - * Author: dtneo * * This file contains the implementation for interacting with the Real-Time Clock (RTC) * on the STM32 microcontroller. It provides a function to obtain the current date and diff --git a/Src/dhcp.c b/Src/dhcp.c index c71aa16..5d3aa43 100644 --- a/Src/dhcp.c +++ b/Src/dhcp.c @@ -1,12 +1,12 @@ -/* - * ENC28J60.c +/** + * @file dhcp.c + * @brief Source file implementing DHCP client functionality for IP address allocation and network configuration. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file contains the implementation of the DHCP client, responsible for obtaining an IP address, + * subnet mask, gateway, and DNS server information from a DHCP server. It includes functions for + * sending DHCP requests, processing DHCP responses, and managing the DHCP lease. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #include "dhcp.h" diff --git a/Src/dnslkup.c b/Src/dnslkup.c index 0086859..2b3cc6e 100644 --- a/Src/dnslkup.c +++ b/Src/dnslkup.c @@ -1,12 +1,12 @@ -/* - * dnslkup.C +/** + * @file dnslkup.c + * @brief Source file implementing DNS lookup functionality for resolving hostnames to IP addresses. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file contains the implementation of DNS client functionality, including sending DNS queries, + * processing DNS responses, and handling DNS errors. It provides functions for setting the DNS server, + * sending DNS requests, and resolving hostnames to IP addresses. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #include "defines.h" diff --git a/Src/enc28j60.c b/Src/enc28j60.c index c518f78..398b6ad 100644 --- a/Src/enc28j60.c +++ b/Src/enc28j60.c @@ -1,12 +1,13 @@ -/* - * ENC28J60.c +/** + * @file ENC28J60.c + * @brief Source file containing the implementation for interfacing with the ENC28J60 Ethernet controller. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file includes functions for initializing the ENC28J60, handling SPI communication, + * reading and writing registers, managing buffers, and controlling power states. It also + * contains functions for sending and receiving Ethernet packets, as well as configuring + * various network settings. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ #include "defines.h" diff --git a/Src/error_handler.c b/Src/error_handler.c index 96096f6..4e5ef00 100644 --- a/Src/error_handler.c +++ b/Src/error_handler.c @@ -1,6 +1,11 @@ +/** + * @file error_handler.c + * @brief Source file + */ + #include "error_handler.h" void __attribute__((weak)) ENC28j60_Error_Handler(enum ENC28j60_Error error) { while(1) {} -} \ No newline at end of file +} diff --git a/Src/ip_arp_udp_tcp.c b/Src/ip_arp_udp_tcp.c index af2b9e2..aa00830 100644 --- a/Src/ip_arp_udp_tcp.c +++ b/Src/ip_arp_udp_tcp.c @@ -1,14 +1,15 @@ -/* - * ip_arp_udp_tcp.c +/** + * @file ip_arp_udp_tcp.c + * @brief Source file containing the implementation of IP, ARP, UDP, and TCP protocols for network communication. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file includes functions for handling IP, ARP, UDP, and TCP packets, managing network communication, + * and processing checksums. It also provides implementations for ARP requests, UDP packet transmission, + * and basic network initialization. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ + #include "defines.h" #include "net.h" #include "enc28j60.h" diff --git a/Src/net.c b/Src/net.c index e52ae0c..ecf7ce6 100644 --- a/Src/net.c +++ b/Src/net.c @@ -1,12 +1,11 @@ -/* - * net.C - * - * Created on: Jun 4, 2024 - * Author: dtneo +/** + * @file net.c + * @brief Implementation of network-related functions for ENC28J60 Ethernet controller. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * This file implements various functions to interact with the ENC28J60 Ethernet controller, including + * handling ICMP echo requests (ping), registering callbacks for ICMP replies, checking for ICMP replies, + * making ICMP echo replies, and sending Wake-on-LAN (WOL) packets. It also includes the management of TCP + * client connections and settings related to a web server port. */ #include "net.h" diff --git a/Src/ntp.c b/Src/ntp.c index ad387a7..d2d204c 100644 --- a/Src/ntp.c +++ b/Src/ntp.c @@ -1,13 +1,13 @@ -/* - * packet.C - * - * Created on: Jun 4, 2024 - * Author: dtneo +/** + * @file packet.c + * @brief Functions for handling network packets, including NTP requests and responses. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * This file contains functions to send NTP UDP packets and process NTP server responses. It also + * includes the logic to update the RTC (Real-Time Clock) of the STM32 microcontroller based on the NTP + * timestamp received from the server. The NTP implementation follows the specifications from + * RFC 958. Additional utility functions for handling various network packets are provided. */ + #include "ntp.h" #include "packet.h" #include "tcp.h" diff --git a/Src/packet.c b/Src/packet.c index f9eaf64..590d1e7 100644 --- a/Src/packet.c +++ b/Src/packet.c @@ -1,13 +1,14 @@ -/* - * packet.C +/** + * @file packet.c + * @brief Source file implementing packet processing functions for handling Ethernet, TCP, UDP, ICMP, and HTTP protocols. * - * Created on: Jun 4, 2024 - * Author: dtneo + * This file includes functions to process incoming Ethernet packets, including handling ARP, IP, TCP, UDP, + * and ICMP protocols. It also manages TCP connection states, processes HTTP requests, and handles UDP-based + * commands and NTP responses. * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * @note For more information, refer to the `license.md` file located at the root of the project. */ + #include "packet.h" // Function to process all incoming packets diff --git a/Src/tcp.c b/Src/tcp.c index f74af47..a8b0001 100644 --- a/Src/tcp.c +++ b/Src/tcp.c @@ -1,13 +1,12 @@ /* - * tcp.C + * @file tcp.c + * @brief Source file * - * Created on: Jun 4, 2024 - * Author: dtneo - * - * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. - * It includes control register definitions, chip enable/disable macros, and configurations for delays and - * chip select (CS) handling. + * This source file provides the implementation of the TCP protocol for the ENC28J60 Ethernet controller. + * It includes functions for handling TCP connections, creating and sending TCP packets, managing TCP states, + * and performing TCP-related tasks such as checksum calculations and logging. */ + #include "defines.h" #include "tcp.h" #include "net.h" diff --git a/Src/timers.c b/Src/timers.c index 0ad8534..adf1d4e 100644 --- a/Src/timers.c +++ b/Src/timers.c @@ -1,8 +1,12 @@ -/* - * timers.c +/** + * @file timers.c + * @brief Source file implementing timer-related functions for STM32, including initialization, logging, and interrupt handling. * - * Created on: Aug 7, 2024 - * Author: dtneo + * This file contains functions to initialize and manage timers on the STM32 platform. It includes + * interrupt handling for the TIM4 timer and logging of timer states. The functions ensure that + * timers are properly initialized, started, and managed during runtime. + * + * @note For more information, refer to the `license.md` file located at the root of the project. */ #include "timers.h" diff --git a/Src/websrv_help_functions.c b/Src/websrv_help_functions.c index 95cc8a0..49483bd 100644 --- a/Src/websrv_help_functions.c +++ b/Src/websrv_help_functions.c @@ -1,8 +1,6 @@ /* - * websrv_help_functions.C - * - * Created on: Jun 4, 2024 - * Author: dtneo + * @file websrv_help_functions.c + * @brief Source file * * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. * It includes control register definitions, chip enable/disable macros, and configurations for delays and diff --git a/inc/timers.h b/inc/timers.h deleted file mode 100644 index 663de9b..0000000 --- a/inc/timers.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * timers.h - * - * Created on: May 30, 2024 - * Author: dtneo - */ - -#ifndef TIMERS_H -#define TIMERS_H - -#include "main.h" - -extern TIM_HandleTypeDef htim4; - -void timerLog(TIM_HandleTypeDef *htim); -void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); -void generalTimersInit(); - - -#endif // TIMERS_H From 3a1bf297dc24e23af7a1c7ca1a8ed32d2e4e482f Mon Sep 17 00:00:00 2001 From: DtNeo Date: Mon, 26 Aug 2024 21:00:11 +0200 Subject: [PATCH 27/27] defines.c --- Src/defines.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Src/defines.c diff --git a/Src/defines.c b/Src/defines.c new file mode 100644 index 0000000..25034ba --- /dev/null +++ b/Src/defines.c @@ -0,0 +1,52 @@ +/** + * @file defines.c + * @brief Source file containing the implementation of global variables and command functions for the STM32 and ENC28J60 Ethernet module. + * + * This file defines the network configuration variables such as MAC address, IP address, subnet mask, + * gateway, and DNS server addresses. It also includes the implementation of command functions for + * interacting with the ENC28J60 and RTC modules. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + + +#include "defines.h" + +uint8_t macaddrin[6] = {00, 00, 00, 00, 00, 01}; // MAC address of ENC28J60 +uint8_t ipaddrin[4] = {192, 168, 0, 100}; // IP address, prioritize DHCP server assignment +uint8_t maskin[4] = {255, 255, 255, 0}; // Subnet mask +uint8_t gwipin[4] = {192, 168, 0, 1}; // Default gateway +uint8_t dhcpsvrin[4] = {192, 168, 0, 1}; // DHCP server +uint8_t dnssvrin[4] = {192, 168, 0, 1}; // DNS server confirmed by DHCP +uint8_t dnsipaddr[4] = {192, 168, 0, 1}; // DNS server confirmed by DHCP +uint8_t ntpip[4] = {192, 168, 0, 1}; // NTP server IP address +char domainName[] = "www.google.com"; +uint8_t dest_ip[4] = {192, 168, 0, 254}; // Destination IP address, corresponding to a syslog server running on Debian (syslog-ng) +uint16_t dest_port = 10001; // Destination port +uint16_t srcport = 10002; // Source port +uint8_t dstport_h = 0x27; // High byte of port 10001 +uint8_t dstport_l = 0x11; // Low byte of port 10001 +uint8_t dip[] = {192, 168, 0, 254}; // Destination IP address, corresponding to a syslog server running on Debian (syslog-ng) +uint16_t dport = 514; // UDP port of the syslog server running on Debian (syslog-ng) +uint16_t sport = 8; // Source port + + + +// Define the command table +CommandMapping commandTable[] = { + {"sendSTM32State", sendSTM32State, NULL}, + {"sendRTCDateTime", sendRTCDateTime, NULL}, + {NULL, NULL, NULL} // End marker +}; + + +// Implement the command functions +void sendSTM32State() { + //enc28j60getrev(void); +} + +void sendRTCDateTime() { + getRTCDateTime(); +} + +