How Do
Webhooks
Webhooks

Now we see how to handle the webhooks on a Raspberry Pi using C.

This example can also be easily modified to work on Windows using Cygwin, the file download also includes the Windows version.

In order to receive data from the PHP file we need to listen on a socket. This example shows how to get the incoming data from the socket and process the parameters that are passed.

Once the parameters are found the Raspberry Pi then either performs an action or sends back some data to the socket.


First we declare an enum for the parameters so that we don't have to remember the parameter positions.

Parameter enumerations
  enum PARAMS
  {
      COM,
      VAL,
      KEY,
      SUB,
      DEV
  };

It's a good idea to listen for data on a new thread, otherwise the rest of the program will hang while listening for an incoming connection.

Here we start the listening ServerThread.

Start server thread
void StartServer()
{
    pthread_t tl;
    pthread_create(&tl,NULL,ServerThread,NULL);
}

In the server thread we set up the server and start listening for an incoming connection:

Server thread
void * ServerThread()
{
   struct addrinfo hints, *server;
   memset(&hints, 0, sizeof hints);

   hints.ai_family =  AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags = AI_PASSIVE || SOCK_NONBLOCK;
   getaddrinfo(NULL, "8090", &hints, &server);

   int sockfd = socket(server->ai_family,server->ai_socktype, server->ai_protocol);
   bind(sockfd, server->ai_addr, server->ai_addrlen);
   listen(sockfd, 100);

   struct sockaddr_storage client_addr;
   socklen_t addr_size = sizeof client_addr;

   char buffer[2048];

   while(1)
   {
      printf("Waiting for connection\n");
      client_fd = accept(sockfd,(struct sockaddr *) &client_addr, &addr_size);
      printf("Client connected\n");

      struct timeval tv;
      tv.tv_sec = 11;
      tv.tv_usec = 0;
      //timeout in seconds until the read function gives up
      setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
      //allow the address to be reused so that we can reconnect straight away
      setsockopt(client_fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));

Once the client is connected we read the data that was sent.

Read data
      if (client_fd > 0)
      {
          printf("Data received from client\n");
          while(read(client_fd, buffer, 2048) > 0)
          {
            printf("Received: %s\n", buffer);

The incoming parameters are split at the commas and stored in an array.

Split parameters
            char *p_buf = buffer;
            char Arguments[5][128];
            char *token;
            int count = 0;
            while ((token = strsep(&p_buf, ",")) != NULL && count<5)
            {
                strcpy(Arguments[count],token);
                printf("Data at argument %d: %s\n",count,Arguments[count]);
                count++;
            }

The commands implemented are 'show' and 'get'.

The 'show' command takes a value parameter of 1, 2 or 3 and when executed displays a message on screen.

The 'get' command can be used with the key parameter of 'value1', 'value2' or 'value3' and when executed returns a value to the client.

Process command
            char returnBuffer[10];
            bool found=false;
            //Check the command
            if(strncmp("show",Arguments[COM],4) == 0)
            {
                //perform a command
                printf("Command received\n");

                //Get the value parameter
                char x = Arguments[VAL][0];
                switch(x)
                {
                    case '1':
                        printf("1 Selected\n");
                        found=true;
                        strcpy(returnBuffer,"OK\n");
                        break;
                    case '2':
                        printf("2 Selected\n");
                        found=true;
                        strcpy(returnBuffer,"OK\n");
                        break;
                    case '3':
                        printf("3 Selected\n");
                        found=true;
                        strcpy(returnBuffer,"OK\n");
                        break;
                    default:
                        printf("Invalid value\n");
                        break;
                }

            }
            if(strncmp("get",Arguments[COM],3) == 0)
            {
                printf("Get variable\n");
                if( strncmp("value1",Arguments[KEY],6)==0)
                {
                    sprintf(returnBuffer,("Value 1\n"));
                    found=true;
                }
                if( strncmp("value2",Arguments[KEY],6)==0)
                {
                    sprintf(returnBuffer,("Value 2\n"));
                    found=true;
                }
            }

We now send the message back to the client.

Reply
            if(!found)
            {
                sprintf(returnBuffer,("FAIL\n"));
            }

            printf("Replying with: %s",returnBuffer);
            write(client_fd, returnBuffer, strlen(returnBuffer));
            fflush(stdout);

Once the command has been processed, the connection is closed.

Close connection
            printf("Closing client connection\n");
            close(client_fd);
          }
      }
  }
}