As I often drive to work using an electric bike, I decided to mount some portable sensors (light, particular matter, etc.) on it. Possible use cases are:
- To find the route with the least amount of light in order to avoid getting a tan.
- To estimate position without GPS but sensor data instead.
- To find the route with the best air quality.
- To estimate positions without GPS (more energy efficient)
I visualized the collected data on this map.
What you need
- Raspberry Pi 3 with a SD card
- Enviro+ Sensor
- USB Cable
- Ply Wood and Plastic glass
- Particular Matter Sensor
- USB GPS. I used a VK-162 USB, which outputs NMEA compatible format. I recommend using a USB GPS device, as the serial ports are blocked by the Enviro sensor.
- Battery Pack with USB Output to power the Raspberry Pi.
Please note the following tutorial does not make attemps to secure the webserver running on the Pi and check data integrity etc.. Hence, do not use your Raspberry Pi for anything else but this project. Also the sensors are not calibrated yet. Also note the service script currently stops all python3 processes.
Building the case
Buy some plywood and build a case. I used a 2 layer design and attached a battery pack and the particular matter sensor at the lower layer and the Pi on the upper layer. To make the Enviro display visible and get a better GPS signal, I used acrylic glass on the top instead of wood. The final case looks like this:

Set up a headless Raspberry Pi
First, download Raspbian from their website.
and enable ssh in sudo raspi-config under Interfacing options.
I connected the Pi to a router using cable, as we will later use it's WLAN module to serve a website.
On the Pi, you can get the current IP via ifconfig terminal command and login via ssh afterwards to develop from your Desktop PC.
Install the Enviro+ Library
Follow the instructions from here in order to install the python library from here. (Make sure to use the Enviroplus and not Enviro Github Page!)
Next, enter raspi-config enable I2C in the "Interfaces" section.
To experiment a bit, I recommend to install something like Ipython via sudo apt-get install ipython ipython3.
Now reboot. Open ipython3 in the console and test the light sensor
try:
from ltr559 import LTR559
ltr559 = LTR559()
except ImportError:
import ltr559
print(ltr559.get_lux())
to see if the API is working.
Build a webserver on the Pi
Next, we will serve a webpage via WLAN. We will use this in order to view some of the sensor data from our smartphone. You can also use this to stream velocity and gps information to your smartphone.
First, we install the Flask server package:
sudo apt-get install python3-flask
Next, we navigate to the home directory (cd ~) generate a directory "templates" with a file index.html and generate a additional server.py file:
import flask
APP = flask.Flask(__name__)
@APP.route('/')
def index():
return flask.render_template('index.html')
if __name__ == '__main__':
APP.debug=True
APP.run(debug=True, port=80, host='0.0.0.0')
You can start it by typing sudo python3 server.py into the terminal (Sudo is needed as port 0 to 1023 can only be assigned by sudo). Now we can extend the server.py file to our needs. I will not print the complete code in this tutorial as you can download the completed files at the bottom of this page.The server.py file available for download, uses a second thread main_write_function to query the different sensors and stores its data in the global last_measurement variable. If a users opens the hotspot page `192.16.42.1 it looks like this:

GPS Interfacing
As on cloudy days the longitude and latitude lines are often not visible in the sky, we need to use a sensor to get the longitude and latitude information here.
Make sure cgps is disabled: sudo systemctl disable gpsd. I did not manage to get CGPS working with my GPS module. In case it works with your gps module, you can also use cgps (which is a better approach).
First, let's check if the USB GPS module is available via lsusb. Find the name of the serial port using dmesg | grep tty. In my case it is called ttyACM0.
We can view the raw output via cat /dev/ttyACM0.
You should see something like
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,2,1,05,01,,,22,02,,,22,03,,,23,04,,,22*7A
$GPGSV,2,2,05,05,,,23*78
This is how the GPS NMEA porotocol meassages look like. Next, remove the tty simlink:
sudo systemctl stop serial-getty@ttyACM0.service
sudo systemctl disable serial-getty@ttyACM0.service
You can test the data now using GPSMon
gpsmon /dev/ttyACM0
It may take around 10 to 15 minutes to get a GPS fix (which is necessary to retrieve longitude and latitude information).
To read nmea meassages in python, we install the pynmea2 package using pip: pip3 install pynmea2.
The readout basically works like in the following script, which should output latitude and longitude information after some time:
import pynmea2
import serial
def readline():
while 1:
try:
while ser.read().decode("utf-8") != '$':
pass
line = ser.readline().decode("utf-8")
if not line.startswith("$"):
line = "$"+line
return line
except:
return ""
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1) # Open Serial port
try:
while True:
lineraw = readline()
if lineraw != "":
#print("rawliune is", lineraw)
try:
line = pynmea2.parse(lineraw)
if hasattr(line, 'lat'):
print("lat", line.lat, "lon", line.lon)
except:
print("could not parse", lineraw)
except KeyboardInterrupt:
pass # Exit
This is basically how GPS data is retrieved in our final server.py file later on.
Setup Access Point
We convert the Raspberry Pi to a WLAN access point,
which is accessible by your smartphone. We need to install the packages hostapd and dnsmasq first:
sudo apt-get install hostapd isc-dhcp-server dnsmasq
Now the dhcpcd config file /etc/dhcp/dhcpd.conf. Here
comment the following (e.g. adding a # in front of the line)
option domain-name "example.org";
option domain-name-servers ns1.example.org, ns2.example.org;
and uncomment (remove the #)
#authoritative;
Also add the following in /etc/dhcpcd.conf, which will define the IP, the control website is available on the Pi later on:
interface wlan0
static ip_address=192.168.42.1/24
Next edit /etc/default/isc-dhcp-server
and set interfaces to wlan0:
INTERFACESv4="wlan0"
INTERFACESv6="wlan0"
Restart dhcpcd using sudo systemctl restart dhcpcd and check if both adapters (Ethernet and WLAN) are available via ip l:
Next, create a file /etc/dnsmasq.conf, content:
interface=wlan0
no-dhcp-interface=eth0
dhcp-range=192.168.42.100,192.168.42.200,255.255.255.0,24h
dhcp-option=option:dns-server,192.168.42.1
We need to disable systemd resolved, as it conflicts with dnsmasq.
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
Now we can test and enable dnsmasq
dnsmasq --test -C /etc/dnsmasq.conf
sudo systemctl restart dnsmasq
sudo systemctl status dnsmasq
sudo systemctl enable dnsmasq
Next, we setup the hotspot. Create a etc/hostapd/hostapd.conf file
with this content:
interface=wlan0
ssid=Manuels Ebike
channel=1
hw_mode=g
ieee80211n=1
ieee80211d=1
country_code=DE
wmm_enabled=1
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=raspberry
We need to reference the configuration for the daemon. Hence, edit the file /etc/default/hostapd
and add:
DAEMON_CONF="/etc/hostapd/hostapd.conf"
Run hostapd via sudo /usr/sbin/hostapd /etc/hostapd/hostapd.conf to test. Ensure the wlan0 interface is up (sudo ip link set wlan0 up).
Login to the hotspot and then start the service
sudo service hostapd start
Also edit /etc/sysctl.conf and enable IP forwarding by uncommenting
#net.ipv4.ip_forward=1
to
net.ipv4.ip_forward=1
Activate it with
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT
sudo sh -c "iptables-save > /etc/iptables/rules.v4"
sudo sh -c "iptables-save > /etc/iptables/rules.v4"
### Add everything to autostart
Add a file /etc/init.d/ebike.sh
and add the following content. Make sure `#! /bin/sh` is the first line!
```#! /bin/sh
### BEGIN INIT INFO
# Provides: E-Bike WLAN Access Point
# Required-Start: $syslog
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: bike wlan access point
# Description:
### END INIT INFO
case "$1" in
start)
echo "start ebike service"
sudo python3 /home/pi/server.py
;;
stop)
echo "exit ebike service"
killall python3
;;
*)
echo "Usage: /etc/init.d/ebike.sh {start|stop}"
exit 1
;;
esac
exit 0
make the script executable and autostart:
sudo chmod 755 /etc/init.d/ebike
sudo update-rc.d ebike defaults
You can check the stattus with service ebike status
You can not start (and stop) the server and access point via
sudo /etc/init.d/ebike.sh start
Enable hostapd at startup via sudo update-rc.d hostapd defaults
FAQ
- Hostapd problems - Check syslog via
cat /var/log/syslog | grep hostapd. Also ensure theresolvedservice is not running. - Can not access WLAN website - Disable mobile internet on the smartphone.
Code
Can be downloaded here. Only for personal use.