Introduction: 16 Relay Module With Raspberry Pi 3 Using Socket

About: Do you like technology? Follow my channel on Youtube and my Blog. In them I put videos every week of microcontrollers, arduinos, networks, among other subjects.

Today, we are going to talk about Raspberry Pi 3, which is actually an enormous microcomputer due to its Quad core processor that comes with plenty of memory and a Linux operating system. In this project, which consists of a 16 relay module with Socket, my goal here is to start automation with Raspberry PI, introduce socket and Layer concepts, program in C Unix / Linux, show a client-server, and use Lib WiringPI functions.

Step 1: Resources Used

In this tutorial, we will cover important things about network and communication protocol, which summarizes IoT (Internet Of Things). For those who want to do the project today but don’t have the 16 relay boards, this won’t be a problem. You can use LEDs attached to the ports of the Raspberry Pi using a 330-Ohm resistor.

Step 2: Goals

- Start automation with Raspberry Pi

- Introduce socket and layer concepts

- Show a client-server

- Introduce programming in C Unix / Linux

- Use Lib WiringPI functions

Step 3: Geany Configuration

After installing Raspberry Pi 3, you need to configure the Geany IDE before compiling the codes. Inside Raspberry Pi, we will program in C language using GCC compiler.

1. Click the menu

Build -> Set Build Commands

(Build -> Define Construction Commands);

2. Add the following commands:

In "Compile" add: "-lwiringPi" and "-lthread"

In "Build" add: "-lwiringPi" and "-lthread"

In "Execute" add: "sudo" as in the image

Step 4: Client.c and Server.c Programs

In our example today, we will make a program Client.c and Server.c. Both will run within the Linux operating system of Raspberry Pi 3 using IP 127.0.0.1, which is the internal IP LoopBack. So there are two memory processes, where one Client / Server will be talking to the other. But, of course, you can change the IP and run it on your notebook, for example, as long as you have a GCC there for the compiling that is required.

Step 5: Connecting the Jumpers

In the assembly, we made it so the jumpers connect to the relay board to the GPIOs of the Raspberry.

Step 6: Socket or HTTP?

The type of programming we are talking about today is a type of communication we call a socket. But which is better? Socket or HTTP? There are some scenarios that are critical; some of these require speed, while others demand security. These scenarios often occur during more complicated projects. Usually these situations involve military applications or financial applications. In these cases, Socket is the preferred method. The same goes for robotics.

The Socket, for Americans reading this, is like a wire with plugs at both ends, which enables the connection between two devices, as well as the exchange of information between such components.

So imagine a situation: you have an executable program, and it has to go through several layers of the operating system until you get to the physical layer and go through it. It goes through the operating system again until it reaches the other executable program. This second executable program responds and proceeds with the same path. In this process, we have a smaller amount of layers, and we have binary data traffic, with everything quick and reliable.

In another situation, this time in HTTP, we have, for example, an application that is in PHP or Java Script, and it will have to go through the browser, through apache, through the Java script. Thus, it will be interpreted several times until it reaches the operational system. Only there are already the eight layers until you reach the physical layer of the network that will take you to the second application, where it goes through the layers of the operating system. Here, I have more layers and text-mode data.

What I mean is that the excess number of layers increases insecurity, as you consequently increase the number of entry points that allow your system to be invaded or monitored, among other situations. You can understand this better by watching the movie "Snowden".

However, I want to make it clear that I have no preference for Socket or HTTP, since each has its own application.

Some time ago, IBM did a study that compared HTTP communication to the binary TCP with Socket. The Socket result was at almost 100% when compared to HTTP. However, I've seen cases that the performance of the binary TCP is up to ten times greater.

Step 7: Status Diagram of the Client / Server Model

We are going to have two programs that we will compile and run on Raspberry Pi. I then assembled this diagram to make it easier to see.

Below is an example of a smartphone, a computer, an ESP8266, and an ESP32 as the client of Raspberry Pi, which can also serve as a Server, with the differentiation made through the program that is running within this device or devices.

Step 8: Código Servidor Main()

int main(int argc , char *argv[])
{  

   int socket_servidor;
   
   setaPinos(); //seta os pinos dos reles
   
   if(!preparaSocket(&socket_servidor)) //cria e prepara o socket
   return 0;
  
   iniciaServidor(socket_servidor); //inicia o servidor e executa um loop de recepção de mensagens
}

Still creating the Socket:

<p>bool preparaSocket(int *conexao)<br>{
  struct sockaddr_in servidor;
   
  //cria conexão socket
  *conexao = socket(AF_INET, SOCK_STREAM , 0); 
   
  if (*conexao == -1)
  {
    puts("Nao foi possivel criar o socket");
    return false;
  }
  puts("Socket criado");
   
  //Prepara a estrutura sockaddr_in
  servidor.sin_family = AF_INET;
  servidor.sin_addr.s_addr = INADDR_ANY;
  servidor.sin_port = htons(PORTA);</p>

When INADDR_ANY is specified in the connection call, the socket is bound to all local interfaces.

Still in the Server Code, we go to the Bind function:

//efetua ligação
  if(bind((*conexao),(struct sockaddr *)&(servidor) , sizeof(servidor)) < 0)
  {
    puts("Falha na ligacao");
    return false;
  }
  puts("Ligacao efetuada");
  return true;
}

Server_function (int socket_server), which involves Listen and Accept:

void iniciaServidor(int socket_servidor)
{
  int socket_cliente, *novo_socket; 
   
  //inicia a recepção de mensagens
  listen(socket_servidor , 3); 
   
  puts("Aguardando novas conexoes...");
   
  //loop infinito, para cada conexão criada uma thread que recebe as mensagens
  while((socket_cliente = accept(socket_servidor, (struct sockaddr *)0, (socklen_t*)0)))
  {
    puts("Conexao aceita");
     
    //cria thread
    pthread_t thread;
    novo_socket = malloc(sizeof(*novo_socket));
    *novo_socket = socket_cliente;
 
    //caso ocorra algum erro neste momento o loop é abortado
    if(pthread_create(&thread,NULL , comunicacaoSocket, (void*) novo_socket) < 0)
    {
      puts("Nao foi possivel criar a thread.");
      close(*novo_socket);
      free(novo_socket);
      return;
    }
    puts("Thread criada");
  }
void *comunicacaoSocket(void *conexao)
{
  puts("Thread de conexao iniciada");
  int tamanhoMensagemRecebida, i;
  char mensagem[TF_MENSAGEM];
  mensagem[0] = '\0';
   
  //enquanto não for recebida nenhuma mensagem, permanece em loop
  while(1) 
  {
   //recebe mensagem
   tamanhoMensagemRecebida = recv(*(int*)conexao ,&mensagem, sizeof(mensagem), MSG_PEEK);
    
   //define o final da mensagem (este comando impede que o 'lixo' de memória fique junto à  mensagem)
   mensagem[tamanhoMensagemRecebida] = '\0';
    
   //imprime mensagem recebida
   printf("\nRecebido: %s\n",mensagem);
    
   //deixa a mensagem em maiusculo, assim tanto faz "a1" ou "A1"
   for(i=0; i<tamanhoMensagemRecebida; i++)
  {
      mensagem[i] = toupper(mensagem[i]);
   }
//acende ou apaga reles de acordo com a mensagem recebida
   if(strcmp(mensagem,"A1")==0)
    ativaRele(PINORELE_1);
   else
   if(strcmp(mensagem,"D1")==0)
    desativaRele(PINORELE_1);
   else
   if(strcmp(mensagem,"A2")==0)
    ativaRele(PINORELE_2);  
   else
   if(strcmp(mensagem,"D2")==0)
    desativaRele(PINORELE_2);
   else
   if(strcmp(mensagem,"A3")==0)
    ativaRele(PINORELE_3);
   else
   if(strcmp(mensagem,"D3")==0)
    desativaRele(PINORELE_3);
// continua assim até o A16 e D16
puts("Thread finalizada e socket fechado.");
  //libera memória desta conexão
  free(conexao);
   
  return 0;
}
void ativaEmSequencia()
{
 int vet[16];
 vet[0] = PINORELE_1;
 vet[1] = PINORELE_2;
 vet[2] = PINORELE_3;
 vet[3] = PINORELE_4;
 vet[4] = PINORELE_5;
 vet[5] = PINORELE_6;
 vet[6] = PINORELE_7;
 vet[7] = PINORELE_8;
 vet[8] = PINORELE_9;
 vet[9] = PINORELE_10;
 vet[10] = PINORELE_11;
 vet[11] = PINORELE_12;
 vet[12] = PINORELE_13;
 vet[13] = PINORELE_14;
 vet[14] = PINORELE_15;
 vet[15] = PINORELE_16;
  
        for(int i=0; i<16; i++)
        {
        digitalWrite(vet[i],LOW);
        delay(200);
        digitalWrite(vet[i],HIGH);
        delay(200);
   }
}

Step 9: Client Code

connects () - Socket creation

//cria socket e conecta,
bool conecta(int *conexao) { struct sockaddr_in client; //cria conexão socket *conexao = socket(AF_INET , SOCK_STREAM , 0); //caso ocorra um erro na criação, aborta a conexão if (*conexao == -1) return false; //imprime mensagem puts("Socket criado");

Connect

//prepara estrutura do cliente para conexão
client.sin_addr.s_addr = inet_addr("127.0.0.1"); client.sin_family = AF_INET; client.sin_port = htons(PORTA); //conecta o cliente if (connect(*conexao , (struct sockaddr *)&client, sizeof(client)) < 0) return false; //imprime mensagem puts("Conectado\n"); return true; }

Send and Close Function

int main()
{ int conexao; char msg[TF_MENSAGEM]; //recebe mensagem puts("Digite a mensagem:\n[1] - Para sair\n"); fflush(stdin); gets(msg); //envia mensagens até que "1" seja recebido while(strcmp(msg,"1")!=0) { //conecta cliente if(!conecta(&conexao)) { puts("Erro de conexão"); break; } //envia mensagem if(send(conexao ,&msg, TF_MENSAGEM, 0) < 0) puts("Falha no envio\n"); else puts("Enviada!\n"); puts("Digite a mensagem:\n[1] - Para sair\n"); fflush(stdin); gets(msg); } //fecha o socket close(conexao); return 0; }

Step 10: Pin Treatment (wiringPi.h Library)

The sequence of the relays used in this example is from pin 8 to pin 10, respecting the following sequence:

#define PINORELE_1 8
#define PINORELE_2 9 #define PINORELE_3 7 #define PINORELE_4 15 #define PINORELE_5 16 #define PINORELE_6 0 #define PINORELE_7 1 #define PINORELE_8 2 #define PINORELE_9 3 #define PINORELE_10 4 #define PINORELE_11 5 #define PINORELE_12 12 #define PINORELE_13 13 #define PINORELE_14 6 #define PINORELE_15 14 #define PINORELE_16 10

For any questions concerning the outputs of Raspberry P, consult the table.

Step 11: Connecting the Jumpers

Here I'll show you the connection: the output of Raspberry 3 on relay 1, output 5 on relay 2, and so on, as shown in the table.

Step 12: Starting the Server

After compiling, you need to run the program with the "./server" command. This will start the server and all incoming messages will be reported to the console.

Step 13: Sending Commands by Putty

I always recommend that you have some program like this putty, which works as a client for SSH, Telnet, and Rlogin network protocols.

Type the IP of Raspberry Pi (to find out this IP, type 'ifconfig' in the console) and the same port used in the algorithm.