How do you call a REST API using C programming that can be flashed into a Arduino board?

Checklist to start off

Code that is written in plain C program can always be burnt into Arduino device. This makes things easy for us to get started. Following is a check list that we need to get the job done.

  • CodeLite IDE or Arduino Create.
  • REST API hosted on a server or localhost.
  • Request format of the POST web service.

IDE (CodeLite will be used in this tutorial)

About codelite: CodeLite is an open source, free, cross platform IDE specialised in C, C++, PHP and JavaScript (mainly for backend developers using Node.js) programming languages which runs best on all major Platforms ( OSX, Windows and Linux ). You can download the IDE from here

REST API

If you are familiar with php, the approach would be to clone the slim frame from GitHub and create your first web service real quick. Link to GitHub project. Download this template and start adding methods onto the routes.php file. Download source code

Below is a POST method which accepts the following data format and sends back the response if the program sends the request as intended. http://x.x.x.x/rest/iot_tuts/services/staging/v1/app/addDeviceData



Request 
{
  "account_id": "001",
  "device_id": "a001",
  "timestamp": "1633565366",
  "longitude": "13.0836",
  "latitude": "80.2392",
  "ch_1": 10.2,
  "ch_2": 34.32,
  "ch_10": 65.34
}

Response
{
   "error":false,
   "message":"data added."

}

Now that we have the pre-requisites ready its time that we open codelite, create a workspace and a project as shown below,

Ensure that you select the gcc compiler option while creating the project. Once the project is created you will see a main.c file that is generated. Copy paste the following code on to the file.



/*
 * Plain C code to hit POST and GET REST API
 * 
 * Author: Srimath (srimath.balakrishnan@codeneuron.com)
 * Created: 27-07-2018
 * Editor: CodeLite OSX
 * 
 */


#include stdio.h
#include stdlib.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include netdb.h
#include arpa/inet.h

char jsonData[1000]="{\"account_id\": \"001\",\"device_id\": \"a001\",\"longitude\": \"13.0836\",\"latitude\": \"80.2392\",\"ch_1\": 10.2,\"ch_2\": 34.32,\"ch_6\": 65.34";
//char jsonData[1000]="{\"timestamp\":\"125532665\"}";
char jsonRequest[1000]={0};
char serviceMethod[]="rest/iot_tuts/services/staging/v1/app/addDeviceData";
//char serviceMethod[]="rest/shape/services/staging/postConnectivityTest";
char requestBuilder[150]={0};
char hostIP[30]="x.x.x.x";
char token[200] = "2-uok5PvRILXwSINDMdo0vT-KM8hHcVJr-BDuOEx-GQ";
char request[1000];
char response[1000];
char responseTrimmed[1000];
struct hostent *server;
struct sockaddr_in serveraddr;
char recvline[100];
int port = 80;    
int c = 0, d = 0;

char timestamp[20];
char jsonDataBuilder[1000];
char timestampBuilder[40];

void pushData ()
{
    sprintf(requestBuilder,"/%s HTTP/1.1",serviceMethod);
    sprintf(timestamp, "%lu", (unsigned long)time(NULL)); 

    sprintf(timestampBuilder, ",\"timestamp\":\"%s\"}",timestamp);
    strcat (jsonData, timestampBuilder);
    printf (jsonData);

    //---Working Code---
    sprintf(jsonRequest,"POST %s\r\nHost: %s\r\nauthorization: Bearer %s\r\ncontent-type: application/json\r\ncontent-length: %d\r\n\r\n%s\r\n",requestBuilder,hostIP,token,strlen(jsonData),jsonData);
    //---If GET use this---
    //sprintf(aszJSONRequest, "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n", serviceMethod, hostIP);

    printf("\n-----REQUEST-----\n");
     
    int tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
     
    if (tcpSocket < 0)
        printf("\nError opening socket");
    else
        printf("\nSuccessfully opened socket\n\n");
     
    server = gethostbyname(hostIP);
    //strcpy (server, "52.15.222.145"); 
    if (server == NULL)
    {
        printf("gethostbyname() failed\n");
    }
    else
    {
        //printf("\nserver - %s", server->h_addr_list[0]);
        printf("IP\n");
        unsigned int j = 0;
        while (server -> h_addr_list[j] != NULL)
        {
            printf("%s", inet_ntoa(*(struct in_addr*)(server -> h_addr_list[j])));
            j++;
        }
    }
     
    printf("\n");
 
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
 
    bcopy((char *)server->h_addr, (char *)&serveraddr.sin_addr.s_addr, server->h_length);
     
    serveraddr.sin_port = htons(port);
     
    if (connect(tcpSocket, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
        printf("\nError Connecting");
    else
        printf("\nSuccessfully Connected\n");
   
    printf("\n%s\n", jsonRequest);
     
    if (send(tcpSocket, jsonRequest, strlen(jsonRequest), 0) < 0)
        fprintf(stderr, "Error with send()");
    else
        fprintf(stderr, "Successfully sent request");
        
    printf("\n-----END-----\n");
     
    bzero(response, 1000);
     
    printf("\n-----SERVER RESPONSE----\n");
    
    recv(tcpSocket, response, 999, 0);
    printf("\n%s", response);
    
    printf("\n-----END-----\n");
     
    close(tcpSocket);
}

int main(int argc, char** argv) {
    
    pushData ();
     
    return (EXIT_SUCCESS);
}


As the as the code goes between line 20 and 40, the first important bunch is variable declaration which has got the JSON body that is set up in the format that the API expects, along with a variable to point to the service method. As the declaration goes we would need a buffer to append the request and then read the response line by line. Finally, a port number that is usually 80 in which Apache runs.

The key here is follow the format right from line 43 to 51, where in dynamic variables are string formatted on to the request (line 43, 44 and 46). Then finally the actual response in built. Do ensure that the format with which this request is set up is used without any tweak since this is a standard that the server understands and expects in the request. The /r/n, the number of times they occur for each of the fields such as Host, Authorization, Content type and Content length is important. Tweaks to these will cause an incorrect understanding of the header format at the server end and cause the server to through error codes. Replace all the values for the variables with the method name, request format, port and server IP against desired values of your API and server. Hit the services, check them and flash the code on to the board.

Narayan B.S.S

Close to 10 years of experience in Mobile, Web, Gaming, AR, VR, IoT, AI, ML and Image processing. Have architected several products to success. Loves to code, learn and share knowledge. Similar minds please connect through linkedin.