Spisu treści:

Wysyłanie danych liczbowych z jednego Arduino do drugiego: 16 kroków
Wysyłanie danych liczbowych z jednego Arduino do drugiego: 16 kroków

Wideo: Wysyłanie danych liczbowych z jednego Arduino do drugiego: 16 kroków

Wideo: Wysyłanie danych liczbowych z jednego Arduino do drugiego: 16 kroków
Wideo: Kurs Arduino odc 14: Liczby losowe 2024, Listopad
Anonim
Wysyłaj dane liczbowe z jednego Arduino do drugiego
Wysyłaj dane liczbowe z jednego Arduino do drugiego

Wstęp

David Palmer, CDIO Tech. na Uniwersytecie Aston.

Czy kiedykolwiek musiałeś przesyłać numery z jednego Arduino do drugiego? Ta instrukcja pokazuje, jak.

Możesz łatwo przetestować, jak działa, po prostu wpisując ciąg liczb do wysłania na terminalu monitora szeregowego, i zobacz, jak numery powracają na drugim monitorze szeregowym podłączonym do drugiego Arduino. Możesz nawet użyć łącza Bluetooth.

Co to robi

Dwa programy Arduino (szkice w języku Arduino) wymagają opracowania, jeden program Master do łączenia się z komputerem hosta z Arduino Serial Monitor, jeden do działania jako Slave do odbierania wiadomości szeregowej od Master, dekodowania jej i odsyłania. Slave może opcjonalnie wyświetlać liczby, z którymi ma do czynienia na monitorze szeregowym drugiego IDE - na wypadek, gdybyś chciał tego użyć. Może to przede wszystkim pomóc w sprawnym działaniu i pomóc, jeśli zdecydujesz się wprowadzić zmiany w programach, aby dopasować je do własnych wymagań.

Ekwipunek

  • 2 Arduino
  • 2 przewody USB
  • przewody krosowe (w razie potrzeby)
  • 1 komputer PC/laptop z załadowanym środowiskiem Arduino IDE (dostępny do bezpłatnego pobrania ze strony Arduino.cc)

Krok 1: Konfiguracja - najpierw skonfiguruj sprzęt

Konfiguracja - najpierw skonfiguruj swój sprzęt
Konfiguracja - najpierw skonfiguruj swój sprzęt
Konfiguracja - najpierw skonfiguruj swój sprzęt
Konfiguracja - najpierw skonfiguruj swój sprzęt

Podłącz 2 Arduino do 2 portów USB w komputerze.

Wskazówka, dobrym pomysłem jest oznaczenie ich jako M i S (master i slave), aby później nie wpaść w zamieszanie (jak pokazano na 2 zdjęciu tutaj).

Krok 2: Konfiguracja - Ustaw swój ekran

Konfiguracja - Ustaw swój ekran
Konfiguracja - Ustaw swój ekran

Najlepszą rzeczą jest skonfigurowanie ekranu tak, abyś miał

  • IDE załadowane programem Master po lewej stronie i
  • że z niewolnikiem po prawej.

Trzymaj monitory szeregowe dla Maser i Slave po lewej i prawej stronie, jak pokazano na zrzucie ekranu tutaj.

Krok 3: Skonfiguruj Master End, a następnie połącz ze sobą - część 1

Skonfiguruj Master End, a następnie połącz ze sobą - część 1
Skonfiguruj Master End, a następnie połącz ze sobą - część 1

Gdy konfigurujesz Master End Serial Monitor do wysyłania dwóch liczb, musisz zawsze używać początku i końca, znaków separatora oraz znaku separatora przecinka, jak widać tutaj.

Teraz musisz połączyć 2 Arduino razem przez port szeregowy. Odbywa się to za pomocą dwóch przewodów krosowych.

Użyłem zielonego i żółtego

  • Najpierw weź żółty, musi być podłączony do D6 w jednym Arduino i D7 w drugim
  • Następnie odwrotnie dla zielonego przewodu, D7 na pierwszym i D6 na drugim Arduino.

Alternatywnie, jeśli masz coś dostępnego, jak para modułów Bluetooth – takich jak HC-05 – będą one również działać, aby dać dokładnie taki sam efekt, jak powyższe przewody.

Krok 4: Skonfiguruj Master End, a następnie połącz ze sobą - część 2

Skonfiguruj Master End, a następnie połącz ze sobą - część 2
Skonfiguruj Master End, a następnie połącz ze sobą - część 2
Skonfiguruj Master End, a następnie połącz ze sobą - część 2
Skonfiguruj Master End, a następnie połącz ze sobą - część 2

Korzystamy z biblioteki Software Serial. Dalsze informacje są dostępne pod tym linkiem

Możesz zobaczyć to wywołane w wierszu 7 każdego z programów. Konfiguruje piny cyfrowe 7 i 6 jako TX i RX (nadawanie i odbieranie). W ten sposób dane będą przesyłane z Master Arduino przez zielony przewód do Slave, a gdy program Slave w drugim Arduino zakończy swoją pracę, z powrotem przez żółty przewód. Na dole tej samej ilustracji (w oknie Monitora szeregowego) widać, że przesyłane przez nas dane pomyślnie ominęły opisaną tutaj pętlę i wróciły do komputera jako para liczb całkowitych ładnie oddzielona.

Krok 5: Przegląd szkiców / programów - struktura programu

Przegląd szkiców / programów - struktura programu
Przegląd szkiców / programów - struktura programu
Przegląd szkiców / programów - struktura programu
Przegląd szkiców / programów - struktura programu

Układ Jak we wszystkich szkicach Arduino są 3 podstawowe części:

  • Deklaracje
  • Ustawić
  • Główna pętla

Jak to często bywa, wykorzystaliśmy tutaj czwartą sekcję, która jest dodatkiem „Funkcje”. Jeśli nie jesteś zaznajomiony z używaniem funkcji, możesz wyszukać w Google „funkcje Arduino”, a witryny z objaśnieniami, takie jak przykład, znajdziesz w tym linku: www.tutorialspoint.com/arduino/arduino_functions…..

Wykorzystaliśmy również zakładki, aby podzielić program na łatwiejsze w zarządzaniu bloki.

Trzy bloki, których użyliśmy, można zobaczyć na górze każdej ilustracji okien IDE powyżej:

  • prostyRxTx0330Master
  • pospolity
  • notatki

W rzeczywistości są to oddzielne pliki w folderze programu, jak widać w tym widoku Eksploratora Windows plików programu Slave.

Jest bardzo dobry powód, dla którego to zrobiliśmy.

  • Kiedy budowaliśmy program, zdaliśmy sobie sprawę, że większość programu dla Mistrza była taka sama jak dla Niewolnika.
  • Skończyło się na wyciągnięciu wszystkich wspólnych części do zakładki, którą nazwaliśmy „wspólne”, a następnie za każdym razem, gdy debugowaliśmy część (przetestowaliśmy ją i byliśmy zadowoleni, że działa dobrze), po prostu kopiowaliśmy i wklejaliśmy całą kartę od Master do Slave lub odwrotnie.
  • Zdarza się, że zakładki notatek są identyczne, ponieważ projekt jest ogólny.

Żadna z funkcji nie jest wywoływana z setupu, wszystkie są wywoływane z loopa, więc utworzyliśmy je po setup, ale przed pętlą.

Krok 6: Projektowanie odgórne

Dobrym pomysłem jest zaprojektowanie szkicu, zaczynając od definicji tego, co chcesz zrobić.

Kiedy już to masz, możesz zacząć robić szkice, wykonując te rzeczy. Ogólnie rzecz biorąc, jeśli jest jakiś szczegół, którego jeszcze nie wiesz, jak zrobić, po prostu ustaw go jako funkcję i zostaw tworzenie funkcji na później.

Jest to zgodne z filozofią dobrego projektowania, nauczaną na wielu uniwersytetach, zwaną CDIO (jeśli jeszcze tego nie znasz, możesz go wyszukać w Google i znaleźć strony, w których można to wyjaśnić, takie jak: https://www.cdio.org/s.) To w zasadzie mówi: Nie zaczynaj Projektu, zanim nie będziesz miał jasnego Konceptu. Nie rozpoczynaj implementacji, dopóki nie uzyskasz jasnego projektu. Nie oczekuj, że będzie działać, zanim nie uzyskasz jasnej implementacji. Najpierw C, potem D, I i O. Na każdym kolejnym etapie wykonujesz iterację (cofasz się o pętle), więc gdy będziesz zadowolony z początkowej pętli Projektu, sprawdź, czy nadal odpowiada Koncepcji, i zaktualizuj C, jeśli trzeba. I tak dalej, więc nawet jeśli masz już do pracy, idź z powrotem na górę i ponownie zobacz, jak teraz wygląda C, potem D i I, i wykonaj i sprawdź wszystko zmiany w razie potrzeby. W przypadku szkiców programowania działa to tak samo, jeśli projektujesz od góry do dołu.

Krok 7: Koncepcja i projekt - część 1

Koncepcja i projekt - część 1
Koncepcja i projekt - część 1
Koncepcja i projekt - część 1
Koncepcja i projekt - część 1

Koncepcja tutaj wygląda jak zarys wymagań określonych w zakładce „uwagi”.

Projekt może wyglądać jak wczesna wersja pętli, która pasuje do zakładki notatek i może wyglądać tak, jak widać na tym rysunku

Zobacz, jak lubię zacząć od faktycznego skopiowania komentarzy CTRL-C do nagłówka pętli, a następnie zacznij wypełniać puste miejsca poleceniami, które zrobią te rzeczy.

To faktycznie kompiluje się OK, jak widać na dole ekranu na rysunku. To sięga od etapu D CDIO do I, a gdy rozwijamy kod, dobrym pomysłem jest kontynuowanie tej pętli D-I.

Teraz pora przejść do następnego etapu, jest tam komentarz, który mówi, że: //odbierzemy coś ze sprzętowego USB, a następnie prześlemy to na programowy kanał szeregowy. Piszemy ten kod, aby tak się stało - linie od 133 do 138 pokazane tutaj żółtym wyróżnieniem

Krok 8: Koncepcja i projekt – część 2

Koncepcja i projekt - część 2
Koncepcja i projekt - część 2
Koncepcja i projekt - część 2
Koncepcja i projekt - część 2

Dwie pierwsze funkcje, które tutaj wprowadzamy, to (recv() i tran() do odbierania z portu sprzętowego i przesyłania do portu oprogramowania - stąd wywoływanie ich z wyświetlonymi parametrami 'hw' lub 'sw').

Oprócz nich dodaliśmy test na zmiennej globalnej o nazwie newData. To jest flaga, którą ustawimy wewnątrz funkcji " void recv(); ". Gdy wiadomość zostanie odebrana, zmienna ta jest oznaczona flagą z false na true. Robimy to tak, że przesyłamy wiadomość tylko wtedy, gdy została odebrana (flaga ==true) w linii 134. A kiedy już prześlemy naszą wiadomość, która jest „wykonana”, usuwamy flagę z powrotem na fałsz w linii 137.

Ponownie możemy sprawdzić kompilację (od D do I) i tym razem mamy komunikat o błędzie „nie zadeklarowany” (pokazany). To mówi nam, że nie zadeklarowaliśmy funkcji recv(); funkcjonować. Planujemy to zrobić później, więc na razie, aby umożliwić nam czystą kompilację, musimy utworzyć funkcję fikcyjną lub zastępczą, jak pokazano poniżej.

Ponownie możemy sprawdzić kompilację (od D do I) i tym razem mamy kolejny komunikat o błędzie „nie zadeklarowany” dla tran(); funkcjonować. To wymaga podobnego tworzenia kodu pośredniczącego. Ponownie możemy sprawdzić kompilację (od D do I) i tym razem stwierdzimy, że działa to doskonale; jak na razie dobrze.

Krok 9: Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie ze Slave Arduino

Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie od Slave Arduino
Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie od Slave Arduino
Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie od Slave Arduino
Zakończ główną pętlę: A) Odbieranie z USB, B) Odbieranie od Slave Arduino

Jest jeszcze jeden ostatni element, który dodaliśmy, aby zakończyć tę część, a mianowicie dodać trochę kodu debugującego.

Jest jeszcze inna instrukcja dotycząca debugowania szkiców, do której można się odnieść, aby zrozumieć, co tutaj zrobiliśmy i dlaczego. Zapoznaj się z instrukcją „Jak budować i testować szkice Arduino, dopóki nie zadziałają”

Tak więc te wiersze debugowania [136-139 pokazano] są dodawane następnie w głównej pętli i, lo i oto, można je przetestować na końcu Master, ustawiając zmienną debugowania true, i kompilując (I), a następnie, jeśli podłączasz Arduino, które możesz przesłać, otwórz monitor szeregowy i sprawdź, czy to, co wraca do monitora szeregowego, jest takie, jak pokazano tutaj (czy widzisz dodany komunikat „TRYB DEBUGOWANIA”?)

Krok 10: Odbieranie i obsługa danych w podrzędnym Arduino

Odbieranie i obsługa danych w podrzędnym Arduino
Odbieranie i obsługa danych w podrzędnym Arduino
Odbieranie i obsługa danych w podrzędnym Arduino
Odbieranie i obsługa danych w podrzędnym Arduino

Odbieranie od Slave Arduino

Dodaj niezbędny kod dla drugiego kanału do głównej pętli, programowego odbiornika szeregowego, jak pokazano - linie 149 do 155.

Czy widzisz, że struktura jest luźno oparta na tym, co napisaliśmy powyżej dla przypadku Master?

Zobaczysz również, że otrzymujemy błąd kompilatora, kolejną niezadeklarowaną funkcję - tym razem parseData(); - więc musimy zrobić skrót dla tego, zanim będziemy mogli uruchomić bezbłędną kompilację testową.

Obsługa danych w Slave Arduino

Dodaj kod pętli głównej wymagany dla Arduino, jeśli jest skonfigurowany jako urządzenie Slave, jak pokazano - linie 163 do 174. Czy widzisz, że jego struktura jest bardzo podobna do struktury pierwszego kanału?

I powinieneś się przekonać, że tym razem kompiluje się absolutnie dobrze.

Krok 11: Napisz funkcję odbioru

Napisz funkcję odbioru!
Napisz funkcję odbioru!

Funkcja Receive - void recv(char from){} - ma dwa główne zadania.

1, aby otrzymać ciąg znaków z kanału USB, oraz

2, aby otrzymać jeden z kanału Arduino na Arduino.

Do pierwszego będziemy musieli użyć, ponieważ wykorzystuje on wbudowany sprzętowy UART Arduino, a do drugiego za pomocą standardowej Biblioteki Arduino: oprogramowania UART.

Kiedy zaczynamy dodawać kod do funkcji - aby stworzyć funkcję, która coś robi, a nie tylko kod pośredniczący - musimy pamiętać o usunięciu lub zakomentowaniu zastępowanego kodu pośredniczącego. W przeciwnym razie otrzymujemy błąd kompilacji: refinyfikacja 'void lrec(char)'.

Spróbuj uzyskać błąd, a następnie wypróbuj jeden ze sposobów sugerowanych powyżej, aby się go pozbyć.

Zacznij od funkcji, która wygląda tak, jak pokazaliśmy tutaj wiersze od 75 do 88 w kolorze żółtym.

Wiesz już, że mając kod będziesz musiał spróbować operacji kompilacji. Daje ci błąd, taki jak te, które mieliśmy wcześniej, typu: nazwa funkcji nie zadeklarowanej w tym zakresie. Na początku będziemy potrzebować innego kodu pośredniczącego, który pozwoli nam skompilować po tym błędzie, więc dodaj go tak jak poprzednio i upewnij się, że możesz teraz uzyskać kompilację bez błędów.

Teraz spójrzmy na kod, który napisaliśmy dla funkcji recv().

Jest całkiem czysty, możesz zobaczyć użycie warunku 'if' do wytworzenia dwóch części funkcji, o której mowa powyżej.

Kod wewnątrz części 'sw' i części 'hw' ma tę samą formę i opiszę to tutaj.

Pierwsza z par linii w każdym przypadku jest początkiem pętli while. Jeśli nie znasz while, możesz poszukać wyjaśnień i przykładów na stronie Arduino.cc/Reference. Tutaj czekamy 'gdy' wbudowana funkcja 'Serial' nie otrzymała żadnych znaków i ponieważ zmienna newData została wyłączona (tzn. warunek newData == false jest spełniony). Jak tylko znak - lub więcej niż jeden znak - zostanie odebrany, while "przeskoczy" do drugiej linii w tej parze. Spowoduje to wywołanie funkcji recAstringChar(char); funkcja do obsługi bieżącego znaku. Ta para linii będzie się następnie zmieniać, dopóki (lub tak długo, jak) będą jakieś znaki, które nadal wymagają odbioru. Gdy wszystkie są skończone, stan while kończy się, zezwalając na przejście do następnego poziomu if lub else, a następnie zezwalając na rec(char); funkcja do końca. W ten sposób otrzymano pełną wiadomość.

Krok 12: Napisz podfunkcję odbierania - część 1

Napisz podfunkcję odbioru - część 1
Napisz podfunkcję odbioru - część 1
Napisz podfunkcję odbioru - część 1
Napisz podfunkcję odbioru - część 1

Teraz musimy napisać funkcję o nazwie recAstringChar(char);. Jak widać z komentarza do wiersza 50 na górze, jego zadaniem jest aktualizacja dwóch buforów kopiami przychodzącej wiadomości szeregowej. [Okazało się, gdy próbowałem to wszystko uruchomić, że jedną rzeczą, której się nauczyłem, było to, że potrzebuję dwóch różnych buforów - a przynajmniej był to najłatwiejszy sposób na obejście niektórych problemów, stąd tak jakby wyewoluował w potrzebę 2 buforów, więc Właśnie je zrobiłem.] Wywołałem jeden bufor: receiveData, a drugi: receiveChars.

Bufory są zmiennymi globalnymi, więc są deklarowane na poziomie modułu, patrz wiersze 9 i 10 wspólnej zakładki. Istnieją inne zmienne zadeklarowane wewnątrz tej funkcji, które w związku z tym mają zasięg lokalny - pokazany w wierszach 51-54 tutaj. To nie jest miejsce na wyjaśnianie różnic między globalami a lokalnymi, ale więcej informacji na ten temat można znaleźć na https://www.arduino.cc/glossary/en/ w sekcji Lokalne i Globalne.

Możesz również dowiedzieć się wszystkiego o typach danych i modyfikatorach typu: static, boolean, byte, const, char na stronie https://www.arduino.cc/reference/en/#variables, pokazanej tutaj.

Główny przepływ programu w tej funkcji jest kontrolowany przez if w wierszu 56 tutaj i odpowiadający mu else w wierszu 74. Dotyczy to dwóch scenariuszy

a) [od linii 74.], gdy rozpoczyna się odebrana wiadomość. Dzieje się tak, gdy startMarker jest zauważony - został on zdefiniowany jako znak „ <”, dlatego za każdym razem, gdy testujemy szkic, zawsze zaczynamy nasz ciąg od tego znaku. Jeśli tego nie zrobimy, nic nie zostanie przetworzone jako odebrane, wszystko zostanie zignorowane, tak jakbyśmy wpisywali bzdury po znaku zachęty klawiatury „Serial Monitor”.

b) [linie od 56 do 73], które odbierają wszystkie inne znaki, niezależnie od tego, jakie one są, ale zajmują się tylko znakami po prawidłowym uruchomieniu (odebrano '>' jak w a) powyżej.)

W tych wierszach (od 74 do 78) odebrane < umieszczamy w jednym z buforów (receivedData[0]), ale nie w drugim. Dopasowujemy wskaźnik bufora (zmienna: char ndx), aby wskazywał na następną zapasową pozycję bufora (receivedData[1]) za pomocą polecenia post-inkrementacji (++) w linii ndx++; i ustawiamy flagę w toku na true.

Przebieg programu w tej części funkcji jest kontrolowany przez if w wierszu 57. i odpowiadające mu else w wierszu 65. Dotyczy to dwóch scenariuszy

a) [od linii 65. wł.] gdy odebrana wiadomość zostanie zakończona. Dzieje się tak, gdy endMarker jest zauważony - zdefiniowany jako >, dlatego za każdym razem, gdy testujemy nasz szkic, zawsze kończymy nasz ciąg tym znakiem. Jedną z rzeczy, które mają miejsce po odebraniu znaku końca jest to, że flaga globalna (zmienna techniczna) newData jest ustawiana na wartość true w momencie zakończenia funkcji, tak że funkcja, która wywołała naszą podfunkcję (funkcja wywołująca: recv(char);) może 'wiedzieć', że prawidłowe nowe dane zostały odebrane jako kompletne.

b) [linie od 57 do 64], które odbierają wszystkie inne znaki, bez względu na to, jakie one są. Po prostu starannie parkuje je w rzędach w obu buforach.

Krok 13: Napisz podfunkcję odbierania - część 2

Napisz podfunkcję odbioru - część 2
Napisz podfunkcję odbioru - część 2
Napisz podfunkcję odbioru - część 2
Napisz podfunkcję odbioru - część 2

Pomocne może być podanie przykładu tego, jak wyglądają 2 bufory po ich zapełnieniu. Gdybyśmy wprowadzili enter, bufory miałyby pokazane w nich znaki:

Więc teraz widać, że mamy jeden bufor, który zawiera dokładnie te same znaki, które wpisaliśmy po raz pierwszy, i jeden bufor, który ma tylko dwie wartości i przecinek oddzielający. Teraz mamy kod, który może odbierać znaki, które wpisujemy na klawiaturze monitora szeregowego, możemy przejść z fazy I CDIO do O, wpisać kilka ciągów i zobaczyć, co się stanie. Prześlij kod do Master Arduino, otwórz Serial Monitor i spróbuj wpisać coś ważnego, np. Enter. Czy na ekranie monitora szeregowego pojawia się echo, takie jak pokazano tutaj?

Krok 14: Napisz funkcje przesyłania i analizowania

Napisz funkcje przesyłania i analizowania
Napisz funkcje przesyłania i analizowania
Napisz funkcje przesyłania i analizowania
Napisz funkcje przesyłania i analizowania

Najpierw dla przekazu

Więc teraz otrzymaliśmy napis, możemy napisać funkcję transmisji: tran(char); zastąpić jego odcinek. Umożliwi nam to przesłanie stringa z Mastera do Slave Arduino, więc upewnij się, że oba urządzenia są podłączone i połączone ze sobą, aby przetestować tę nową funkcję.

Wprowadź tę funkcję, jak pokazano tutaj w wierszach od 117 do 133. Jak zauważysz, ma dwie części, jedną do transmisji do kanału USB (sprzętowy UART), a drugą do przesyłania do drugiego Arduino (programowy UART). Powinno to spowodować błąd kompilacji -za darmo i możesz od razu przesłać szkic i zobaczyć, co się stanie. Tym razem wyślę. Czy otrzymujesz pokazany wynik?

Zrzut ekranu jest interesujący, ponieważ ciąg Received… powinien wyglądać poprawnie jak poprzednio, a ciąg Transmitted… powinien teraz wyglądać poprawnie. Należy jednak pamiętać, że konwersja liczb całkowitych nie zadziałała. Aby to zadziałało, trzeba jeszcze dodać trochę kodu.

Krok 15: Napisz funkcje przesyłania i analizowania

Napisz funkcje przesyłania i analizowania
Napisz funkcje przesyłania i analizowania
Napisz funkcje przesyłania i analizowania
Napisz funkcje przesyłania i analizowania

Następnie do Parse

Jest to fragment kodu, który analizuje otrzymany ciąg w celu pobrania liczbowych ciągów częściowych i konwertuje je na wartości całkowite. Jest to void parseData(); funkcja pętli głównej

Zastąp parse stub kodem pokazanym w wierszach 98 - 113. Prześlij go i zobaczmy, czy problem, który mieliśmy z 2 wartościami całkowitymi, został naprawiony. Spróbujmy.

Tak, działa, jak pokazano, znalezione liczby całkowite to 49 i 98.

Krok 16: Finał

Finał!
Finał!

Dane te krążą po pętli od komputera PC przez urządzenie nadrzędne do urządzenia podrzędnego iz powrotem przez urządzenie nadrzędne do komputera. Po załadowaniu gotowej wersji common do końcówek Master i Slave oraz przy wyłączonym teraz trybie debugowania, możemy zobaczyć dane poprawnie odebrane na obu końcach, jak pokazano tutaj.

Zalecana: