tgun926
tgun926

Reputation: 1633

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

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.

Upvotes: 15

Views: 27902

Answers (3)

Darkness
Darkness

Reputation: 39

I managed to create an HTTP client for raspberry pi Pico W using the example here.

It uses the httpc_get_file or httpc_get_file_dns functions from the sdk.

However, that example is incomplete since it has a memory leak. You will need to free the memory taken by the struct pbuf *hdr in the headers function and struct pbuf *p in the body function with respectively pbuf_free(hdr); and pbuf_free(p);

Without those modifications, it will stop working after about 20 calls (probably depends on the size of the response).

Upvotes: 2

tgun926
tgun926

Reputation: 1633

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.

Upvotes: 25

Robert Deml
Robert Deml

Reputation: 12532

Take a look at the HTTP example in Wikipedia. The client will send the GET and HOST lines. The server will respond with many lines for a response. The first line will have the response code.

Upvotes: 1

Related Questions