Making the Raspberry Pi use RF signals for logic

Making the Raspberry Pi use RF signals for logic

This post will explain how to get the Raspberry Pi to read and interpret RF signal-codes to be used for logic.

Introduction

After radion frequency (RF) signals are detected on a Raspberry Pi using a 433MHz RF Receiver module, the signal can be used for logic. Here we show how to get the Raspberry Pi to read and interpret RF signal codes to be used programmatically. RF signals will be “captured” as string values which can be compared and assigned to IF or ELSE statements in Bash or Python scripts.

Requirements and assumptions

A fully functional Raspberry Pi with the Raspberry Pi OS (formerly known as Raspbian) installed was used.

The terminal and command lines were used to do testing and write Python and Bash coding to receive signals and apply logic to them.

Input to the Raspberry Pi will be required for installation, setup, coding, and testing purposes. PuTTY and/or WinSCP can be used to connect to the Raspberry Pi from a remote PC in the case where a screen, keyboard, and mouse are not connected to the Raspberry Pi.

For testing purposes, it is recommended to have an additional set of working speakers/earphones connected to the audio jack of the Raspberry Pi.

eSpeak needs to be installed & audible.

Getting started

In order to get the Raspberry Pi to use RF signals for logic, we must first get it up and running with receiving RF signals. After following my 433MHz RF Communication to a Raspberry Pi post, you should have an Arduino connected, up and running with a transmitter and a Raspberry Pi to a receiver of a 433MHz RF Transmitter Receiver modules pair. This was the code snippet we used then to send signal-codes to the Raspberry Pi:

#include 

RCSwitch mySwitch = RCSwitch();

void setup() {
  Serial.begin(9600);
  // Transmitter is connected to Arduino Pin #10  
  mySwitch.enableTransmit(10);

  // Optional set pulse length.
  // mySwitch.setPulseLength(320);

  // Optional set protocol (default is 1, will work for most outlets)
  // mySwitch.setProtocol(2);

  // Optional set number of transmission repetitions.
  // mySwitch.setRepeatTransmit(15);

}

void loop() {
  /* Using decimal code */
  mySwitch.send(1234, 24);
  Serial.print("Attempting to send 1234");
  delay(4000);
  mySwitch.send(4321, 24);
  Serial.print("Attempting to send 4321");
  delay(4000);

  /* Using binary code */
  //mySwitch.send("000000000001010100010001");
  //delay(1000);  
  //mySwitch.send("000000000001010100010100");
  //delay(1000);*/
}

With this code, the Arduino will send the decimal code ‘1234’, wait for four seconds and then send ‘4321’. It will run over and over until the Arduino’s power is disconnected. There are many ways to incorporate microcontrollers with code and other modules to give specific signal codes, but for now, only these two signals will be used.

Make sure the Raspberry Pi receives them correctly.

It was mentioned that after installing wiringPi and 433Utils we will be using 433Utils’ RFSniffer.cpp to receive signals.

RFSniffer.cpp only reads and displays the signals it received on the terminal. To continue we will need to modify this file and bring in a Python script file to do the logic part where the modified RFSniffer.cpp will trigger the Python file.

The initial Python script

With this file, we will simply make the correct signal-code received, trigger the Python file to log the signal-code received.

Create and/or go to your working directory and create the Python file. I will be using /home/pi/bin as my working directory and sniffer.py as my initial Python file,

sudo nano /home/pi/bin/sniffer.py

copy the following script code to it:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, getopt
import uuid

log_file_directory = "/home/pi/logs" # don't use a '/' at the end!
log_file_name = "sniffed.txt"

def main(argv):
  opts, args = getopt.getopt(argv,"s:")

  for opt, arg in opts:
    if opt == '-s': #meaning there's valid args/codes in it
      full_log_file_name = log_file_directory + "/" + log_file_name
        with open(full_log_file_name, 'a') as outfile:
          sniffed_code = arg + "\n"
          outfile.write(sniffed_code)

if __name__ == "__main__":
  main(sys.argv[1:])

and make it fully accessible for all:

sudo chmod 777 /home/pi/bin/sniffer.py

When triggered, the Python script will log the signal to /home/pi/logs/sniffed.txt. You can change these destinations if you like. It will sequentially log them one by one in the same file.

Create & compile a new RFSniffer.cpp file

To edit and recompile RFSniffer.cpp, you need to be in the 433Utils/RPi_utils directory:

cd 433Utils/RPi_utils

First, create a backup of your original file,

sudo cp RFSniffer.cpp RFSniffer_original.cpp

then open and edit the file,

sudo nano RFSniffer.cpp

and remove all the old code and copy and paste the following code to it:

/*
  RFSniffer

  Usage: ./RFSniffer []
  [] = optional

  Hacked from http://code.google.com/p/rc-switch/
  by @justy to provide a handy RF code sniffer

  Adjusted from http://stackoverflow.com/questions/26948861/raspberry-rfsniffer-read-output-with-python
  by Renier Delport to provide Python integration

*/

#include "RCSwitch.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <sstream>
#include <unistd.h>

RCSwitch mySwitch;

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

  // This pin is not the first pin on the RPi GPIO header!
  // Consult https://projects.drogon.net/raspberry-pi/wiringpi/pins/
  // for more information.
  int PIN = 2;

  if(wiringPiSetup() == -1) {
    printf("wiringPiSetup failed, exiting...");
    return 0;
  }

  int pulseLength = 0;
  if (argv[1] != NULL) pulseLength = atoi(argv[1]);

  mySwitch = RCSwitch();
  if (pulseLength != 0) mySwitch.setPulseLength(pulseLength);
  mySwitch.enableReceive(PIN);  // Receiver on interrupt 0 => that is pin GPIO pin #2 (pin 13)

  while(1) {
    int count = 0;
    std::string signals;
    signals = "";

    while (count < 10000) {
      if (mySwitch.available()) {
        int value = mySwitch.getReceivedValue();

        if (value == 0) {
          printf("Unknown encoding\n");
        }
        else {
          std::string signal;
          std::stringstream signalStream;
          signalStream << value; signal = signalStream.str();
          if (signals.length() > 0) {
            signals.append(",");
          }
          else {
            // first receive, so reset timeframe here
            count = 0;
          }
          signals.append(signal);
          printf("Received %s\n", signal.c_str() );
        }
        mySwitch.resetAvailable();
      }
      usleep(50);
      count += 1;
    }
    if (signals.length() > 0) {
      std::string command;
      command.append("python /home/pi/bin/sniffer.py -s ");
      command.append(signals);
      printf("Call %s\n", command.c_str());
      system(command.c_str());
    }
  }
  exit(0);
}

This code snippet will chain together signal-codes received in a short frame of time as they are received. These signal-codes are passed in as comma-separated-values to your python script (argument -s code1,code2,code3).

Remember to save your changes (Ctrl + X then Y).

To recompile the changed code, while still in the 433Utils/RPi_utils directory use:

make RFSniffer.cpp

or:

make all

After successfully compiling your file, go back to the /home/pi directory (cd). To start RFSniffer you can use the following command:

sudo /home/pi/433Utils/RPi_utils/RFSniffer

The terminal will now be waiting for RF signals and if it receives them, will display it on the terminal screen and also send them intermittently to the Python sniffer script. You might be seeing other code in between there as well, but it should mainly be ‘1234’ followed by ‘4321’. If you received these code signals on your terminal, also check your /home/pi/logs/sniffed.txt.

Take a breather

That’s the basis of sending and receiving RF signal codes on a Raspberry Pi. When RFSniffer is running, you will now be able to use the signals in the sniffer.py file for further logic. You can keep your Arduino running, but exit out of RFSniffer by pressing Ctrl + Z.

The adapted Python script

A part of the adopted Python file is an additional Python file that will store our recognised codes and command lines. In the same directory as your initial sniffer.py (/home/pi/bin) create an additional file called sniffer_commands.py:

sudo nano /home/pi/bin/sniffer_commands.py

Copy the following script code to it,

#!/usr/bin/python
# -*- coding: utf-8 -*-

code_commands = {
"1234": "sudo /home/pi/bin/test1.sh",
"4321": "sudo /home/pi/bin/test2.sh",
"911": "path to Bash or Python script here, etc.",
"102": "path to last Bash or Python script here" # note that last line does not end with a comma ","
}

exit and save (Ctrl + X then Y) and make it fully accessible for all:

sudo chmod 777 /home/pi/bin/sniffer_commands.py

This is the file you will edit in the future to update your signal codes and paths. You can add as many signal codes with their respective paths as you like as long as you keep the layout the same. You can do so then by using:

sudo nano /home/pi/bin/sniffer_commands.py

Now we’re going to adapt the initial Python file, /home/pi/bin/sniffer.py to issue these commands once your specified code signal has been received.

To open and edit your /home/pi/bin/sniffer.py file again use:

sudo nano /home/pi/bin/sniffer.py

Remove all the previous code and paste the following updated script code to it:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from sniffer_commands import code_commands # imports a path of a Bash or Python script to execute when a specific code is received //
import sys, getopt
import uuid
import os

log_file_directory = "/home/pi/logs" # don't use a '/' at the end!
log_file_name = "sniffed.txt"

def main(argv):
  opts, args = getopt.getopt(argv,"s:")
  for opt, arg in opts:
    if opt == '-s': #meaning there's valid args/codes in it
      full_log_file_name = log_file_directory + "/" + log_file_name
        with open(full_log_file_name, 'a') as outfile:
          sniffed_code = arg + "\n"
          outfile.write(sniffed_code)
        code_list = arg.split(",")
        cleaned_code_list = list((set(code_list))) #removing duplicates
        for sniffed_code in cleaned_code_list:
          #single_sniffed_code = sniffed_code + "\n"
          #with open(full_log_file_name, 'a') as outfile:
          #  outfile.write(single_sniffed_code)
          for compare_sniffed_code, elem in code_commands.items():
            if compare_sniffed_code == sniffed_code :
              code_command = code_commands[compare_sniffed_code]
              os.system(code_command)

if __name__ == "__main__":
    main(sys.argv[1:])

The additional code will take the chained signal codes and break them into single signal codes. It will then compare them one by one with the contents of the /sniffer_commands.py file to see if there is a recognised signal code linked to a path. If the signal code is recognised, it will trigger that file.

Testing

To test whether a command line is triggered, we’ll write two little Bash scripts each containing a specific eSpeak command to test.

Now let’s create two tester Bash scripts named test1.sh and test2.sh in the same directory as specified in the sniffer_commands.py file:

sudo nano /home/pi/bin/test1.sh

Paste the following code to it,

#!/bin/bash

# This is a simple tester script to see if it is triggered.
# When triggered, you should hear "The testx.sh script file has been triggered."
# Make sure to test it separately first!

full_bash_name=`basename "$0"`

espeak "The $full_bash_name script file has been triggered."

exit and save (Ctrl + X then Y).

Make a copy,

sudo cp /home/pi/bin/test1.sh /home/pi/bin/test2.sh

and make both files accessible for all:

sudo chmod 777 /home/pi/bin/test1.sh
sudo chmod 777 /home/pi/bin/test2.sh

Fire up RFSniffer again and let it happen:

sudo /home/pi/433Utils/RPi_utils/RFSniffer

You should now hear eSpeak’s robot voice every time a Bash script is triggered!

Leave a Reply

Your email address will not be published. Required fields are marked *