Witam majsterkowiczów! Dzisiaj chciałbym przedstawić Wam pokrótce, co to jest i jak można wykorzystać port szeregowy, i jego monitor w zastosowaniach z Arduino. W tym wpisie wyświetlimy na panelu LCD tekst wpisywany poprzez serial monitor. Artykuł kierowanny jest głównie dla osób raczkujących w temacie Arduino, całość opisana jest pod wersję UNO.
Co będzie potrzebne?
Co to właściwie jest ten serial port?
Określenie serial port pochodzi z j. angielskiego i tłumaczone jest jako port szeregowy. Służy on do przesyłania i odbierania informacji, z i do komputera. W jego nazwie “szeregowy” oznacza, to że dla transmisji danych w danym kierunku dostępny jest tylko 1 pin. Przesyłać dane można przez ten port z różną, wcześniej ustaloną prędkością – jest to konieczne aby transmisja przebiegła prawidłowo.
Zaczynamy! – begin();
Chęć korzystania w danym programie z portu szeregowego trzeba oczywiście zadeklarować :), do czego służy funkcja begin(). Parametry jakie przyjmuje to:
- prędkość – wyrażona w bodach (ang. baud), czyli bitach na sekundę. Najczęściej stosowaną prędkością jest 9600 bitów na sekundę, w zależności od potrzeb może być ona większa lub mniejsza. Należy pamiętać, aby zadeklarowana prędkość na obu urządzeniach które mają się komunikować(w tym przypadku komputer i Arduino) była taka sama!
- drugi argument funkcji jest opcjonalny i konfiguruje takie parametry jak bit stopu i bit kontroli parzystości, nie będziemy go dziś używać(przynajmniej nie innego, niż domyślny : ).
Teraz przejdziemy do pierwszego programu, jako że minimum teorii już mamy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void setup() { //otwarcie portu i ustawienie prędkości na 9600 bodów Serial.begin(9600); //wypisanie linijki tekstu Serial.println("Kolejno odlicz!"); } int k = 0; void loop() { //wypisywanie zmiennej k w nowym wierszu. Serial.println(k); //inkrementujemy k k++; //czekamy sekundę na kolejny przebieg pętli delay(1000); } |
W tym miejscu omówię jeszcze komendę Serial.println() – drukuje ona całą linijkę. Jeśli chcemy aby kilka rzeczy zostało wypisanych w tym samym wierszu moża użyć Serial.print(). Zakończyć ten wiersz można poprzez wydrukowanie znaku nowej lini tj. Serial.print(“\n”);
Na posiadaczy Arduino LEONARDO czeka w tym miejscu mała pułapka. Kod raczej nie zadziała poprawnie z tego wzgledu, że płytka nie restartuje automatycznie programu po uruchomieniu serial monitora, tak więc kod powinien wyglądać tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void setup() { //otwarcie portu i ustawienie prędkości na 9600 bodów Serial.begin(9600); //dopóki serial monitor nie jest otwarty NIE rób nic while (!Serial) ; //wypisanie linijki tekstu Serial.println("Kolejno odlicz!"); } int k = 0; void loop() { //wypisywanie zmiennej k w nowym wierszu. Serial.println(k); //inkrementujemy k k++; //czekamy sekundę na kolejny przebieg pętli delay(1000); } |
Więcej na ten temat można przeczytać tutaj.
Czy da się podejrzeć dane które są wysyłane i odbierane poprzez port szeregowy?
Tak! W środowisko programistyczne Arduino, wbudowane jest takie narzędzie jak serial monitor. Służy ono do odczytywania i wysyłania danych poprzez dany port szeregowy. Jak odczytywać a nawet i zapisywać na komputerze dane wędrujące poprzez ten port zostało opisane już tutaj: zapis danych temperatury, dlatego dziś skupię się głównie na komunikacji w drugą stronę – wysyłanie danych z komputera do Arduino.
Po załadowaniu szkicu do Arduino, otwieramy serial monitor, można zrobić to na kilka sposobów:
Naszym oczom powinno ukazać sie coś takiego:
Jeżeli prędkość została źle ustawiona, pokaże nam się to samo okienko, tyle że z innymi informacjami:
Po otworzeniu serial monitora prędkość można spokojnie zmienić, spowoduje to kolejne zrestartowanie programu.
Wypisywanie otrzymanych danych do serial monitora
W tym przykładzie po krótce omówię funkcje available() i spolszczony przykład kodu z manuala. Funkcja zwraca ilość bajtów które po przesłaniu poprzez port szeregowy czekają na odczytanie. Nie przyjmuje żadnych parametrów. Na pokład Arduino ładujemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//zmienna liczbowa do przechowywania danych odebranych przez port szeregowy int przeslanyBajt = 0; void setup() { Serial.begin(9600); } void loop() { // sprawdzanie czy są dostępne nowe dane do odczytania if (Serial.available() > 0) { //przypisywanie do zmiennej odczytanego bajtu przeslanyBajt = Serial.read(); // wypipsywanie otrzymanych danych Serial.print("Otrzymano: "); //wypisanie przelanych danych Serial.println(przeslanyBajt, DEC); } } |
Po otwarciu serial monitora i wpisaniu przykładowych danych widzimy:
Zaraz, zaraz… Coś tu nie gra! Jestem pewny, że przesłałem literkę a! Tak… W podanym przykładzie zapisywaliśmy otrzymane dane do zmiennej liczbowej (int), tak więc otrzymane dane są wyświetlane jako liczba. Owa liczba, jest reprezentacją przesłanego znaku w tablicy ASCII. Przy wyświetleniu otrzymanych danych( Serial.println(przeslanyBajt, DEC); ), ustawilismy wyświetlanie w postaci liczb dziesiętnych (ang. decimal). Jest możliwość reprezentacji znaków w innych systemach liczbowych: dwójkowym(BIN, ang. binary ) lub szestastkowym(HEX, ang. hexadecimal).
Wypisywanie wiadomości na LCD za pomocą serial monitora
W tym przykładzie przesłane dane, wyświetlimy w formie takiej, jakiej je pierwotnie wysyłaliśmy, czyli tekst jako tekst i liczby jako liczby. Pod Arduino podłączamy LCD.
Schemat:
Połączenie LCD do Arduino w ten sposób zostało dokładnie opisane tutaj.
Po podłączeniu sprzętu, ładujemy na pokład następujący program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
// dołączenie biblioteki wyświetlacza #include <LiquidCrystal.h> // piny pod które został podpięty wyświetlacz LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //tworzymy pusty łańcuch znaków do którego będziemy w pętli //dodawać po 1 odczytanym znaku z przesłanych danych String odczyt_serial = ""; //zmienna pomocnicza za pomocą której będziemy sprawdzać //czy można wyświtlić odczytane dane z serial monitora boolean koniec_odczytu = false; void setup() { //deklaracja wielkości ekrau podpiętego pod arduino lcd.begin(16, 2); Serial.begin(9600); //wypisanie pierwszej linijki danych do serial monitora Serial.println("Wpisz cos: "); } void loop() { // sprawdzamy czy są nowe dane if (Serial.available() > 0) { //czyścimy lcd lcd.clear(); //wykonujemy pętle do momentu w którym są dane while (Serial.available()) { //do zmiennej znakowej znak, ładujemy w każdym obrocie pętli kolejny znak //przesłanego tekstu, zaznaczając poprzez (char), że chcemy otrzymywać znaki //w ramach eksperymentu proponuję usunąć (char) i zobaczyć co wtedy się nam //na wyjściu ukaże char znak = (char)Serial.read(); //do wcześniej utworzzonej zmiennej łańcuchowej(tekstowej) //dodajemy kolejno po znaku odczyt_serial += znak; //jeśli ostatni wczytany znak, jest znakiem końca linii //ustawiamy zmienną na wartość prawda, tak żeby móc wyświetlić //tekst na lcd w kolejnej instrukcji if if(znak == '\n') { koniec_odczytu = true; } } if(koniec_odczytu == true) { //wypisanie na serial monitorze tego //co przedchwilą wysłaliśmy Serial.print("otrzymalem: "); Serial.println(odczyt_serial); //wypisanie na ekranie LCD wpisanego tekstu lcd.print(odczyt_serial); //po wypisaniu ustawiamy zmienną znów na fałsz, //tak żeby instrukcje zawarte w tym ifie //nie zostały wykonane ponownie bez wczesniejszego //otrzymania danych koniec_odczytu = false; //wyczyszczenie łańcucha znaków by móc znów //do niego wpisywać kolejne dane odczyt_serial = ""; } } } |
Po załadowaniu programu, odpalamy serial monitor i wpisujemy dowolny tekst:
Przed kliknięciem entera bądź wyślij z menu rozwijanego obok prędkości ustawiamy “Nowa linia (NL)” – oznaczać to będzie, że każdorazowo wysłany tekst będzie zakonczony znakiem nowej linii – a jest to warunek konieczny do tego, by nasz napisany wcześniej program zadziałał. Jeśli parametr był wczesniej ustawiony na “No line ending”, a wysyłaliście jakieś wiadomości to czekają one na odbiór w buforze i zostaną odczytane z najbliższą wiadomością która będzie zakonczona znakiem nowej linii. Całość oczywiście wyświetli się na naszym podłączonym LCD:
Minusem takiego rozwiązania sprawy jest dziwny znak wyświetlony na końcu wiadomości na LCD – jest to znak nowej linii, który został dodany do zmiennej odczyt_serial podczas odczytywania danych z bufora. Można temu w prosty sposób zaradzić, wystarczy zmienić miejsce w którym do zmiennej łańcuchowej był dodawany kolejno znak, po znaku(linijka 39) na:
1 2 3 4 |
if(znak != '\n') { odczyt_serial += znak; } |
Powyższa instrukcja oznacza “jeśli znak odczytany z bufora nie jest znakiem nowej lini(\n), przypisz zmienną znak, do zmiennej odczyt_serial.
Dla bardziej zaawansowanych – jeśli nie znacie, polecam zapoznanie się z funkcją serialEvent().
Po tym mało praktycznym wstępie ciężko zauważyć użyteczność portu szeregowego, jednakże wysyłając odpowiednie komendy z arduino na port szeregowy i monitorując go programem napisanym w innym języku(polecam Pythona), można zrobić na przykład systemSMS powiadamiania o wciśniętym przycisku na pilocie : ) z tym, że zamiast odbiornika podczerwieni podłączyć można np. czujnik temperatury, wilgotności itd…
fajny artykuł. Przydałby się jeszcze artykuł opisujący połączyć “gołą” atmege8 tak aby móc korzystac z serial monitora (uzywam USBasp jako programatora).
potrzebujesz konwertera usb RS232 ttl
Albo czegokolwiek innego co będzie w stanie połączyć PC z ATMegą. Sam wykorzystuje układ do komunikacji po Bluetooth – naprawdę świetna rzecz również gdy nie ma się nic innego pod ręką do debugowania.
Zobacz http://arduinoj.blogspot.com/2013/04/pierwsza-produkcyjna-implementacja_6.html
Kiedyś sterowałem Arduino przez serial po telnecie (OpenWrt), komendami typu W1 (Wnetylator ON), P0 (Wyłącz przekaźnik) itp. Pomocna w tym jest biblioteka Messenger http://sprae.jogger.pl/2011/11/15/efektywne-sterowanie-arduino-z-komputera/. Wiele robotów ma “na plecach” router z OpenWrt i sterowanie odbywa się w ten sposób :)
Artykuł bardzo dobry, fajnie że dużo tematów się ostatnio pojawia na Majsterkowie :)
Przydała by się aktualizacja tego wpisu bo biblioteka została wbudowana w Serial. Teraz Serial ma metody parseInt i parseFloat.
Maleńka, tyciusieńka uwaga: “bynajmniej” to nie synonim “przynajmniej”.
Dzięki za uwagę, już poprawione : )
“prędkość – wyrażona w bodach (ang. baud), czyli bitach na sekundę. Najczęściej stosowaną prędkością jest 9600 bitów na sekundę”
A w Wikipedii jak byk:
“Uwaga: Bod nie powinien być mylony z prędkością transmisji danych (mierzoną w bitach na sekundę). Każda zmiana sygnału może nieść informację o jednym lub więcej bitach (przykładowo w modulacji 16-QAM 4 bity)”
Dziękuje za uwagę – rozchodzi się tutaj nie o mój błąd, a o małą precyzję w tym, co napisałem. Dla tego konkretnego przypadku, czyli portu szeregowego, który ma 2 kanały (nadawanie i odbieranie sygnału), dane transmitowane są bit po bicie, czyli chcąc nie chcąc w tym przypaku wychodzi na bity na sekundę. Zagadnienie modulacji, które tutaj podałeś jest mi zupełnie nie znane.
Paweł podlacz pod atmege rx,tx pin 0 i 1 max232 zrobilem sobie w ten sposob (klona arduino) i z arduino ide mozna zgrac sketch pod warunkiem ze masz usb->rs232 (max232 u mnie w elektronicznym to koszt kolo 5zl)
Może mi ktoś wytłumaczyć dlaczego na wyświetlaczu mam same kwadraty ?
Jeżeli w jednym rzędzie, to masz programowo nie uruchomiony wyświetlacz. Jeżeli we wszystkich, to źle ustawiony kontrast.
Idzie podłączyc bez potencjometru LCD?:)
Bo bez niego nic mi sie nie wyświetla.
Jak podepnę pin 3 do masy (GND) to mam same kwadraciki jak ktoś wyżej pisał.
Znalazłem jeszcze takie coś:
http://forum.arduino.cc/index.php/topic,28486.0.html
I tam jest wzmianka:
As an alternative to the potentiometer you can connect the Vo pin of the LCD with any available PWM-output (e.g. pin 9) and add the following code in the setup() section:
pinMode(9, OUTPUT);
analogWrite(9, CONTRAST);
replace CONTRAST with the value of your choice. Values around 10-20 give a nice bright output.
Pytanie jak to w praktyce podpiac
Musisz podpiac jakis pin pwn arduino(w tym przykladzie pin 9) do zlacza wyswietlacza oznaczonego Vo i w programie w void setup ustawic pin 9 jako wyjscie
pinMode(9, OUTPUT) //pin 9 ustawiony jako wyjscie
oraz ustawic wartosc wypelnienia pwn na tym pinie
analogWrite(9, 20) //wartosc wypelnienia pwn dla pinu 9 wynosi 20
jak napisano w cytowanym tekscie wartosc w przedziale 10-20 powinna dac optymalny kontrast
Pin 3 wyświetlacza LCD(V0) służy do regulacji kontrastu. Zamiast potencjometru wolno Ci równierz podłączyć rezystor 4.7 kOhm do masy. Im większa rezystancja tym mniejszy kontrast. Co do rezystorów to one są tanie i kosztują maks 80 gr. Wylutowywanie ich ze starych sprzętów to by była wiocha :). 4.7 kohm paski:
żółty | fioletowy | czerwony | złoty. Zamiast złotego może być inny kolor
mam wyświetlacz WC1602A i nie działa
Witam, Zajmuję się Arduino Mega i udało mi się skomunikować Win8 i Megę, Komunikacja jest dość trudna do ogarnięcia ale mogę sterować Megą z poziomu programu napisanego w Pythonie. Odnośnie “dziwnych znaczków” w serial port monitorze to polecam poczytać dobrą książkę o kodowaniu znaków w C, C++.
Mam taki może banalne pytanie a mianowicie :
– jak odczytamy ilość odczytanych danych poleceniem Serial.available() i będzie to np. 3 to aby nic nie pozostało w buforze odczytu musimy trzykrotnie użyć polecenia Serial.read(); aby bufor się WYCZYŚCIŁ ?
Czy może istnieje inne polecenie czyszczenia bufora odczytu?
Na szybko-wystarczy zrestartować arduino albo monitor portu szeregowego. Bufor wyczyszczony.
Ja osobiście do czyszczenia bufora używam polecenia: Serial.readString(); bez zapisywania tego do zmiennej
Czemu po zastosowaniu identycznego kodu i wpisaniu “a” wyświetla mi się Otrzymano: 97 a linijkę niżej Otrzymano: 10?