---
# System prepended metadata

title: Untitled

---

#### 1DT305 Tillämpad IoT - Tutorial - Lexx Damm, adaha09


## Human agent biometric and stat supervisor system

Lexx Damm, adaha09

My aim with this project is to prototype a future system for personel biometric and stats supervisor system for my own fictional group. The system should comprise of a system of wearable devices that gather various biometric, enviromental and position metrics. I estimate it would take 1 to 3 hours to wire up, write the code and configure the systems.
 
### Objective

I decided early on that I wanted to do something fun and futuristic. I am into cybernetics and wearables and one of my favorite sci-fi movie is Aliens 2 which if I remember correctly have a supervisor system for the marines which show among other things camera, ECG and some other things. I also wanted to incoperate my own NFC-implants into the design somehow by making my wearable(s) NFC-connected.

I believe my prototype will make it more clear the pratical use of such a supervisor system and what drawbacks or unforseen problems that may present itself. I also believe it will prove a practical use of my implants.

In my project I will only focus on a monitor system that is supervised, basically a human being or AI monitoring personel stats in real-time.

### Material

List of materials:
| Item | Cost | Supplier |
| ------------- | - |-|
| Pycom Lopy4   | 34.95 EUR| Pycom webshop|
| Pycom Fipy    | 54 EUR| Pycom webshop|
| Pycom Pyscan  | 39.90 EUR | Pycom webshop|
| Pycom Pytrack | 34.95 | Pycom webshop|
| Playknowlogy Komponent-kit | 299.90 SEK | Kjell & co|
| Mikroe ECG 4 CLICK kit | 699.09 SEK + TAX | RS Components |

In my project the main processing devices I have chosen to use is the Pycom line of developer boards. I chose them because it was the recommended device to use, but personally I like the balance between down to the metal but also easy of use it provides but at a good performance and minimal physical space needed.

I decided early on that I wanted to do something with both NFC and GPS, so I ordered the Pyscan and Pytrack extension boards for both my Pycom.
I looked up if there was a ECG board available and found the Click line of boards from Mikroe. I first bought the ECG 2 CLICK but after some testing realized it was to complicated so I ordered the more simpler ECG 4 CLICK from Mikroe which use a UART interface and simple communication and data protocol.
 
### Computer setup
 
Software links:
- Atom
https://atom.io/
- pycom-fwtool ubuntu deb
https://pycom.io/downloads/

I used the Atom IDE on Ubuntu 20.04 and installed the pymakr package through the Atom package manager. I also installed pycom-fwtool to update my Pycom boards to the latest firmware. I uploaded the code to the two Pycom devices via Pymakr.
 
### Putting everything together

The FiPy device is plugged into the Pytrack extension board while the LoPy is plugged into the Pyscan board. I then used a breadboard to plug in the Mikroe ECG 4 CLICK and wired the UART lines (TX and RX) to one of the headers on the Pyscan board.

![](https://i.imgur.com/TlmrfvR.jpg)

![](https://i.imgur.com/ICiH6bz.jpg)

![](https://i.imgur.com/fOuGgBk.jpg)

I didn't have the time or resources to make a custom PCB, a housing for the boards so my setup is only for prototyping.

### Platform

I used the The Things Network to gather my data from LoRaWAN and then forwarded it to Ubidots to visualize the data.

For my prototype I used a simple dashboard on Ubidots visualizing the GPS data with a map and the ECG data with a value for the interval between heartbeats.

### The code

comms - FiPy code:

```python=

import machine
import time
import pycom
import socket
import ubinascii
import struct
import _thread
from network import LoRa
from L76GNSS import L76GNSS
from pytrack import Pytrack

# TTN EUI and APP KEY
app_eui = ubinascii.unhexlify('70B3D57ED0031632')
app_key = ubinascii.unhexlify('10D4E433364CF8A3E78F0708BA22EC42')

pycom.heartbeat(False)
pycom.rgbled(0x060000)
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
py = Pytrack()
l76 = L76GNSS(py, timeout=30)

lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)
pycom.rgbled(0x0a0a00)

while not lora.has_joined():
    print('Not yet joined')
    time.sleep(3)

print('Joined LoRa TTN')
pycom.rgbled(0x000f00)

sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
sock.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
sock.setblocking(True)
sock.bind(1)

def gps_loop():
    while (True):
        coord = l76.coordinates()
        if (coord[0] == None and coord[1] == None):
            pycom.rgbled(0x0a0a00)
            continue
        pycom.rgbled(0x00000a)
        print('Coordinates: {:.6f}, {:.6f}'.format(coord[0], coord[1]))
        pack = bytearray(struct.pack('f', float(coord[0])) + struct.pack('f', float(coord[1])))
        sock.send(pack)
        time.sleep(60 + 60 + 60 + 60 + 60)

_thread.start_new_thread(gps_loop, ())

```


id - LoPy4 code:
```python=
iimport time
import pycom
import _thread
import ubinascii
import math
from machine import UART
from machine import Pin
from machine import Timer
from pyscan import Pyscan
from MFRC630 import MFRC630
from network import LoRa

# TTN EUI and APP KEY
app_eui = ubinascii.unhexlify('70B3D54993281B0B')
app_key = ubinascii.unhexlify('3987D8D92D72242591EC37D7E327A7F8')

# Dictonary of stored uid and their user is
my_ids_dict = {
    b'\x04~e\x12\xff8\x81\x00\x00\x00': b'\x01',
    b'\x04\x19\x08\x12\xff8\x85\x00\x00\x00': b'\x02'
}

# 2's compliment to get 16 bit value from 2 bytes
def twos_comp(val, bits):
    if (val & (1 << (bits - 1))) != 0:
        val = val - (1 << bits)
    return val

# Wait for scanning the correct NFC implant
def auth_thread_loop():
    global auth_busy
    global auth_logged_in
    global auth_user_id
    auth_busy = True
    auth = False
    while (not auth):
        atqa = nfc.mfrc630_iso14443a_WUPA_REQA(nfc.MFRC630_ISO14443_CMD_REQA)
        if (atqa != 0):
            uid = bytearray(10)
            uid_len = nfc.mfrc630_iso14443a_select(uid)
            if (uid_len > 0):
                print('UID: {}'.format(uid))
                for stored_uid, user_id in my_ids_dict.items():
                    if (uid == stored_uid):
                        print('Logged in')
                        auth_logged_in = True
                        pycom.rgbled(0x000f00)
                        auth_user_id = user_id
                        auth = True
                        auth_busy = False
                        break

# ECG loop
def ecg_thread_loop():
    global ecg_busy
    global ecg_interval
    ecg_busy = True
    ecg_interval = None
    ecg_state = 'PRE'
    ecg_data = []
    counter = 0
    while (counter < 2000):
        if (uart.any()):
            char = uart.read(1)
            if (uart.any() and char and char[0] == 0xaa):
                char = uart.read(1)
                if (uart.any() and char and char[0] == 0xaa):
                    char = uart.read(1)
                    if (uart.any() and char and char[0] == 4):
                        payload_len = char[0]
                        payload = uart.read(payload_len)
                        if (payload):
                            crc = uart.read(1)
                            code = payload[0]
                            if (code == 0x80):
                                data_row_len = payload[1]
                                data_row = bytearray(payload[2:4])
                                ecg_value = twos_comp(int(ubinascii.hexlify(data_row), 16), 16)
                                ecg_data.append(ecg_value)
                                counter += 1

    i = 0
    found = False
    for value in ecg_data:
        print(value)
        if (ecg_state == 'PRE' and value > 1800):
            ecg_state = 'PEAK'
        if (ecg_state == 'INTER' and value > 1800):
            ecg_state = 'PEAK'
        if (ecg_state == 'PEAK' and value < -1800):
            ecg_state = 'DIP'
        if (ecg_state == 'DIP' and value < 200 and value > -200):
            ecg_state = 'INTER'
            if (found == True):
                print('Found interval')
                ecg_interval = i
                ecg_busy = False
                return ecg_interval
            else:
                i = 0
                found = True
        i += 1
    ecg_busy = False

def send_lora_bytes(port, bytes):
    sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    sock.bind(port)
    sock.setblocking(True)
    sock.send(bytes)

pycom.heartbeat(False)

auth_uplink_timer = Timer.Chrono()
ecg_uplink_timer = Timer.Chrono()

# Init the UART and configure it to use the pins 19 and 15 as UART TX and RX pins connected to the Mikroe ECG 4 CLICK uart
uart = UART(1, 57600, bits=8, parity=None, stop=1, pins=('P19', 'P15'))

py = Pyscan()
nfc = MFRC630(py)
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)

nfc.mfrc630_cmd_init()

pycom.rgbled(0x0f0000)

lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)

auth_busy = False
auth_logged_in = False
auth_user_id = None
ecg_busy = False
ecg_interval = None
auth_uplink_timer.start()
ecg_uplink_timer.start()
while (True):
    print('Waiting for LoRa connection to TTN')
    if (lora.has_joined()):
        print('Connected to TTN via LoRa')
        if (auth_uplink_timer.read() > 300):
            if (not auth_busy):
                _thread.start_new_thread(auth_thread_loop, ())
            if (auth_logged_in):
                print('Sending user login')
                bytes_pack = bytearray(struct.pack('c', auth_user_idl))
                send_lora_bytes(2, bytes_pack)
                auth_uplink_timer.reset()
                auth_logged_in = False
        if (ecg_uplink_timer.read() > 300):
            if (not ecg_busy):
                _thread.start_new_thread(ecg_thread_loop, ())
            if (ecg_interval != None):
                print('Sending ecg interval')
                bytes_pack = bytearray(struct.pack('f', ecg_interval))
                send_lora_bytes(3, bytes_pack)
                ecg_uplink_timer.reset()
                ecg_interval = None
    time.sleep(30)

```

Payload decoder - TTN:
```javascript=
function Decoder(bytes, port) {
  function bytesToFloat(bytes) {
    // JavaScript bitwise operators yield a 32 bits integer, not a float.
    // Assume LSB (least significant byte first).
    var bits = bytes[3]<<24 | bytes[2]<<16 | bytes[1]<<8 | bytes[0];
    var sign = (bits>>>31 === 0) ? 1.0 : -1.0;
    var e = bits>>>23 & 0xff;
    var m = (e === 0) ? (bits & 0x7fffff)<<1 : (bits & 0x7fffff) | 0x800000;
    var f = sign * m * Math.pow(2, e - 150);
    return f;
  }
  function bytesToShort(bytes) {
    var i = bytes[0]<<8 | bytes[1];
    return i;
  }
  var decoded = {};

  if (port === 1) {
    decoded = {
      gps: {
        lat: +bytesToFloat(bytes.slice(0, 4)),
        lng: +bytesToFloat(bytes.slice(4, 8))
      }
    }
  }
  else if (port === 2) {
    decoded = {
      ecgInterval: bytesToShort(bytes)
    }
  }
  return decoded;
}
```

### Transmitting the data / connectivity

I had ordered the FiPy which have Celluar connection but ended up using only LoRa. I planned to use Bluetooth between the FiPy and LoPy but revised my idea although I got Bluetooth working, because I figured if the main unit (FiPy) whent down, the LoPy would not get it's data to the internet, so I decided to use LoRaWAN on both the FiPy and LoPy indepentily.

Due to the limitations of LoRaWAN i only sends the GPS coordinates from the FiPy every 5 minutes. I used LoRaWAN with The Things Network.

Orginally I had plan to send the ECG data from the LoPy in realtime, every second, but rethinked and ended up sending the heart interval every 5 minutes. I also send the specific user logged in every 5 min.

### Presenting the data

As my orginal idea centered around a human supervised monitoring system I chose to only show the stats in real-time and don't use a database. Due to time contraints I used a simple dashboard on Ubidots.

![](https://i.imgur.com/U0JmgHB.png)

### Finalizing the design

In the end I ran out of time and hit some limitations of specifically LoRaWAN which made my original ideas impossible. In my original idea or vision I had pictured a realtime monitoring system which updated at least every second so I could send and then visualize a ECG plot. I had to rethink a way to use the ECG meaningfully and had to compromize.

If I had a lot more resources and time I think I would have come much closer to my original vision. Replacing LoRaWAN with WLAN and/or have a central communication device or even a satelite modem (would make much more sense for my original application out in the field where no LoRaWAN would be available)

I would also have liked to do much more with the NFC part of the project, now I only use the UID to log in to my device.

I wanted originally to send the raw ECG data to the internet and a dashboard, when faced with the limitations of LoRaWAN i did look up if I could use a machine learning algorimth on the Pycom device to sense different ECG states of the agent wearing the device, but in the end it would take to much time and I decided to only send the interval between heartbeats.


I had a lot of problems with connecting to LoRa due to bad coverage so it was hard to test the system as a whole.

I also had a lot of problems with the ECG data protocol, some kind of error sometimes manifested it self when I read the UART data. This took a lot of time from the project and resulted in trying to get something basic to work.


The most successful part of the project in my mind is what lessons I have learned. If I wanted it to work and be practical I would have to find a another wireless tehcnology which have much more data allowences AND coverage. A more powerful main device to run a AI locally would benefit the project when radio coverage and/or ability to transmit would be affected.

It did also provide me with some practical knowledge about to read and process ECG data. In a more ambitious project I would have incorporated some machine learning algorithm, both on the edge devices and in a central server.

![](https://i.imgur.com/bWD2TMw.jpg)

![](https://i.imgur.com/VqDGzQG.png)

![](https://i.imgur.com/aiUIYnP.jpg)

{%youtube ULU7ldsv5jw %}