Mostly academic, but still useful and interesting I want to track humidity and temperature data in my apartment. I’m interested to see if there’s a correlation between good quality sleep, good allergy days, and a specific temperature/humidity.

This project covers a few new things for me, including:

  • Using a Raspberry Pi
  • Electronics
  • Bluetooth LE

For that reason, I wanted to document here how I worked through the project, mostly for future Andy.

Note: This article is a WIP.

Setup the Circuit

I connected my DHT22, an RGB LED (with a resistor) and a reset wire to the Pi. You can see from picture how this looks exactly.

Setup the Pi

Firstly I setup a Raspberry Pi Zero w. There’s a handy tool called PiBakery that lets you configure the installation up-front (e.g. logging into WiFi and setting the hostname) which means as soon as the SD card has been prepped from your laptop, you can just SSH into the Pi using the hostname and work with it without needing any external monitor, keyboard or mouse. I set my Pi’s hostname to “storm” and we’re ready to go.

PiBakery

With the first boot complete, I’m not ready to start thinking about the software I need on the Pi to support the humidity sensor. I’m going to use Python3 for this, so I’ll need:

  • python3-pip (a package manager for python 3)
  • python3-gpiozero (for working with the GPIO pins)
  • Adafruit_DHT (for working with the DHT22 hardware sensor)
  • pybluez (for working with bluetooth in Python)

Firstly, it’s important to update the OS package manager to make sure we’ve got the latest references:

1
sudo apt update

Next lets install the python package manager and update it:

1
2
sudo apt install python3-pip
sudo python3 -m pip install --upgrade pip setuptools wheel

And finally let’s install the Python packages we want:

1
2
sudo pip3 install Adafruit_DHT
sudo pip3 install pybluez

To interact with Bluetooth, we use Bluez, some of which already ships as part of the kernel on the Pi0w. However, to get access the latest Bluez dev tools, visit the bluez website and install the source, like below. Note that it’s important to install the latest rather than using what ships with the Pi0w as it’s a very active development community, so it gets outdated faster than you might expect.

1
2
3
4
5
6
7
8
cd ~
wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.37.tar.xz
tar xvf bluez-5.37.tar.xz
cd bluez-5.37
sudo apt-get install -y libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev
./configure
make
sudo make install

Setup the software

In order to activate the BLE hardware, you first have to change the service that is launched on your Pi0w.

Firstly, let’s look at where your bluetooth service is defined by running

1
systemctl status bluetooth

You’ll see something like

1
2
3
4
  bluetooth.service - Bluetooth service
   Loaded: loaded (/lib/systemd/system/bluetooth.service; disabled)
  Active: inactive (dead)
    Docs: man:bluetoothd(8)

We have to sudo vim /lib/systemd/system/bluetooth.service and change the ExecStart path to include --experimental at the end.

Now we just need to reload it like this:

1
sudo systemctl daemon-reload

and then start the bluetooth:

1
sudo systemctl restart bluetooth

If you now type bluetoothctl and then power on your BLE device will be active.

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python3

import Adafruit_DHT
from gpiozero import LED, Button
from time import sleep
from pybleno import *
import sys

class HumidityCharacteristic(Characteristic):
	def __init__(self):
		Characteristic.__init__(self, {
			'uuid': 'DD407B430FDC4C4D94D5B4C0E950DE16',
			'properties': ['read'],
			'value': None,
			'descriptors': [
				Descriptor(
					uuid = '2901',
					value = 'Humidity (percentage)'
				),
				Descriptor(
					uuid = '2904',
					value = array.array('B', [0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 ]) # maybe 12 0xC unsigned 8 bit
				)
			]
		})

		self._value = array.array('B', [0] * 0)
		self._updateValueCallback = None

	def onReadRequest(self, offset, callback):
		humidity, temperature = Adafruit_DHT.read_retry(sensor, dht22_pin)
		print('Humidity={0:0.1f}%'.format(humidity))
		humidityBytes = bytearray(struct.pack("f", humidity))  
		callback(Characteristic.RESULT_SUCCESS, humidityBytes)

class TemperatureCharacteristic(Characteristic):
	def __init__(self):
		Characteristic.__init__(self, {
			'uuid': 'B6AC8909198D4621B6DB1D8602A2B2CF',
			'properties': ['read'],
			'value': None,
			'descriptors': [
				Descriptor(
					uuid = '2901',
					value = 'Temperature (Celsius)'
				),
				Descriptor(
					uuid = '2904',
					value = array.array('B', [0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]) # maybe 12 0xC unsigned 8 bit
				)
			]
		})

		self._value = array.array('B', [0] * 0)
		self._updateValueCallback = None

	def onReadRequest(self, offset, callback):
		humidity, temperature = Adafruit_DHT.read_retry(sensor, dht22_pin)
		print('Temp={0:0.1f}*C'.format(temperature))
		temperatureBytes = bytearray(struct.pack("f", temperature))  
		callback(Characteristic.RESULT_SUCCESS, temperatureBytes)


class SensorService(BlenoPrimaryService):
	def __init__(self):
		BlenoPrimaryService.__init__(self, {
			'uuid': 'B6002C0B69884E56A1DF9BA303C49FD3',
			'characteristics': [
				HumidityCharacteristic(),
				TemperatureCharacteristic()
			]
		})

def onStateChange(state):
	print('on -> stateChange: ' + state);

	if (state == 'poweredOn'):
		bleno.startAdvertising('Storm', [primaryService.uuid]);
	else:
		bleno.stopAdvertising();

def onAdvertisingStart(error):
	print('on -> advertisingStart: ' + ('error ' + error if error else 'success'));

	if error:
		print(error)
	else:
		def on_setServiceError(error):
			print('setServices: %s'  % ('error ' + error if error else 'success'))

		bleno.setServices([
			primaryService
		], on_setServiceError)
		
bleno = Bleno()
primaryService = SensorService();

# Define Pins
red_led = LED(17)
blue_led = LED(27)
green_led = LED(22)

reset_button = Button(24)
dht22_pin = 23

# Define sensor
sensor = Adafruit_DHT.DHT22

bleno.on('stateChange', onStateChange)
bleno.on('advertisingStart', onAdvertisingStart)
bleno.startAdvertising('Sensor', [primaryService.uuid]);

print ('Hit <ENTER> to disconnect')

if (sys.version_info > (3, 0)):
	input()
else:
	raw_input()

bleno.stopAdvertising()
bleno.disconnect()

print ('terminated.')
sys.exit(1)

# Helper function to change the colour of the LED
def led_rbg(redOn, greenOn, blueOn):
	if redOn:
		red_led.on()
	else:
		red_led.off()
		
	if greenOn:
		green_led.on()
	else:
		green_led.off()
		
	if blueOn:
		blue_led.on()
	else:
		blue_led.off()

iOS App

// TODO