Arduino to Raspberry wireless communication – some improvements

In my previous post, I experimented with sending measurements values from an Arduino UNO to a Raspberry Pi via a 433 MHz radio protocol. After testing the setup for a few days, I decided to make some improvements:

  • Add an additional sensor for measuring outdoor temperatures. Now there will be four different sensor values transmitted from the Arduino to the Raspberry Pi.
  • Add the possibility to send float values for more precision and, for adopting to the Swedish climate, allow negative values.
  • On the receiver side (the Raspberry Pi), add storage of the values to a csv file so that the measurements can be visualized in graphs with Excel or a similar application.
  • Improve the noise tolerance.

Outdoor temperature sensor

To my experimental Arduino board, I added a waterproof version of the DS18B20 sensor. It can handle negative temperatures and has an accuracy of ± 0.5 degrees C.

Handling decimals and negative values

My previous wireless protocol only considered positive integers stored in two bytes. But now, for a measurement with decimal precision, I multiply the measurement with 100 and use the highest bit as a flag for negative/positive value. On the receiver side, when a measurement of this type arrives, the sign bit is handled and the value is divided with 100.

Noise tolerance

The 433 MHz band and the very inexpensive transmitter and receiver that I use are quite sensitive to noise. I had already implemented a simple check sum for detecting some errors, but after using the setup for a one day measurement cycle, I realized that this was not sufficient. Some noise would still pass through and mess up the received sensor values, so I decided on an additional error handling:

  • The transmitter sends each sensor value twice, with a two second delay in between
  • For accepting a measurement as valid, the receiver code requires that two identical values are received in sequence.

The wireless library that I use (RCSwitch) allows automatic transmits (and I use that feature), but it is not sufficient. I need a small delay between each transmit for getting reliable values for the receiver.

Extending the Arduino breadboard

The receiver circuit that is connected to the Raspberry Pi stays the same as in the previous post, but the Arduino UNO’s breadboard gets a new temperature sensor, DS18B20. It has 3 cables: red = 5V, black (ground) and yellow (digital signal output). 5V and ground goes to Arduino’s 5V & ground. The output signal goes to Arduino’s digital pin 3. There is also a 4,7 kOhm pull-up resistor between 5V and the signal.

DS18B20.jpg

Programming the Arduino

To get the DS18B20 sensor to work with the Arduino, there are two libraries that can be used:

This page https://arduino-info.wikispaces.com/Brick-Temperature-DS18B20, has a very good description of the wiring and some example code.

I have modified my previous sensor/transmitter code so that the additional DS18B20 sensor is handled. All transmitted messages are now sent twice with a delay in between so that the receiver has a better chance of filtering out noisy signals. The new sensor’s value is a float and it gets a special encoding before it is sent to RCSwitch.


// Depends on the RCSwitch library https://github.com/sui77/rc-switch
// and the DHT sensor-, OneWire- and DallasTemperature libraries
#include "RCSwitch.h"
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Unique ID:s (4 bits, 0-15) for each measurement type so that the receiver
// understands how to interpret the data on arrival
#define LDR_MEASUREMENT_ID 1
#define TEMP_MEASUREMENT_ID 2
#define HUMIDITY_MEASUREMENT_ID 3
#define OUTDOOR_MEASUREMENT 4
// Setup for light sensor
#define LIGHT_IN 0 // Light measurement goes to A0 pin
// Setup for DHT11
#define DHTPIN 2 // Signal in from DHT11 goes to digital pin 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// Setup for DS18B20
#define ONE_WIRE_BUS 3 // Uses digital pin 3
OneWire ourWire(ONE_WIRE_BUS);
DallasTemperature sensors(&ourWire);
// Setup for radio transmitter
#define TX_PIN 10 // PWM output pin to use for transmission
#define DELAY_BETWEEN_TRANSMITS 60000 // in milliseconds
RCSwitch transmitter = RCSwitch();
void setup()
{
// Serial.begin(9600);
pinMode(LIGHT_IN, INPUT);
transmitter.enableTransmit(TX_PIN);
transmitter.setRepeatTransmit(25);
}
unsigned long Code32BitsToSend(int measurementTypeID, unsigned long seq, unsigned long data)
{
unsigned long checkSum = measurementTypeID + seq + data;
unsigned long byte3 = ((0x0F & measurementTypeID) << 4) + (0x0F & seq);
unsigned long byte2_and_byte_1 = 0xFFFF & data;
unsigned long byte0 = 0xFF & checkSum;
unsigned long dataToSend = (byte3 << 24) + (byte2_and_byte_1 << 8) + byte0;
return dataToSend;
}
// Encode a float as two bytes by multiplying with 100
// and reserving the highest bit as a sign flag
// Values that can be encoded correctly are between -327,67 and +327,67
unsigned int EncodeFloatToTwoBytes(float floatValue)
{
bool sign = false;
if (floatValue < 0)
sign=true;
int integer = (100*fabs(floatValue));
unsigned int word = integer & 0XFFFF;
if (sign)
word |= 1 << 15;
return word;
}
void TransmitWithRepeat(unsigned long dataToSend)
{
transmitter.send(dataToSend, 32);
delay(2000);
transmitter.send(dataToSend, 32);
delay(2000);
}
// A rolling sequence number for each measurement
// Restarts at 0 after seqNum=15 has been used
unsigned long seqNum=0;
unsigned long previousTime = 0;
void loop()
{
unsigned long currentTime = millis();
if (currentTime – previousTime <= DELAY_BETWEEN_TRANSMITS)
{
return;
}
previousTime = currentTime;
//
// DS18B20 sensor, outdoor temperature
//
sensors.requestTemperatures();
float outdoorTemp = sensors.getTempCByIndex(0);
unsigned int encodedFloat = EncodeFloatToTwoBytes(outdoorTemp);
unsigned long dataToSend = Code32BitsToSend(OUTDOOR_MEASUREMENT,seqNum,encodedFloat);
TransmitWithRepeat(dataToSend);
//
// Get the light measurement (0-1023)
//
unsigned int data = analogRead(LIGHT_IN);
dataToSend = Code32BitsToSend(LDR_MEASUREMENT_ID,seqNum,data);
TransmitWithRepeat(dataToSend);
//
// DHT11, get temperature
//
unsigned long t = (unsigned long)dht.readTemperature();
if (!isnan(t))
{
dataToSend = Code32BitsToSend(TEMP_MEASUREMENT_ID,seqNum,t);
TransmitWithRepeat(dataToSend);
}
//
// DHT11, get humidity
//
unsigned long h = (unsigned long)dht.readHumidity();
if (!isnan(h))
{
dataToSend = Code32BitsToSend(HUMIDITY_MEASUREMENT_ID,seqNum,h);
TransmitWithRepeat(dataToSend);
}
seqNum++;
if (seqNum > 15)
{
seqNum = 0;
}
}

Programming the Raspberry Pi

The receiver code from the previous post now handles yet another measurement type, “Outdoor Temp”. To decode a float value from the two bytes, the highest bit is read to check if it is a negative value and then the value (with the highest bit cleared) is divided by 100.

The receiver code has also been extended with storage of the data to a comma-separated file. This file can be used in Excel or similar for viewing the data in graphs.


# Python 2.7.9
# Uses pi_switch from https://github.com/lexruee/pi-switch-python
# See pi_switch readme for details on setup
from pi_switch import RCSwitchReceiver
import time
import csv
receiver = RCSwitchReceiver()
receiver.enableReceive(2)
acceptedTypes = { 1 : "Indoor Light", 2 : "Indoor Temp", 3: "Indoor Humidity",4:"Outdoor Temp" }
def recordIncomingMeasurements(writer):
prev_value = 0L
numIdenticalInARow=1
while True:
if receiver.available():
value = receiver.getReceivedValue()
# print(value)
if value == prev_value:
numIdenticalInARow += 1
else:
numIdenticalInARow = 1
# decode byte3
byte3 = (0xFF000000 & value) >> 24
typeID = int((0xF0 & byte3) >> 4)
seqNum = int((0x0F & byte3))
# decode byte2 and byte1
data = int((0x00FFFF00 & value) >> 8)
# decode byte0
checkSum = int((0x000000FF & value))
calculatedCheckSum = 0xFF & (typeID + seqNum + data)
if typeID == 4:
# Handle float values that can be negative from -327.67 to +327.67
# Bit 15 contains the sign flag,
# the rest of the word (max 0x7FFF) contains the float value * 100
floatResult = 0.0
if (data & 0x8000 > 0):
# this should be a negative value
data &=~(1 << 15)
floatResult = data
else:
data &=~(1 << 15)
floatResult = data
data = floatResult/100.0
# Sanity checks on received data
correctData = True
if calculatedCheckSum != checkSum:
# print("Incorrect checksum!")
correctData = False
elif typeID not in acceptedTypes:
# print("Incorrect ID!")
correctData = False
elif seqNum > 15:
# print("Incorrect SeqNum")
correctData = False
if correctData:
timeValue = time.ctime()
if numIdenticalInARow == 2:
# only store values if message was registred twice
# if registred more than two times, ignore the value
print(str.format("{0}: {1}={2} SeqNum={3}",timeValue,acceptedTypes[typeID],data,seqNum))
writer.writerow({'Time':timeValue,acceptedTypes[typeID]:data})
csvfile.flush()
prev_value = value
receiver.resetAvailable()
with open('results.csv', 'w') as csvfile:
fieldNames = ['Time',acceptedTypes[1],acceptedTypes[2],acceptedTypes[3],acceptedTypes[4]]
writer = csv.DictWriter(csvfile,fieldnames=fieldNames)
writer.writeheader()
recordIncomingMeasurements(writer)

All code from this post can be downloaded from https://github.com/LarsBergqvist/ArduinoToRaspberry_via_433Mhz_RF.

 

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s