Files
nebuleair_pro_4g/README.md
PaulVua e733cd27e8 Doc: parser Miotiq dans README + mise a jour error_flags.md
- README: ajout section parser Miotiq avec firmware version (bytes 69-71)
- error_flags.md: parser mis a jour (version_major/minor/patch + reserved 22)
- error_flags.md: correction note init bytes 66-68 a 0x00

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:46:32 +01:00

12 KiB
Executable File

nebuleair_pro_4g

Based on the Rpi4 or CM4.

Installation

Express

You can download the installation_part1.sh and run it:

wget http://gitea.aircarto.fr/PaulVua/nebuleair_pro_4g/raw/branch/main/installation_part1.sh
chmod +x installation_part1.sh
sudo ./installation_part1.sh

After reboot you can do the same with part 2.

wget http://gitea.aircarto.fr/PaulVua/nebuleair_pro_4g/raw/branch/main/installation_part2.sh
chmod +x installation_part2.sh
sudo ./installation_part2.sh

General

Line by line installation.

sudo apt update
sudo apt install git gh apache2 sqlite3 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus -y
sudo pip3 install pyserial requests RPi.GPIO adafruit-circuitpython-bme280 crcmod psutil ntplib pytz gpiozero adafruit-circuitpython-ads1x15 numpy nsrt-mk3-dev --break-system-packages
sudo mkdir -p /var/www/.ssh
sudo ssh-keygen -t rsa -b 4096 -f /var/www/.ssh/id_rsa -N ""
sudo ssh-copy-id -i /var/www/.ssh/id_rsa.pub -p 50221 airlab_server1@aircarto.fr
sudo git clone http://gitea.aircarto.fr/PaulVua/nebuleair_pro_4g.git /var/www/nebuleair_pro_4g
sudo mkdir /var/www/nebuleair_pro_4g/logs
sudo touch /var/www/nebuleair_pro_4g/logs/app.log /var/www/nebuleair_pro_4g/logs/loop.log /var/www/nebuleair_pro_4g/wifi_list.csv
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/create_db.py
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/set_config.py
sudo chmod -R 777 /var/www/nebuleair_pro_4g/
git config --global core.fileMode false
git -C /var/www/nebuleair_pro_4g config core.fileMode false
git config --global --add safe.directory /var/www/nebuleair_pro_4g
sudo crontab /var/www/nebuleair_pro_4g/cron_jobs
sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/create_db.py

Apache

Configuration of Apache to redirect to the html homepage project

sudo sed -i 's|DocumentRoot /var/www/html|DocumentRoot /var/www/nebuleair_pro_4g|' /etc/apache2/sites-available/000-default.conf
sudo systemctl reload apache2

Sudo athorization

To make things simpler we will allow all users to use "nmcli" as sudo without entering password. For that we need to open the sudoers file with sudo visudo and add this to the bottom of the file:

ALL ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/sbin/reboot
www-data ALL=(ALL) NOPASSWD: /usr/bin/git pull
www-data ALL=(ALL) NOPASSWD: /usr/bin/ssh
www-data ALL=(ALL) NOPASSWD: /usr/bin/python3 *
www-data ALL=(ALL) NOPASSWD: /bin/systemctl *
www-data ALL=(ALL) NOPASSWD: /usr/bin/pkill
www-data ALL=(ALL) NOPASSWD: /var/www/nebuleair_pro_4g/*

Serial

Need to open all the uart port by modifying sudo nano /boot/firmware/config.txt

enable_uart=1
dtoverlay=uart0
dtoverlay=uart1
dtoverlay=uart2
dtoverlay=uart3
dtoverlay=uart4
dtoverlay=uart5

And reboot !

Then we need to authorize connection over device on /etc/ttyAMA*

sudo chmod 777 /dev/ttyAMA*

I2C

Decibel meter, BME280 and the RTC module (DS3231) is connected via I2C.

Need to activate by modifying sudo nano /boot/firmware/config.txt

dtparam=i2c_arm=on

And authorize access to /dev/i2c-1.

sudo chmod 777 /dev/i2c-1

Attention: sometimes activation with config.txt do not work, you need to activate i2c with sudo raspi-config and go to "Interface" -> I2C -> enable.

It is possible to manage raspi-config only with cli: sudo raspi-config nonint do_i2c 0

I2C addresses: use sudo i2cdetect -y 1 to check the connected devices.

BME280

The python script is triggered by the main loop every minutes to get instant temp, hum and press values (no need to have a minute average). BME280 address is 0x76.

RTC module (DS3231)

Noise sensor

As noise varies a lot, we keep the C program running every seconds to create a moving average for the last 60 seconds (we also gather max and min values). To keep the script running at boot and stay on we create a systemd service.

Nois sensor address is 0x48.

Create the service with sudo nano /etc/systemd/system/sound_meter.service and add:

[Unit]
Description=Sound Meter Service
After=network.target

[Service]
ExecStart=/var/www/nebuleair_pro_4g/sound_meter/sound_meter_moving_avg
Restart=always
User=airlab
WorkingDirectory=/var/www/nebuleair_pro_4g/sound_meter

[Install]
WantedBy=multi-user.target

Then start the service:

sudo systemctl daemon-reload
sudo systemctl enable sound_meter.service

sudo systemctl start sound_meter.service

SSH Tunneling

To have a remote access to the RPI we can start a SSH tunneling at boot with the command:

ssh -p 50221 -R <device_sshTunelPort>:localhost:22 airlab_server1@aircarto.fr

To make things simpler we need to connect via a ssh key.

ssh-keygen -t rsa -b 4096

And add the key to the server with ssh-copy-id -p 50221 airlab_server1@aircarto.fr

Crontabs

Attention, authorization for uart are reinitialized after reboot. Need to add the command to sudo crontab -e

@reboot chmod 777 /dev/ttyAMA* /dev/i2c-1

And start the Hotspot check:

@reboot /var/www/nebuleair_pro_4g/boot_hotspot.sh >> /var/www/nebuleair_pro_4g/logs/app.log 2>&1

And set the base URL for Sara R4 communication:

@reboot sleep 30 && /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_setURL.py ttyAMA2 data.nebuleair.fr >> /var/www/nebuleair_pro_4g/logs/app.log 2>&1

UDP Payload Miotiq — Structure 100 bytes

Bytes Taille Nom Format Description
0-7 8 device_id ASCII Identifiant unique du capteur
8 1 signal_quality uint8 Qualite signal modem (AT+CSQ)
9 1 protocol_version uint8 Version protocole (0x01)
10-11 2 pm1 uint16 BE PM1.0 en ug/m3 (x10)
12-13 2 pm25 uint16 BE PM2.5 en ug/m3 (x10)
14-15 2 pm10 uint16 BE PM10 en ug/m3 (x10)
16-17 2 temperature int16 BE Temperature en C (x100, signe)
18-19 2 humidity uint16 BE Humidite en % (x100)
20-21 2 pressure uint16 BE Pression en hPa
22-23 2 noise_cur_leq uint16 BE Bruit LEQ en dB(A) (x10)
24-25 2 noise_cur_level uint16 BE Bruit instantane en dB(A) (x10)
26-27 2 noise_max uint16 BE Bruit max en dB(A) (x10)
28-29 2 envea_no2 uint16 BE NO2 en ppb
30-31 2 envea_h2s uint16 BE H2S en ppb
32-33 2 envea_nh3 uint16 BE NH3 en ppb
34-35 2 envea_co uint16 BE CO en ppb
36-37 2 envea_o3 uint16 BE O3 en ppb
38-39 2 npm_ch1 uint16 BE NPM canal 1 (5-channel)
40-41 2 npm_ch2 uint16 BE NPM canal 2 (5-channel)
42-43 2 npm_ch3 uint16 BE NPM canal 3 (5-channel)
44-45 2 npm_ch4 uint16 BE NPM canal 4 (5-channel)
46-47 2 npm_ch5 uint16 BE NPM canal 5 (5-channel)
48-49 2 mppt_temperature int16 BE Temperature MPPT en C (x10, signe)
50-51 2 mppt_humidity uint16 BE Humidite MPPT en % (x10)
52-53 2 battery_voltage uint16 BE Tension batterie en V (x100)
54-55 2 battery_current int16 BE Courant batterie en A (x100, signe)
56-57 2 solar_voltage uint16 BE Tension solaire en V (x100)
58-59 2 solar_power uint16 BE Puissance solaire en W
60-61 2 charger_status uint16 BE Status chargeur MPPT
62-63 2 wind_speed uint16 BE Vitesse vent en m/s (x10)
64-65 2 wind_direction uint16 BE Direction vent en degres
66 1 error_flags uint8 Erreurs systeme (voir detail)
67 1 npm_status uint8 Registre status NextPM
68 1 device_status uint8 Etat general du boitier
69 1 version_major uint8 Version firmware major
70 1 version_minor uint8 Version firmware minor
71 1 version_patch uint8 Version firmware patch
72-99 28 reserved Reserve (initialise a 0xFF)

Consommation data (UDP Miotiq uniquement)

Taille par paquet : 100 bytes payload + 8 bytes UDP header + 20 bytes IP header = 128 bytes

Toutes les 60s Toutes les 10s
Paquets/jour 1 440 8 640
Par jour ~180 KB ~1.08 MB
Par mois ~5.3 MB ~32.4 MB
Par an ~63.6 MB ~388.8 MB

Note : ces chiffres ne comptent que l'UDP vers Miotiq. Les envois HTTP (AirCarto) et HTTPS (uSpot) consomment des donnees supplementaires.

Parser Miotiq

16|device_id|string|||W
2|signal_quality|hex2dec|dB||
2|version|hex2dec|||W
4|ISO_68|hex2dec|ugm3|x/10|
4|ISO_39|hex2dec|ugm3|x/10|
4|ISO_24|hex2dec|ugm3|x/10|
4|ISO_54|hex2dec|degC|x/100|
4|ISO_55|hex2dec|%|x/100|
4|ISO_53|hex2dec|hPa||
4|noise_cur_leq|hex2dec|dB|x/10|
4|noise_cur_level|hex2dec|dB|x/10|
4|max_noise|hex2dec|dB|x/10|
4|ISO_03|hex2dec|ppb||
4|ISO_05|hex2dec|ppb||
4|ISO_21|hex2dec|ppb||
4|ISO_04|hex2dec|ppb||
4|ISO_08|hex2dec|ppb||
4|npm_ch1|hex2dec|count||
4|npm_ch2|hex2dec|count||
4|npm_ch3|hex2dec|count||
4|npm_ch4|hex2dec|count||
4|npm_ch5|hex2dec|count||
4|npm_temp|hex2dec|°C|x/10|
4|npm_humidity|hex2dec|%|x/10|
4|battery_voltage|hex2dec|V|x/100|
4|battery_current|hex2dec|A|x/100|
4|solar_voltage|hex2dec|V|x/100|
4|solar_power|hex2dec|W||
4|charger_status|hex2dec|||
4|wind_speed|hex2dec|m/s|x/10|
4|wind_direction|hex2dec|degrees||
2|error_flags|hex2dec|||
2|npm_status|hex2dec|||
2|device_status|hex2dec|||
2|version_major|hex2dec|||
2|version_minor|hex2dec|||
2|version_patch|hex2dec|||
22|reserved|skip|||

Byte 66 — error_flags

Bit Masque Description
0 0x01 RTC deconnecte
1 0x02 RTC reset (annee 2000)
2 0x04 BME280 erreur
3 0x08 NPM erreur
4 0x10 Envea erreur
5 0x20 Bruit erreur
6 0x40 MPPT erreur
7 0x80 Vent erreur

Byte 67 — npm_status

Bit Masque Description
0 0x01 Sleep mode
1 0x02 Degraded mode
2 0x04 Not ready
3 0x08 Heater error
4 0x10 THP sensor error
5 0x20 Fan error
6 0x40 Memory error
7 0x80 Laser error

Byte 68 — device_status

Bit Masque Description
0 0x01 Modem reboot au cycle precedent
1 0x02 WiFi connecte
2 0x04 Hotspot actif
3 0x08 Pas de fix GPS
4 0x10 Batterie faible
5 0x20 Disque plein
6 0x40 Erreur base SQLite
7 0x80 Boot recent (uptime < 5 min)

Notes

Wifi Hotspot (AP)

To connect the device to the internet we need to create a Hotspot using nmcli.

Command to create a AP with SSI: nebuleair_pro and PASS: nebuleaircfg:

sudo nmcli device wifi hotspot ifname wlan0 ssid nebuleair_pro password nebuleaircfg

#we also need to set IP addresses
sudo nmcli connection modify Hotspot ipv4.addresses 192.168.4.1/24
sudo nmcli connection modify Hotspot ipv4.method shared

This will create a new connection called "Hotspot" using device "wlan0". You can connect to this network via wifi and access to the self-hosted webpage on 192.168.4.1 (to get the IP ip addr show wlan0 or nmcli device show wlan0).

Only problem is that you cannot perform a wifi scan while wlan0 is in AP mode. So you need to scan the available networks just before creating the Hotspot.

Second issue: hotspot need to be lauched at startup only if it cannot connected to the selected wifi network.

Wifi connection check, wifi scan and activation of the Hotspot need to be lauched at every startup.

This can be doned with script boot_hotspot.sh.

@reboot chmod 777 /dev/ttyAMA* /dev/i2c-1
@reboot /var/www/nebuleair_pro_4g/boot_hotspot.sh

Claude Code

Instructions to use claude code on the RPI.

Install NPM

sudo apt install -y nodejs npm
node -v
npm -v

Install Claude

sudo npm install -g @anthropic-ai/claude-code

Run claude

claude