# Mailbox Temperature and Delivery Report (Tutorial)
**`by Frida Lindberg - fl223du`**
_Estimated time to complete: ~ 8h_
## Objective
The objective for this project was to get familiar with IoT, and the electronics behind it. As a .NET-developer student I was aiming to get more understanding of the connection of more than just the code, and I thought this was a great opportunity to get it!
The problem I was facing was that I live in a house in a small town - and the Mail here can arrive a bit irregular. Sometimes when you order stuff it can sit in the Mailbox for quite a while before you come and pick it up. If you're like me and you order stuff like plants and seeds, or other sensitive stuff - you kind of wanna know if you're in a hurry to get it. So I wanted to solve this problem by measuring the temperature in the mailbox, to measure if the lid was opened and also send a notification if the tempereature drops below a certain point inside the mailbox and if it's been opened.
## After This Tutorial
:::info
**You'll be..**
* Able to connect sensors to a **Microcontroller**
* Able to understand **sensor libraries**
* Having a basic understanding of the **code behind** the smart thing
* Able connect the device to **WiFi**
* Presenting and visualizing the data to **Datacake**
:::
## Material
### All Items Bought
| Item | Quantity | Bought At | Price |
|:---------------------------------------------------- |:--------:| ------------------ | ---------- |
| **Heltec Lora ESP32 with OLED Screen** :warning: | 1 | www.amazon.se | 329SEK |
| **LNU – 1DT305 Tillämpad IoT - Sensors Only Bundle** | 1 | www.electrokit.com | 129SEK |
| **USB Micro Cable** :warning: | 1 | www.electrokit.com | 16SEK |
| | | **Total Cost** | **524SEK** |
:::warning
:bulb: You should also get a battery pack or power supply for your project directly. Just Google battery packs for Heltec devices and you're good to go.
*(Optional)* - if you want, you can also make or order a **3D printed** case for your project.
:warning: This Heltec device came with the pins not attached, so this required soldering. (Pretty easy DIY if yo have the right tools)
:warning: Please make sure to get a Micro USB-Cable that supports **_data transferring_**.
:::
### All Items Used
| Item(s) | Used For |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Heltec Lora ESP32 with OLED Screen** | Microcontroller that’s used to program and transfer data - in this project i used WiFi. |
| **Temperature Sensor** (MCP9700) | This sensor is used for measuring the temperature. |
| **Tilt Sensor** (SW-200D) | This sensor is used for measuring if the lid on the Mailbox is being lifted. |
| **Breadboard** | Used for easy development setup, to test connections without having to solder directly to the Heltec board. |
| **Jumper Wires** | To make the connections between the Heltec and the bredboard. |
| **USB Micro Cable** | This is used to update the firmware, transfer data and upload code to the Heltec device. |
## Computer Setup
I choose **Atom** for my IDE since it was easy to follow along.
The setup was made on a **Macbook Air M1 (2020) - with the Satechi USB-C converter**.
### Install Node.js and Atom IDE
* **Step 1:** Download and install **[Node.js (here)](https://nodejs.org/en/)**
* **Step 2:** Download and install **[Atom (here)](https://atom.io/)**
* **Step 3:** Open Atom and the package manager from: `Atom >> Preferences >> + Install`
* **Step 4:** Look for **Pymakr** (needed for MicroPython support) and Install it. (It takes a while; wait until it shows it's successfully installed)
### Update Firmware
* **Step 1:** Connect your **Heltec** board to your Mac with a USB-cable.
* **Step 2:** The driver has already integrated into the macOS. Check if your Mac have detected your Heltec Board by clicking `Apple icon (upper left corner) >> About This Mac >> Overview >> System Report... >> Hardware >> USB`
:::info
Your **USB Device Tree** should look like this; and you can find the **CP2102 USB to UART Bridge Controller** in the USB Device Tree.


:::
* **Step 3:** Download **[this firmware-file](https://github.com/H-Ryan/Heltec/blob/main/PyCom%20MicroPython/Heltec%20PyCom%20MicroPython.zip?raw=true)** and extract them on your Mac.
* **Step 4:** Open **[ESP32 Web Flasher](https://nabucasa.github.io/esp-web-flasher/)**:
* 1. Follow the numbered steps to connect to your development board:

* 2. Press on **Erase** - then **OK** to delete files from your board:

---

---

* 3. Choose **Offset, Downloaded Files**, and then press **Program** to write firmware to your board. (It takes a while and during this time don’t disconnect your board from the USB cable)

## Run the Code
* **Step 1:** Open your **Atom IDE** and at the bottom right, you see the **Pymakr** plugin. Then open Settings on **Pymakr** and choose **Global settings**. Uncheck the check box selection from **Auto connect** and **Safe-boot before upload**.

* **Step 2:** Enter the device port into the `Device address (list)` field. The device address can be found using the following command in the Terminal.
```shell=1
ls /dev/
```


:::danger
:warning: Please Restart **Atom**
:::
* **Step 3:** Open **Atom**, click on `Connect device` in **Pymakr** and choose your device port.


* **Step 4:** Confirm the success by entering your first line of code:
```python
print("Hello from Heltec!")
```


### Create the Project
* **Step 1:** Create a folder on your computer. `Mailbox Project`.
* **Step 2:** In **Atom**, click on `File >> Open` and select your created folder.
* **Step 3:** Right-click on your project folder and choose `New File` to create a new file and name it `main.py`
:::info
:bulb: All projects needs the `main.py` file since it is the starting point of the code execution.
:::
* **Step 4:** Copy the following lines in `main.py` and save it `CMD + S`.
```python
from machine import Pin # Used for LED pin addressing
import time # Used for the delay between blinking
led = Pin('P22', mode=Pin.OUT) # Used LED pin as output
# Loop for blinking
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
```
* **Step 5:** Now upload the file to your board from the upload project button:

:::success
:tada: The *integrated* LED on your board starts to blink because of your loop.
:::
## Sensor Connection & Data Reading
:::info
**On all development boards, there are different "pinouts":**
### Heltec (WiFi LoRa 32 V2)

:::
### Connecting Temperature Sensor
:::danger
:warning: **Please disconnect the power/USB cable from the Development board before connecting sensors or changing a sensor pin!**
:::
* **Step 1:** If your board was delivered with the pins unsoldered, you need to solder them first, and then attach the pinout stickers that came with the board - like this (don't worry about the jumper wires just yet):

:::info
:bulb: I used the breadboard to make the soldering easier (I had no flux at home, so I had to do my best to solder the pins anyway. Please don't use my picture as a reference guide for how soldering _should_ look like.
**If you want tips and tricks on how to solder - just search online.**
:::
* **Step 2:** Connect your Heltec (with the soldered pins to the breadboard) - just make sure you have space for the jumper wires later:

Should look something like this:

* **Step 3:** Please connect the jumper wires and `Temperature Sensor (MCP9700)` like this:

:::info
:bulb: The colors on the jumper wire is just to make it easier to see the connections. You can choose any colors you want. Just make sure to be consistant.
:bulb: If you're interested in how a breadboard works in detail, there's good tutorials for that online.
:::
* **Step 4:** In **Atom** - replace the code in `main.py` with this code:
```python=
from mcp9700 import MCP9700
from machine import ADC
adc = ADC(bits=12)
pin = 'P16'
temp = MCP9700(adc, pin)
while True:
print(temp.read())
```
* **Step 5:** In **Atom** - right-click on your project and create a new folder called `lib` - then rightclick on that folder and create a `New File` called `mcp9700.py` and paste the following code (sensor library):
```python=
import array
class MCP9700:
def __init__(self, adc, pin, samples=2048):
self.zero = 500 # Millivolt
self.temp_coeff = 10.0 # Millivolt/Celsius
self.samples = samples
self.channel = adc.channel(pin=pin, attn=adc.ATTN_11DB)
def get_average(self):
buffer = array.array(
"f", self.channel.value_to_voltage(
self.channel()
) for _ in range(self.samples)
)
avg = sum(buffer)/self.samples
del buffer
return avg
def read(self):
return (self.get_average() - self.zero) / self.temp_coeff
```
* **Step 6:** Connect the board to your computer, and upload the files to your board from the upload project button:

:::success
:tada: Now, you should be able to see some temperature measurements running in the terminal!
:::
## Connecting the Tilt Sensor
* **Step 1:** Disconnect the power/USB-cable from your device
* **Step 2:** Connect your `Tilt Sensor (SW-200D)` to the board like this:

:::danger
:warning: **Please check your connections *twice* before plugging the power into the device**
:::
* **Step 3:** Connect the board to your computer and upload the code just like you did in the previous steps, to `main.py`:
```python
from machine import Pin
import time
# TO-DO
Tilt = Pin('P15', Pin.IN, pull=Pin.PULL_DOWN)
while True:
print(Tilt.value())
time.sleep_ms(100)
```
:::success
:tada: You should now see some `0`'s. If you touch (or tilt) the `Tilt Sensor`you should se some `1`'s in the terminal. That means we are getting some measurements from the sensor.
:::
## Connecting to Wifi
:::info
Now we are ready to connect the device to WiFi. Since I don't live in the area of `Helium` or `The Things Network` - I wasn't able to connect to these, but feel free to try them as the Heltec board also supports this, and it gives you more freedom in where you can place your project!
:::
* **Step 1:** In **Atom** - right-click your project folder and choose `New File` - name this file `boot.py` and paste this code:
```python
def do_connect():
from network import WLAN
import time
import pycom
import machine
pycom.wifi_mode_on_boot(WLAN.STA) # choose station mode on boot
wlan = WLAN() # get current object, without changing the mode
# Set STA on soft rest
if machine.reset_cause() != machine.SOFT_RESET:
wlan.init(mode=WLAN.STA) # Put modem on Station mode
if not wlan.isconnected(): # Check if already connected
print("Connecting to WiFi...")
# Connect with your WiFi Credential
wlan.connect('YOUR_SSID_HERE', auth=(WLAN.WPA2, 'YOUR_PASSWORD HERE'))
# Check if it is connected otherwise wait
while not wlan.isconnected():
pass
print("Connected to Wifi")
time.sleep_ms(500)
# Print the IP assigned by router
print('network config:', wlan.ifconfig(id=0))
def http_get(url = 'http://detectportal.firefox.com/'):
import socket # Used by HTML get request
import time # Used for delay
_, _, host, path = url.split('/', 3) # Separate URL request
addr = socket.getaddrinfo(host, 80)[0][-1] # Get IP address of host
s = socket.socket() # Initialise the socket
s.connect(addr) # Try connecting to host address
# Send HTTP request to the host with specific path
s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8'))
time.sleep(1) # Sleep for a second
rec_bytes = s.recv(10000) # Receve response
print(rec_bytes) # Print the response
s.close() # Close connection
# WiFi Connection
do_connect()
# HTTP request
http_get()
```
:::info
Please change the `YOUR_SSID_HERE` and the `YOUR_PASSWORD_HERE` to your own.
:::
:::warning
Please note that you must have a **2.4GHz** network for this to work.
:::
* **Step 2:** Upload the code again like you did before
:::success
:tada: Now you should be able to see something like `Connecting to WiFi...` and `Connected to Wifi` in your terminal.
:::
## Presenting the Data
:::success
:tada: We are now ready for the final step - presenting the data!
:::
### Setting up Connections
* **Step 1:** In **Atom** - right-click your project and choose `New File` - name the file `mqtt.py` and paste this code:
```python
# Wait for a single incoming MQTT message and process it.
# Subscribed messages are delivered to a callback previously
# set by .set_callback() method. Other (internal) MQTT
# messages processed internally.
def wait_msg(self):
res = self.sock.read(1)
self.sock.setblocking(True)
if res is None:
return None
if res == b"":
raise OSError(-1)
if res == b"\xd0": # PINGRESP
sz = self.sock.read(1)[0]
assert sz == 0
return None
op = res[0]
if op & 0xf0 != 0x30:
return op
sz = self._recv_len()
topic_len = self.sock.read(2)
topic_len = (topic_len[0] << 8) | topic_len[1]
topic = self.sock.read(topic_len)
sz -= topic_len + 2
if op & 6:
pid = self.sock.read(2)
pid = pid[0] << 8 | pid[1]
sz -= 2
msg = self.sock.read(sz)
self.cb(topic, msg)
if op & 6 == 2:
pkt = bytearray(b"\x40\x02\0\0")
struct.pack_into("!H", pkt, 2, pid)
self.sock.write(pkt)
elif op & 6 == 4:
assert 0
# Checks whether a pending message from server is available.
# If not, returns immediately with None. Otherwise, does
# the same processing as wait_msg.
def check_msg(self):
self.sock.setblocking(False)
return self.wait_msg()
```
* **Step 2:** To your `main.py` - add this code:
```python
from mqtt import MQTTClient
def sub_cb(topic, msg):
print(msg)
# MQTT Setup
client = MQTTClient(YOUR_SERIAL_NUMBER_HERE, # my created serialnumber
'mqtt.datacake.co', # broker
user=YOUR_API_TOKEN_HERE, # my user name found under API Token
password=YOUR_API_TOKEN_HERE, # same as above
port=1883)
client.set_callback(sub_cb)
client.connect()
print('connected')
my_topic = "YOUR_TOPIC_HERE" # MQTT Config - last link
while True:
payload = temp.read()
client.publish(topic=my_topic, msg=str(payload))
client.check_msg()
time.sleep(10) # Only put this sleep in test, in your free subscription to Datacake you only have 500 credits/day
# Change this interval to a longer sleep once you can see that it's working
```
:::info
We'll go through what to write in `YOUR_SERIAL_NUMBER_HERE` and `YOUR_API_TOKEN_HERE` and `YOUR_TOPIC_HERE` in the next steps.
:::
### Datacake Setup
* **Step 1:** Head over to **[Datacake](https://app.datacake.de/login)** and create an account.
* **Step 2:** Navigate to `Devices` and add a new device:

* **Step 3:** Select `API` and click `Next`

* **Step 4:** Select `New Product`, give your device a name and hit `Next`

* **Step 5:** Then we fill in a **custom** `Serial Number` and a `Name` and hit `Next`

* **Step 6:** Select the Free Plan and hit `Add 1 device` - now you should be directed to the page of the device you just created.

### Send Sensor Data
* **Step 1:** Navigate to your device and select the `Configuration` tab. Scroll down until you see `Fields`.

* **Step 2:** Define the Type of field, give it a `Name`, a `Unit`, and hit `Add Field`.

* **Step 3:** Click on the `Configure` button under `Integrations` > `MQTT`

* **Step 4:** Copy the last line of code like this:

* **Step 5:** Paste this link in in the code we pasted earlier under `YOUR_TOPIC_HERE`
* **Step 6:** Next, we'll do the same for `YOUR_SERIAL_NUMBER_HERE` - you'll find the `Serial Number` on the top of the page - the one you entered earlier. Paste that in in the code.
* **Step 7:** Now the same for the `API Token` - you'll find it under `Edit Profile` >> `API` >> `Show` - Copy the token and paste it in under `YOUR_API_TOKEN_HERE`
### Create Dashboard
* **Step 1:** Navigate to your device and select `Dashboard`.

* **Step 2:** Click on the toggle button - then `Add Widget` - select `Value`

* **Step 3:** Give the widget a name - select `Data` from the middle menu - then select your - then hit `Save`

Make sure to hit te toggle button again to the right - then save your dashboard.

:::success
:tada: Now we have successfully posted the data to **Datacake** and presented it in a widget! Wohoo!
Just repeat this process for your vibration sensor. I did not include the code for this here because it would be too long for the tutorial.
You can import the library for your `Tilt Sensor` **[here](https://github.com/iot-lnu/applied-iot/tree/master/sensor-examples/Tilt%20sensor)**.
:::
:::info
Under the `Rules` tab, you can also choose to get an e-mail alert under certain circumstances, or if you are on a paid plan, you can choose to get an SMS if you like to!
:::
## Finalizing the Design

:::success
If you want, you can also import the library for the OLED to show I the temperature to use it like I did here
:::

:::info
The `Temperature Sensor` is sending data to **Datacake** every 10 minutes (via the time.sleep() function). And is sending an e-mail notification if the temperature is above 25°C.
The `Tilt Sensor` is sending `0`'s and `1`'s depending on it's state. So the `Rule` I have is that if the value is `1` - the lid is beeing opened, and then it triggers a function to send an e-mail notification.
:::
---
## Next Steps
Considering this is a short summer course and the hardware shortage, and that I was a complete beginner at this - I was going to develop this further, but I didn't have the time for it. So I'm planning on getting more things done by myself after the course ends. I unfortunately don't live nearby a hardware provider, so I didn't have the time to order the other things needed in time. But now that I have basic knowledge of how things work, I will definitely try other things out.
**Things I'm planning to do even after the course ends:**
* Connect the Mailbox to Google Home so I'll get notified by a voice message instead of an e-mail
* Get a battery pack
* Buy a 3D-case for it to be more protected from tough weather conditions. (But since it will be in the Mailbox lid I don't think it will be a problem for a while though.)
* Maybe get another sensor for the open/close lid solution, since I find the Tilt Sensor is a bit unreliable
## Conclusion
This course absolutely got my eyes open to the world of IoT. And as a student developer I can definetly see the benefits of knowing more of what's behind the world of IoT and how it's built.
Over all, this project went well - even though some problems took quite a while to figure out. But I wanted to think for myself, so I tried not to get too much help and figure stuff out by myself, as I know this is the case for most developers. This was a great lesson.
At first, I found all the components and the hardware *very* intimidating, but now I'm very glad I took the course and developed a sense for this. It actually has been a very fun project.