Podczas kwarantanny zaproponowano mi udział w opracowaniu urządzenia do pomiaru prędkości modemów LTE dla kilku operatorów sieci komórkowych.
Klient chciał ocenić prędkość różnych operatorów w różnych punktach geograficznych, aby zrozumieć, który operator sieci komórkowej będzie dla niego najoptymalniejszy przy instalacji sprzętu wykorzystującego połączenie LTE, na przykład do transmisji wideo. Przy tym zadanie miało być rozwiązane jak najprościej i najtaniej, bez kosztownego sprzętu.
Od razu powiem, że zadanie nie jest najprostsze i jest praco- oraz naukochłonne, opowiem, z jakimi problemami się spotkałem i jak je rozwiązałem. No to jedziemy.
Uwaga
Pomiar prędkości połączenia LTE to dość skomplikowana kwestia: należy prawidłowo wybrać sprzęt i metodologię pomiaru, również dobrze rozumieć topologię i działanie sieci komórkowej. Dodatkowo na prędkość może wpływać kilka czynników: liczba abonentów na stacji, warunki atmosferyczne, a nawet z komórki do komórki prędkość może znacznie się różnić z powodu topologii sieci. W skrócie, to zadanie z ogromną ilością niewiadomych, a prawidłowo rozwiązać je może tylko operator sieci.
Początkowo klient chciał po prostu wysłać kuriera z telefonami różnych operatorów, dokonywać pomiarów bezpośrednio na telefonie, a następnie zapisywać wyniki pomiarów prędkości w zeszycie. Moje rozwiązanie pomiaru prędkości sieci LTE, choć nie idealne, rozwiązuje postawione zadanie.
Z powodu braku czasu podejmowałem decyzje nie na korzyść wygody czy praktyczności, ale na korzyść szybkości opracowania. Na przykład, do zdalnego dostępu uruchamiano przychodzący ssh, zamiast bardziej praktycznego vpn, w celu zaoszczędzenia czasu na konfigurację serwera i każdego pojedynczego klienta.
Zadanie techniczne
Jak napisano w artykule : Nie pracuj bez specyfikacji! Nigdy, nigdzie!
Specyfikacja techniczna była dość prosta, nieco ją rozszerzę dla zrozumienia końcowego użytkownika. Wybór rozwiązań technicznych i sprzętu został narzucony przez klienta. Oto sama specyfikacja, po wszystkich uzgodnieniach:
Na bazie jednopłytowego komputera vim2 stworzyć tester prędkości połączenia LTE za pomocą modemów Huawei e3372h — 153 kilku operatorów sieci (od jednego do n). Należy również uzyskać współrzędne z odbiornika GPS podłączonego przez UART. Pomiar prędkości należy przeprowadzać za pomocą usługi i zestawić je w tabeli w formacie:
Tabela w formacie csv. Następnie co 6 godzin wysyłać tę tabelę na e-mail. W przypadku wystąpienia błędów migająca dioda LED, która jest podłączona do GPIO.
Specyfikację opisałem w swobodnej formie, po wielu ustaleniach. Jednak sens zadania jest już widoczny. Termin na wszystko wynosił tydzień, ale w rzeczywistości wydłużył się do trzech tygodni. To biorąc pod uwagę, że robiłem to tylko po pracy i w weekendy.
Chcę jeszcze raz zwrócić uwagę, że klient wcześniej ustalił użycie usługi pomiaru prędkości oraz sprzętu, co znacząco ograniczyło moje możliwości. Budżet był również ograniczony, więc nic szczególnego nie kupiłem. Musiałem więc grać według tych zasad.
Architektura i rozwój
Schemat jest prosty i oczywisty. Dlatego zostawię go bez szczególnych komentarzy.

Cały projekt postanowiłem zrealizować w Pythonie, mimo że nie miałem żadnego doświadczenia w programowaniu w tym języku. Wybrałem go, ponieważ było mnóstwo gotowych przykładów i rozwiązań, które mogły przyspieszyć rozwój. Dlatego proszę wszystkich profesjonalnych programistów, żeby nie krytykowali mojego pierwszego doświadczenia w programowaniu w Pythonie, i zawsze z chęcią wysłucham konstruktywnej krytyki, aby poprawić swoje umiejętności.
Podczas prac odkryłem również, że Python ma dwie popularne wersje 2 i 3, więc zdecydowałem się na wersję trzecią.
Węzły sprzętowe
Jednopłytowiec vim2
Jako główną maszynę otrzymałem jednopłytowiec

Świetny, potężny media combine do inteligentnego domu i SMART-TV, ale rzadko odpowiedni do tego zadania, czy powiedzmy, słabo dostosowany. Na przykład, jego głównym systemem operacyjnym jest Android, a Linux jest systemem pomocniczym, w związku z czym nikt nie gwarantuje jakości działania wszystkich komponentów i sterowników pod Linuxem. Przypuszczam, że część problemów była związana ze sterownikami USB tej platformy, dlatego modemy nie działały prawidłowo na tej płycie, jak się spodziewałem. Ma także bardzo złą i fragmentaryczną dokumentację, więc każda operacja zajmowała dużo czasu na przeszukiwanie materiałów. Nawet zwykła praca z GPIO była uciążliwa. Na przykład, aby skonfigurować działanie diody LED, potrzebowałem kilku godzin. Ale, aby być obiektywnym, zasadniczo nie miało znaczenia, jaki to był komputer jednopłytkowy, ważne, że działał i miał porty USB.
Na początek muszę zainstalować Linuxa na tej płycie. Aby nie błądzić po zakamarkach dokumentacji oraz dla tych, którzy będą się zajmować tym komputerem jednopłytkowym, piszę ten rozdział.
Są dwa sposoby na zainstalowanie linuksa: na zewnętrznej karcie SD lub na wewnętrznej MMC. Z kartą zmagam się od wieczora i nie mogę ustalić, jak ją uruchomić, dlatego postanowiłem instalować na MMC, chociaż bez wątpienia z zewnętrzną kartą byłoby znacznie łatwiej.
O programowaniu . Tłumaczę ze strasznego na polski. Aby zaprogramować płytę, muszę podłączyć sprzętowy UART. Podłączyłem go
- Tool Pin GND: Pin17 VIM's GPIO
- Tool Pin TXD: Pin18 VIM's GPIO (Linux_Rx)
- Tool Pin RXD: Pin19 VIM's GPIO (Linux_Tx)
- Tool Pin VCC: Pin20 VIM's GPIO

Po tym, ściągnąłem oprogramowanie . Konkretna wersja oprogramowania .
Aby wgrać to oprogramowanie, potrzebuję odpowiednich narzędzi. O tym więcej szczegółowo . Nie próbowałem programować pod Windows, ale trzeba kilka słów powiedzieć o programowaniu pod Linux. Na początek zainstaluję narzędzia zgodnie z instrukcją.
git clone https://github.com/khadas/utils
cd /path/to/utils
sudo ./INSTALLIii… Nic nie działa. Spędziłem kilka godzin na edytowaniu skryptów instalacyjnych, żeby wszystko poprawnie się zainstalowało. Co tam robiłem, nie pamiętam, ale również był tam niezły cyrk z końmi. Więc bądźcie ostrożni. Ale bez tych narzędzi nie ma sensu dalej męczyć VIM2. Lepiej w ogóle się z nim nie związywać!
Po siedmi kręgach piekiel, konfiguracji skryptów i instalacji, otrzymałem pakiet działających narzędzi. Podłączyłem płytę przez USB do mojego komputera z Linuksem, a także podłączyłem UART zgodnie z powyższym schematem.
Konfiguruję mój ulubiony terminal minicom na szybkość 115200, bez sprzętowej i programowej kontroli błędów. Zaczynamy.

Podczas uruchamiania VIM2 w terminalu UART naciskam dowolny klawisz, na przykład spację, aby zatrzymać ładowanie. Po pojawieniu się linii
kvim2# Wprowadzam komendę:
kvim2# run updateNa hoście, z którego ładujemy, wykonuję:
burn-tool -v aml -b VIM2 -i VIM2_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231.imgWszystko, uff. Wgrałem, na płycie jest Linux. Login/hasło khadas:khadas.
Po tym kilka podstawowych ustawień. Na dalszą część pracy wyłączam hasło dla sudo (tak, niebezpieczne, ale wygodne).
sudo visudoEdytuję linię do postaci i zapisuję
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) NOPASSWD: ALLNastępnie zmieniam aktualną lokalizację, aby czas był według Moskwy, bo inaczej będzie według Greenwich.
sudo timedatectl set-timezone Europe/Moscowlub
ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtimeJeśli to wydaje się trudne, lepiej nie używać tej płyty, lepiej Raspberry Pi. Szczerze mówiąc.
Modem Huawei e3372h — 153
Ten modem naprawdę sprawił mi sporo kłopotów i w zasadzie stał się najsłabszym ogniwem całego projektu. W rzeczywistości, nazwa „modem” dla tych urządzeń zupełnie nie oddaje ich działania: to potężny kombajn, to urządzenie ma złożoną strukturę, która udaje CD-ROM, aby zainstalować sterowniki, a potem przechodzi w tryb karty sieciowej.
Architektonicznie, z punktu widzenia użytkownika Linuksa po wszystkich ustawieniach, wygląda to tak: po podłączeniu modemu, pojawia się interfejs sieciowy eth*, który przez DHCP otrzymuje adres IP 192.168.8.100, oraz domyślną bramę 192.168.8.1.
I najważniejszy punkt! Ten model modemu nie potrafi działać w trybie modemu, który jest zarządzany przez komendy AT.. Wszystko byłoby znacznie prostsze, gdyby stworzyć połączenia ppp dla każdego modemu i dalej nimi operować. Ale w moim przypadku „sam” (dokładniej, sterownik Linuksa zgodnie z zasadami udev), tworzy interfejs eth i przypisuje mu adres IP przez DHCP.
Aby dalej się nie gubić, proponuję zapomnieć o słowie „modem” i mówić karta sieciowa i brama, bo w rzeczywistości, to jak podłączenie nowej karty sieciowej z bramą.
Gdy jest jeden modem, nie sprawia to szczególnych problemów, ale gdy jest ich więcej niż jeden, a dokładniej n-sztuk, to pojawia się następujący obraz sieci.

To znaczy, n kart sieciowych z jednym adresem IP, każda z takim samym domyślnym bramą. Ale w praktyce każdy z nich jest podłączony do swojego operatora.
Na początku miałem proste rozwiązanie: za pomocą polecenia ifconfig lub ip wyłączać wszystkie interfejsy i po kolei włączać jeden i testować go. Rozwiązanie było dobre, z wyjątkiem tego, że w momentach przełączania nie miałem możliwości podłączenia się do urządzenia. Ponieważ przełączenia były częste i szybkie, w praktyce nie miałem szans na podłączenie.
Dlatego wybrałem drogę manualnego zmieniania adresów IP modemów i dalszego przesyłania ruchu za pomocą ustawień routingu.

Na tym moje problemy z modemami się nie skończyły: w przypadku problemów z zasilaniem, odłączały się, potrzebne było dobre stabilne zasilanie hubu USB. Rozwiązałem ten problem, lutując zasilanie bezpośrednio do huba. Inny problem, z którym się zmierzyłem, który zrujnował cały projekt: po ponownym uruchomieniu lub zimnym starcie urządzenia nie wszystkie modemy były rozpoznawane i nie zawsze, a dlaczego tak się działo i na jakiej podstawie nie udało mi się ustalić. Ale po kolei.
Aby modem działał prawidłowo, zainstalowałem pakiet usb-modeswitch.
sudo apt update
sudo apt install -y usb-modeswitch Po tym, modem po podłączeniu będzie prawidłowo rozpoznawany i konfigurowany przez subsystem udev. Sprawdzam, po prostu podłączając modem i upewniając się, że sieć się pojawiła.
Jeszcze jeden problem, którego nie mogłem rozwiązać: jak uzyskać nazwę operatora z tego modemu, z którym pracujemy? Nazwa operatora znajduje się w interfejsie webowym modemu pod adresem 192.168.8.1. To dynamiczna strona internetowa, która uzyskuje dane za pomocą zapytań ajax, więc nie można po prostu pobrać strony i wydobyć nazwy. Dlatego zacząłem badać, jak działa strona internetowa itd. i zrozumiałem, że zajmuję się jakimś bzdurami. W rezultacie poddałem się i zacząłem uzyskiwać operatora za pomocą API samego Speedtest.
Wiele byłoby prostsze, gdyby modem miał dostęp przez polecenia AT. Można by go przestawić, tworzyć połączenie ppp, przypisywać IP, uzyskiwać operatora itd. Ale niestety, pracuję z tym, co dali.
GPS
Odbiornik GPS, który mi wydano, miał interfejs UART i zasilanie. To nie było najlepsze rozwiązanie, ale mimo to działało i było proste. Odbiornik wyglądał mniej więcej tak.

Szczerze mówiąc, po raz pierwszy pracowałem z odbiornikiem GPS, ale jak się spodziewałem, wszystko zostało już dawno wymyślone. Tak więc korzystamy z gotowych rozwiązań.
Na początek włączam uart_AO_B (UART_RX_AO_B, UART_TX_AO_B) do podłączenia GPS.
khadas@Khadas:~$ sudo fdtput -t s /dtb.img /serial@c81004e0 status okayPo tym sprawdzam, czy operacja się powiodła.
khadas@Khadas:~$ fdtget /dtb.img /serial@c81004e0 status
okayWygląda na to, że to polecenie edytuje devtree w locie, co jest bardzo wygodne.
Po pomyślnym zakończeniu tej operacji restartujemy i instalujemy demona GPS.
khadas@Khadas:~$ sudo rebootInstalacja demona GPS. Instaluję wszystko i natychmiast go zatrzymuję do dalszej konfiguracji.
sudo apt install gpsd gpsd-clients -y
sudo killall gpsd
/* Zatrzymanie/wyłączenie demona GPS */
sudo systemctl stop gpsd.socket
sudo systemctl disable gpsd.socketEdytuję plik konfiguracyjny.
sudo vim /etc/default/gpsdUstawiam UART, na którym będzie podłączony GPS.
DEVICES="/dev/ttyS4"A następnie wszystko włączamy i uruchamiamy.
/* GPS daemon enable/start */
sudo systemctl enable gpsd.socket
sudo systemctl start gpsd.socketPo czym łączę GPS.

W rękach trzymam przewód GPS, pod palcami widoczne są przewody UART debuggera.
Restartuję, a następnie sprawdzam działanie GPS za pomocą programu gpsmon.

Na tym zrzucie ekranu nie widać satelitów, ale widać komunikację z odbiornikiem GPS, co oznacza, że wszystko jest w porządku.
Wypróbowałem wiele wariantów działania tego demona w Pythonie, ale zatrzymałem się na tym, który poprawnie działał z Pythonem 3.
Instaluję potrzebną bibliotekę.
sudo -H pip3 install gps3 I tworzę kod do działania.
from gps3.agps3threaded import AGPS3mechanism
...
def getPositionData(agps_thread):
counter = 0;
while True:
longitude = agps_thread.data_stream.lon
latitude = agps_thread.data_stream.lat
if latitude != 'n/a' and longitude != 'n/a':
return '{}' .format(longitude), '{}' .format(latitude)
counter = counter + 1
print ("Czekam na GPS, licznik = %d" % counter)
if counter == 10:
ErrorMessage("Błąd odbiornika GPS!!!")
return "NA", "NA"
time.sleep(1.0)
...
f __name__ == '__main__':
...
#gps
agps_thread = AGPS3mechanism() # Inicjalizacja mechanizmów AGPS3
agps_thread.stream_data() # Z localhosta (), lub innych hostów, na przykład, (host='gps.ddns.net')
agps_thread.run_thread() # Czas spowolnienia po pustym zapytaniu, domyślnie '()' 0.2 dwie dziesiąte sekundy
Jeśli potrzebuję uzyskać współrzędne, robi się to następującym wywołaniem:
longitude, latitude = getPositionData(agps_thread)
I within 1-10 seconds will either receive a coordinate or not. Yes, I had ten attempts to get the coordinates. It's not optimal, it's clumsy, but it works. I decided to do it this way because GPS can catch poorly and not always receive data. If I wait for the data to be received, the program will hang in that place when working in a dead-end room. Therefore, I implemented such an inelegant option.
In principle, if there had been more time, I could have directly received data from GPS via UART, parsed it in a separate thread, and worked with it. But there was no time at all, hence the ugly code. And yes, I'm not ashamed of it.
LED
Connecting the LED was both simple and complicated at the same time. The main difficulty is that the pin number in the system does not correspond to the pin number on the board and because the documentation is poorly written. To match the hardware pin number with the pin number in the OS, you need to run the command:
gpio readallA table will be output showing the correspondence between the pin in the system and on the board. After that, I can manipulate the pin in the OS itself. In my case, the LED is connected to GPIOH_5.

I change the GPIO pin to output mode.
gpio -g mode 421 outI write zero.
gpio -g write 421 0I write one.
gpio -g write 421 1 
Everything lights up after writing '1'.
#gpio subsistem
def gpio_init():
os.system("gpio -g mode 421 out")
os.system("gpio -g write 421 1")
def gpio_set(val):
os.system("gpio -g write 421 %d" % val)
def error_blink():
gpio_set(0)
time.sleep(0.1)
gpio_set(1)
time.sleep(0.1)
gpio_set(0)
time.sleep(0.1)
gpio_set(1)
time.sleep(0.1)
gpio_set(0)
time.sleep(1.0)
gpio_set(1)
def good_blink():
gpio_set(1)
Now, in case of errors, I call error_blink() and the LED will blink nicely for us.
Software Modules
Speedtest API
It's a great joy that speedtest.net has its own Python API, which you can check out at .
What’s good is that there are source codes available that you can also review. How to work with this API (basic examples) can be found at .
I install the Python library with the following command.
sudo -H pip3 install speedtest-cliFor example, you can also install the speed tester directly from the repo in Ubuntu. This is the same Python application that you can then run directly from the console.
sudo apt install speedtest-cli -yAnd measure your internet speed.
speedtest-cli
Pobieranie konfiguracji speedtest.net...
Testowanie z B***** (*.*.*.*)...
Pobieranie listy serwerów speedtest.net...
Wybieranie najlepszego serwera na podstawie pingu...
Hostowane przez MTS (Moskwa) [0.12 km]: 11.8 ms
Testowanie prędkości pobierania................................................................................
Pobieranie: 7.10 Mbit/s
Testowanie prędkości wysyłania......................................................................................................
Wysyłanie: 3.86 Mbit/s
As a result, as I did, I had to delve into the source codes of this speedtest to more fully integrate them into my project. One of the key tasks is to also obtain the name of the service provider to insert it into the table.
import speedtest
from datetime import datetime
...
# Określamy konkretny serwer do testu
#6053) MaximaTelecom (Moskwa, Federacja Rosyjska)
servers = ["6053"]
# Jeśli chcesz użyć testu wielowątkowego
threads = None
s = speedtest.Speedtest()
# pobieramy nazwę operatora komórkowego
opos = '%(isp)s' % s.config['client']
s.get_servers(servers)
# pobieramy tekstowy ciąg z parametrami serwera
testserver = '%(sponsor)s (%(name)s) [%(d)0.2f km]: %(latency)s ms' % s.results.server
# test pobierania
download = s.download(threads=threads)
# test wysyłania
upload = s.upload(threads=threads)
# pobieramy wyniki
s.results.share()
# Po tym tworzy się ciąg do zapisu w pliku csv.
# pobieramy pozycję GPS
longitude, latitude = getPositionData(agps_thread)
# data i czas
curdata = datetime.now().strftime('%d.%m.%Y')
curtime = datetime.now().strftime('%H:%M:%S')
delimiter = ';'
result_string = opos + delimiter + str(curpos) + delimiter +
curdata + delimiter + curtime + delimiter + longitude + ', ' + latitude + delimiter +
str(s.results.download / 1000.0 / 1000.0) + delimiter + str(s.results.upload / 1000.0 / 1000.0) +
delimiter + str(s.results.ping) + delimiter + testserver + "n"
# tutaj następuje zapis do pliku logów
Okazuje się, że sprawa nie jest taka prosta, chociaż mogłoby się wydawać, że to proste. Początkowo parametr servers był równy [], co oznacza wybierz najlepszy serwer. W rezultacie miałem przypadkowe serwery i jak nietrudno się domyślić, niestabilną prędkość. To dość skomplikowany temat, wykorzystanie stałego serwera, jeśli tak, to statycznego lub dynamicznego, wymaga badań. Oto przykład wykresów prędkości operatora Beeline przy dynamicznym wyborze serwera testowego oraz z ustalonym, statycznym serwerem.

Wynik pomiaru prędkości przy wyborze dynamicznego serwera.

Wynik testu prędkości przy jednym ściśle wybranym serwerze.
«Skrzypienie» podczas testowania występuje tam i tam, i należy je usuwać metodami matematycznymi. Jednak przy stałym serwerze jest go trochę mniej i amplituda jest bardziej stabilna.
Ogólnie to miejsce do poważnych badań. I wykonałbym pomiary prędkości do swojego serwera za pomocą narzędzia iperf. Ale trzymamy się wymagań technicznych.
Wysyłanie poczty i błędy
Dla wysyłania poczty próbowałem różnych opcji, ale w rezultacie zatrzymałem się na następującym. Zarejestrowałem skrzynkę pocztową na Yandexie i wziąłem . Sprawdziłem go i wdrożyłem w programie. W tym przykładzie rozpatrywane są różne opcje, w tym wysyłanie z Gmaila itd. Nie chciałem i nie miałem czasu na uruchamianie swojego serwera pocztowego, ale jak się później okazało, to również niepotrzebne.
Wysyłanie logów odbywało się za pomocą harmonogramu, przy dostępności połączenia, co 6 godzin: o 00:00, 06:00, 12:00 i 18:00. Wysyłałem w następujący sposób.
from send_email import *
...
message_log = "Logi testowe płytki nr 1"
EmailForSend = ["dlinyj@trololo.ru", "pupkin@trololo.ru"]
files = ["/home/khadas/modems_speedtest/csv"]
...
def sendLogs():
global EmailForSend
curdata = datetime.now().strftime('%d.%m.%Y')
сurtime = datetime.now().strftime('%H:%M:%S')
try:
for addr_to in EmailForSend:
send_email(addr_to, message_log, "Logi za " + curdata + " " + сurtime, files)
except:
print("Problem z siecią podczas wysyłania maila")
return False
return True
Błędy również były początkowo wysyłane. Najpierw gromadziły się na liście, a potem również wysyłałem je za pomocą harmonogramu, przy dostępności połączenia. Jednak potem pojawiły się problemy z tym, że Yandex ma ograniczenie na liczbę wysyłanych wiadomości dziennie (to ból, smutek i upokorzenie). Ponieważ błędów mogło być ogromnie wiele nawet w ciągu minuty, musiałem zrezygnować z wysyłania błędów pocztą. Więc miejcie na uwadze tę kwestię przy automatycznej wysyłce przez usługi Yandexa.
Serwer zwrotny
Aby uzyskać dostęp do zdalnego sprzętu i mieć możliwość jego dostosowania i przekonfigurowania, potrzebny był mi zewnętrzny serwer. Ogólnie rzecz biorąc, sprawiedliwością byłoby wysyłać wszystkie dane na serwer i w interfejsie webowym budować wszystkie ładne wykresy. Ale nie wszystko naraz.
Jako VPS wybrałem . Można było wybrać najprostszy serwer. Ogólnie rzecz biorąc, do moich celów byłoby to wystarczające. Ale ponieważ nie płaciłem za serwer z własnej kieszeni, postanowiłem wziąć z małym zapasem, aby wystarczyło, jeśli będziemy rozwijać interfejs webowy, swój serwer SMTP, VPN itd. Ponadto mieć możliwość skonfigurowania bota Telegrama i nie mieć problemów z jego blokadami. Dlatego wybrałem Amsterdam i następujące parametry.

Jako sposób komunikacji z urządzeniem vim2 wybrałem połączenie ssh zwrotne i jak pokazała praktyka — nie jest to najlepszy sposób. Przy zerwaniu połączenia serwer utrzymuje port i przez pewien czas nie można się połączyć. Dlatego lepiej jednak używać innych sposobów komunikacji, takich jak VPN. W przyszłości chciałem przejść na VPN, ale nie zdążyłem.
Nie będę wchodził w szczegóły dotyczące konfiguracji zapory, ograniczenia uprawnień, wyłączania połączenia ssh dla użytkownika root i innych oczywistych ustawień VPS. Mam nadzieję, że i tak wszystko wiecie. Aby połączyć się zdalnie, tworzę nowego użytkownika na serwerze.
adduser vimsshNa naszym sprzęcie generuję klucze do połączenia ssh.
ssh-keygenI kopiuję je na nasz serwer.
ssh-copy-id vimssh@host.comNa naszym sprzęcie tworzę automatyczne połączenie zwrotne ssh przy każdym uruchomieniu.
[Unit]
Opis=Auto Reverse SSH
Wymaga=systemd-networkd-wait-online.service
Po=systemd-networkd-wait-online.service
[Service]
Użytkownik=khadas
ExecStart=\/usr\/bin\/ssh -NT -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -CD 8080 -R 8083:localhost:22 vimssh@host.com
RestartSec=5
Restart=always
[Install]
WantedBy=multi-user.target
Zwróć uwagę na port 8083: to on określa, przez który port będę się łączył przez połączenie zwrotne ssh. Dodajemy do autostartu i uruchamiamy.
sudo systemctl enable autossh.service
sudo systemctl start autossh.serviceMożna nawet sprawdzić status:
sudo systemctl status autossh.serviceTeraz, na naszym serwerze VPS, jeśli wykonam:
ssh -p 8083 khadas@localhostWtedy dostaję się do mojego testowego sprzętu. Z tego urządzenia mogę również wysyłać logi i wszelkie dane przez ssh na mój serwer, co jest bardzo wygodne.
Zbieramy wszystko w całość

Zaczynamy, przystępując do opracowania i debugowania
Uff, chyba wszystko, opisałem wszystkie węzły. Teraz nadszedł czas, aby wszystko zebrać w jedną całość. Kod można zobaczyć .
Ważny moment z kodem: Ten projekt w ten sposób „na sucho” może nie zadziałać, ponieważ był dostosowany do określonego zadania, określonej architektury. Mimo że podaję źródła, to jednak najcenniejsze rzeczy wyjaśnię tutaj, w tekście, w przeciwnym razie nic nie będzie zrozumiałe.
Na początku mam inicjalizację gps, gpio i uruchomienie oddzielnego wątku harmonogramu.
#запуск потока планировщика
pShedulerThread = threading.Thread(target=ShedulerThread, args=(1,))
pShedulerThread.start()Harmonogram jest dość prosty: sprawdza, czy nadeszła pora wysyłania wiadomości i jaki jest obecny status błędów. Jeśli wystąpił błąd, to migamy diodą LED.
#sheduler
def ShedulerThread(name):
global ready_to_send
while True:
d = datetime.today()
time_x = d.strftime('%H:%M')
if time_x in time_send_csv:
ready_to_send = True
if error_status:
error_blink()
else:
good_blink()
time.sleep(1)Najtrudniejszy moment w tym projekcie — to zachowanie połączenia zwrotnego ssh przy każdym teście. W każdym teście następuje ponowna konfiguracja bramy domyślnej i serwera dns. Ponieważ nikt nie czyta, to wiedzcie, że pociąg nie jeździ po drewnianych torach. Kto znajdzie ukryty element, temu cukierek.
W tym celu tworzę oddzielną tabelę routingu —set-mark 0x2 i regułę do przekierowania ruchu.
def InitRouteForSSH():
cmd_run("sudo iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 22 -j MARK --set-mark 0x2")
cmd_run("sudo ip rule add fwmark 0x2/0x2 lookup 102")Więcej informacji na temat tego, jak to działa, można .
Następnie przechodzę do nieskończonej pętli, w której za każdym razem otrzymuję listę podłączonych modemów (żeby sprawdzić, czy konfiguracja sieci się zmieniła).
network_list = getNetworklist()Uzyskanie listy interfejsów sieciowych jest dość proste.
def getNetworklist():
full_networklist = os.listdir('/sys/class/net/')
network_list = [x for x in full_networklist if "eth" in x and x != "eth0"]
return network_listPo uzyskaniu listy, przypisuję adresy IP wszystkim interfejsom, tak jak pokazałem na obrazku w rozdziale o modemach.
SetIpAllNetwork(network_list)
def SetIpAllNetwork(network_list):
for iface in network_list:
lastip = "%d" % (3 + network_list.index(iface))
cmd_run ("sudo ifconfig " + iface + " 192.168.8." + lastip +" up")Następnie po prostu przechodzę w pętli przez każdy interfejs. I konfiguruję każdy interfejs.
for iface in network_list:
ConfigNetwork(iface)def ConfigNetwork(iface):
#resetujemy wszystkie ustawienia
cmd_run("sudo ip route flush all")
#Ustalamy bramę domyślną
cmd_run("sudo route add default gw 192.168.8.1 " + iface)
#ustawiamy serwer dns (jest to potrzebne do działania speedtest)
cmd_run ("sudo bash -c 'echo nameserver 8.8.8.8 > /etc/resolv.conf'")Sprawdzam interfejs pod kątem działania, jeśli sieci nie ma, to generuję błędy. Jeśli sieć jest, czas działać!
Tutaj konfiguruję routowanie ssh na danym interfejsie (jeśli nie zostało to zrobione), wysyłam błędy na serwer, jeśli nadszedł czas, wysyłam logi i na koniec przeprowadzam speedtest i zapisuję logi do pliku csv.
if not NetworkAvalible():
....
#Tutaj generujemy błędy
....
else: #Jest sieć, hurra, działamy!
#Jeśli mamy problematyczny interfejs, na którym ssh, to zmieniamy go
if (sshint == lastbanint or sshint == "free"):
print("********** Setup SSH ********************")
if sshint != "free":
cmd_run("sudo ip route del default via 192.168.8.1 dev " + sshint + " table 102")
SetupReverseSSH(iface)
sshint = iface
#skoro sieć działa, to spieszmy się z wysyłką!!!
if ready_to_send:
print ("**** Ready to send!!!")
if sendLogs():
ready_to_send = False
if error_status:
SendErrors()
#i następnie testujemy prędkość i zapisujemy logi. Warto wspomnieć o funkcji konfiguracji odwrotnego ssh.
def SetupReverseSSH(iface):
cmd_run("sudo systemctl stop autossh.service")
cmd_run("sudo ip route add default via 192.168.8.1 dev " + iface + " table 102")
cmd_run("sudo systemctl start autossh.service")No i oczywiście, trzeba dodać całe to piękno do autostartu. W tym celu tworzę plik:
sudo vim /etc/systemd/system/modems_speedtest.serviceI zapisuję w nim:
[Unit]
Opis=Test prędkości modemu
Wymaga=systemd-networkd-wait-online.service
Po=systemd-networkd-wait-online.service
[Service]
Użytkownik=khadas
ExecStart=\/usr\/bin\/python3.6 \/home\/khadas\/modems_speedtest\/networks.py
RestartSec=5
Restart=always
[Install]
WantedBy=multi-user.target
Włączam autostart i uruchamiam!
sudo systemctl enable modems_speedtest.service
sudo systemctl start modems_speedtest.serviceTeraz mogę oglądać logi tego, co się dzieje za pomocą polecenia:
journalctl -u modems_speedtest.service --no-pager -fWyniki
Cóż, teraz najważniejsze, co wynika z tego wszystkiego? Przedstawię kilka wykresów, które udało mi się uchwycić w trakcie rozwoju i debugowania. Wykresy były generowane za pomocą gnuplot zgodnie z następującym skryptem.
#! /usr/bin/gnuplot -persist
set terminal postscript eps enhanced color solid
set output "Rostelecom.ps"
#set terminal png size 1024, 768
#set output "Rostelecom.png"
set datafile separator ';'
set grid xtics ytics
set xdata time
set ylabel "Speed Mb/s"
set xlabel 'Time'
set timefmt '%d.%m.%Y;%H:%M:%S'
set title "Rostelecom Speed"
plot "Rostelecom.csv" using 3:6 with lines title "Download", '' using 3:7 with lines title "Upload"
set title "Rostelecom 2 Ping"
set ylabel "Ping ms"
plot "Rostelecom.csv" using 3:8 with lines title "Ping"
Pierwsze doświadczenie dotyczyło operatora Tele2, które prowadziłem przez kilka dni.

Tutaj użyłem dynamicznego serwera pomiarowego. Pomiar prędkości działa, choć jest bardzo zmienny, jednak wciąż widać pewną średnią, którą można uzyskać poprzez filtrację danych, na przykład, przy pomocy średniej ruchomej.
Później stworzyłem jeszcze szereg wykresów dla innych operatorów. Serwer testowy w tym przypadku był już jeden, a wyniki również były bardzo interesujące.




Jak widać, temat jest bardzo szeroki do badań i przetwarzania tych danych i zdecydowanie nie starcza na kilka tygodni pracy. Ale...
Podsumowanie pracy
Praca została nagle zakończona z przyczyn niezależnych ode mnie. Jedną z słabych stron tego projektu, według mojej subiektywnej opinii, był modem, który niechętnie współpracował jednocześnie z innymi modemami i przy każdym uruchomieniu sprawiał takie numery. W tym celu istnieje ogromna ilość innych modeli modemów, które zazwyczaj mają format Mini PCI-e i są instalowane wewnątrz urządzenia, przez co łatwiej je skonfigurować. Ale to już zupełnie inna historia. Projekt był interesujący i byłem bardzo zadowolony, że mogłem w nim uczestniczyć.
Źródło: habr.com

