
Internet of things projects
Embedded Programming Report
case 2
Report stats:
- Reading Time: 19 mins
Table of Contents
Introduction
Environment Monitoring and Management System by Greenhouse A/S, is an integrated setup combining embedded devices, a centralized server, and seamless networking. It provides continuous monitoring of conditions like temperature and humidity, ensuring ideal plant growth. Sensor data can be analyzed via a website or by the system itself, it will push alarms if any anomalies occurs. This cohesive system ensures agricultural efficiency and proactive management of greenhouse environments.
Architecture
- Service-Oriented Architecture: using source control design: manyrepo instead of monorepo
ER diagram
System Design diagram
Note: The urls defined at
http://meo.local
and http://meo.local/api/
are routes defined by Nginx, that passes tdp packages to the correct urls.
Design principles
- KISS (Keep It Simple, Stupid):
- DRY (Don’t Repeat Yourself):
Server Side
About the Server
The server is running Raspberry Pi OS Lite which is a lightweight version of the Raspberry Pi operating system, based on Debian Linux.
It is optimized for the Raspberry Pi hardware and is a command-line only interface, meaning no graphical desktop.
Main API and endpoints
The API facilitates greenhouse environment monitoring and management, handling sensor data for temperature and humidity, and allows for configuration changes and retrieval of current settings. It supports the creation of new facility, building, and sensor records, and offers a dashboard view for real-time data and alerts
Endpoints
Welcome Page
GET /
Serves the HTML welcome page for the Greenhouse Temperature and Humidity API.
Setup
POST /change_target_building/
Content-Type: application/json
{
"facility": {
"id": 0,
"beskrivelse": "string"
},
"building": {
"id": 0,
"name": "string",
"facility_id": 0
}
}
Change Target Building
POST /change_target_building/
Content-Type: application/json
{
"building_id": [building_id]
}
Endpoint to change the target building to the specified ID.
Change Target Facility
POST /change_target_facility/
Content-Type: application/json
{
"facility_id": [facility_id]
}
Endpoint to change the target facility to the specified ID.
Change Target Sensor Name
POST /change_target_name/
Content-Type: application/json
{
"sensor_name": "[sensor_name]"
}
Endpoint to change the target sensor to the specified name.
Get Target Building
GET /get_target_building/
Retrieves the currently targeted building.
Get Target Facility
GET /get_target_facility/
Retrieves the currently targeted facility.
Get Target Sensor Name
GET /get_target_name/
Retrieves the currently targeted sensor name.
Create Facility
POST /facility/
Content-Type: application/json
{
"facility": "[facility_object]"
}
Creates a new facility entry with the given details.
Create Building
POST /building/
Content-Type: application/json
{
"building": "[building_object]"
}
Creates a new building entry with the given details.
Create Sensor
POST /create_sensor/
Content-Type: application/json
{
"sensor": "[sensor_object]"
}
Creates a new sensor or returns an existing one based on the serial number.
Post Sensor Value
POST /sensor_value/
Content-Type: application/json
{
"sensor_value": "[sensor_value_object]"
}
Creates a new sensor value record.
Create Alarm
POST /alarm/
Content-Type: application/json
{
"alarm": "[alarm_object]"
}
Creates a new alarm based on the sensor data.
Get Sensor by Serial Number
GET /get_sensor_by_serial/{serial_number}
Retrieves sensor details by its serial number.
Dashboard Data
GET /dashboard/
Retrieves dashboard data, including sensor values and alarms.
Get Temperature Data by Sensor ID
GET /get_temp_data/{sensor_id}
Fetches the latest temperature data for a given sensor ID.
Get Humidity Data by Sensor ID
GET /get_humid_data/{sensor_id}
Fetches the latest humidity data for a given sensor ID.
Update Threshold Settings
PUT /threshold-settings/
Content-Type: application/json
{
"sensor_type": "[sensor_type]",
"max_value": [max_value],
"low_value": [low_value]
}
Updates the threshold settings for a given sensor type.
Get Threshold Settings
GET /threshold-settings/
Retrieves the list of all threshold settings for sensors.
Get All Sensor Serial Numbers
GET /get_all_sensor_serial_numbers
Retrieves all sensor serial numbers in the system.
POST /set_working_hours/
Content-Type: application/json
{
"morning_hour": [morning_hour],
"morning_minute": [morning_minute],
"evening_hour": [evening_hour],
"evening_minute": [evening_minute]
}
Endpoint to create working hours configuration.
How to Use It
Server commands
Nginx
commands
systemctl enable nginx.service
systemctl start nginx.service
systemctl status nginx.service
systemctl restart nginx.service
superviser
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status fastapi-app
sudo supervisorctl restart fastapi-app
Gunicorn
Test socket
curl --unix-socket /home/sysadmin/code/fastapi-nginx-gunicorn/run/gunicorn.sock meo.local/<endpoint address>
Tools and Frameworks
-
Tools
-
Nginx (TLDR: I used it mostly has a reverse proxy )
-
NGINX is a high‑performance, highly scalable web server and reverse proxy
- Base Nginx config -> Base Nginx config
- Custom Nginx config -> Custom Nginx config
-
-
Gunicorn
-
Python Web Server Gateway Interface (WSGI)
- My Config -> Gunicorn
-
-
Uvicorn (TLDR: Webserver)
- This is part of FASTapi python framework and works as an ASGI web server implementation for Python.
-
Mariadb (TLDR : Database)
-
Superviser (TLDR: SystemD but more encapsulated )
- Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.
- My Config -> Supervisor
-
-
Framework
- fastapi:
- A modern, fast web framework for building APIs with Python based on standard Python type hints.
- SQLAlchemy:
- A powerful SQL toolkit and Object-Relational Mapping (ORM) library for Python that allows for flexible database access and management.
- SQLModel
- SQLModel is, a thin layer on top of Pydantic and SQLAlchemy, that is designed to simplify interacting with SQL databases in FastAPI applications
- uvicorn:
- An ASGI server implementation for Python, serving as a lightning-fast foundation for ASGI applications like those built with FastAPI.
- logging:
- A standard Python library for implementing flexible event logging systems for applications and libraries.
- mariadb:
- A Python library that facilitates connection to MariaDB databases, enabling operations like queries, updates, and schema changes.
- mysqlclient:
- A fork of MySQL-python, it is a Python database connector for connecting to MySQL databases that provides compatibility with MySQL and MariaDB.
- gunicorn:
- A Python WSGI HTTP Server for UNIX systems that serves as a reliable and efficient interface between web applications and web servers.
- fastapi:
Dashboard API and Website
About the Dashboard
The dashboard is a seprate application to the main API(FastApi-app) and therefore gets all the displayed data from the main api.
How to Use It
Nginx
commands
systemctl enable nginx.service
systemctl start nginx.service
systemctl status nginx.service
systemctl restart nginx.service
superviser
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status dashboard
sudo supervisorctl restart dashboard
#You can test the front page via the following
curl http://meo.local/
# Now if you want the values the dashboard gets you can call the following 2 endpoints
curl http://meo.local/api/threshold-settings/
# and
curl http://meo.local/api/dashboard//
Tools and Frameworks
-
Tools
-
Nginx (TLDR: I used it mostly has a reverse proxy )
-
NGINX is a high‑performance, highly scalable web server and reverse proxy
- Base Nginx config -> Base Nginx config
- Custom Nginx config -> Custom Nginx config
-
-
Gunicorn
-
Python Web Server Gateway Interface (WSGI)
- My Config -> Gunicorn
-
-
Uvicorn (TLDR: Webserver)
- This is part of FASTapi python framework
-
Mariadb (TLDR : Database)
-
Superviser (TLDR: SystemD but more encapsulated )
- Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.
- My Config -> Supervisor
-
-
Framework
- fastapi:
- A modern, fast web framework for building APIs with Python based on standard Python type hints.
- uvicorn:
- An ASGI server implementation for Python, serving as a lightning-fast foundation for ASGI applications like those built with FastAPI.
- logging:
- A standard Python library for implementing flexible event logging systems for applications and libraries.
- gunicorn:
- A Python WSGI HTTP Server for UNIX systems that serves as a reliable and efficient interface between web applications and web servers.
- HTMX
- HTMX is the HTML extension syntax that replaces JavaScript with simple markup
- fastapi:
Client Side
Linux Based Client
About the Client
The client is a raspberry 3 using grovepi sensors
How to Use It
Step 1: Connecting via SSH
- Open your SSH client of choice on your computer.
- Connect to the client device using its IP address or hostname.
Step 2: Cloning the Repository
- Once connected via SSH, navigate to the directory where you want to clone the repository.
- Clone the
embedded_device
repository using the following command:git clone git@github.com:mart337i/embeded_device.git
Step 3: Installing Python and Dependencies
-
Ensure Python 3 is installed on the client device. If not, install it using the device’s package manager.
-
Navigate to the repository directory:
cd embeded_device
-
Install the required Python packages listed in
requirements.txt
with the following command:python3 -m pip install -r requirements.txt
Step 4: Setting Up Systemd Service
- Set up the Systemd unit file by creating a new file with the
.service
extension in the/etc/systemd/system/
directory. - Copy the Systemd configuration from the provided Systemd section into this new file.
- Run the systemd commands listed below in that order
systemd commands
systemctl enable <name of service>.service
systemctl start <name of service>.service
systemctl status <name of service>.service
systemctl restart <name of service>.service
Tools and Frameworks
-
SystemD
-
systemd is system and service manager for most Unix like operating systems As the system boots up, the first process created with process id :
PID = 1
thats systemd. Sytemd then starts/creates the rest of the userspace.For this project it is used to start the client service, to collect information from the sensors. The main benefit is the automatic start on boot and the auto restart on failiure, alongside its loging capabilities using
journalctl -f -u <process name>
in this case-f
is for following the log and-u
is a systemd unit / service
-
SystemD config
[Unit] Description=embeded service After=multi-user.target [Service] StartLimitBurst=0 Type=simple Restart=on-failure StartLimitIntervalSec=10 ExecStart=/usr/bin/python3 /home/pi/code/embeded_device/main.py [Install] WantedBy=multi-user.target
-
-
Python libs
- grovepi
- open-source platform for connecting Grove Sensors to the Rasberry Pi.
- datetime
- Used for constructing datetime objects.
- requests
- allows you to send HTTP/1.1 requests extremely easily.
- python-dotenv
- Python-dotenv reads key-value pairs from a .env file
- json
- built-in package which can be used to work with JSON data.
- logging
- provides a flexible framework for writing log messages
- time
- provides functions for handling time-related tasks (Only used the sleep method)
- grovepi
Embedded Device
About the Device
I am using the M5GO, powered by the ESP32 chip, which is an embedded device designed for IoT applications, capable of collecting data from APIs. It features a dual-core processor, Wi-Fi connectivity, and 16M flash memory.
How to Use It
This guide assumes that you have PlatformIO installed and set up on your device. If you do not have PlatformIO installed, please visit the PlatformIO installation guide before proceeding.
Step 1: Cloning the Repository
- Open your terminal or command line interface.
- Navigate to the directory where you want to clone the repository.
- Run the following command to clone the
embedded_device
repository:git clone git@github.com:mart337i/embedded_device.git
Step 2: Opening the Project in PlatformIO
- Launch PlatformIO from your system’s application menu or command line.
- In PlatformIO, go to ‘File’ > ‘Open Project’.
- Navigate to the directory where you cloned the repository and open it.
Step 3: Uploading the Code to the Device
- Connect your device to your computer via USB.
1.1 If you like me run into permission error on the serial port run
sudo chown <yourname> /dev/<usb port name like : ttyUSB0>
- Select the correct serial port and board configuration in PlatformIO.
- Press the ‘Upload’ button, represented by a right arrow (->), located in the PlatformIO toolbar (Image below).
After completing these steps, the code should be uploaded to your device, and it should be running the program.
The M5GO will automaticly restart and run the code you pushed to it.
Tools and Frameworks
- Platform
.ini
file[env:m5stack-core-esp32] platform = espressif32 board = m5stack-core-esp32 framework = arduino lib_deps = M5Stack ArduinoJson time m5stack/M5Unit-ENV@^0.0.8 adafruit/Adafruit NeoPixel@^1.11.0
Server Configuration
Server configurations
Nginx Base Configuration
# Base config with changes
user pi;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
large_client_header_buffers 8 16k;
client_header_timeout 32;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Custom Nginx Configuration
upstream fastapi_app_server {
server unix:/home/pi/code/fastapi-nginx-gunicorn/run/gunicorn.sock fail_timeout=0;
}
upstream dashboard {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80;
server_name meo.local;
keepalive_timeout 5;
client_max_body_size 8k;
access_log /home/pi/code/fastapi-nginx-gunicorn/logs/nginx-access.log;
error_log /home/pi/code/fastapi-nginx-gunicorn/logs/nginx-error.log;
# Route traffic to FastAPI for /api URLs
location /api/ {
proxy_pass http://fastapi_app_server/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
# Route all other traffic to Dashboard
location / {
proxy_pass http://dashboard/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
}
Gunicorn Configuration
#!/bin/bash
# fastapi-app is used by the main api to startup
NAME=fastapi-app
DIR=/home/pi/code/fastapi-nginx-gunicorn
USER=pi
GROUP=pi
WORKERS=3
WORKER_CLASS=uvicorn.workers.UvicornWorker
VENV=$DIR/.venv/bin/activate
BIND=unix:$DIR/run/gunicorn.sock
LOG_LEVEL=info
cd $DIR
source $VENV
exec gunicorn main:app
-----------New File ----------------
#!/bin/bash
# This is used to start the dashboard app
NAME=dashboard
DIR=/home/pi/code/dashboard_fastapi-nginx-gunicorn
USER=pi
GROUP=pi
WORKERS=3
WORKER_CLASS=uvicorn.workers.UvicornWorker
VENV=$DIR/.venv/bin/activate
BIND=127.0.0.1:8000
LOG_LEVEL=info
cd $DIR
source $VENV
exec gunicorn main:app \
--name $NAME \
--workers $WORKERS \
--worker-class $WORKER_CLASS \
--user=$USER \
--group=$GROUP \
--bind=$BIND \
--log-level=$LOG_LEVEL \
--log-file=-
Supervisor
#Fastapi app / main API : this calles the gunicorn_start bash script
[program:fastapi-app]
command=/home/pi/code/fastapi-nginx-gunicorn/gunicorn_start
user=pi
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/pi/code/fastapi-nginx-gunicorn/logs/gunicorn-error.log
-----------New File ----------------
#Dashboard : this calles the gunicorn_start bash script
[program:dashboard]
command=/home/pi/code/dashboard_fastapi-nginx-gunicorn/gunicorn_start
user=pi
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/pi/code/dashboard_fastapi-nginx-gunicorn/logs/gunicorn-error.log
Firewall settings
The ports specified below are required open:
- OpenSSH: This rule allows remote logins via the SSH (Secure Shell) protocol, which by default uses port 22. This is essential for securely managing the server remotely.
- 22: This explicitly allows traffic on port 22, which is the default port for SSH connections.
- 80: This opens up port 80, which is the default port used for HTTP traffic.
- 443: This port is for HTTPS traffic, which is HTTP over TLS/SSL, providing secure web browsing (i chould close this becouse i dont use https, but i am keeping it open for further development).
The rules are duplicated for IPv6 traffic (indicated by (v6)
), ensuring the server accepts connections via both IPv4 and IPv6 protocols on these ports.
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
22 ALLOW Anywhere
80 ALLOW Anywhere
443 ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
22 (v6) ALLOW Anywhere (v6)
80 (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
Code of conduct
File structure
/home/pi/code
| dashboard
| fastapi (Main api)
| Nginx_log (Error and access log for both)
|
/etc/nginx
| nginx.conf (Base config)
| sites-enabled
| fastapi-app.conf (Custom config)
|
/etc/supervisor/conf.d
dashboard.conf (Dashboard config)
fastapi-app.conf (Fastapi config)
Git guidelines
Not all of the following has been used but is still consideres part of the Code conduct.
[FIX] for bug fixes: mostly used in stable version but also valid if you are fixing a recent bug in development version;
[REF] for refactoring: when a feature is heavily rewritten;
[ADD] for adding new modules;
[REM] for removing resources: removing dead code, removing views, removing modules, …;
[REV] for reverting commits: if a commit causes issues or is not wanted reverting it is done using this tag;
[MOV] for moving files: use git move and do not change content of moved file otherwise Git may loose track and history of the file; also used when moving code from one file to another;
[IMP] for improvements: most of the changes done in development version are incremental improvements not related to another tag;
[MERGE] for merge commits: used in forward port of bug fixes but also as main commit for feature involving several separated commits;
[I18N] for changes in translation files;
Still missing overall
- Fail2ban
- This service is a well known security mesure that would help combat SSH Brute-force attacks. It work be scanning files like
/var/log/auth.log
and bans IP addresses conducting too many failed login attempts
- This service is a well known security mesure that would help combat SSH Brute-force attacks. It work be scanning files like
- JSON Web Token (JWT) or another token based api security
- Using JWT token would help restrict accses to the api endpoint, but would also require client to be able to authenticate itself via a endpoint located on the main api.
- SSL Cert
- Using Secure Sockets Layer (SSL) certificate would enable tcp encryption and therefore make man-in-middle attacks harder to accomplice.
- Auth0 (Maybe over engineering)
- Auth0 is an identity platform to add authentication and authorization. Auth0 would enable users to authenticate themself at diffrent levels of the application. depending on futere development this chould be setup with Nginx to lock down the network the application oprates at. It also provides crossplatform authentication.
- Hide nginx version i network tab
- This is a commen security messure and is done to make it harder to now what exploits the nginx server is vulnerable. A good example of how easy it is find, here is a link with a list of known vulnerabilities
- Block accses to doc files
- This is done to not expose api endpoints for an attacker to find and exploit
Conclusion
This project has personally given me knowlegde about how to set up production ready api’s and server set-ups. While there is still a lot to be done to make it acceptiable to be used in enterprise level production, i feel many steps ahead of then i first started the project.
I have gained knowlegde in the usage of unix based systems and look forward to learn more about the capability of linux. I have also gained knowlegede about how Iot devices can be used to help automate and control devices around us, making our work ligher and, or easier.
Glossary
- API (Application Programming Interface): A set of rules that allows programs to interact with each other, facilitating data exchange and integration.
- Client: The requesting program in a client/server relationship; typically, a user interface that allows interaction with the server.
- Dashboard: A user interface that organizes and presents information in an easy to read and change values.
- ER (Entity-Relationship) Diagram: A diagram that shows the relationships of entity sets stored in a database.
- Gunicorn: A Python WSGI HTTP Server for UNIX systems, designed to serve fast web applications.
- IoT (Internet of Things): The interconnection of computing devices embedded in everyday objects, enabling them to send and receive data.
- Linux: An open-source operating system that is the foundation of many servers and embedded systems.
- MariaDB: A community-developed fork of the MySQL relational database management system, intended to remain free under the GNU GPL.
- Monorepo: A software development strategy where code for many projects is stored in the same repository.
- Manyrepo: A software development strategy where code for many projects is stored in many diffrent repositories to keep the project organized.
- NGINX: A web server that can also be used as a reverse proxy, load balancer, mail proxy, and HTTP cache.
- Raspberry Pi OS Lite: A lightweight version of the Raspberry Pi operating system without a graphical user interface.
- SSH (Secure Shell): A cryptographic network protocol for secure data communication, remote command-line login, remote command execution, and other secure network services between two networked computers.
- Supervisor: A client/server system that allows users to monitor and control a number of processes on UNIX-like operating systems.
- SystemD: A system and service manager for Linux operating systems that uses units to manage system resources.
- Uvicorn: A lightning-fast ASGI server for Python, used as the foundation for frameworks like FastAPI.