komunikacja SPI (radiówka nRF24L01)

Komunikacja SPI

(Wpis ten jest reaktywacją wpisu z 2017 r. Okazało się, że warto podzielić oryginalny wpis na dwie mniejsze części – jedna dotycząca komunikacji I2C, druga SPI właśnie).

Tutaj https://www.arduino.cc/en/reference/SPI można poczytać czym jest protokół SPI. Nam wystarczy, że jest to szybka komunikacja synchroniczna na krótki dystans, wykorzystująca przynajmniej 3 linie nazwane MISO, MOSI i SCK. Dodatkowo są jeszcze linie CE i SCN. Jeśli więc w jakimś module zobaczymy tak nazwane piny – to znak, że działa w standardzie SPI właśnie. 

Specjalne piny SPI w Arduino to MISO (12 w Arduino), MOSI (11 w Arduino) oraz SCK (13 w Arduin0). Inne piny – CE i CSN są dowolne cyfrowe (występuje różne nazewnictwo – SS to Slave Select i odpowiada CE, który musi działać jako OUTPUT, w odróżnieniu od CSN). W poniższym przykładzie wybrałem 9 i 10 – a dlaczego? okaże się to później (na kolejnych spotkaniach naszego koła). 

nRF24L01+

Bardzo fajny moduł (no i bardzo tani – około 5 zł, co w porównaniu do modułów Bluetooth jest 1/5 ceny) – ale najpierw trzeba wiedzieć, jak podłączać go do Arduino:

UWAGA: zasilanie VCC jest z przedziału 3.3V-3.6V – podłączając do Arduino 5V USZKODZISZ moduł.

Oprogramowanie? Ponownie jest kilka bibliotek, ja użyłem nRF24L01.h by TMRh20. Jak poprzednio – doinstalowujemy do naszego Arduino IDE i tworzymy program odbierający dane:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10);//CE, CS
const byte rxAddr[6] = "grzyb";

void setup()
{
  Serial.begin(9600);
  Serial.print("nRF24 INIT=");
  bool ok=radio.begin();
  Serial.println(ok);
  radio.openReadingPipe(0, rxAddr);
  
  radio.startListening();
}

char text[32];

void loop()
{
  if (radio.available())
  {
    radio.read(&text, sizeof(text));
    
    Serial.print("t=");
    Serial.print(millis()/1000);
    Serial.print("s, text=");
    Serial.println(text);
  }
}

W linii 6 nadalismy nazwę naszemu strumieniowi – dowolne 5 znaków, niekoniecznie musi być tak jak u mnie… Ale nadajnik też musi nadawać na tym samym „paśmie”:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <printf.h>

RF24 radio(9, 10);//CE, CS

uint8_t rxAddr[6] = "grzyb";

void setup()
{
  Serial.begin(9600);
  Serial.print("nRF24 INIT=");
  bool ok=radio.begin();
  Serial.println(ok);
  printf_begin;
  radio.printDetails();
  radio.setRetries(15, 15);
  radio.openWritingPipe(rxAddr);
  
  radio.stopListening();
}

char text[] = "Fiza:         OK!";

void loop()
{
  radio.write(&text, sizeof(text));
  text[7]=48+rand()%10;  
  text[8]=48+rand()%10;
  text[9]=48+rand()%10;
  text[10]=48+rand()%10;
  text[11]=48+rand()%10;
  Serial.println(text);
  delay(3000);
}

Maksymalna długość jednorazowo przesłanego napisu wynosi 32 bajty, a inne ważne rzeczy – w dokumentacji (zachęcam do czytania). 

Problemy? Modyfikacje? 

Np. problem ze zrywaniem połączenia – ludzie piszą o niestabilnym napięciu z pinu 3.3V Arduino oraz o problemie z prądem (pin 3.3V może maksymalnie dać tylko 50mA), dlatego dodają dodatkowy kondensator (mały, np 4.7 μF, 10 μF) do zasilania modułu:

Mogą pojawić się też problemy z anteną, więc albo kupujemy moduł z „wypasioną antenką”:

albo samodzielnie robimy sobie antenkę:

Polecam zajrzeć tutaj aby poczytać o problemach i różnych sposobach ich rozwiązywania, a także o oprogramowywaniu tego moduły.

(c) K.G. 2017, 2019

komunikacja I2C (LCD na hd44780)

(Wpis ten jest reaktywacją wpisu z 2017 r. Okazało się, że warto podzielić oryginalny wpis na dwie mniejsze części – jedna dotycząca komunikacji I2C właśnie, druga SPI).

Komunikacja I2C

To bardzo popularny interface komunikacyjny, obsługujący za pomocą tylko 2 linii aż 127 urządzeń! tymi pinami są SDA (w Arduino pin A4) oraz SDC (w Arduino pin A5). Oznacza to, że gdy podłączamy coś na I2C „tracimy” piny A4 i A5. Trudno – coś za coś. Zresztą, to nic nowego – podobnie jest z komunikacją UART – Serial.begin(xxx) – „zabiera” nam cyfrowe piny #o (TX) i #1 (RX). Przy czym UART to tylko komunikacja z jednym urządzeniem – a tutaj 2 piny i możliwość obsługi do 127 urządzeń!

HD44780 i LCD 16×2

Jako przykład komunikacji I2C użyliśmy wyświetlacza LCD 16×2 z dodatkowym sterownikiem hd44780. Po co ten sterownik? wszak to bardzo popularny wyświetlacz i można go podłączyć w ten sposób:

Przypatrzmy się uważnie co my tu widzimy – zwracam uwagę na liczbę pinów potrzebnych do obsługi wyświetlacza przez Arduino. Uważne oko uczestnika sdanaszych spotkań zauważy, że potrzeba aż 6 cyfrowych pinów. To sporo! Wszystko jest OK, gdy robimy proste programy, poznajemy moduły (w tym właśnie taki LCD) więc tych 6 pinów nie robi problemu. Ale my mamy już nasz pojazd i wykorzystanych 6 pinów do jego sterowania (a w planach kolejne rozbudowy). Jak więc poradzić sobie z problemem (powoli) kończących się pinów w Arduino UNO? Zakupić Megasa? A może użyć właśnie sterownika na I2C – wówczas potrzeba jedynie 2 pinów!

My skorzystaliśmy z wymienimego sterownika PLUS biblioteki LiquidCrystal_I2C autorstwa Franka de Brabander-a. Sporo jest podobnych bibliotek, więc doinstalowywując ją sobie warto zwrócić uwagę na tą użytą przeze mnie, a nie inną. Na szybko poznaliśmy kilka metod nowego obiektu do obsługi wyświetlacza:

include <Wire.h>   // standardowa biblioteka Arduino
#include <LiquidCrystal_I2C.h> // dolaczenie pobranej biblioteki I2C dla LCD

LiquidCrystal_I2C lcd(0x27, 16, 2);
//LiquidCrystal_I2C lcd(0x3f, 16, 2);


void setup(){
  lcd.init();
  lcd.begin(16,2);   // Inicjalizacja LCD 2x16
  
  lcd.backlight(); // zalaczenie podwietlenia 
  lcd.setCursor(0,0); // Ustawienie kursora w pozycji 0,0 (pierwszy wiersz, pierwsza kolumna)
  lcd.print("pomidor!");
  delay(500);
  lcd.setCursor(0,1); //Ustawienie kursora w pozycji 0,0 (drugi wiersz, pierwsza kolumna)
  lcd.print("LCD 16x2 I2C -- hd44780");

}

int nico=7;
void loop() 
{
   lcd.backlight(); // zalaczenie podswietlenia
   lcd.setCursor(10,1);
   lcd.print(millis()/1000);
   delay(1000);
   lcd.noBacklight(); // wylaczenie podswietlenia
   lcd.setCursor(0,0);
   lcd.print(nico);
   nico=nico*2;
   delay(1000);
}

Warto ją poznać dokładniej, czytając dokumentację.

Adresacja użądzeń I2C 

Skoro do tych samych pinów SDA i SDC można podłączyć do 127 urządzeń, to skąd wiadomo, kto co nadaje? Np. jeśli chcemy użyć dwóch LCD-ków, na jedynym wyświetlać pewne informacje a na innym inne to jak kierować napisy na różne urządzenia?

Ważne jest poznanie adresu naszego użądzenia – w tym celu należy wygooglać świetny programik i2c_scanner i wgrać do Arduino z podłączonym wyświetlaczem (zresztą, nie tylko wyświetlaczem – czymkolwiek na I2C).

A jak zmienić adres w omawianym sterowniku hd44780? Bo gdy kupiny sobie dwa takie i chcemy je wykorzystać razem to będzie to niemożliwe – oba będą mieć fabrycznie te same adresy (albo 0x27 – gdy to moduł PCF8574/PCF8574T lub 0x3f – dla modułów PCF8574A). Jest możliwość zmiany adresu zwierając ze sobą (lutując) piny A012 na spodzie modułu:

Kliknij obrazek by dowiedzieć się co i jak.

(c) K.G. 2017, 2019

 

Maskotka – reaktywacja (2-gi etap)

Pan Przemek (na moją prośbę) zabrał się za Maskotkę i wymienił w niej sterowanie: zamiast modułu Bluetooth wstawił radiówkę nRF24. Trudność polegała na zrozumieniu tego, co już zostało zrobione na płycie głównej Maskotki, co jak jest tam sterowane, co wyłączyć i co gdzie podłączyć… Udało się – moduł podłączony poprawnie, sprawdzony w działaniu z tekstowym protokołem.

Warte zapamiętania: ponieważ większości pinów na pierwszym Arduino UNO została już wykorzystana, zaszła konieczność podłączenia modułu nRF24 w dość niestandardowy sposób – pin CS radiówki został połączony z A0 Arduino. To spora rozrzutność, ale cóż zrobić, gdy nie ma już wolnych pinów cyfrowych w Arduino? Został „poświęcony” analogowy, który może działać jako cyfrowy input i właśnie działa! Potrzeba matką wynalazców 😛

W kolejnym tygodniu wymiana kół i komunikacja pomiędzy dwoma Arduino UNO w Maskotce.

(c) K.G. 2019

Ws2812b oraz potencjometr

Kolejne spotkanie z kolorowymi paskami LEDów WS2812B – tym razem sterowane potencjometrem.

UWAGA: TinkerCAD (czyli „wirtualne Arduino”) został wzbogacony właśnie o omawiane moduły RGB. Trzeba przyznać, że działają całkiem fajnie — dlatego zachęcam do zabawy nawet dla osób, które nie mają ich w realu!

Zadanie do wykonania

Zaświecić LEDem o numerze zgodnym z ustawioną wartością na potencjometrze. Inne LEDy mają być wyłączone. Kręcimy potencjometrem – „przesuwamy” LEDa. Kolor LEDa? Twój ulubiony 😉

Pozycję potencjometru odczytujemy przez funkcję analogRead(A0) – o ile środkowy pin potencjometru został podłączony do pinu A0 właśnie. W potencjometrze podłączone są także piny do GND i 5V w Arduino – zgodnie z powyższym rysunkiem.

Numer LEDa – dzielenie całkowite!

analogRead(a) zwraca wartość z przedziału 0..1023, natomiast my mamy tylko (aż?) 16 LEDów. Należy więc przeliczyć wartość a analogRead() na numer pinu… w prosty sposób:

nr = analogRead(A0)*16/1024;    //dobrze!

Ważna jest tutaj kolejność obliczeń, bo gdy napiszemy

nr = 16/1024*analogRead(A0);   //źle :-(

to choć z matematycznego punktu widzenia oba wyrażenia są równoważne, to jednak w drugim przypadku najpierw wykonuje się dzielenie – a jest tutaj dzielenie całkowite (bo zarówno 16 jak i 1024 są liczbami całkowitymi) więc wynosi zero. A zero mnożone przez cokolwiek da zawsze zero. Pierwszy wzorek jest poprawny, bo najpierw wykonamy mnożenie analogRead(A0)*16 a dopiero potem wynik tego iloczynu dzielimy (dzielenie całkowite, ale teraz nie przeszkadza) – i otrzymujemy liczbę z przedziału 0..15.

Dyskoteka – czyli efekt stroboskopu

Skoro mamy już ustalony numer LEDa, to teraz możemy zgasić wszystkie inne, a potem zaświecić tego konkretnego – o numerze nr właśnie. Ale ale… za chwilkę ponownie ustalimy położenie potencjometru, ponownie wyliczmy nr LEDa i co – ponownie zgasimy wszystkie, aby włączyć ten o numerze nr? Obawiam się, że w związku z wyłączaniem i włączaniem otrzymamy efekt „dyskoteka” (stroboskopu). Poza tym to nieprofesjonalne 😛

#include <Adafruit_NeoPixel.h>
     
#define PIN 7 
#define NDIOD 16 
int delta=1024/NDIOD;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NDIOD, PIN, NEO_GRB + NEO_KHZ800);

int nr,poprzedni;

void setup() {
    Serial.begin(9600);
    pixels.begin(); 

    nr=analogRead(A0)/delta;
    pixels.setPixelColor(nr, 10, 0, 0);
    pixels.show();
    poprzedni = nr;
}

void loop() {
    nr=analogRead(A0)/delta;
//    Serial.println(nr); 
        
    if(nr != poprzedni){
      //AKCJA!
      Serial.print("zmiana! nr="); 
      Serial.println(nr); 
      pixels.setPixelColor(poprzedni, 0, 0, 0);
      poprzedni = nr;
      pixels.setPixelColor(nr, 10, 0, 0);
      pixels.show();
    }
}

Powyższy kod rozwiązuje efekt stroboskopu – dzięki dodatkowej zmiennej poprzedni zapamiętujemy, który LED był ostatnio włączony. W głównej pętli programu ustalamy numer LEDa zgodnego z ustawieniami potencjometru, ale „akcja” dzieje się tylko wówczas, gdy zmieniliśmy ustawienia potencjometra. Dobrze dodać wypisywanie na monitor szeregowy informacji tylko wóczas, gdy dzieje się „akcja” – dzięki temu mamy pewność, że napisy (a tym samym i sama „akcja”) wykonuje się jedynie w przypadku zmian ustawień potencjometru.

Uwaga #1 – int VS float

Proszę zwrócić uwagę, że zarówno zmienna nr jak i zmienna poprzedni są zadeklarowane jako int, co w zupełności wystarczy – nie ma potrzeby używać arytmetyki liczb rzeczywistych (powolnych, szczególnie na Arduino).

Uwaga #2 – być bardziej PRO 😛

Zwróć uwagę, że wywołuję funkcję pixels.show() tylko wówczas, gdy coś się zmieniło – niby po co miałbym wołać tą funkcję za każdym razem w loop()-ie, niezależnie od tego, czy coś się zmieniło czy nie? W końcu pixels.show() programuje LEDy zgodnie ze wcześniejszymi ustawieniami pixels.setPixelColor(), więc nie ma senesu wołać ją bez potrzeby.

Zadanie #2 (także tinkercad)

Zmienić kod tak, by świeciły się LEDy od pierwszego do tego konkretnego, zgodnego z ustawieniami potencjometru. Unikać efektu „dysko”.

Zadanie #3

Joystick to dwa potencjometry – można tu go wykorzystać i oprogramować tak, by ruch na osi X zmieniał numer LEDa (jak dotychczas), a ruch na osi Y zmieniał kolor – w jakiś zabawny sposób (może tęcza?). Proszę się pochwalić wynikami!

(c) K.G. 2019

Dni Otwartego Biznesu – oferta PZU

Otrzymaliśmy zaproszenie do uczestnictwa w szkoleniach dla studentów – PZU oferuje bilety PKP oraz jeden ciepły posiłek.

Dni Otwartego Biznesu trwają od 2 do 13 grudnia 2019 r. Każdy dzień poświęcony jest innemu zagadnieniu. Tematy omawiane na szkoleniach to m.in. robotyka, DevOps, sprytne utrzymanie aplikacji i wiele więcej. Obejrzyj załącznik z wszystkimi tematami warsztatów, opisami i datami.

Na zapisy na szkolenie studenci mają czas do 10 listopada 2019 r. Aplikacje zbierane są poprzez stronę https://www.pzu.pl/kariera/studenci/dni-otwartego-biznesu

Każdy ma możliwość zapisania się na wszystkie warsztaty jednak otrzyma zaproszenie tylko na jeden dzień szkoleń.

Szkolenia odbywają się w Warszawie w siedzibie Grupy PZU (około 10-15 min od dworca PKP). Organizator zapewnia każdemu uczestnikowi bilety PKP oraz ciepły posiłek w czasie warsztatów. Szkolenia odbywają się w godzinach 10-17.

Każdy z uczestników otrzymuje certyfikat potwierdzający zdobycie nowych kwalifikacji. Szkolenia prowadzone są przez menadżerów co najmniej średniego szczebla.

(c) K.G. 2019

Ws2812b, potencjometr oraz Monster

Kolejne spotkanie z kolorowymi paskami LEDów WS2812B zapętlonymi w koło – sterowane potencjometrem. Dla nowych studentów to fajna zabawa – odczytujemy napięcie funkcją analogRead() a efekt przekładamy na wędrujący w kole piksel… Prace trwają 😉

Do zapamiętania:

  1. elektronika/fizyka: dzielnik napięć
  2. informatyka: dzielenie całkowite i dzielenie rzeczywiste, rzutowanie typów
  3. Arduino: sprawdź wartość wyjścia 5V i dostosuj formułki do obliczania odczytanego napięcia
  4. Arduino: przetworniki ADC – rozdzielczość pomiaru, błąd odczytu.
W tinkercadzie można też sprawdzić potencjometr!

Efekty w kółku – bez delay()a

Dwie strony naszego kółka wyświetlają różne kolory – rozpoczynamy od góry kółka, gdzie kolejne piksele zapalają się co zadany interwał czasu, a po „dotarciu” do środka kółka – piksele wracają (gaszą się). Ale… lewa strona jest cztery razy szybsza niż prawa strona. Na dodatek nie można używać funkcji delay()!

D E M O

Brawo dla Pana Łukasza!

Monster – moduł sterownika silnika DC

Mocny sterownik do silników DC – prąc pracy 12A (chwilowy 30A), napięcie pracy 16 V – bije na głowę poczciwego L293x i dlatego jest MONSTEREM 😉 12A to brzmi dumnie, ale w postaci BEZ RADIATORA to możemy pracować do 6A.

Układ nazywa się VNH2SP30 i jest warty polecenia, ale uwaga – w wielu sklepach (nawet tych renomowanych) można kupić uszkodzone sterowniki, które kręcą kółkami tylko w jedną stronę! Sprzedawcy wymieniają wadliwe sztuki, ale piszę to w celu zaoszczędzenia nerwów – gdy coś nie działa a powinno.

Moduł Monster dla pojedynczego silnika DC – ze stronki https://protosupplies.com/product/vnh2sp30-single-monster-motor-module/

Warto przeczytać opis użycia modułu oraz jego specyfikację, gdy coś nie idzie tak, jak powinno.

Generalnie wszystko jest proste: 5V, GND do odpowiednich na Arduino, INA oraz INB do dowlonych pinów w Arduino, którymi chcemy sterować silniczkiem (tak samo jak w L293x), ale pin PWM musi być podłączony do Arduino – w przeciwnym razie sterowanie odbywa się z ZERO procent mocy. Pin EN (enabled) może być podłączony do 5V w Arduino, choć i tak jest w stanie wysokim, więc układ jest włączony, pin CS to diagnostyczny, na razie pomijamy (odczytuje prąd pracy układu, temperaturę…). Silnik podłączamy do OUTA oraz OUTB, natomiast zasilanie do + (plus) oraz – (minus) po przeciwnej stronie modułu – uwaga na właściwą polaryzację! Tutaj ważne jest ten plus i minus, oby nie odwrotnie!

No i ostatnia ISOTONA kwestia: zasilanie musi być przynajmniej 5.5V, nie może być mniej!

(c) K.G. 2019