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.

Optimizing UDP in lwIP on TIVA - how to determine when a packet has been sent?

Other Parts Discussed in Thread: EK-TM4C1294XL

I have a project running on the TM4C129X Eval kit that uses lwIP UDP to transfer large amounts of data. Everything essentially works, with one caveat. I have to add a time delay between the calls to udp_sendto( ). I need about 45 microseconds for a 1022 byte packet.

From what I can tell, udp_sendto( ) queues the data to be sent but does not wait for the transmission to complete before returning. This is substantiated by this statement from http://lwip.wikia.com/wiki/Maximizing_throughput : "ATTENTION: keep in mind that for DMA-enabled MACs, the packet may not have been sent when udp_send returns!".

Is there a way to determine when the packet has been sent instead of using a blind delay?

Thanks,

Jeff

  • You may be able to poll the Ethernet MAC status register.  It has a TXFE bit that will tell you if there is still data in the TX FIFO.

    Check the datasheet the register descriptions start about page 1658.

  • I added the following code and removed the time delay:

        while (EMACStatusGet(EMAC0_BASE) & EMAC_STATUS_TX_NOT_EMPTY){};

    This seems to work but it sure feels like a hack. Is this the best way to do this?

    Thanks

  • Jeff,

    I'm experimenting with EK-TM4C1294XL launchpad and wanted to setup the board for UDP and constantly transmit "TEST".
    Using the "enet_io" example project for this board I made some modification to how I think UDP is set up with lwIP.
    Is it possible for you to help me out.  I think i'm doing this all wrong.

    I'm including the "enet_io.c" code i'm using.

    Regards,
    Alonso

    //*****************************************************************************
    //
    // enet_io.c - I/O control via a web server.
    //
    // Copyright (c) 2013-2014 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.1.0.12573 of the EK-TM4C1294XL Firmware Package.
    //
    //*****************************************************************************
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_nvic.h"
    #include "inc/hw_types.h"
    #include "driverlib/flash.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/timer.h"
    #include "driverlib/rom_map.h"
    #include "utils/locator.h"
    #include "utils/lwiplib.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "httpserver_raw/httpd.h"
    #include "drivers/pinout.h"
    #include "io.h"
    #include "cgifuncs.h"
    
    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>Ethernet-based I/O Control (enet_io)</h1>
    //!
    //! This example application demonstrates web-based I/O control using the
    //! Tiva Ethernet controller and the lwIP TCP/IP Stack.  DHCP is used to
    //! obtain an Ethernet address.  If DHCP times out without obtaining an
    //! address, a static IP address will be chosen using AutoIP.  The address that
    //! is selected will be shown on the UART allowing you to access the
    //! internal web pages served by the application via your normal web browser.
    //!
    //! Two different methods of controlling board peripherals via web pages are
    //! illustrated via pages labeled ``IO Control Demo 1'' and ``IO Control
    //! Demo 2'' in the navigation menu on the left of the application's home page.
    //! In both cases, the example allows you to toggle the state of the user LED
    //! on the board and set the speed of a blinking LED.
    //!
    //! ``IO Control Demo 1'' uses JavaScript running in the web browser to send
    //! HTTP requests for particular special URLs.  These special URLs are
    //! intercepted in the file system support layer (io_fs.c) and used to
    //! control the LED and animation LED.  Responses generated by the board are
    //! returned to the browser and inserted into the page HTML dynamically by
    //! more JavaScript code.
    //!
    //! ``IO Control Demo 2'' uses standard HTML forms to pass parameters to CGI
    //! (Common Gateway Interface) handlers running on the board.  These handlers
    //! process the form data and control the animation and LED as requested before
    //! sending a response page (in this case, the original form) back to the
    //! browser.  The application registers the names and handlers for each of its
    //! CGIs with the HTTPD server during initialization and the server calls
    //! these handlers after parsing URL parameters each time one of the CGI URLs
    //! is requested.
    //!
    //! Information on the state of the various controls in the second demo is
    //! inserted into the served HTML using SSI (Server Side Include) tags which
    //! are parsed by the HTTPD server in the application.  As with the CGI
    //! handlers, the application registers its list of SSI tags and a handler
    //! function with the web server during initialization and this handler is
    //! called whenever any registered tag is found in a .shtml, .ssi, .shtm or
    //! .xml file being served to the browser.
    //!
    //! In addition to LED and animation speed control, the second example also
    //! allows a line of text to be sent to the board for output to the UART.
    //! This is included to illustrate the decoding of HTTP text strings.
    //!
    //! Note that the web server used by this example has been modified from the
    //! example shipped with the basic lwIP package.  Additions include SSI and
    //! CGI support along with the ability to have the server automatically insert
    //! the HTTP headers rather than having these built in to the files in the
    //! file system image.
    //!
    //! Source files for the internal file system image can be found in the ``fs''
    //! directory.  If any of these files are changed, the file system image
    //! (io_fsdata.h) should be rebuilt by running the following command from the
    //! enet_io directory:
    //!
    //! ../../../../tools/bin/makefsfile -i fs -o io_fsdata.h -r -h -q
    //!
    //! For additional details on lwIP, refer to the lwIP web page at:
    //! http://savannah.nongnu.org/projects/lwip/
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // Defines for setting up the system clock.
    //
    //*****************************************************************************
    #define SYSTICKHZ               100
    #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
    
    //*****************************************************************************
    //
    // A set of flags.  The flag bits are defined as follows:
    //
    //     0 -> An indicator that the animation timer interrupt has occurred.
    //
    //*****************************************************************************
    #define FLAG_TICK            0
    static volatile unsigned long g_ulFlags;
    
    //*****************************************************************************
    //
    // External Application references.
    //
    //*****************************************************************************
    extern void httpd_init(void);
    extern void udp_init(void);
    
    //*****************************************************************************
    //
    // SSI tag indices for each entry in the g_pcSSITags array.
    //
    //*****************************************************************************
    #define SSI_INDEX_LEDSTATE  0
    #define SSI_INDEX_FORMVARS  1
    #define SSI_INDEX_SPEED     2
    
    //*****************************************************************************
    //
    // This array holds all the strings that are to be recognized as SSI tag
    // names by the HTTPD server.  The server will call SSIHandler to request a
    // replacement string whenever the pattern <!--#tagname--> (where tagname
    // appears in the following array) is found in ".ssi", ".shtml" or ".shtm"
    // files that it serves.
    //
    //*****************************************************************************
    static const char *g_pcConfigSSITags[] =
    {
        "LEDtxt",        // SSI_INDEX_LEDSTATE
        "FormVars",      // SSI_INDEX_FORMVARS
        "speed"          // SSI_INDEX_SPEED
    };
    
    //*****************************************************************************
    //
    // The number of individual SSI tags that the HTTPD server can expect to
    // find in our configuration pages.
    //
    //*****************************************************************************
    #define NUM_CONFIG_SSI_TAGS     (sizeof(g_pcConfigSSITags) / sizeof (char *))
    
    //*****************************************************************************
    //
    // Prototypes for the various CGI handler functions.
    //
    //*****************************************************************************
    static char *ControlCGIHandler(int32_t iIndex, int32_t i32NumParams,
                                   char *pcParam[], char *pcValue[]);
    static char *SetTextCGIHandler(int32_t iIndex, int32_t i32NumParams,
                                   char *pcParam[], char *pcValue[]);
    
    //*****************************************************************************
    //
    // Prototype for the main handler used to process server-side-includes for the
    // application's web-based configuration screens.
    //
    //*****************************************************************************
    static int32_t SSIHandler(int32_t iIndex, char *pcInsert, int32_t iInsertLen);
    
    //*****************************************************************************
    //
    // CGI URI indices for each entry in the g_psConfigCGIURIs array.
    //
    //*****************************************************************************
    #define CGI_INDEX_CONTROL       0
    #define CGI_INDEX_TEXT          1
    
    //*****************************************************************************
    //
    // This array is passed to the HTTPD server to inform it of special URIs
    // that are treated as common gateway interface (CGI) scripts.  Each URI name
    // is defined along with a pointer to the function which is to be called to
    // process it.
    //
    //*****************************************************************************
    static const tCGI g_psConfigCGIURIs[] =
    {
        { "/iocontrol.cgi", (tCGIHandler)ControlCGIHandler }, // CGI_INDEX_CONTROL
        { "/settxt.cgi", (tCGIHandler)SetTextCGIHandler }     // CGI_INDEX_TEXT
    };
    
    //*****************************************************************************
    //
    // The number of individual CGI URIs that are configured for this system.
    //
    //*****************************************************************************
    #define NUM_CONFIG_CGI_URIS     (sizeof(g_psConfigCGIURIs) / sizeof(tCGI))
    
    //*****************************************************************************
    //
    // The file sent back to the browser by default following completion of any
    // of our CGI handlers.  Each individual handler returns the URI of the page
    // to load in response to it being called.
    //
    //*****************************************************************************
    #define DEFAULT_CGI_RESPONSE    "/io_cgi.ssi"
    
    //*****************************************************************************
    //
    // The file sent back to the browser in cases where a parameter error is
    // detected by one of the CGI handlers.  This should only happen if someone
    // tries to access the CGI directly via the broswer command line and doesn't
    // enter all the required parameters alongside the URI.
    //
    //*****************************************************************************
    #define PARAM_ERROR_RESPONSE    "/perror.htm"
    
    #define JAVASCRIPT_HEADER                                                     \
        "<script type='text/javascript' language='JavaScript'><!--\n"
    #define JAVASCRIPT_FOOTER                                                     \
        "//--></script>\n"
    
    //*****************************************************************************
    //
    // Timeout for DHCP address request (in seconds).
    //
    //*****************************************************************************
    #ifndef DHCP_EXPIRE_TIMER_SECS
    #define DHCP_EXPIRE_TIMER_SECS  5  // was 45
    #endif
    
    //*****************************************************************************
    //
    // The current IP address.
    //
    //*****************************************************************************
    uint32_t g_ui32IPAddress;
    
    //*****************************************************************************
    //
    // The system clock frequency.  Used by the SD card driver.
    //
    //*****************************************************************************
    uint32_t g_ui32SysClock;
    
    
    
    struct udp_pcb *remote_pcb;
    struct pbuf* pbuf1;
    char buf [120];
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    //*****************************************************************************
    //
    // This CGI handler is called whenever the web browser requests iocontrol.cgi.
    //
    //*****************************************************************************
    static char *
    ControlCGIHandler(int32_t iIndex, int32_t i32NumParams, char *pcParam[],
                      char *pcValue[])
    {
        int32_t i32LEDState, i32Speed;
        bool bParamError;
    
        //
        // We have not encountered any parameter errors yet.
        //
        bParamError = false;
    
        //
        // Get each of the expected parameters.
        //
        i32LEDState = FindCGIParameter("LEDOn", pcParam, i32NumParams);
        i32Speed = GetCGIParam("speed_percent", pcParam, pcValue, i32NumParams,
                &bParamError);
    
        //
        // Was there any error reported by the parameter parser?
        //
        if(bParamError || (i32Speed < 0) || (i32Speed > 100))
        {
            return(PARAM_ERROR_RESPONSE);
        }
    
        //
        // We got all the parameters and the values were within the expected ranges
        // so go ahead and make the changes.
        //
        io_set_led((i32LEDState == -1) ? false : true);
        io_set_animation_speed(i32Speed);
    
        //
        // Send back the default response page.
        //
        return(DEFAULT_CGI_RESPONSE);
    }
    
    //*****************************************************************************
    //
    // This CGI handler is called whenever the web browser requests settxt.cgi.
    //
    //*****************************************************************************
    static char *
    SetTextCGIHandler(int32_t i32Index, int32_t i32NumParams, char *pcParam[],
                      char *pcValue[])
    {
        long lStringParam;
        char pcDecodedString[48];
    
        //
        // Find the parameter that has the string we need to display.
        //
        lStringParam = FindCGIParameter("DispText", pcParam, i32NumParams);
    
        //
        // If the parameter was not found, show the error page.
        //
        if(lStringParam == -1)
        {
            return(PARAM_ERROR_RESPONSE);
        }
    
        //
        // The parameter is present. We need to decode the text for display.
        //
        DecodeFormString(pcValue[lStringParam], pcDecodedString, 48);
    
        //
        // Print sting over the UART
        //
        UARTprintf(pcDecodedString);
        UARTprintf("\n");
    
        //
        // Tell the HTTPD server which file to send back to the client.
        //
        return(DEFAULT_CGI_RESPONSE);
    }
    
    //*****************************************************************************
    //
    // This function is called by the HTTP server whenever it encounters an SSI
    // tag in a web page.  The iIndex parameter provides the index of the tag in
    // the g_pcConfigSSITags array. This function writes the substitution text
    // into the pcInsert array, writing no more than iInsertLen characters.
    //
    //*****************************************************************************
    static int32_t
    SSIHandler(int32_t iIndex, char *pcInsert, int32_t iInsertLen)
    {
        //
        // Which SSI tag have we been passed?
        //
        switch(iIndex)
        {
            case SSI_INDEX_LEDSTATE:
                io_get_ledstate(pcInsert, iInsertLen);
                break;
    
            case SSI_INDEX_FORMVARS:
                usnprintf(pcInsert, iInsertLen,
                        "%sls=%d;\nsp=%d;\n%s",
                        JAVASCRIPT_HEADER,
                        io_is_led_on(),
                        io_get_animation_speed(),
                        JAVASCRIPT_FOOTER);
                break;
    
            case SSI_INDEX_SPEED:
                io_get_animation_speed_string(pcInsert, iInsertLen);
                break;
    
            default:
                usnprintf(pcInsert, iInsertLen, "??");
                break;
        }
    
        //
        // Tell the server how many characters our insert string contains.
        //
        return(strlen(pcInsert));
    }
    
    //*****************************************************************************
    //
    // The interrupt handler for the SysTick interrupt.
    //
    //*****************************************************************************
    void
    SysTickIntHandler(void)
    {
        //
        // Call the lwIP timer handler.
        //
        lwIPTimer(SYSTICKMS);
    }
    
    //*****************************************************************************
    //
    // The interrupt handler for the timer used to pace the animation.
    //
    //*****************************************************************************
    void
    AnimTimerIntHandler(void)
    {
        //
        // Clear the timer interrupt.
        //
        MAP_TimerIntClear(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
    
        //
        // Indicate that a timer interrupt has occurred.
        //
        HWREGBITW(&g_ulFlags, FLAG_TICK) = 1;
    }
    
    //*****************************************************************************
    //
    // Display an lwIP type IP Address.
    //
    //*****************************************************************************
    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(pcBuf);
    }
    
    //*****************************************************************************
    //
    // Required by lwIP library to support any host-related timer functions.
    //
    //*****************************************************************************
    void
    lwIPHostTimerHandler(void)
    {
        uint32_t ui32Idx, ui32NewIPAddress;
    
        //
        // Get the current IP address.
        //
        ui32NewIPAddress = lwIPLocalIPAddrGet();
    
        //
        // 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);
                UARTprintf("\n");
                UARTprintf("Open a browser and enter the IP address.\n");
    
            }
    
            //
            // Save the new IP address.
            //
            g_ui32IPAddress = ui32NewIPAddress;
        }
    
        //
        // If there is not an IP address.
        //
        if((ui32NewIPAddress == 0) || (ui32NewIPAddress == 0xffffffff))
        {
            //
            // Loop through the LED animation.
            //
    
            for(ui32Idx = 1; ui32Idx < 100; ui32Idx++) //was < 17
            {
    
                //
                // Toggle the GPIO
                //
                MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1,
                        (MAP_GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_1) ^
                         GPIO_PIN_1));
    
                SysCtlDelay(g_ui32SysClock/(ui32Idx << 1));
    
            }
        }
    }
    
    //*****************************************************************************
    //
    // This example demonstrates the use of the Ethernet Controller and lwIP
    // TCP/IP stack to control various peripherals on the board via a web
    // browser.
    //
    //*****************************************************************************
    int
    main(void)
    {
        uint32_t ui32User0, ui32User1;
        uint8_t pui8MACArray[8];
    
        //
        // 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.
        //
        SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ);
    
        //
        // Run from the PLL at 120 MHz.
        //
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                                 SYSCTL_OSC_MAIN |
                                                 SYSCTL_USE_PLL |
                                                 SYSCTL_CFG_VCO_480), 120000000);
    
        //
        // Configure the device pins.
        //
        PinoutSet(true, false);
    
        //
        // Configure debug port for internal use.
        //
        UARTStdioConfig(0, 115200, g_ui32SysClock);
    
        //
        // Clear the terminal and print a banner.
        //
        UARTprintf("\033[2J\033[H");
        UARTprintf("Ethernet IO Example\n\n");
    
        //
        // Configure SysTick for a periodic interrupt.
        //
        MAP_SysTickPeriodSet(g_ui32SysClock / SYSTICKHZ);
        MAP_SysTickEnable();
        MAP_SysTickIntEnable();
    
        //
        // 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))
        {
            //
            // Let the user know there is no MAC address
            //
            UARTprintf("No MAC programmed!\n");
    
            while(1)
            {
            }
        }
    
        //
        // Tell the user what we are doing just now.
        //
        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);
    
        //
        // Initialze the lwIP library, using DHCP.
        //
        lwIPInit(g_ui32SysClock, pui8MACArray, 0, 0, 0, IPADDR_USE_AUTOIP);
        //
        // Setup the device locator service.
        //
        LocatorInit();
        LocatorMACAddrSet(pui8MACArray);
        LocatorAppTitleSet("EK-TM4C1294XL enet_io");
    
        //
        // Initialize a sample httpd server.
        //
        httpd_init();
    udp_init(); struct udp_pcb * mypcb; mypcb = udp_new(); if (mypcb == NULL) { UARTprintf("udp failed.\n"); } else { UARTprintf("udp up.\n"); } if (udp_bind(mypcb, IP_ADDR_ANY, 8760) != ERR_OK) { UARTprintf("udp bind failed.\n"); } // // 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(INT_EMAC0, ETHERNET_INT_PRIORITY); MAP_IntPrioritySet(FAULT_SYSTICK, SYSTICK_INT_PRIORITY); // // Pass our tag information to the HTTP server. // http_set_ssi_handler((tSSIHandler)SSIHandler, g_pcConfigSSITags, NUM_CONFIG_SSI_TAGS); // // Pass our CGI handlers to the HTTP server. // http_set_cgi_handlers(g_psConfigCGIURIs, NUM_CONFIG_CGI_URIS); // // Initialize IO controls // io_init(); // // Loop forever, processing the on-screen animation. All other work is // done in the interrupt handlers. // while(1) { // // Wait for a new tick to occur. // while(!g_ulFlags) { // // Do nothing. // } // // Clear the flag now that we have seen it. // HWREGBITW(&g_ulFlags, FLAG_TICK) = 0; // // Toggle the GPIO // MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, (MAP_GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_1) ^ GPIO_PIN_1)); // // Transmit to UDP // static int n = 0; pbuf1 = pbuf_alloc(PBUF_TRANSPORT, 100, PBUF_RAM); pbuf1->payload = (void*)buf; pbuf1->tot_len = 15; pbuf1->len = 15; buf[0] = 0x10; buf[1] = 0; usprintf(&buf[4], " test %d", n++); udp_send(remote_pcb, pbuf1); pbuf_free(pbuf1); } }

  • The biggest problem here is the fact that you are calling lwIP from a context other than the Ethernet interrupt handler. lwIP is not re-entrant so you must only call it from one context. When using an RTOS, this means only calling the lwIP APIs from a single thread. When not using an RTOS (like most of our examples), you must make calls in the context of the Ethernet interrupt handler. This can be tricky to do but there's a timer callback implemented that makes it a bit easier. Make sure you set HOST_TMR_INTERVAL to some non-zero value (I think it's a number of milliseconds) and implement lwIPHostTimerHandler in your application then make all your lwIP calls from the lwIPHostTimerHandler function.

    This may sound awkward but it's vital. Calling udp_send from the main loop will likely work for a while but I guarantee you that it will crash after some period of time as some internal data structure in lwIP gets corrupted. This kind of problem is VERY difficult to debug so it's far better to fix this now rather than trying to pretend everything is OK and have it fail catastrophically later.

  • Dave,

    So the reason why the code above was not working was because I was using a non-instantiated PCB structure.
    Once I replaced this: "udp_send(remote_pcb, pbuf1);" with this: "udp_send(mypcb, pbuf1);" it started transmitting the UDP packets.

    Once this was working I left it alone for a few hours, then when I came back to try something else I noticed that it had gone to an error state.  Probably your comment was right about calling the udp_send function in the main loop vs the HostTimer interrupt.  So now I call the udp_send from "lwIPHOstTimerHandler(void)" function instead.


    What I'm trying to do now is have this device start up with a static IP address.  The sample code has it where it times out then assigns an IP address but this takes about 4 minutes to do this, way too long.  I have tried the following: 

    lwIPInit(g_ui32SysClock, pui8MACArray, 0xA9FED3A1, 0xFFFFFF00, 0xA9FED302, IP_ADDR_USE_STATIC);

    But this does not seem to work as the board seems to be stuck trying to acquire an address.
    The other thing I was trying to do is use the "udp_sendto" instruction but this also gives me problems.
    Just replacing the "udp_send" with "udp_sendto" does not work.  
    This is how i'm using "udp_sendto":

    udp_send(mypcb, pbuf1);  //this works
    
    struct ip_addr udpDestIpAddr;
    IP4_ADDR(&udpDestIpAddr, 169, 254, 211, 75);
    udp_sendto(mypcb, pbuf1, &udpDestIpAddr, 1234); // this does not work

    any tips on these issues?

    Regards,
    Alonso

     

  • What you are doing to select a static IP address looks OK to me (though, in fairness, I've not actually tried this for a while). You may like to edit your lwipopts.h and make sure that you define both LWIP_DHCP and LWIP_AUTOIP to 0 so that these protocols are no longer available and see if this helps.

    As far as use of udp_send vs. udp_sendto is concerned, my guess (and it's only a guess) would be that udp_sendto doesn't work if you've previously called udp_connect for the PCB. If that doesn't turn out to be the answer, your best bet is likely to pose the question on the lwIP user's mailing list which you can sign up for here. The developers follow this list and you will usually get a good answer pretty quickly.

  • Dave,

    Thanks for the help. Your first recommendation fixed both problems.

    Regards,
    Alonso

  • Dave,

    I'm encountering another problem.  
    I now have two tiva C board connected through a switch along with a computer to sniff the traffic.
    One board is set to transmit "test" via udp and the other board is simply set to receive udp traffic.
    The problem is that when the transmit board sends out a packet WireShark sees an ARP message, such as this: "Who has 169.254.211.116? Tell 169.254.211.100"

    I have changed lwipopts.h to what I think is disabling APR check, but it does not work.

    Do you have any tips?

    Regards,
    Alonso

    here is my code setup:

    main
    {
    //........
    lwIPInit(g_ui32SysClock, pui8MACArray, 0xA9FED364, 0xFFFFFF00, 0, IPADDR_USE_STATIC);
    httpd_init();
    udp_unit();
    udp_bind(mypcb, IP_ADDR_ANY, 1234);
    mypcb = udp_new() //mypcb is a global veriable
    udp_recv(mypcb, udp_recv_packet, NULL);
    //......
    }
    
    lwIPHostTimerHandler(void)
    {
    //.....
    udp_sendto(mypcb, pbuf1, &udpDestIpAddr, 1234);
    //.......
    }

  • Hi Alonso,

    Noticed Systick timer for LWIP and there is no SYSCLK divisor insuring it to be10ms or 100Hz default. Had similar issue with UDP 23 in that the LWIP interrupt set inside LWIP as Systick interrupt handler set to EthClientTick(10).

    After moving Systick INT handler into (main.c) setting the divisor of Systick (200) or 5ms and the LWIP options HostInterval Timer 200 the same with EthClientTick(200), UDP packet flow improved a 100 fold no more jerky updates or locks of the TCP stack. Something about UDP and ICMP messages seems to ring a bell here but that bell has long departed the why part.

    Here we have a multi protocol stack that serves both TCP and UDP ports oddly via (tcp.c) in the same IP stack transport.

    #define ICMP_DEBUG  LWIP_DBG_OFF

  • ARP is a required part of the IP stack. The board cannot transmit to the destination IP until it has successfully ARPed to find the destination IP in terms of its Ethernet MAC Address. The actual MAC Address may be that of an intermediate step, such as a router.

  • David, I used to believe the same thing about ARP as it is taught at Berkley the TCPIP home of the internet protocol.

    Yet No ARP is enabled in my (LWIPoptions.h) and the client finds the destination address of the UDP target without error, that is the destination address exists on the same subnet. This No ARP configuration is being used for IOT internet of things to locate the web Cloud endpoint without ARP. Oddly it works ok and finds this single endpoint without ARP but does use (dns.c) to resolve the name to IP address.

    They teach us ARP is useful when the destination address of a Host is not located on the same subnet as the client. The address resolution protocol ARP then does an adjacency test to locate the destination IP on other network addresses were an ACK is then returned to signify the Host is alive. Often a timer is set on ARP to constrain how much time he can do this find of the destination. Hence we already have the destination IP address in TCP prior to looking for it since DNS gave it to TCP which ARPed the destination address is dead or alive. Seemingly ARP comes in handy to traverse other VLANS network subnet addresses in order to validate the endpoint is alive when it does not exit within the same network.

     

  • Might add in the posters silence looking at (lwiplib.c) the LWIP timer services ARP table refresh function called however Debug enabled OFF in LWIP options was believed to disable the timer call to function. Sniffing the clients UDP Telnet packets at the host and see not one ARP protocol entry, only TCP (SYN) (ACK) | (PSH, ACK).

    Not sure why anyone would want to rewrite LWIP when it works so well to provide a TCP stack for the launch pad. In as much foggy do recall TCP topic often discuses ARP tough it seldom gets much attention. One can clear the ARP table in windows issuing (ARP -d) or look at it simply type ARP at a command console.

    Agree with David the MAC address of the destination is the unknown though in TCP not sure that's significant. Recall the 7 network layers, (device layer) MLID must have the destination MAC address (when IPX/SPX protocol) of the host even though client knows the destination IP address using TCP he is unaware of the hosts MAC when IPX and is unable to create a MAC frame to the host without it. That MAC frame was part of IPX/SPX where and IP address destination did not exist. Now days we have routing tables on Windows so are not ARP MAC frames useless in that alone and might it be NETBUI the Microsoft client transport (print & file share) uses ARP?

    Always disable NETBUI on the internet NIC or you will get hacked when someone ARPS your MAC they can take control at the device layer of the host.

    Sure glad the days of IPX/SPX  & SAP are long gone. 

     

  • Hi Jeff,

    I know that your topic is 3 years old but did you finally succeed to make run your UDP transfer ? Did you succeed to send your data to a computer for example ?

    I'm looking for a telnet example but I don't find how to start. Do you have some tips to share ? I don't absolutely need to do a telnet but I need to send some date to a computer and I'm totally lost actually. I don't even know how to start. Would you have an example or would you agree to share your working code ?

    Many thanks in advance

    Nicolas

  • Hello Nicolas,

    Please open a new post for a better response.