/post/ebikesensor

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:

I visualized the collected data on this map.

What you need

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:

Wlan Website

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:

Wlan Website

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

Code

Can be downloaded here. Only for personal use.

Tutorials that helped me