How can I send a simple HTTP request with a lwIP stack?

tgun926 picture tgun926 · Oct 4, 2014 · Viewed 18k times · Source

Please move/close this if the question isn't relevant.

Core: Cortex-M4

Microprocessor: TI TM4C1294NCPDT.

IP Stack: lwIP 1.4.1

I am using this microprocessor to do some data logging, and I want to send some information to a separate web server via a HTTP request in the form of:

http://123.456.789.012:8800/process.php?data1=foo&data2=bar&time=1234568789

and I want the processor to be able to see the response header (i.e if it was 200 OK or something went wrong) - it does not have to do display/recieve the actual content.

lwIP has a http server for the microprocessor, but I'm after the opposite (microprocessor is the client).

I am not sure how packets correlate to request/response headers, so I'm not sure how I'm meant to actually send/recieve information.

Answer

tgun926 picture tgun926 · Jan 23, 2015

This ended up being pretty simple to implement, forgot to update this question.

I pretty much followed the instructions given on this site, which is the Raw/TCP 'documentation'.

Basically, The HTTP request is encoded in TCP packets, so to send data to my PHP server, I sent an HTTP request using TCP packets (lwIP does all the work).

The HTTP packet I want to send looks like this:

HEAD /process.php?data1=12&data2=5 HTTP/1.0

Host: mywebsite.com

To "translate" this to text which is understood by an HTTP server, you have to add "\r\n" carriage return/newline in your code. So it looks like this:

char *string = "HEAD /process.php?data1=12&data2=5 HTTP/1.0\r\nHost: mywebsite.com\r\n\r\n ";

Note that the end has two lots of "\r\n"

You can use GET or HEAD, but because I didn't care about HTML site my PHP server returned, I used HEAD (it returns a 200 OK on success, or a different code on failure).

The lwIP raw/tcp works on callbacks. You basically set up all the callback functions, then push the data you want to a TCP buffer (in this case, the TCP string specified above), and then you tell lwIP to send the packet.

Function to set up a TCP connection (this function is directly called by my application every time I want to send a TCP packet):

void tcp_setup(void)
{
    uint32_t data = 0xdeadbeef;

    /* create an ip */
    struct ip_addr ip;
    IP4_ADDR(&ip, 110,777,888,999);    //IP of my PHP server

    /* create the control block */
    testpcb = tcp_new();    //testpcb is a global struct tcp_pcb
                            // as defined by lwIP


    /* dummy data to pass to callbacks*/

    tcp_arg(testpcb, &data);

    /* register callbacks with the pcb */

    tcp_err(testpcb, tcpErrorHandler);
    tcp_recv(testpcb, tcpRecvCallback);
    tcp_sent(testpcb, tcpSendCallback);

    /* now connect */
    tcp_connect(testpcb, &ip, 80, connectCallback);

}

Once a connection to my PHP server is established, the 'connectCallback' function is called by lwIP:

/* connection established callback, err is unused and only return 0 */
err_t connectCallback(void *arg, struct tcp_pcb *tpcb, err_t err)
{
    UARTprintf("Connection Established.\n");
    UARTprintf("Now sending a packet\n");
    tcp_send_packet();
    return 0;
}

This function calls the actual function tcp_send_packet() which sends the HTTP request, as follows:

uint32_t tcp_send_packet(void)
{
    char *string = "HEAD /process.php?data1=12&data2=5 HTTP/1.0\r\nHost: mywebsite.com\r\n\r\n ";
    uint32_t len = strlen(string);

    /* push to buffer */
        error = tcp_write(testpcb, string, strlen(string), TCP_WRITE_FLAG_COPY);

    if (error) {
        UARTprintf("ERROR: Code: %d (tcp_send_packet :: tcp_write)\n", error);
        return 1;
    }

    /* now send */
    error = tcp_output(testpcb);
    if (error) {
        UARTprintf("ERROR: Code: %d (tcp_send_packet :: tcp_output)\n", error);
        return 1;
    }
    return 0;
}

Once the TCP packet has been sent (this is all need if you want to "hope for the best" and don't care if the data actually sent), the PHP server return a TCP packet (with a 200 OK, etc. and the HTML code if you used GET instead of HEAD). This code can be read and verified in the following code:

err_t tcpRecvCallback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    UARTprintf("Data recieved.\n");
    if (p == NULL) {
        UARTprintf("The remote host closed the connection.\n");
        UARTprintf("Now I'm closing the connection.\n");
        tcp_close_con();
        return ERR_ABRT;
    } else {
        UARTprintf("Number of pbufs %d\n", pbuf_clen(p));
        UARTprintf("Contents of pbuf %s\n", (char *)p->payload);
    }

    return 0;
}

p->payload contains the actual "200 OK", etc. information. Hopefully this helps someone.

I have left out some error checking in my code above to simplify the answer.