This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi
I really hope someone can help point out the source of the subtle but significant issue I'm experiencing.
I am having a really fustrating time trying to get a TCP client working using static IP allocation, it just wont establish a connection to a server (I'm running both socektest and hurcules). My client app works fine if DHCP is specified and the server examples provided in simplelink_msp432e4_sdk_4_20_00_12\examples\nortos\MSP_EXP432E401Y\lwip work when either DHCP or static IP allocation is used to initialize the lwIP library, just not my client app.
I have tried the same code on the EK-TM4C129EXL which similarly runs a Cortex M4 and I experience the same issue when running a LwIP v2 port.
However, the Tivaware SDK comes with LwIP 1.4.1 and this estblishes a connection and allows messages to be sent back and forth regardless of whether DHCP or static IP allocation is used, which therefore leads me to, possibly incorrectly, believe its not my app but a configuration setting (I've ensured that LWIP_DHCP and LWIP_AUTOIP are enabled and disabled when switching between DCHP and static allocation respectivey) but at this stage I have run out of ideas. Any suggestions/ help would be gratefully recieved.
Thanks HL
//***************************************************************************** // // enet_tcp_echo_server.c - Sample Echo Server Application using lwIP. // // Copyright (c) 2019-2020 Texas Instruments Incorporated. All rights reserved. // Software License Agreement // // Texas Instruments (TI) is supplying this software for use solely and // exclusively on TI's microcontroller products. The software is owned by // TI and/or its suppliers, and is protected under applicable copyright // laws. You may not combine this software with "viral" open-source // software in order to form a larger program. // // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL // DAMAGES, FOR ANY REASON WHATSOEVER. // // This is part of revision 2.2.0.295 of the EK-TM4C129EXL Firmware Package. // //***************************************************************************** #include <stdbool.h> #include <stdint.h> #include <string.h> #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "driverlib/debug.h" #include "driverlib/flash.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/sysctl.h" #include "driverlib/systick.h" #include "drivers/pinout.h" #include "driverlib/emac.h" #include "lwip/tcp.h" //#include "utils/locator.h" #include "utils/lwiplib.h" #include "utils/uartstdio.h" #include "utils/ustdlib.h" //***************************************************************************** // //! \addtogroup example_list //! <h1>Ethernet TCP Echo Server (enet_tcp_echo_server)</h1> //! //! This example application demonstrates the operation of the TM4C129x //! Ethernet controller using the lwIP TCP/IP Stack. DHCP is used to obtain //! an Ethernet address. If DHCP times out without obtaining an address, //! AutoIP will be used to obtain a link-local address. The address that is //! selected will be shown on the UART. The application echoes back the data //! received from the client. //! //! UART0, connected to the Virtual Serial Port and running at 115,200, 8-N-1, //! is used to display messages from this application. //! //! For additional details on lwIP, refer to the lwIP web page at: //! http://savannah.nongnu.org/projects/lwip/ // //***************************************************************************** #define TCP_PORT (23) #if DEF_DYNAMIC_IP //#define SERVER_IP_ADDR (0xD601A8C0) /** 192.168.01.214 */ #define SERVER_IP_ADDR (0xC65AA8C0) /** 192.168.90.198 */ #else #define CLIENT_IP_ADDR (0x80000002) /** 128.0.0.2 */ #define SERVER_IP_ADDR (0x01000080) /** 128.0.0.1 */ #define GATEWAY_ADDR (0x00000000) /** 0.0.0.0 */ #define NETWORK_MASK (0xFFFFFF00) /** 255.255.255.0 */ #endif /** DEF_DYNAMIC_IP */ //***************************************************************************** // // Defines for setting up the system clock. // //***************************************************************************** #define SYSTICKHZ 1000 #define SYSTICKMS (1000 / SYSTICKHZ) //***************************************************************************** // // Interrupt priority definitions. The top 3 bits of these values are // significant with lower values indicating higher priority interrupts. // //***************************************************************************** #define SYSTICK_INT_PRIORITY 0x80 #define ETHERNET_INT_PRIORITY 0xC0 //***************************************************************************** // // The variable g_ui32SysClock contains the system clock frequency in Hz. // //***************************************************************************** uint32_t g_ui32SysClock; //***************************************************************************** // // The current IP address. // //***************************************************************************** uint32_t g_ui32IPAddress; //***************************************************************************** // // Global counter to keep track the duration the connection has been idle. // //***************************************************************************** uint32_t g_ui32tcpPollTick = 0; //***************************************************************************** // // Volatile global flag to manage LED blinking, since it is used in interrupt // and main application. The LED blinks at the rate of SYSTICKHZ. // //***************************************************************************** volatile bool g_bLED; //***************************************************************************** // // This is the buffer to receive the TCP payload. Data in this buffer is then // processed before echoing back to the host. // //***************************************************************************** char g_pctcpBuffer[4096] = {0}; typedef enum { NOT_CONNECTED = 0, SERVER_CONNECTED, MSG_SENT_AWAITING_RESPONSE, SERVER_RESPONSE_INVALID, SERVER_RESPONSE_VALID, SIZEOF_CLIENT_CONN_STATUS_T } client_conn_status_t; static client_conn_status_t conn_status = NOT_CONNECTED; static const char *const client_message = "Hello hairy.\r"; static const char *const server_reply = "I AM HAIRY.\r"; static struct tcp_pcb *p_tcp_pcb; static void DisplayIPAddress(uint32_t ui32Addr); static void echo_init(void); static err_t echo_conn(void *arg, struct tcp_pcb *pcb, err_t err); static void close_conn (struct tcp_pcb *pcb); static err_t echo_recv( void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); static void echo_sent(void); //***************************************************************************** // // The error routine that is called if the driver library encounters an error. // //***************************************************************************** #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif //***************************************************************************** // // The interrupt handler for the SysTick interrupt. // //***************************************************************************** void SysTickIntHandler(void) { // // Call the lwIP timer handler. // lwIPTimer(SYSTICKMS); // // Tell the application to change the state of the LED (in other words // blink). // g_bLED = true; } //***************************************************************************** // // Display an lwIP type IP Address. // //***************************************************************************** static void DisplayIPAddress(uint32_t ui32Addr) { char pcBuf[16]; // // Convert the IP Address into a string. // usprintf(pcBuf, "%d.%d.%d.%d", ui32Addr & 0xff, (ui32Addr >> 8) & 0xff, (ui32Addr >> 16) & 0xff, (ui32Addr >> 24) & 0xff); // // Display the string. // UARTprintf("%s\n", pcBuf); } //***************************************************************************** // // Create a new TCP connection on telenet port 23 and bind it any IP addressed // acquired by the DHCP server. // //***************************************************************************** static void echo_init(void) { struct tcp_pcb *tcp_pcb; // // Creates a new TCP connection identifier (PCB). // tcp_pcb = tcp_new(); // // Bind the PCB to all local IP addresses at port. // tcp_bind(tcp_pcb, IP_ADDR_ANY, TCP_PORT); // // Connect to the server: send the SYN // const ip_addr_t ip_addr_ti = { SERVER_IP_ADDR }; tcp_connect(tcp_pcb, (ip_addr_t *)&ip_addr_ti, TCP_PORT, echo_conn); } //***************************************************************************** // // Callback function for tcp_accept when a new connection arrives. // //***************************************************************************** static err_t echo_conn(void *arg, struct tcp_pcb *pcb, err_t err) { LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err); // // Sets the callback function - echo_recv that will be called when new // data arrives on the connection associated with PCB. The callback // function will be passed a NULL pbuf to indicate that the remote host // has closed the connection. // tcp_recv(pcb, echo_recv); // // Error callback function currently not implemented. // tcp_err(pcb, NULL); // // Obtain a copy of the tcp_pcb to enable a message to be transmitted. // p_tcp_pcb = pcb; UARTprintf("Connection established.\n"); conn_status = SERVER_CONNECTED; return ERR_OK; } //***************************************************************************** // // Callback function to process the received data from the client. // //***************************************************************************** static err_t echo_recv( void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { char *p_rx_payload; conn_status = SERVER_RESPONSE_INVALID; if ((ERR_OK == err) && (NULL != p)) { // // tcp_recved must be called when the application has processed the // data & is prepared to receive more. // tcp_recved(pcb, p->tot_len); // // Obtain the payload length & the pointer to the payload. // p_rx_payload = p->payload; // // Check the validity of the retunred message. // const uint32_t length = p->tot_len; if (strlen(server_reply) == length) { if (0 == strncmp(p_rx_payload, server_reply, length)) { UARTprintf("%s\n", server_reply); conn_status = SERVER_RESPONSE_VALID; } } // // Dereference a pbuf chain. // pbuf_free(p); } else { // // If there is an error during pbuf allocation then free the pbuf. // pbuf_free(p); } return ERR_OK; } //***************************************************************************** // // // //***************************************************************************** void SetupLwIP(void) { uint8_t pui8MACArray[8]; uint32_t ui32User0, ui32User1; // // Configure the hardware MAC address for Ethernet Controller filtering of // incoming packets. The MAC address will be stored in the non-volatile // USER0 and USER1 registers. // MAP_FlashUserGet(&ui32User0, &ui32User1); if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff)) { // // We should never get here. This is an error if the MAC address has // not been programmed into the device. Exit the program. // Let the user know there is no MAC address. // UARTprintf("No MAC programmed!\n"); while(1) { } } // // Tell the user what we are waiting for. // UARTprintf("Waiting for IP.\n"); // // Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC // address needed to program the hardware registers, then program the MAC // address into the Ethernet Controller registers. // pui8MACArray[0] = ((ui32User0 >> 0) & 0xff); pui8MACArray[1] = ((ui32User0 >> 8) & 0xff); pui8MACArray[2] = ((ui32User0 >> 16) & 0xff); pui8MACArray[3] = ((ui32User1 >> 0) & 0xff); pui8MACArray[4] = ((ui32User1 >> 8) & 0xff); pui8MACArray[5] = ((ui32User1 >> 16) & 0xff); #if DEF_DYNAMIC_IP // // Initialize the lwIP library, using DHCP. // lwIPInit(g_ui32SysClock, pui8MACArray, 0, 0, 0, IPADDR_USE_DHCP); UARTprintf("DYNAMIC IP\r\n"); #else lwIPInit(g_ui32SysClock, pui8MACArray, CLIENT_IP_ADDR, NETWORK_MASK, GATEWAY_ADDR, IPADDR_USE_STATIC); UARTprintf("STATIC IP\r\n"); #endif MAP_IntPrioritySet(INT_EMAC0, ETHERNET_INT_PRIORITY); } //***************************************************************************** // // Required by lwIP library to support any host-related timer functions. // //***************************************************************************** void lwIPHostTimerHandler(void) { uint32_t ui32NewGWAddress; uint32_t ui32NewIPAddress; uint32_t ui32NewSubNet; // // Get the current IP address. // ui32NewIPAddress = lwIPLocalIPAddrGet(); // // Get the current network mask. // ui32NewSubNet = lwIPLocalNetMaskGet(); // // Get the current Gateway address. // ui32NewGWAddress = lwIPLocalGWAddrGet(); // // See if the IP address has changed. // if(ui32NewIPAddress != g_ui32IPAddress) { // // See if there is an IP address assigned. // if(ui32NewIPAddress == 0xffffffff) { // // Indicate that there is no link. // UARTprintf("Waiting for link.\n"); } else if(ui32NewIPAddress == 0) { // // There is no IP address, so indicate that the DHCP process is // running. // UARTprintf("Waiting for IP address.\n"); } else { // // Display the new IP address. // UARTprintf("IP Address: "); DisplayIPAddress(ui32NewIPAddress); // // Display the new Subnet mask. // UARTprintf("Subnet: "); DisplayIPAddress(ui32NewSubNet); // // Display the new Gateway address. // UARTprintf("Gateway: "); DisplayIPAddress(ui32NewGWAddress); // // Initialise the client application. // echo_init(); } // // Save the new IP address. // g_ui32IPAddress = ui32NewIPAddress; } // // If there is not an IP address. // if((ui32NewIPAddress == 0) || (ui32NewIPAddress == 0xffffffff)) { // // Do nothing and keep waiting. // } } static void echo_sent(void) { tcp_write(p_tcp_pcb, client_message, strlen(client_message), 1); // // send the data right now // tcp_output(p_tcp_pcb); UARTprintf("%s\r", client_message); conn_status = MSG_SENT_AWAITING_RESPONSE; } //***************************************************************************** // // This example demonstrates the use of the Ethernet Controller. // //***************************************************************************** int main(void) { uint8_t count = 0, cnt = 0; // // Make sure the main oscillator is enabled because this is required by // the PHY. The system must have a 25MHz crystal attached to the OSC // pins. The SYSCTL_MOSC_HIGHFREQ parameter is used when the crystal // frequency is 10MHz or higher. // MAP_SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ); // // Run from the PLL at 120 MHz. // Note: SYSCTL_CFG_VCO_240 is a new setting provided in TivaWare 2.2.x and // later to better reflect the actual VCO speed due to SYSCTL#22. // g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 120000000); // // Configure the device pins. // PinoutSet(true, false); // // Initialize the UART and write initial status. // UARTStdioConfig(0, 115200, g_ui32SysClock); UARTprintf("Ethernet lwIP TCP echo example.\r\n"); // // Configure Port N1 for as an output for the animation LED. // MAP_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_1); // // Initialize LED to OFF (0). // MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, ~GPIO_PIN_1); // // Configure SysTick for a periodic interrupt. // MAP_SysTickPeriodSet(g_ui32SysClock / SYSTICKHZ); MAP_SysTickEnable(); MAP_SysTickIntEnable(); // // Set the interrupt priorities. We set the SysTick interrupt to a higher // priority than the Ethernet interrupt to ensure that the file system // tick is processed if SysTick occurs while the Ethernet handler is being // processed. This is very likely since all the TCP/IP and HTTP work is // done in the context of the Ethernet interrupt. // MAP_IntPrioritySet(FAULT_SYSTICK, SYSTICK_INT_PRIORITY); SetupLwIP(); UARTprintf("READY\n\r"); // // Loop forever, processing the LED blinking. All the work is done in // interrupt handlers. // while(1) { // // Wait till the SysTick Interrupt indicates to change the state of the // LED. // while(g_bLED == false) { } if ((SERVER_CONNECTED == conn_status) || (SERVER_RESPONSE_VALID == conn_status) || (MSG_SENT_AWAITING_RESPONSE == conn_status) && (10 <= cnt)) { cnt = 0; echo_sent(); } // // Clear the flag. // g_bLED = false; if (count++ > 150) { count = 0; if (MSG_SENT_AWAITING_RESPONSE == conn_status) { cnt++; } // // Toggle the LED. // MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, (MAP_GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_1) ^ GPIO_PIN_1)); } } }
Hi Hairy,
There a several posts on this topic. Have you seen this one -> SIMPLELINK TCP CLIENT EXAMPLE
Hi Dennis,
Thanks, I have undertaken a trawl of the forum and agree there are a number of posts regarding TCP Client applications. Unfortunately none seem to cover my specific issue, the closest being by Utsav who has authored the post you kindly provided a link to and who has previously reported an issue with Static IP allocation. However, it appears that Utsav is using TI-RTOS and in turn NDKTCPIP; not LwIP.
If my client application didn’t work at all I could understand it, but given it works for both DHCP and Static IP allocation in LwIP 1.4.1, but only DHCP in LwIP 2.x, with the exact same application code, it is somewhat more difficult to get my head around.
HL
So I've had a development.
If I don’t run the code through the (IAR) debugger, but instead leave the application to boot up and run on target (albeit still a debug build), it establishes a connection when Static IP allocation is used. It seems repeatable as the same behaviour is exhibited whether I use my MSP-EXP432E401Y or EK-TM4C129EXL.
I have been wondering if it might be a timing issue. Any suggestions as to what might be the cause of this timing descrepancy?
Hi Hairy,
It's been a couple of days so I wanted to check on your progress. I asked around, but there doesn't seem to be anyone that has a clue on this one.
I've not made any further progress unfotunatley. Tho my focus has been elsewhere. It's a puzzling and concerning one which I suspect is going to take a while to get to the bottome of.
Thanks,
HL
Hi Hairy,
As I'm looking around for other possible causes...
If I don’t run the code through the (IAR) debugger, but instead leave the application to boot up and run on target (albeit still a debug build), it establishes a connection when Static IP allocation is used.
From what I can piece together about IAR is, when creating a debug version of your project, some code may not be optimized. Since this could potentially change the timing of the SW I thought this might be a clue, but you go on to say you run the target without the debugger attached, and with the debug code build, so this shoots down that theory. This does seem to point the finger at the debugger tool itself as the potential cause. You may want to reach out to IAR to see if they can provide any help from their side.
Hi Hairy,
It's been a few days since I have heard from you so I’m assuming you were able to resolve your issue.
I will mark this posting as RESOLVED. If this isn’t the case, please click the "This did NOT resolve my issue" button and reply to this thread with more information.
If this thread locks, please click the "Ask a related question" button and in the new thread describe the current status of your issue and any additional details you may have to assist us in helping to solve your issues.
Unfortunately I’ve not got the bottom of this due to other commitments. I’m just drafting a ticket to IAR now. If i do get a resolution I'll let you know