# Make a smart mailbox with IoT :mailbox:
> Name: Anton Fridlund
> Student credendtials: af222xh
> Estimated time: 8h
[ToC]
## Overview
:::info
This tutorial will cover how to connect a reed switch and an ultrasonic sensor to your LoPy4, send sensor data via Wi-Fi to [Pybytes](https://pybytes.pycom.io/) and visualize the data using [Datacake](https://datacake.co/). As well as walking you through the installation of all the necessary tools needed for this project.
:::
## Objective
The reason I decided to build a smart mailbox is because we frequently receive newspaper subscriptions, postcards, packages and other important mail and therefore check the mailbox daily. A lot of the time another family member has already emptied the mailbox or there were no mail to begin with.
Having a smart mailbox would minimize the struggle of daily checking the mailbox without knowing if we actually received any mail or not. It would also allow us to check if the mailbox is getting full which could be useful for when going on vacation.
The data generated by the device could be used to send notifications when we receive mail, keep statistics for how often we receive mail, notify us when the mailbox is getting full and much more.
The insight that the end product will provide is basically *when* to empty the mailbox.
## Material
:::warning
The device in this project is meant to be powered via USB since our mailbox is mounted to the facade of the house. You might experience issues with the ultrasonic sensor if on battery.
:::
| :gear: Part | :memo: Description | :shopping_trolley: Shop | :moneybag: Price |
| -------- | -------- | -------- | -------- |
| `1x` KY-021 <br/> Reed switch | Electrical magnetic field switch. <br/>Used to sense when the mailbox is opened. | [Electrokit](https://https://www.electrokit.com/produkt/magnetkontaktmodul-mini/) | 25 SEK |
| `1x` HC-SR04 <br/> Ultrasonic sensor | Used to measure the depth of the mailbox. | [Electrokit](https://www.electrokit.com/produkt/avstandsmatare-ultraljud-hc-sr04-2-400cm/) | 59 SEK |
| `1x` 30-pack of <br/> Jumper cables | Used to connect sensors. 13 cables needed. | [Electrokit](https://www.electrokit.com/produkt/labbsladdar-100mm-hane-hane-30-pack/) | 33 SEK |
| `1x` LoPy4 | Main development board. | [Pycom](https://pycom.io/product/lopy4/) | €38.45 <br/> (~390 SEK) |
| `1x` Expansion <br/> Board 3.0 | Provides easy battery, pin and USB access. | [Pycom](https://pycom.io/product/expansion-board-3-0/) | €17.60 <br/> (~180 SEK) |
| `1x` USB cable<br/> A to micro B M/M | Used to connect sensors. 13 cables needed. | [Electrokit](https://www.electrokit.com/produkt/usb-kabel-a-hane-micro-b-5p-hane-1-8m/) | 39 SEK |
| `1x` Breadboard | Makes it easy to connect components. | [Electrokit](https://www.electrokit.com/produkt/kopplingsdack-400-anslutningar/) | 59 SEK |
| Total | | | ~785 SEK |
If you don't already have a magnet at home you will have to buy that as well.
You might want three male/female jumper cables in order to connect the reed switch without the breadboard.
## Computer setup
The operating system used in this project is Windows 10.
* First things first, LoPy4 runs Python so we need to install the latest version of Python using the official webpage [here](https://www.python.org/downloads/).
* Install [node.js](https://nodejs.org/en/)!
* To easily write, edit and upload Python projects to LoPy4 we need to install the [Atom IDE](https://atom.io/) and add the [Pymakr](https://atom.io/packages/pymakr) package to Atom. If you encounter any issues when installing the Pymakr package you might need to install [vs-build-tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) first.
* Now for the last setup step we will add the device to Pybytes and update the firmware with the help of the firmware updater tool in order to easily connect with Pybytes in the future.
Tutorial for [getting started](https://docs.pycom.io/gettingstarted/), [adding the device to Pybytes as well as updating the firmware](https://docs.pycom.io/pybytes/gettingstarted)!
That's all for the initial setup. Later on I will show how to configure Pybytes and Datacake.
## Putting everything together

As you can see in this setup we make use of a breadboard to easier connect the components but you are free to connect the components in any way you like. The color coding of the wires in this image goes as such: Red=5V, Black=Ground, Blue=In signal, Green=Out signal.
No resistors are needed, but in case you are trying to run it on batteries you might want to have a look at [this link](https://core-electronics.com.au/tutorials/hc-sr04-ultrasonic-sensor-with-pycom-tutorial.html). It's a tutorial for the ultrasonic sensor that briefly covers the battery issue.
## Platform
The platform used in this project is Pybytes and Datacake.
Pybytes in this case is mainly used because it's very easy to get connected and getting started sending data to their platform. They also offer an option to forward the data we send to other platforms such as Datacake using **webhooks**.
In order to send a webhook from Pybytes we first need to add the device to Datacake. This can be done by following their documentation [here](https://docs.datacake.de/api/internal-mqtt/get-started) and is very straight forward.
Now that the device is setup we need to get the **URL endpoint** pointing to the device in Datacake. We can do this by going to: `devices -> *your device* -> configuration` and then scrolling down to the "HTTP Endpoint URL" where we will see a URL looking like this: `https://api.datacake.co/integrations/api/8ef00463-9c14-1527-1485-12125f58bb7a/`.
Now that we have the URL we can setup the webhook by going to Pybytes homepage, navigating to integrations and selecting "Webhook".

When creating a new webhook there is a common problem with Pybytes where it removes the last forward slash in the URL and therefore we need to add **/ignore** to the end of it.
We will need a **custom body** that will contain the payload that we want to forward to Datacake. It's a JSON format body and we include the event name in order to differentiate between other webhooks in the future:
`{"payload": MESSAGE_PAYLOAD, "event": "WEBHOOK_EVENT_NAME"}`

Choose the device that you want to trigger the webhook at the bottom click and save.
Now whenever we send data to Pybytes we will also get that data to Datacake, meaning we can create beautiful dashboards and setup rules to receive email notifications and warnings.
In order to setup a visual presentation of the data we receive we need to **add a field** to our device (in Datacake) called "Distance" with type float.
We also need to decode the payload by adding a bit of code to the **HTTP Payload Decoder**:
```javascript=1
function Decoder(request) {
var body = JSON.parse(request.body)
var payload = body.payload
var device = payload.device
var data = payload.data
return [{
device: device,
field: "DISTANCE",
value: data.distance
}]
}
```
Now we should be able to go to the **dashboard** and add a widget that displays the field's data.
I personally like to add a **chart widget** and a **value widget**.
That would be all we need.
## The code
The project files will look something like this:
```
project
|-lib
| |-keys.py
|-boot.py
|-main.py
|-pybytes_config.json
```
`boot.py` In this case is empty and *could* be removed. I've kept it in case I wanted to add anything later on in the project.
`keys.py` Contains the serial number of the device and can be found by clicking on the device in the Datacake platform. The serial number is stored as a variable:
```python=1
device_serial = '4d7d1313-f86f-41e5-b7fd-vc71561ae77h'
```
`pybytes_config.json` Should have been automatically added when we updated the firmware of our device and it contains WiFi credentials, device id, registered email and more:
```json=1
{"wifi": {"ssid": "xxx", "password": "xxx"}, "wlan_antenna": 0, "dump_ca": false, "server": "mqtt.pybytes.pycom.io", "ssl": false, "lte": {"apn": null, "cid": 1, "reset": false, "carrier": null, "band": null, "type": null}, "device_id": "xxxxxxxx-xxx-xxx-xxx-xxxxxxxxxxxx", "network_preferences": ["wifi"], "ota_server": {"port": 443, "domain": "software.pycom.io"}, "pybytes_autostart": true, "username": "xxx@xxx.xxx"}
```
`main.py` Contains the majority of the code in this project and is used to collect and send data:
```python=1
from machine import Pin
import machine
import utime
# Define pins.
reed_pin = 'P16'
reed_switch = Pin(reed_pin, mode=Pin.IN)
# Define variables.
wake_pins = [reed_pin]
wake_reason = machine.wake_reason()[0]
def pin_deepsleep():
machine.pin_sleep_wakeup(wake_pins, machine.WAKEUP_ANY_HIGH, False)
machine.deepsleep()
# Mailbox opened: Wait for 2 min.
if wake_reason == machine.PIN_WAKE:
machine.deepsleep(120000)
# Manual boot: 10s to close the mailbox.
if wake_reason == machine.PWRON_WAKE:
utime.sleep(10)
pin_deepsleep()
import keys
import ujson
# Define pins.
echo = Pin('P20', mode=Pin.IN)
trigger = Pin('P21', mode=Pin.OUT)
# Trigger pulse.
trigger(0)
utime.sleep_us(2)
trigger(1)
utime.sleep_us(10)
trigger(0)
while echo() == 0: pass
start = utime.ticks_us()
while echo() == 1: pass
finish = utime.ticks_us()
# Calculate distance.
distance = (((utime.ticks_diff(start, finish)) * .034)/2) * -1
# Create and send payload.
data = {
"device": keys.device_serial,
"data": { "distance": distance }
}
pybytes.send_signal(1, ujson.dumps(data))
pin_deepsleep()
```
Here is an example of how the device works together with the sensors and the chosen platforms.
:::warning
:bulb: Note that some parts are simplified. The "cloud" label refers to Pybytes and Datacake.
:::
```sequence
note right of reed switch: mailbox opened
reed switch->device: pin high
note right of device: boot sequence
device->main: run
Note right of main: wait for mailman to leave
main->deep sleep: sleep for 2 minutes
deep sleep-->device: wake up
note right of device: boot sequence
device->main: run
note right of main: get depth of mailbox
main->ultrasonic sensor: measure distance
ultrasonic sensor-->main: sensor data
note right of main: send sensor data
main->cloud: sensor data
note right of main: sleep until reed pin high
main->deep sleep: sleep
```
The sequence diagram is meant to give an overview of how the system works but the diagram in itself does not follow any standards of how a sequence diagram should look like/be structured.
## Transmitting the data
As seen in the sequence diagram above, data is only transmitted when and only when someone opens the hatch to the mailbox. There is a hard limit to how often data can be sent specified by the 2 minutes of deep sleep that occurs in order to give the mailman time to leave.
The device uses **WiFi** even though the project originally was supposed to transmit the data using LoRaWAN since it drains less power and is used for many IoT devices and is therefore very good to have knowledge about. But since the closest LoRa gateway (TTN) was too far away to get a connection I settled with WiFi.
Now how exactly does the data get transmitted? If you remember the Pybytes config file you might recognize this part `"server": "mqtt.pybytes.pycom.io"`. This is the server that Pybytes library communicates with and it uses the **MQTT** (Message Queuing Telemetry Transport) protocol to publish data. After sending the data to Pybytes it gets forwarded to Datacake using a **webhook (TCP)**.
## Presenting the data
Now that Pybytes and Datacake is configured correctly we should be able to send data to Pybytes and the data sent should be visualized in Datacake's dashboard. All the data sent is **stored** for exactly one month and then **deleted**. This is the case for both Pybytes and Datacake.
This is the dashboard in Datacake showing how much space is left in the mailbox.

The data in the image also shows the debug and test data that was sent to Datacake and it's worth noting that all the data sent is correctly displayed but does not reflect a real life scenario.
I have also added a **trigger** for when the mailbox depth reaches a certain threshold. If the distance value goes under the threshold Datacake sends a warning to my mail.

## Finalizing the design
Unfortunately I was not able to put the device in my mailbox due to heavy rain and thunderstorms but instead installed it in a similar "lab environment" where I could throw some mail in a box and get the depth of the box.
Here is the result:

Of course it's a requirement to draw something on the front. Otherwise the device won't work. :slightly_smiling_face:

In this temporary setup I used three male/female jumper cables to connect my reed switch in order to get it close enough to the magnet. This worked very well.
The breadboard does actually have a sticky backside if you remove the protective film but I did not want to waste it on my cardboard box and instead used a lot of tape to hold everything up.
I'm pretty happy with the results of this project and everything has worked quite smoothly. I did have some issues with connecting to Datacake in the beginning but quickly realized that the problem was with Pybytes webhook removing the forward slash at the end of the URL.
If I were to redo this project or work on it more I would like to first of all buy a case for the device since I would be afraid to leave it in the mailbox without any protection. I would also like to order more sensors to expand the possibilities of the device and maybe even set up my own LoRa gateway to switch over from WiFi to LoRaWAN.
Overall not many problems at all and the device works as intended.
Learned a lot!