Adding new features to the application

We have gone through the steps to add an application to the Linux image running on Intel Galileo. If you have a service process running in the background, you should work out the ways in which you communicate with the process, to send commands to the application.

There are many methodologies that you can apply or implement for your application to receive commands and send outputs to the requested client. We will investigate two methodologies to send and receive messages to and from the application. We will use named pipes and network sockets. Named pipes help you deliver messages locally to the home automation service. Network sockets give you the ability to deliver messages through the network connection.

Using named pipes

A named pipe is usually used to create an inter-process communication structure between processes. The idea is to create a special Linux device file to read and write data and bytes to the named file. Named pipes use the first in first out (FIFO) principle. The first message in the FIFO will be received by the process in the named pipe.

We will follow the following steps to create a named pipe in the filesystem and read/write messages from the pipe.

  1. We have to create a pipe file. If you create a pipe from the command line, you can use the mknod tool and set its permissions for the chmod tool. See the example shown here:
    root@clanton:~# mknod test p
    root@clanton:~# chmod 666 test
    root@clanton:~# ls –all test
    prw-rw-rw-    1 root     root             0 Jan  2 05:50 test

    If you want to create the pipe in the application, here is the sample code:

    /* Create the FIFO if it does not exist */         
    umask(0);
    mknod("PIPE_FILE", S_IFIFO|0666, 0);
  2. We created our named pipe, so we need to be able to read the incoming messages from the pipe. We will use standard file libraries to open and read from the named pipe. A basic reading can be done with the following code:
    FILE *fp; 
    char readbuf[80];
    fp = fopen(FIFO_FILE, "r"); 
    fgets(readbuf, 80, fp); 
    printf("Received string: %s
    ", readbuf);
    fclose(fp);
  3. In order to write to the pipeline, you can simply use the echo tool or you can use the standard file library to write into the file you've opened.

When you execute the following command from the command line interface, it will send the show home status message to the SMARTHOMEPIPE variable and the application, which has opened the pipe, will receive it.

root@clanton:~# echo "show home status" > SMARTHOMEPIPE

You can write in a named pipe in a C application as shown in the following code:

FILE *fp;
if((fp = fopen(FIFO_FILE, "w")) == NULL) {
     perror("fopen");
     exit(1);
}
fputs(argv[1], fp);
fclose(fp);

Using named pipes in the application

In the application, we have created a thread to receive messages from the named pipe. In our application, we only needed to receive commands as we will write the output to an XML file. The thread's implementation is shown in the next code:

/**
 * Handle User Requests from PIPE
 */
void* named_pipe_handler(void* arg) {
  FILE *fp;
  char readbuf[USERBUF];

  /* Create the FIFO if it does not exist */
  umask(0);
  mknod(FIFO_FILE, S_IFIFO | 0666, 0);
  fp = fopen(FIFO_FILE, "r");
  while (1) {
    if (fgets(readbuf, USERBUF, fp) != NULL) {
      snprintf(command, sizeof(readbuf), readbuf);
    }
  }
  fclose(fp);
  return NULL;
}

Network sockets

Network sockets are extremely common in today's connected world to create communication between two devices connected to the Internet. Sockets are the virtual endpoints of a system on a network. You can imagine them as pipelines used from two different processes on different devices. If you want to communicate, Intel Galileo and an Android phone can send and receive messages through allocated network sockets, which is like using pipelines in the system.

You will use network sockets to enable your home automation application to receive and send messages from/to remote devices. This ability will enable your application to receive from any device with an Internet connection. For example, if you want to manage your Smart Home application with an Android device, you need to implement the code for the Android application to connect the defined network socket and send defined commands to our application such as switching a wall plug or lamp holder on and off.

We have defined a new module in the application named socket_listener. We have added the socket_listener.c and socket_listener.h variables to define a new method to work as a new thread to listen the defined network socket and receive commands from the outside.

#ifndef SOCKET_LISTENER_H_
#define SOCKET_LISTENER_H_

#define SOCKET 3500
#define MAXBUF 5000

void* socket_worker(void* arg);

#endif /* SOCKET_LISTENER_H_ */

The socket_worker function takes one argument, which is the command variable from the main function. The thread writes the received bytes to the command and the main function checks the received message. The content of the socket_listener.c file that includes the code for the socket_worker function is given here; the code for the socket_worker function is listening to the defined socket, which is 3000 in this case, and sends ACK as an acknowledgment.

#include "socket_listener.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

void* socket_worker(void* arg) {
  char buffer[MAXBUF + 1];
  char* msg = "ACK
";
  struct sockaddr_in dest; /* socket info about the machine connecting to us */
  struct sockaddr_in serv; /* socket info about our server */
  int homesocket; /* socket used to listen for incoming connections */
  socklen_t socksize = sizeof(struct sockaddr_in);

  memset(&serv, 0, sizeof(serv)); /* zero the struct before filling the fields */
  serv.sin_family = AF_INET; /* set the type of connection to TCP/IP */
  serv.sin_addr.s_addr = htonl(INADDR_ANY); /* set our address to any interface */
  serv.sin_port = htons(SOCKET); /* set the server port number */

  homesocket = socket(AF_INET, SOCK_STREAM, 0);

  /* bind serv information to mysocket */
  bind(homesocket, (struct sockaddr *) &serv, sizeof(struct sockaddr));

  /* start listening, allowing a queue of up to 1 pending connection */
  listen(homesocket, 1);
  int consocket = accept(homesocket, (struct sockaddr *) &dest, &socksize);
  while (consocket) {
    send(consocket, msg, strlen(msg), 0);
    int received = -1;
    /* Receive message */
    if ((received = recv(consocket, buffer, MAXBUF, 0)) >= 0) {
      //printf("Received %s from %s 
", buffer, inet_ntoa(dest.sin_addr));
      snprintf(arg, sizeof(buffer), buffer);
      memset(buffer,0,sizeof(buffer));
    }
    close(consocket);
    consocket = accept(homesocket, (struct sockaddr *) &dest, &socksize);
  }
  return NULL;
}

Let's review the code step by step. First, you need to include the required Linux libraries in your application; <arpa/inet.h>, <sys/types.h>, <netinet/in.h>, and <sys/socket.h> are the required header files. Then, in the socket_worker function, you need to define the variables that are needed to create a network socket, send/receive data, and socket file descriptor. The sockaddr_in and socklen_t structs are defined in the libraries that we have included.

After variable definitions and declarations, we initialize the network socket and bind it to any interface, Wi-Fi, or Ethernet interface. Then, run a loop to accept the incoming connection and use the recv function to receive messages from the incoming connection, and use the send function to send the ACK message.

Now, copy the received message buffer to arg, which is the command variable from the main process. The main process checks the incoming message to execute the command.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset