Instrukcje sterujące, operatory logiczne i porównania. Nadajnik kodu Morse’a. – część 1

Instrukcje sterujące, operatory logiczne i porównania. Nadajnik kodu Morse’a. – część 1

Witaj ponownie w kursie Arduino i ESP8266! W dzisiejszej części zajmiemy się kolejnym bardzo ważnym elementem kodu, czyli instrukcjami sterującymi. Dzięki tym instrukcjom będziesz w stanie tworzyć znacznie ciekawszy i bardziej skomplikowany kod, który będzie wykonywał pewne zadania wielokrotnie lub w zależności od pewnych warunków. O tym jak użyteczne są te elementy kodu przekonasz się tworząc prawdziwy nadajnik kodu Morse’a, który będzie zamieniał wiadomości tekstowe na zakodowane komunikty! 🙂 Przy okazji pokażę Ci również jak można podejść do realizacji tego typu większych projektów i jak rozbijać je na mniejsze elementy składowe. Bez zbędnego przedłużania ruszamy więc do akcji, pewnie już nie możesz się doczekać!

Kod Morse’a

Dzisiejsze przygody z kodem zaczniemy od zapoznania się z zasadami kodu Morse’a. Kod ten został stworzony do przesyłania komunikatów za pomocą telegrafu, później był on również wykorzystywany w telekomunikacji radiowej. Jest stosowany do dziś, na przykład w awiacji do sygnalizacji kodu lotniska, do którego zbliża się samolot. Swoją uniwersalność i ponadczasowość kod ten zawdzięcza bardzo prostym zasadom kodowania.

Nadawane komunikaty dzieli się na pojedyncze słowa, oddzielane przez odpowiednio długą przerwę. Następnie każde słowo jest dzielone na pojedyncze znaki (litery, cyfry, znaki specjalne), oddzielane krótszymi niż w przypadku słów przerwami. Znaki nadawane są za pomocą grup specjalnych symboli. Symbolami tymi są oczywiście kropka (ang. dot) i kreska (ang. dash). Kolejne symbole oddzielane są przerwami jeszcze krótszymi, niż te pomiędzy poszczególnymi znakami. Kropka i kreska nadawane są za pomocą sygnałów dźwiękowych lub świetlnych o określonym czasie trwania. Wspomniane przerwy są reprezentowane przez wyciszenie dźwięku lub zgaszenie źródła światła na określony czas.

Istnieje pięć reguł międzynarodowego kodu Morse’a, prezentują się one następująco:

  • sygnał kropki stanowi punkt odniesienia dla czasów trwania pozostałych sygnałów (przyjmijmy, że w naszym programie będzie on trwał 100ms),
  • sygnał kreski powinien trwać trzykrotnie dłużej od sygnału kropki (300ms),
  • odstęp pomiędzy elementami nadawanego znaku powinien trwać tyle co jedna kropka (100ms),
  • przerwa między poszczególnymi znakami powinien trwać tyle co trzy kropki (300ms),
  • odstęp pomiędzy grupami znaków powinien trwać siedem kropek (700ms).

Dodatkowo, w podstawowej wersji naszego programu będziemy nadawali ten sam komunikat w nieskończonej pętli. Kolejne powtórzenia komunikatu oddzielajmy przerwą trwającą 1500ms (15x czas kropki).

To już wszystkie zasady nadawania wiadomości za pomocą kodu Morse’a, które musimy znać, żeby stworzyć nasz projekt! Zacznijmy jednak od zaplanowania pracy nad nim.

Planowanie pracy nad projektem

Tym razem podjęliśmy się zdecydowanie bardziej skomplikowanego zadania niż w poprzednich częściach kursu. Jeśli spróbujesz usiąść przed IDE i napisać cały kod naraz, najprawdopodobniej skończy się to występowaniem licznych błędów kompilacji i błędów logicznych. O wiele lepszą strategią w przypadku takiego projektu jest podział na mniejsze zadania, które są łatwiejsze w implementacji i prostsze do przetestowania. Takie części składowe muszą być oczywiście zaprogramowane w sposób, który umożliwi późniejsze łatwe ich połączenie oraz przetestowanie takiej integracji (to często spotykane w programowaniu słowo, więc dobrze, żebyś się z nim oswoił ;)). Kiedy zastosujesz takie podejście, jakość pisanego przez Ciebie kodu automatycznie wzrośnie, ponieważ kiepsko napisany kod nie będzie podatny na wielokrotne wykorzystanie lub łączenie w większe moduły. Zastanówmy się więc, co musimy zaprogramować w ramach naszego projektu:

  • funkcja setup, która przygotuje wyjście do którego podłączona jest dioda wbudowana w płytkę NodeMCU,
  • funkcja loop, która będzie nadawała w pętli zakodowaną wiadomość,
  • zestaw stałych liczbowych, które będą sterowały długością nadawanych sygnałów i przerw między nimi,
  • metody włączające i wyłączające diodę z odpowiednimi odstępami czasowymi
  • funkcja zamieniająca znak ASCII na odpowiednią sekwencję kropek, kresek i odstępów,
  • funkcja przyjmująca łańcuch tekstowy i zamieniająca jego poszczególne znaki na kod Morse’a.

Zwróć uwagę na kolejność tych punktów. Można łatwo zauważyć jak przechodzimy tutaj od prostszych, fundamentalnych zadań, do łączenia ich w większą całość. Spróbujmy zatem wykonać po kolei te czynności! 🙂

Utworzenie nowego szkicu, funkcje setup i loop

Tak jak zawsze, pracę nad nowym projektem zaczniemy od utworzenia nowego szkicu w Arduino IDE. Jako „gratis” od środowiska programistycznego dostaniemy gotowe metody setup() oraz loop(). W ciele funkcji setup dopisz inicjalizację wyjścia do którego podłączona jest dioda wbudowana w płytkę NodeMCU (tak jak robiliśmy to tutaj). Tym razem dodamy jednak małe usprawnienie – globalną definicję nazwy portu wyjściowego. Brzmi strasznie? Może trochę, ale wcale takie nie jest! 🙂

Instrukcja #define

Komenda #define jest bardzo użytecznym fragmentem składni języka C++. Umożliwia ona nadanie nazwy pewnej stałej wartości (wzór składni poniżej).

Zwróć uwagę, na trzy bardzo ważne rzeczy:

 Wykorzystując komendę #define NIE stosujemy średnika kończącego linię oraz NIE podajemy znaku równości pomiędzy nazwą a wartością! Używając #define nie podajemy również typu wartości, jest on ustalany w momencie kompilacji na podstawie miejsca wykorzystania. W przeciwnym wypadku dojdzie do błędów kompilacji, które będą trudne do zrozumienia! 

Może to być dla Ciebie nieco zaskakujące, ponieważ opis ten brzmi identycznie jak opis działania modyfikatora const i masz rację! Różnica polega na tym, że słowo kluczowe #define jest interpretowane przez preprocesor. Oznacza to, że zanim rozpocznie się proces kompilacji programu, w każdym miejscu w którym jest wykorzystywana nazwa stałej podstawiana jest jej wartość. Pozwala to na zaoszczędzenie pewnej ilości pamięci programu. Prześledźmy działanie komendy #define na przykładzie, żeby lepiej zrozumieć jej działanie. Załóżmy, że chcemy zdefiniować OUTPUT_PIN jako nazwę portu mikroprocesora oznaczonego D4 (dioda LED wbudowana w NodeMCU). Nasz kod będzie wyglądał następująco (zwróć uwagę, że według konwencji wykorzystania C++ nazwy piszemy WIELKIMI_LITERAMI oddzielając słowa podkreślnikiem):

Można wykorzystać to następnie w metodzie setup ustawiając wyjście mikrokontrolera:

Nasz kompilator rozpoczynając swoją pracę będzie to jednak rozumiał jako:

O zaletach i wadach instrukcji #define mógłby powstać cały oddzielny artykuł, byłby on jednak wyjątkowo techniczny (czyt. nudny), dlatego na tę chwilę przyjmijmy, że będziemy tej instrukcji używać do nazywania wejść i wyjść mikrokontrolera i wróćmy do tworzenia podstawy naszego programu.

Funkcje setup i loop – ciąg dalszy

Zgodnie z tym co omówiliśmy przed chwilą, dopisz definicję nazwy portu D4 na samej górze kodu programu. Następnie zastąp w wywołaniu metody pinMode mało mówiącą nazwę D4 nazwą OUTPUT_PIN. Możesz również dodać inicjalizację portu szeregowego, tak jak robiliśmy to w poprzedniej części kursu. W funkcji loop dodaj wypisywanie przez port szeregowy dowolnego tekstu oraz sekundę opóźnienia pomiędzy kolejnymi kropkami za pomocą linii:

Skompiluj program i wgraj go na płytkę. Otwórz monitor portu szeregowego, żeby sprawdzić, czy wszystko działa jak należy. Dzięki temu, w późniejszym etapie programowania, będziesz wiedział w którym miejscu nie musisz szukać źródła problemów. Jeśli wszystko poszło zgodnie z planem, możemy przejść do implementacji podstawowych zasad kodu Morse’a!

Implementacja zasad kodu Morse’a

Jak już wspomnieliśmy wcześniej, zasady kodu Morse’a sprowadzają się do istnienia pięciu stałych czasowych. Określają one czasy trwania nadawanych sygnałów oraz przerw symbolizujących odstępy między kolejnymi symbolami, znakami i słowami. Po poprzedniej części kursu powinieneś już doskonale wiedzieć jak dodawać wartości stałe w kodzie programu. Dodaj więc wszystkie potrzebne wartości stałe pomiędzy definicją nazwy pinu a funkcją setup. Stałe powinny być typu int, pamiętaj również, że będziemy je wykorzystywali jako argument funkcji delay, który wyraża czas podawany w milisekundach. Zauważ również, że wszystkie stałe czasowe są uzależnione od czasu trwania kropki, niech więc ich wartość będzie wielokrotnością tej stałej. W ten sposób łatwiej będziesz mógł wprowadzać modyfikacje w kodzie – jeśli uznasz, że wiadomości nadawane są zbyt szybko, będziesz mógł to poprawić zmieniając wartość jednej stałej. Jeśli będziesz miał problemy z wykonaniem tego zadania, pamiętaj, że zawsze możesz podejrzeć całość kodu z tej lekcji na moim repozytorium.

Funkcje odpowiadające za wysyłanie sygnałów

Kolejnym etapem naszej pracy będzie dopisanie funkcji, które będą powodowały włączanie i wyłączanie diody na odpowiednie okresy czasu. Zastanówmy się najpierw ilu funkcji potrzebujemy do realizacji tego zadania i co będziemy w nich wykonywać! Na pierwszy rzut oka może nam się wydawać, że potrzebujemy aż pięciu funkcji – kropka, kreska, odstęp między symbolami, znakami oraz słowami. Możemy jednak zauważyć, że funkcje możemy podzielić na dwie grupy – zaświecenie diody oraz gaszenie diody. Moglibyśmy więc utworzyć dwie funkcje przyjmujące jako argument czas świecenia/wygaszenia. O wiele zwięźlejsze i czytelniejsze będzie utworzenie funkcji dot (kropka), dash (kreska) oraz turnOff (ang. wyłącz), której argument to duration (ang. czas trwania) typu int.

Funkcja turnOff

Zacznijmy od napisania funkcji turnOff. Jak już wspomnieliśmy będzie ona przyjmowała jako argument czas trwania wyłączenia diody, nie musi ona natomiast zwracać żadnej wartości. W ciele tej funkcji musimy wywołać metodę digitalWrite, która ustawi wyjście mikroprocesora w stan wysoki (HIGH), co spowoduje wyłączenie diody LED. Następnie wywołamy metodę delay z argumentem wejściowym funkcji turnOff, co spowoduje utrzymanie stanu wyłączenia diody przez odpowiedni czas. Kod funkcji możesz zobaczyć poniżej.

Funkcja dot

Analogicznie możemy teraz napisać funkcję dot. Nie będzie ona potrzebowała żadnych argumentów, ani nie będzie zwracała żadnej wartości. Pamiętaj, że żeby włączyć diodę, musisz wywołać metodę digitalWrite z argumentem LOW. Następnie musimy dopisać wywołanie metody delay z odpowiednią stałą jako argumentem, aby zapewnić odpowiedni czas świecenia diody. Możemy też zauważyć, że po każdym zaświeceniu diody należy ją zgasić na czas równy odstępowi między elementami znaku. Dopiszmy więc na końcu metody wywołanie metody turnOff przekazując odpowiednią stałą jako argument funkcji. Poniżej przedstawiam gotowy kod.

Dopisanie funkcji dash zostawiam Tobie, ponieważ jest ona bardzo podobna do funkcji dot.

Testowanie!

W funkcji loop możesz teraz dopisać wywołanie funkcji dot, dash oraz turnOff ze stałą odpowiadającą za odstęp między kolejnymi słowami. Oznacza to nadawanie litery „a” w nieskończonej pętli. 🙂 Uruchom program i sprawdź czy napisane funkcje pełnią oczekiwane role.

Konwersja znaków na kod Morse’a

Dotarliśmy do bardzo ważnego etapu tworzenia naszego projektu. Mamy już całą niezbędną infrastrukturę, żeby zbudować funkcje dokonujące tłumaczenia pojedynczych znaków ASCII na kod Morse’a. Oznacza to, że w zależności od wybranej przez użytkownika litery, funkcja powinna wysłać odpowiadającą jej kombinację kropek, kresek i przerw. Wiesz już jak możesz napisać takie funkcje, jednak jak wybrać która z funkcji zostanie wywołana? Jedna z możliwości realizacji takiego wybierania na podstawie pewnych danych wejściowych pojawiła się już w poprzedniej części kursu, jednak nie była szerzej omawiana. Jest to instrukcja if, z którą teraz zapoznamy się bliżej.

Instrukcja if

Bardzo często używaną w C++ oraz innych językach programowania instrukcją jest if (z ang. jeżeli). Umożliwia ona wykonanie pewnego bloku kodu tylko wtedy, kiedy zostanie spełniony pewien warunek logiczny. Składnię funkcji if znajdziesz poniżej:

Myślę, że składnia ta jest bardzo czytelna i intuicyjna – po słowie kluczowym if w nawiasach okrągłych podajemy warunek logiczny, który jest sprawdzany. Jeżeli warunek jest spełniony, to instrukcje zawarte w nawiasach klamrowych zostaną wykonane. W przeciwnym wypadku program będzie wykonywał instrukcje znajdujące się za nawiasami klamrowymi. Żeby w pełni zrozumieć działanie funkcji if musimy jednak najpierw poznać nowy typ danych. Będziemy musieli również dowiedzieć się jak można zapisać przykładowe warunki logiczne.

Typ danych bool

Prędzej czy później każdy z nas w szkole spotkał się z algebrą Boole’a. Jednym z przykładów algebr Boole’a jest dwuelementowa algebra wartości logicznych {0,1}. Na wartościach tych można wykonywać m.in. operacje koniunkcji, alternatywy oraz negacji w celu uzyskania wartości wynikowej, która również jest jedną z wartości 0 (oznaczana jako fałsz) lub 1 (prawda). Ten typ algebry jest szeroko stosowany w informatyce, która opiera się przecież w dużym uproszczeniu na przetwarzaniu zer i jedynek. 🙂

Zmienna typu bool umożliwia przechowywanie wartości określającej stan logiczny. Stany logiczne w C++ zapisuje się jako wartości true (równoznaczne z wpisaniem 1) oraz false (równoznaczne z 0). Przykładową zmienną bool’owską można więc zainicjalizować w następujący sposób:

Znasz już składnię istrukcji if oraz umiesz posługiwać się zmiennymi typu bool. Możesz więc spróbować dopisać w pętli loop instrukcję, która będzie wypisywała komunikat w monitorze portu szeregowego w zależności od użytej zmiennej logicznej. Jest to jednak bardzo podstawowe zastosowanie instrukcji if. O wiele ciekawsze wyniki można uzyskać tworząc warunki logiczne z wykorzystaniem operatorów porównania oraz operatorów logicznych. Zacznijmy od omówienia operatorów boolowskich.

Operatory logiczne

Jeżeli zajęcia z algebry Boole’a masz już za sobą, operatory logiczne nie będą stanowiły dla Ciebie zapewne żadnego wyzwania. W Arduino wykorzystujemy trzy operatory boolowskie:

  • !negacja (odpowiednik bramki NOT) – zamienia wyrażenie logiczne na przeciwną wartość,
  • && – iloczyn logiczny/koniunkcja (bramka AND) – zwraca wartość true jeśli oba wyrażeniaprawdziwe,
  • || – suma logiczna/alternatywa (bramka OR) – zwraca wartość true jeśli jedno z wyrażeń jest prawdziwe.

Omówimy teraz kilka sposobów wykorzystania tych operatorów. Operator negacji możesz wykorzystać do odwrócenia wartości zmiennej logicznej:

Możesz też użyć go wewnątrz warunku funkcji if:

Analogicznie możesz użyć operatora koniunkcji:

Możesz też użyć operatora sumy logicznej wraz z negacją jednego z argumentów tej operacji (o kolejności wykonywania działań wspomnimy w jednej z kolejnych części kursu):

Zwróć uwagę, że koniunkcja i alternatywa logiczna zapisywane są za pomocą dwóch znaków. Użycie tylko jednego znaku & lub | oznacza wykorzystanie tzw. operatora bitowego, które omówimy w jednej z przyszłych części kursu. Pamiętaj jednak póki co, aby nie pomylić się przy zapisywaniu warunków logicznych. Unikniesz w ten sposób nieprzewidzianych wyników i czasochłonnego debugowania.

Zauważ też, że !, czyli operator negacji jest jednym z nielicznych operatorów, które umieszczane są po lewej stronie zmiennej lub wyrażenia. W przeciwieństwie do koniunkcji i alternatywy logicznej zapisuje się go za pomocą jednego znaku (negacja bitowa zapisywana jest za pomocą znaku ~)

Operatory porównania

Operatory logiczne stanowią bardzo dobre połączenie z operatorami porównania. Jak wskazuje ich nazwa, operatory porównania służą do porównywania ze sobą dwóch wartości. Jest ich nieco więcej niż operatorów boolowskich:

  • ==równość – prawdziwe, jeśli dwie zmienne są sobie równe,
  • !=nierówność – zwraca true, jeśli zmienne nie są równe,
  • <mniejsze niż – zwraca true, jeśli zmienna po lewej stronie jest mniejsza od zmiennej po prawej stronie operatora,
  • >większe niż – analogicznie, sprawdza czy zmienna po lewej jest większa,
  • <=mniejsze lub równe – zwraca true, jeśli zmienna po lewej jest mniejsza lub równa zmiennej stojącej po prawej stronie operatora,
  • >=większe lub równe – analogicznie, sprawdza czy zmienna po lewej jest większa lub równa.

Wynikiem operacji porównania jest oczywiście wartość typu bool. Operatory porównania możesz stosować na zmiennych o różnych typach danych, lecz jest to niezalecane, ponieważ taka operacja może przynieść nieoczekiwane rezultaty. Uważaj również porównując zmienne o typach ze znakiem ze zmiennymi bez znaku. Poniżej znajdziesz kilka przykładów operacji porównania:

Instrukcja else

Omówilismy instrukcję if oraz nauczyliśmy się tworzyć ciekawe warunki logiczne, które mogą sterować wykonywaniem naszego kodu. Mam dla Ciebie jeszcze jedną niespodziankę! Instrukcję if możemy połączyć z komendą else (z ang. w przeciwnym wypadku), aby utworzyć jeszcze bardziej skomplikowany kod. Komenda else umożliwia dodanie bloku kodu, który zostanie wykonany jeżeli warunek podany za instrukcją if okaże się być fałszywy. Spójrz na przykład obrazujący tę składnię:

Możesz pomyśleć, że instrukcja else jest zbędna, przecież kod za nawiasem klamrowym instrukcji if i tak zostałby wykonany. Jest to prawda, ale zwróć uwagę, że kod umieszczony w bloku else będzie wykonany tylko wtedy, kiedy warunek jest fałszywy. Gdyby został on umieszczony po prostu za klamrami instrukcji if, zostałby wykonany niezależnie od wyniku sprawdzenia warunku logicznego. Poza tym instrukcja else ma jeszcze jedno ciekawe zastosowanie, o którym opowiemy nieco później.

Zagnieżdżanie instrukcji if

Jak to mawiał Steve Jobs „jest jeszcze jedna rzecz”, którą można zrobić z instrukcją if. Można je zagnieżdżać oraz łączyć. Zagnieżdżanie instrukcji if polega na tym, że wewnątrz bloku kodu jednej instrukcji if można umieścić drugą instrukcję if (oczywiście może ona też posiadać blok else), na przykład:

Jest to jednak wyjątkowo nieczytelny sposób programowania i należy unikać tworzenia takich konstrukcji. Samemu będziesz mógł się przekonać jak ciężko jest zapanować nad poprawnością logiki tak napisanego kodu.

Łączenie instrukcji if

Nieco lepszą praktyką jest łączenie funkcji if poprzez użycie instrukcji else. Spójrz na przykład:

W pierwszej kolejności sprawdzamy, czy x zawiera się w przedziale obustronnie otwartym od 0 do 10. Jeżeli x nie zawiera się w tym przedziale sprawdzamy czy x jest mniejsze od 0 i wypisujemy odpowiedni komunikat. Jeżeli x jest większe lub równe 0 wykonywany jest kod umieszczony w bloku else.

Nie nadużywaj również tego sposobu programowania, ponieważ jest mały czytelny i łatwo jest popełnić błąd przygotowując warunki logiczne. Znacznie ciekawszą instrukcję, która umożliwi przełączanie wykonywanego kodu w zależności od wartości poznamy w następnej części kursu.

Zadanie dla Ciebie

W tym momencie przerwiemy pracę nad naszym projektem. Jeśli chcesz wypróbować nowo poznane instrukcje, spróbuj dopisać instrukcję if…else, która będzie wypisywała kodem Morse’a literę ‚a’ lub ‚b’ w zależności od warunku logicznego. Spróbuj też użyć odczytywania wartości z portu szeregowego do zmieniania wartości zmiennej, która będzie sterowała instrukcją if…else. Będziesz mógł potem wykorzystać ten kod do rozszerzenia działania programu o kodowanie wiadomości wpisywanych z klawiatury.

Możesz też dopisać wypisywanie w oknie monitora portu szeregowego znaku „*” za każdym wypisaniem kropki oraz znaku „” przy wysyłaniu kreski. Spróbuj też dodać do metody turnOff instrukcji if…else, która będzie wyświetlała jedną spację w przypadku gdy czas wyłączenia diod jest równy odstępowi między znakami. Jeśli czas wyłączenia jest równy odstępowi między słowami, niech port szeregowy wysyła cztery spacje. Umożliwi to prostsze debugowanie naszego programu i wysyłanie kodu Morse’a w formie tekstowej.

Rozwiązywanie problemów

Zaczynamy pisać coraz bardziej skomplikowany kod. Coraz trudniej będzie mi zatem przewidzieć i wskazać najczęstsze przyczyny błędów. Pamiętaj, że do każdego posta na moim blogu, który będzie opisywał jakiś mniejszy lub większy projekt, utworzone będzie specjalne repozytorium z kodem, na którym możesz podejrzeć sugerowane rozwiązanie zadań do samodzielnego wykonania. Będę dokładał wszelkich starań, żeby te rozwiązania prezentowały profesjonalne podejście do rozwiązania problemu – były zwięzłe, czytelne i odporne na błędy. Pamiętaj jednak, że jestem tylko człowiekiem i czasem coś może nie pójść zgodnie z planem, będę wtedy wdzięczny za wszelkie komentarze z propozycjami usprawnień.

W przypadku dzisiejszego programu wprowadzamy kilka nowych elementów składni, które potrafią narobić nieco bałaganu. Zwróć szczególną uwagę na poprawność wykorzystania składni define. Może też zdażyć Ci się pomyłka przy używaniu operatora porównania „==, który możesz pomylić z operatorem przypisania pisanym za pomocą jednego znaku „=„. Wciąż musisz pamiętać też o średnikach na końcach linii, odpowiednim zamykaniu nawiasów itp.

Jeśli pomimo wszelkich pomocy wciąż masz problemy z działaniem programu, napisz w sekcji komentarzy, postaramy się znaleźć rozwiązanie!

Podsumowanie

Wow, znowu wyszedł mi strasznie długi post! Pracę nad naszym relatywnie prostym projektem będziemy musieli dokończyć niestety przy następnym spotkaniu. Nie możemy wprowadzać zbyt dużo nowych elementów składni za jednym razem, bo zdecydowanie może to przyprawić o ból głowy i zniechęcić do programowania! 😉 Następnym razem poznamy zatem tajemniczą instrukcję switch i opowiemy sobie o pętlach (nie, nie o takich jak w grze w wisielca!). Dzięki nowym elementom, które poznamy napiszemy swego rodzaju słownik, który będzie tłumaczył litery i cyfry na ciągi kropek i kresek. Pętle umożliwią nam z kolei kodowanie złożonych wiadomości.

Koniecznie daj znać czy podobał Ci się taki sposób wspólnej pracy nad projektem, czy może powinienem coś usprawnić. Możesz też napisać co sądzisz o nowych instrukcjach języka C++, które dziś poznaliśmy – jakie widzisz dla nich zastosowanie, w czym mogą pomóc.

Podziel się stroną ze znajomymi! :)

Dodaj komentarz