.~~.   .~~.
		    '. \ ' ' / .'
		     .~ .~~~..~.                       _                          _
		    : .~.'~'.~. :      ___ ___ ___ ___| |_ ___ ___ ___ _ _    ___|_|
		   ~ (   ) (   ) ~    |  _| .'|_ -| . | . | -_|  _|  _| | |  | . | |
		  ( : '~'.~.'~' : )   |_| |__,|___|  _|___|___|_| |_| |_  |  |  _|_|
		   ~ .~ (   ) ~. ~                |_|                 |___|  |_|
		    (  : '~' :  )
		     '~ .~~~. ~'
			 '~'
		

Raspberry Pi jako system powiadamiania przez SMS

Jakiś czas temu spotkała mnie dość niemiła niespodzianka, wchodzę do jednej z serwerownii, którymi się opiekuje i uderzyła mnie fala gorąca. Klimatyzacja przestała działać, zabezpieczenie nadprądowe klimatyzatora było wyłączone. Podejrzewam je jako winowajcę, mogło się przez te lata już wygrzać i stracić swoje właściwości. Nie mniej jednak udało się je załączyć i klimatyzacja na obiekcie znów zaczęła pracować. Czas, przez który klimatyzacja nie działała nie był długi, żadne z systemów nie wyłączyło się, dlatego nikt nic nie zauważył, że coś jest nie tak. Pomyślałem jednak, że potrzebuje systemu powiadania dzięki czemu będę wiedzieć, że jest problem i trzeba go rozwiązać.

Mógłbym użyć czujnika temperatury wykorzystywanego w elektronicznych projektach hobbystycznych oraz skorzystać z zakupionego jakiś czas temu modemu LTE. Na potrzeby tego rozwiązania wykorzystałem czujnik temperatury DS18B20 podobno bardzo dokładny. Wykorzystuje on magistralę 1-wire, dzięki czemu za pomocą jednego przewodu jest w stanie przesłać dane do Raspberry Pi. Innym czujnikiem jest LM35, wymaga on konwertera analogowo-cyfrowego i dużo więcej zabawy z lutownicą i programowania a moje umiejętności elektronicze nie są na najwyższym poziomie. Przy czujniku DS18B20, jeśli wszystko dobrze podłączymy to temperaturę można odczytać z pliku za pomocą zwykłego polecenia cat.

Czujnik DS18B20 ma trzy wyprowadzenia, jego zasilanie wymaga 3,3V. Jednak aby móc odczytać z niego temperaturę musimy połączyć linię danych z zasilaniem przez rezystor 4,7kΩ. Ja do połączenia wykorzystałem kabel stykowe i wcisnąłem rezystor razem z wyprowadzeniami czujnika i zakleiłem to taśmą izolacyjną z drugiej strony - ze zględu na to, że potrzebujemy pierwszego pinu (zasilania 3,3V), czwartego pinu (pin magistrali 1-wire Raspberry Pi) oraz piątego pinu (masy - GND) wewnętrznego rzędu pinów GPIO Raspberry Pi - zmontowałem sobie taką elegancką wtyczkę uzupełniając przerwę między pinami pustymi plastikowymi wtykami kabeli stykowych i wszystko zakleiłem taśmą izolacyjną, kabel stykowe również dodtakowo zaizolowałem tworząc prowizoryczną sondę.

Oczywiście przed sklejeniem wszystko złożyłem używając płytki prototypowej i przetestowałem. Instalacji RaspiOS oraz jej początkowej konfiguracji nie będę tutaj opisywał. Nie mniej jednak obsługa magistrali 1-wire jest domyśnie wyłączona i należy włączyć przy użyciu narzędzia raspi-config. Z menu wybieramy kolejno:

xf0r3m@sms-alert:~$ sudo raspi-config
5 Interfacing Options -> P7 1-Wire

Potwierdzamy włączenie interfejsu, ze względu na to, że włączyliśmy sterownik (moduł jądra), przy opuszczaniu programu, będzie on sugerować potrzebę ponownego uruchamiania. Przystajemy na tę sugestię. Po ponownym uruchomieniu urządzenia, po wydaniu polecenia lsmod, powinniśmy zobaczyć w wierszu dla modułu wire taki ciąg znaków jak: w1_therm.

xf0r3m@sms-alert:~ $ lsmod
...
wire                   40960  2 w1_gpio,w1_therm
...

Jeśli nie ma takiego ciągu znaków, oznacza to że urządzenie (czujnik temperatury) nie korzysta z tego modułu, a to oznacza że mamy problemy z komunikacją między czujnikiem a Raspberry Pi. Często powodem takiego działania jest źle dobrany rezystor. Jeśli natomiast czujnik się zgłosił, to możemy przjeść do odczytu temperatury, na ścieżce /sys/bus/w1/devices, możemy znaleźć wszystkie urządzenia podłączone do 1-wire. Magistrala ta wysyła w ramkach unikatowy identyfikator przez co można do niej podłączyć wiele urządzeń. Wspominam o tym dlatego, że katalog przenaczony dla naszego czujnika jest opisany właśnie tym identyfikatorem:

xf0r3m@sms-alert:~ $ ls /sys/bus/w1/devices/
28-0316850291ff  w1_bus_master1

Katalog 28-0316850291ff jest katalogiem naszego czujnika, z którego możemy możemy odczytać temperature. Starsze wersje sterowanika 1-wire umieszczały dane z ramki w pliku w1_slave i tam możemym odczytać temperaturę w drugiej linii jako wartość t.

f0r3m@sms-alert:~ $ cat /sys/bus/w1/devices/28-0316850291ff/w1_slave 
32 01 4b 46 7f ff 0c 10 8f : crc=8f YES
32 01 4b 46 7f ff 0c 10 8f t=19125

Ta wartość, aby była przydana musi zostać wyłuskana za pomocą dodatkowych poleceń powłoki. Obecnie sterownik umieszcza czysty odczyt temperatury w oddzielnym pliku.

xf0r3m@sms-alert:~ $ cat /sys/bus/w1/devices/28-0316850291ff/temperature 
18937

Temperatura podawana jest z dokładnością do części tysięcznej stopnia celsjusza, aby otrzymać lepiej czytelny dla nas wynik możemy podzielić wartość zwracaną z czujnika przez 1000, przy użyciu narzędzia bc, koniecznie z właczoną biblioteką zmiennoprzecinkową dodaną opcją -l.

xf0r3m@sms-alert:~ $ echo "$(cat /sys/bus/w1/devices/28-0316850291ff/temperature) / 1000" | bc -l | cut -c 1-6
19.000

Teraz wiedząc jak odczytwać temperaturę, chciałbym ją przesłać do siebie najlepiej z jakimś komunikatem. Oczywistym sposobem przesłanie takich danych może być poczta elektronicza. Jak ze względu na ilość wiadomości jakie już przychodzą na moje skrzynki od różnych systemów, powiadomienia z poczty mam wyłączone, a one i tak mogą zagubić się między innymi wiadomościami. Skuteczne w tym przypadku wydają się wiadomości SMS. Do tego jednak potrzebujemy urządzenia oraz oprogramowania do obsługi sieci komórkowej. Oczywiście tutaj mogłem skorzystać z modułu GSM za parę groszy, ale wolałem skorzystać modemu 4G Huawei. Korzystając z modemu cały backend komunikacji szeregowej z modemem spoczywa na opgramowaniu a nie na mnie (sam bym musiał to pisać, korzystając z poleceń AT) gratis mam połączenie internetowe z dobrą przepustowością.

Do korzystania z modemu LTE, wykorzystamy oprogramowanie ModemManager, które jest już zainstalowane na naszym Raspberry. Po podłączeniu modemu do portu USB, należy wydać następuje polecenie, aby zobaczyć czy nasz modem jest wykrywalny przez ModemManager.

$ mmcli --list-modems
  /org/freedesktop/ModemManager1/Modem/0 [huawei] E3272

Aby dowiedzieć się wiecej na temat samego połączenia GSM lub czy np. nie trzeba aktywować modemu lub podać numeru PIN, możemy wydać następujące polecenie:

$ mmcli -m 0

To polecenie zwróci nam informacje nt. połączenia GSM. 0 jest indeksem, dzieki któremu możemy odwołać się do modemu. Uzyskujemy go z poprzedniego polecenia, jest to ostatni element ścieżki zwróconej przez polecenie. Obecne karty SIM, szczególnie te prepaidowe, nie wymagają kodu PIN. Ale w przypadku kart abonamentowych czy telemetrycznych ten kod może być wymagany. Wówczas takie połączenie będzie niezarejestrowane u operatora. Aby odblokować kartę sim należy wydać poniższe polecenie.

$ mmcli -m 0 | grep SIM
SIM | path: '/org/freedesktop/ModemManager1/SIM/0'
$ mmcli -i 0 --pin=1111
successfully sent PIN code to the SIM

Na początku musimy pobrać indeks karty SIM, następnie odwołując się po przez ten indeks, odblokowujemy kartę SIM podając kod PIN. Tak odblokowana karta powinna się zarejestrować u operatora w ten sposób uzyskaliśmy połączenie GSM. Inna przydatną umiejętnością poza samym wysyłaniem SMS będzie także ich odczyt. Na początku musimy wyświetlić listę otrzymanych SMS, aby poznać ich indeksy.

$ mmcli -m 0 --messaging-list-sms
    /org/freedesktop/ModemManager1/SMS/1 (received)
    /org/freedesktop/ModemManager1/SMS/0 (received)

Odczyt wiadomości SMS, realizujemy poprzez poniższe polecenie.

$ mmcli -m 0 --sms 0

To polecenie wyświetli nam szczegóły wiadomości wraz z jej treścia.

Teraz możemy przjeść do wysyłania wiadomości. Pierwszą czynnością jest jej utworzenie. Niestety nie jest to zbyt wygodne, należy jednak pamiętać, że klasyczny SMS ma ograniczenie do 160 znaków, po przekroczeniu tej wartości wiadomość jest dzielona. Nie mniej jednak nie testowałem tego w przypadku modemu. Tak zachowują się telefony. Aby wysłać SMS należy wydać następujące polecenie:

$ mmcli -m 2 --messaging-create-sms="text='Hello, World!',number='+1234567890'" 
  Successfully created new SMS: /org/freedesktop/ModemManager1/SMS/5

Tak utworzonej wiadomości został nadany indeks 5, odwołując się do wiadomości za pomocą indeksu wyślemy ją do odbiorcy.

$ mmcli -s 5 --send

Tutaj nie podajemy nawet modemu, ponieważ wszystko jest już zapisane w wiadomości. Wracając jeszcze do poprzedniego polecenia, zwróćmy uwagę w jaki sposób zapisana jest treść wiadomości. Taki sposób nie jest zbyt wygodny dlatego też możemy wysłać za pomocą SMS zawartość pliku, który przekażemy za pomocą ścieżki.

$ mmcli -m 2 --messaging-create-sms="number='+1234567890'" --messaging-create-sms-with-data=temp.txt
Successfully created new SMS: /org/freedesktop/ModemManager1/SMS/6
$ mmcli -s 6 --send
successfully sent the SMS

Teraz już wiemy w jaki sposób możemy wysłać SMS. Dodatkowo w prosty sposób możemy wykorzystać nasz modem do dodatkowego połączenia z internetem. Wystarczy jedno polecenie, jeśli przyjrzeliśmy się szczegółom dotyczącym modemu, to tam znajduje się również nazwa interfejsu jaki został utworzony w systemie właśnie na potrzeby połączenia z internetem. W przypadku Raspberry Pi jest to wwan0. Jeśli połączenie zostanie zestawione wówczas należy pobrać od operatora adres IP i tak podłączony modem będzie zapeniać dostęp do internetu.

$ mmcli -m 2 --simple-connect="apn=plus"
successfully connected the modem
$ sudo dhclient wwan0

Polecenie do połączenia internetowego (w przypadku LTE), wymaga nazwy APN, możemy ją znaleźć w recznej konfiguracji transmisji danych dla telefonów komórkowych na stronie swojego operatora. Teraz wiedząc jak odczytywać temperaturę oraz jak wysłać SMS napisałem dwa skrypty, jeden z nich jest uruchamiany przez cron co 5 minut i bada temperaturę jeśli przekroczy ustawioną wartość wówczas uruchomi ten drugi skrypt który wyśle SMS z temperaturą do określonego odbiorcy. Poniżej znajduje się skrypt monitorujący temperature:

#!/bin/bash

MAX_TEMP=

currentTemp=$(cat /sys/bus/w1/devices/28-0316850291ff/temperature);
if [ $currentTemp -ge $MAX_TEMP ]; then
  /usr/local/bin/send-temperature-alert > /dev/null 2>&1;
fi

Skrypt wysyłający powiadomienie SMS:

#!/bin/bash

if [ -f /tmp/temp.txt ]; then
  sudo rm /tmp/temp.txt;
fi

echo -e "Uwaga! W serwerownii temperatura powietrza wynosi: \
$(echo "$(cat /sys/bus/w1/devices/28-0316850291ff/temperature) / 1000" \
| bc -l \
| cut -c 1-6) oC.\nTa wiadomosc zostala wygenerowana automatycznie prosze na nia nie odpowiadac."\
| sudo tee /tmp/temp.txt > /dev/null 2>&;

RECV=""
sudo mmcli -s $(basename \
$(sudo mmcli -m 0 --messaging-create-sms="number=\"${RECV}\"" \
--messaging-create-sms-with-data=/tmp/temp.txt | cut -d ":" -f 2)) --send;

Źródła:

  1. Podłączenie DS18B20 do Raspberry Pi
  2. How to read text messages from USB modem
  3. mmcli - manpage

~xf0r3m