This post will explain how to get the Raspberry Pi to read and interpret RF signal-codes to be used for logic.
Table of contents
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.
- Get the Raspberry Pi 4B 4GB Starter Kit from Amazon.com
- Get the Raspberry Pi 4B 8GB Starter Kit from Amazon.com
- Get the 433Mhz RF Transmitter Receiver Module Kit from Amazon.com or BangGood
- Get the Compact USB Powered Mini Stereo Sound Bar with Audion Jack from Amazon.com
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!