logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808

p.kaczmarek2 25 Lis 2023 22:13 951 3
  • DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Dzisiaj przedstawię najprostszą wersję DIY kamery internetowej opartej o płytkę M1S Dock. Pokażę jak można skompilować demo przesyłania obrazu JPG z kamery przez TCP do serwera w Pythonie oraz przeanalizuję jego działanie, ze szczególnym naciskiem na komunikację pomiędzy dwoma rdzeniami BL808. Następnie przerobię je tak, by działało w trybie serwera HTTP, czyli by można było podejrzeć obraz z kamery wchodząc na prostą stronkę internetową hostowaną na samym M1S Dock.

    Powiązany temat:
    Pierwsze starcie z modułem SiPEED M1S DOCK - AI+IoT RTOS_Linux All-Round Module

    Wstęp
    Zacząć trzeba od tego, że BL808 zawiera w sobie dwa osobne procesory o architekturze RISC, jeden 32-bitowy, a drugi aż 64-bitowy, których szczegółowe parametry prezentuję poniżej:
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Widzimy tutaj, że jeden rdzeń odpowiada multimedia, a drugi za komunikację bezprzewodową:
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    W SDK M1s_BL808 panuje następujące nazewnictwo
    - c906 - to rdzeń D0 (multimedia)
    - e907 - to rdzeń M0 (komunikacja)
    Jego znajomość ułatwi nam poruszanie się po SDK od M1S.

    Kompilacja przykładów i wgrywanie
    Przykłady udało mi się skompilować na Ubuntu. W oficjalnej dokumentacji m1s jest naprawdę szczegółowa instrukcja opisująca cały ten proces krok po kroku, nie ma sensu jej tutaj przepisywać:
    https://wiki.sipeed.com/hardware/en/maix/m1s/other/start.html#SDK-Compile
    Pod c906 mamy tam całą gamę przykładów, m. in:
    - hello_world - najprostszy program wyświetlający komunikat przez UART
    - gpio_demo - demonstracja operacji na GPIO
    - lvgl_demo - przykłady i benchmark operacji graficznych
    - image_processing_demo - przykład przetwarzania obrazów
    - pwm_demo - generowanie sygnału PWM
    - camera_streaming_through_wifi - streamowanie obrazu z kamerki do serwera przez TCP (serwer napisany jest w Pythonie, klientem jest BL808)

    Pod e907 jest jeden wsad, nazwany po prostu firmware.

    Spróbujmy uruchomić c906/camera_streaming_through_wifi . Kompilujemy oba wsady, czyli c906/camera_streaming_through_wifi oraz z e908/firmware.
    Wgrywamy oba przez UART, a dokładniej przez BLDevCube:
    https://wiki.sipeed.com/hardware/en/maix/m1s/other/start.html#SDK-Compile
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    W BLDevCube należy ustawić:
    - tablicę partycji (z SDK M1S)
    - boot2 (najnowszy boot2 z BLDevCube, wersja debug)
    - firmware - to jest wsad na rdzeń e907
    - d0fw - to jest wsad na rdzeń c906
    Dodatkowo trzeba zresetować płytkę trzymając przycisk BOOT, ale wszystko jest w zalinkowanej dokumentacji.
    U mnie wgrywanie nie chciało działać na jednej z maszyn, nie wiem czemu, na drugim laptopie ruszyło, a oba są z Windows 10.
    Teraz można by już odpalić program, ale najpierw przeanalizujmy jego działanie...

    Przykład kamerki internetowej - wysyłanie obrazów JPG do serwera w Pythonie
    Zatem otwórzmy kod źródłowy programu wysyłającego obraz z kamery. Ten program uruchamia na BL808 klienta TCP, który wysyła obrazy JPG do serwera napisanego w Pythonie.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Hasło WiFi i IP serwera należy zmienić przed kompilacją.
    Zaskoczeni? Tu naprawdę nie ma dużo kodu. Cała magia powiązana jest z m1s_xram_wifi_init, czyli systemem komunikacji przez XRAM.
    Wyszukiwarka kodu na Githubie może nam pomóc zrozumieć co się tu dzieje:
    https://github.com/search?q=repo%3Asipeed%2FM1s_BL808_SDK+m1s_xram_wifi_connect&type=code
    Wyszukiwana metoda znajduje się w M1s_BL808_SDK/components/sipeed/c906/m1s_c906_xram/src
    /m1s_c906_xram_wifi.c
    :
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Wypełnia ona strukturę reprezentującą operację WiFi:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Następnie przekazuje ją dalej:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższa funkcja pozwala na dostęp do zasobu współdzielonego między rdzeniami, czyli właśnie do sekcji XRAM. Wszystkiego pilnuje mutex (skrót od mutual exclusion), czyli system blokad, który zapewnia, że tylko jeden wątek na raz może uzyskać dostęp do zasobu XRAM. Jest to istotne w kontekście wielowątkowego programowania, ponieważ bez odpowiedniej synchronizacji dostępu do zasobów współdzielonych mogą pojawić się błędy i nieprzewidywalne wyniki działania programu.
    Następnie, gdy jest już pewność, że tylko my dobieramy się do pamięci, zapisywane do tej są bajty struktury reprezentującej komendę poprzez XRAMRingWrite.
    Co dalej z tą komendą?
    Wyszukajmy po jej kodzie - XRAM_WIFI_CONNECT:
    https://github.com/search?q=repo%3Asipeed%2FM1s_BL808_SDK%20XRAM_WIFI_CONNECT&type=code
    Wcześniej operowaliśmy na rdzeniu c906, teraz patrzymy na e907.
    Poniżej widzimy funkcję, która odczytuje operację ze współdzielonej pamięci RAM oraz odpowiednio wywołuje jej obsługę:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Przykładowo, dla XRAM_WIFI_CONNECT, wywoływana jest funkcja xram_wifi_connect:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższa funkcja realizuje dwie operacje. Najpierw wywołuje połączenie się przez WiFi, czyli funkcję wifi_connect. Następnie zapisuje ona odpowiedź do współdzielonej pamięci RAM, by drugi rdzeń mógł poznać ewentualne rezultaty tego działania.

    Analogicznie obsługiwana jest komenda XRAM_WIFI_UPLOAD_STREAM:
    https://github.com/search?q=repo%3Asipeed%2FM...8_SDK%20XRAM_WIFI_UPLOAD_STREAM&type=code
    Poniżej kod źródłowy już samej implementacji wysyłania obrazów:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Widzimy tutaj, że obrazy przesyłane są przez TCP jako pliki JPG, ale o tym więcej za chwilę.

    W ten sposób realizowana jest komunikacja pomiędzy rdzeniami. Warto zapoznać się z pełnym kodem obu "stron" xram dostępnym w folderach poniżej:
    https://github.com/sipeed/M1s_BL808_SDK/tree/main/components/sipeed/e907/m1s_e907_xram
    https://github.com/sipeed/M1s_BL808_SDK/tree/main/components/sipeed/c906/m1s_c906_xram

    Program na PC i końcowe efekty
    Program na PC jest napisany w Pythonie, stanowi on serwer TCP czekający na połączenie. Potrzebny będzie Python 3 oraz opencv. Uruchamiamy go tak:
    
    py ./main.py
    
    Bez OpenCV otrzymamy błąd:
    
    C:\Users\Tester\Downloads\M1s_BL808_example-main\M1s_BL808_example-main\c906_app\camera_streaming_through_wifi>py ./main.py
    Traceback (most recent call last):
      File "./main.py", line 3, in <module>
        import cv2 as cv
    ModuleNotFoundError: No module named 'cv2'
    

    Zatem instalujemy opencv:
    
    pip install opencv-python
    

    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Potem program powinien się uruchomić. Warto jeszcze sprawdzić, czy w ogóle BL808 podłączyło się do naszego WiFi:
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Uruchamiamy program i sukces - odbieramy obraz przekazywany na żywo z kamerki:
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Zobaczmy skrypt Python, który za to odbieranie odpowiada:
    Kod: Python
    Zaloguj się, aby zobaczyć kod

    Powyższy program jest naprawdę bardzo prosty. Nasłuchuje czekając na połączenia TCP a potem obsługuje takie połączenie w pętli, po kolei odczytując nagłówki i dane plików JPG, które potem są wyświetlane na ekranie.


    Przerabiamy demko na serwer HTTP - część 1
    W oryginalnym przykładzie użyty jest czysty TCP, BL808 jest klientem, a maszyna z Pythonem jest serwerem. Wymaga to instalacji Pythona oraz ogólnie nie jest wygodne w użyciu.
    Z tego powodu proponuję wykonać prostą modyfikację tego przykładu tak, aby od teraz BL808 był serwerem i to HTTP, takim który jest w stanie przetworzyć GET od każdego zwyczajnego urządzenia z przeglądarką, np. z telefonu.
    Zasadniczo aby to zrobić musimy:
    - postawić prosty serwer TCP na BL808
    - potem na tym serwerze odbierać pakiety, pakiety już HTTP, czyli przetworzyć żądanie GET HTTP w jakiś prosty sposób (właściwie to jeśli mamy tylko jeden zasób na tym serwerze, to nawet nie ma co przetwarzać)
    - odpowiedzieć potem na te żądanie strumieniem bajtów nagłówek HTTP z odpowiednim content-type oraz zawartością, w tym przypadku będą to dane pliku JPG
    Najpierw zatem tworzymy własne zdarzenie, poczynając od własnej enumeracji:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Dodajemy też funkcje wysyłającą i odbierającą przez XRAM:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Oraz implementujemy jego obsługę na rdzeniu od WiFi:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższy kod tworzy serwer HTTP który nasłuchuje na porcie 80 i w pętli obsługuje klientów.
    Samą obsługę klienta przedstawia funkcja poniżej, na próbę zrobiłem wysyłanie napisu 'Hello world' przez HTTP:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powyższa funkcja nie sprawdza co wysłała przeglądarka, na ślepo odpowiada zawsze HTTP 200 OK z tekstem (content-type text) 'Hello world'. To na próbę.
    Sam task serwera HTTP podpinamy do wcześniej napisanej obsługi zdarzenia:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Potem musimy skompilować i wgrać oba wsady. Końcowo, po wejściu na adres IP kamerki otrzymamy:
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Wygląda na to, że wszystko działa.


    Własny serwer HTTP przedstawiający obraz z kamery
    Mamy już prosty serwer 'hello world', teraz wystarczy go zmodyfikować.
    Tak jak pisałem - na żądanie GET odpowiemy odsyłając plik JPG który mamy już zasadniczo gotowy w firmware, wraz z odpowiednim content-type.
    Całość jak wcześniej, w wątku, a w nim w pętli - obsługa jednego żądania po drugim.
    Poniżej pełen kod który opracowałem:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Poza przekopiowaną obsługą kamery z demka wysyłania obrazu przez TCP oraz mojego serwera HTTP z poprzedniego akapitu nie ma tu nic nowego za wyjątkiem lepszego tworzenia odpowiedzi HTTP 200 OK:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Nagłówek HTTP 200 OK musi zawierać zarówno pole Content-Type, jak i Content-Length aby przeglądarka wiedziała jak dużej liczby bajtów się spodziewać i jak je zinterpretować.
    Ten serwer HTTP jest naprawdę bardzo prymitywny. W zasadzie odbiera on tylko dane na danym porcie TCP i od razu na nie odpowiada, wcale ich nie sprawdzając. Nie parsuje nawet żądania GET, po prostu na ślepo wysyła odpowiedź o odpowiednim typie content-type, tutaj pliku jpg.
    To wystarcza, by móc pobrać obraz z kamerki:
    DIY Webcam/Aparat - M1S Dock - komunikacja pomiędzy dwoma rdzeniami BL808
    Powyższa implementacja ma jedną wadę - czasem nie pobiera całego obrazu, muszę sprawdzić czemu tak jest, może muszę dopisać sleep. Będę ją jeszcze rozwijać.
    Oczywiście tu jeszcze zostaje kwestia skąd to wywołać, ale mamy dwie opcje - albo można tym zastąpić stary kod kamerki która była w formie klienta, albo można zaimplementować osobną komendę XRAM. To już zostawiam czytelnikowi, chociaz możliwe, że spróbuję podesłać mój kod jako kolejny przykład do repozytorium B1S Dock jako pull request, jeśli go przyjmą to zaktualizuję temat.

    Podsumowanie
    Nie była to pełnoprawna kamera internetowa gdyż obrazy były wysyłane jeden po drugim (jako pliki JPG) a nie jako strumień video, ale i tak myślę, że może kogoś to zainteresuje. Ewentualne streamowanie video na tej platformie dopiero muszę odpalić, ale już teraz można by ją wykorzystać jako chociażby fotopułapka lub prosty podgląd video przez WiFi.
    Przy okazji też nauczyliśmy się jak zrealizować komunikację pomiędzy rzdzeniami na BL808, ale tu akurat to była kwestia drugorzędna.
    Po więcej szczegółów odsyłam do SDK i dokumentacji:
    https://wiki.sipeed.com/hardware/en/maix/m1s/other/start.html
    https://github.com/sipeed/M1s_BL808_example
    https://github.com/sipeed/M1s_BL808_SDK

    Fajne? Ranking DIY
    Pomogłem? Kup mi kawę.
    O autorze
    p.kaczmarek2
    Moderator Smart Home
    Offline 
  • #2 20833480
    TechEkspert
    Redaktor
    Niektóre 'pełnoprawne' kamery pozwalały na ustawienie w parametrach strumienia mjpg zamiast np. H.264 i taki mjpg podejrzewam, że działa podobnie do tego co uzyskałeś. Natomiast po zalogowaniu się na interfejs WEB kamery zwykle jest możliwość podglądu online i ten często działa jako jpg odświeżany na jednej z zakładek konfiguracji urządzenia.
  • #3 20833562
    p.kaczmarek2
    Moderator Smart Home
    Rzeczywiście, zresztą na wikipedii też można troszkę o tym poczytać, chociażby akapit "Digital Cameras" tutaj: https://en.wikipedia.org/wiki/Motion_JPEG
    Może teraz zapoznam się z GPIO tej płytki i zrobię jakąś prostą sterowaną kamerkę bądź robocika na gotowym podwoziu.
    Pomogłem? Kup mi kawę.
  • #4 20833578
    TechEkspert
    Redaktor
    Na platformach o mniejszej mocy obliczeniowej mjpeg jest możliwy do uruchomienia:
    https://github.com/arkhipenko/esp32-cam-mjpeg
    https://github.com/GCY/ESP32-CAM-MJPEG-Stream-Decoder-and-Control-Library
    wiadomo CCTV poszło w H.264/H.265 aby zaoszczędzić pasmo/HDD, kosztem specjalizowanych układów.
REKLAMA