Pojďme programovat elektroniku | Cyklistika

Programování elektroniky: Vyrobíme blikačku s Bluetooth pro pořádné cyklogeeky

  • Moje cyklistické blikačky nic nevydrží
  • Došla mi trpělivost a začal jsem si stavět vlastní
  • Pomohlo 5 LED, čip ESP32, tranzistor a 3D tiskárna

Při jednom z letních cyklovýjezdů jsem ztratil svoji oblíbenou zadní blikačku z AliExpressu, a protože dny se už citelně zkracují, rychle jsem objednal značkové světlo s akumulátorem z prvního e-shopu pro cyklisty.

Blikačka ale nevydržela ani jeden pořádný prázdninový déšť, a tak jsem rezignovaně sedl do své redakční dílničky a začal hledat cokoliv, z čeho by se dala poskládat provizorní zadní svítilna pro těch několik posledních teplých týdnů letošního roku.

Našel jsem hrstku 5mm RGB LED se společnou anodou (+), několik konfekčních bipolárních NPN tranzistorů 2N2222 nejistého původu z Číny, kolébkový spínač a zásobu českých úsporných desek řady ESP32-LPKit s Wi-Fi/Bluetooth čipem Espressif ESP32.

Video: Stavíme cykloblikačku s BLE od A do Z

Deska je vyzbrojená nabíjecím čipem TP4054  a konektorem pro jednočlánkový lithiový akumulátor, takže mám v podstatě všechno ke stavbě elementární svítilny, kterou budu moci ovládat i skrze Bluetooth Low Energy třeba ze svého mobilního telefonu. Podívejte se na video výše.

Po zapnutí začne blikat pět rudých LED

Z dvaceti RGB LED nakonec v červeném kanálu svítilo jen pět exemplářů, to ale bohatě stačí. Pětimilimetrová dioda je už totiž přece jen robustnější a mohu do ní bezpečně pustit 20-30 miliampérů elektrického proudu, který se promění v docela solidní záři.

708d3855-b750-46bd-8d4e-0f8b6a299b1e
RGB LED se společnou anodou a katodou. My dnes použijeme pouze červený kanál R

LEDky zapojím paralelně, takže celkový protékající proud bude součtem těch dílčích a neměl by přesáhnout 150 mA. Druhým důvodem co nejnižšího proudu při zachování dobré svítivosti bude skutečnost, že nebudu světýlka napájet přímo z baterie, ale pro jednoduchost zapojení z 3,3V regulovaného zdroje na desce ESP32-LPKit.

3a843f9b-1a4b-4dc4-9ae9-8fe325ff763f56b954ee-7e8b-48a7-9802-2e0f7f18010e4dd9dd9c-6ab2-4db0-9a60-de80545629bf
Postupná evoluce prototypu BLE lampy od nepájivého pole a zkoušení difuzního krytu po venkovní konstrukci, která už pomaličku začíná připomínat skutečný výrobek z obchodu

Destička je vyzbrojená stabilizátorem HT7833, který při napětí 3,3 V nabídne až 500 mA elektrického proudu. Regulátor nicméně živí i samotný čip ESP32 a nechci, aby se zbytečně přehříval. Naštěstí nebudu pracovat s energeticky náročnou Wi-Fi a zároveň snížím takt procesoru na 80 MHz.

Tranzistor bude spínat silový okruh

Zdroj elektřiny bychom tedy měli, ale ještě musím nějak zajistit blikání a omezit protékající proud v obvodu svítivých diod. O to se postará naprosto konfekční bipolární NPN tranzistor 2N2222. který nechybí snad v žádné sadě pro kutily.

2c874205-5027-4470-ba87-2244200e5241
Bipolární NPN tranzistor 2N2222 a jeho schéma zapojení

I v tomto případě se jedná o exemplář z AliExpressu, o jehož kvalitě, specifikaci a původu mohu jen spekulovat, když ale nahlédnu do dokumentace obdobných modelů, měly by si bez problému poradit s protékajícím proudem až okolo 600 mA. V praxi ale bude mnohem menší. Sníží jej zapojený rezistor a samotné blikání, během kterého bude každé světýlko spalovat energii vždy jen polovinu periody.

5adfe71a-ebb3-48ef-b927-360e301640b2
Zjednodušené schéma zapojení LED s tranzistorem

Tranzistor v typickém obalu TO-92 má tři vodiče: emitor, bázi a kolektor. Kolektor připojíme ke katodám (-) pětice paralelně zapojených LED, emitor do referenční země GND naší prototypovací desky ESP32-LPKit a na bázi budeme konečně zvedat imaginární polovodičová stavidla, která spojí, nebo rozpojí obvod mezi kolektorem a emitorem. Když stavidla otevřeme, začne obvodem protékat proud a světýlka se rozsvítí.

Bázový rezistor

Stavidla budeme otevírat nastavování logického stavu na bázi, kterou připojíme k některému z volných pinů GPIO na desce ESP32-LPKit. V našem experimentu použiji pin číslo 27.

1d718a5f-e7a7-49e2-adaa-80da1a083f58
Testovací zapojení na nepájivém poli jen se čtyřmi LED a silnějším rezistorem báze

Kdybych ale na tomto pinu jen tak nastavil vysoký logický stav HIGH, stavidla budou mít jen relativně nízký elektrický odpor, otevřou se naplno, obvodem pětice LED začne protékat příliš vysoký proud (horním limitem je oněch 500 mA, které vyrábí stabilizátor HT7833) a mohu je rychle zničit a zbytečně přetížit celou desku.

Proto mezi náš pin GPIO 27 a bázi připojím rezistor s vhodným odporem. Mohu začít třeba na odporu 10 kΩ a multimetrem měřit protékající proud, který by neměl být vyšší než násobek počtu paralelně připojených LED a proudu, který zvládnou.

484c8ec8-2839-4044-9783-b3f2fe7502b7
Obvodem se čtyřmi paralelně zapojenými LED nyní protéká 64 mA, což dělá 16 mA na každou z nich a jsem v bezpečném limitu 20-30 mA 

Na fotografii níže mám na nepájivém poli zapojené pouze 4 LED a 10kΩ bázový rezistor. Stolní multimetr měří odběr zhruba 64 mA, což dělá 16 mA na LED. Jsem tedy v normě a světýlka přitom už svítí poměrně jasně.

V hotovém obvodu nicméně budu mít zapojeno rovnou pět LED s bezpečných odběrem 20-30 mA, a tak mohu stavidla ještě o něco otevřít volbou slabšího rezistoru se zhruba polovičním odporem 4,7 kΩ. Schválně jsem zvolil hodnoty, které jsou opět velmi typické a nechybějí v žádné sadě drobných rezistorů pro kutily.

LED deska na pájivém poli

V produkční verzi kvůli pevnosti elektrických spojů a rozměrům samozřejmě nebudu moci použít nepájivé pole. Kdybych měl o něco více času, v EasyEDA si navrhnu vlastní tištěný spoj, který mi vyrobí třeba asijské studio JLCPCB. Už jsme si to ukázali v praxi na začátku roku.

Já ale vyrábím blikačku na poslední chvíli, a tak jsem nakonec sáhl po méně elegantním pájivém poli – tedy perforované destičce, do které se drátky nezacvakávají, ale pevně se připájí.

fd292a9f-a67c-48a7-98fa-f428552e9251
Za toto asi cenu za průmyslový design nedostanu, ale funguje to

Ano, je to ošklivé a příští verze už bude tištěná a oboustranná, svůj účel to ale splnilo. Na pájivém poli je matice 5 rudých LED a zároveň obvod se spínacím tranzistorem a regulačním bázovým rezistorem. Z modulu vedou tři vodiče pro připojení 3,3V (červený), systémovou zemi GND (černý) a samotný signální zelený vodič, který připojíme na pin 27 a softwarovým nastavováním vysokého a nízkého logického stavu budeme zapínat a zhasínat světýlka.

Ještě změřit akumulátor

Každá chytrá BLE lampička musí umět posílat údaj o stavu baterie. Samotná deska ESP32-LPKit ji změřit nedokáže, takže si musíme poradit sami. Nejjednodušší možnosti je obvod děliče napětí se dvěma rezistory jako na obrázku níže.

830ca662-c776-4e61-a8d7-084e85582395
Dělič nám pomůže se snížením napětí baterie, abychom jej mohli přečíst pomocí A/D převodníku na čipu ESP32

Když bude jednočlánkový lithiový akumulátor nabitý na maximum, dosáhne napětí zhruba 4,2 V. Pomocí děliče s rezistory 27 kΩ a 100 kΩ (nebo jinou kombinací, viz různé kalkulačky) jej můžeme snížit na bezpečné pracovní napětí desky 3,3 V, odečíst skrze A/D převodník a funkci analogRead na některém z analogových pinů a přepočítat zpět na analogové napětí.

Nemusí to být ale nejpřesnější, druhou možností jsou proto hotové moduly voltmetrů a ještě o kousíček sofistikovanější senzory upravené pro detekci stavu lithiového článku. K těm nejpopulárnějším mezi kutily patří třeba moduly s čipem MAX17043 od Maxim Integrated, které komunikují skrze I2C a změří jak napětí baterie, tak odhadovaný procentuální stav nabití.

Senzor lithiového článku DFR0563

Na laciný AliExpress se mi ale čekat nechtělo, a tak jsem na Farnellu narychlo objednal mnohem dražší destičku DFR0563 s totožným čipem. DFRobot pro něj připravil i vlastní knihovnu, kterou stáhnete z GitHubu, nebo nainstalujete ze správce přímo v Arduinu.

3c552d3d-425a-4e44-826e-98081f89161e
Akumulátorové čidlo DFR0563

Vedle komunikace po sběrnici I2C nabízí MAX17043 ještě signál ALRT – alert –, na kterém se změní logický stav v případě, kdy nabití lithiového článku klesne pod stanovenou hranici. Pro jednoduchost dnešní ukázky ale toto asynchronní varování

Schránka z 3D tiskárny

Co nám zbývá? Nějaký hezký obal z 3D tiskárny. Bude mít tvar strohého válce, ve kterém bude zasunutá relativně dlouhá deska ESP32-LPKit, která bude definovat jeho rozměry. Později bych mohl desku vyměnit za něco menšího, pro jednoduchost příkladu se mi ale hodí, že je vyzbrojená nabíjecím obvodem.

eacdbe6d-b943-4e0e-976a-ae6a7e5e06b3
Příprava minimalistické krabičky v Tinkercadu

Ve válci bude otvor pro kolébkový retro spínač a na zadní straně otvor pro šroub k ukotvení úchytu na tyč sedlovky. Čelní stěnu válce, který mohu vytisknout v libovolné barvě, pak uzavře krytka vytištěná z čiré – transparentní hmoty.

Princip domácího FFF/FDM 3D tisku a postupné pokládání natavené struny se postarají o to, aby plast fungoval jako docela použitelný difuzér, který světlo rozptýlí do všech stran a za tmy bude spojitě svítit celá stěna válce.

d1ce0e76-4823-4719-8569-717d047a29b6c0453b08-3230-470a-9682-3ca310dcbf8bb5227b00-9374-4008-8698-a7c8ee8d7ad5
A takto vypadá výsledek

Zatímco průhlednou krytku vytisknu z odolného materiálu PETG, tubus a úchyt na sedlovku z neméně robustního tiskového plastu ASA. Pokud několikatýdenní testování v terénu vyjde, později mohu sáhnout po ještě odolnějších termoplastech na bázi karbonu, anebo po těch ohebných, aby úchyt na sedlovce pohltil všechny otřesy a neulomil se po prvním dnu.

BLE služby a charakteristiky

Když Espressif před lety představil svůj kombinovaný čip ESP32 s podporou Wi-Fi a Bluetooth, podpora modrého zubu byla zpočátku velmi omezená a slabě dokumentovaná. Už se to ale zlepšilo a to i pro jednoduché programování v Arduinu, pro které je k dispozici i několik knihoven a hromada příkladů, jenž používají úsporný protokol BLE.

Základní principy práce s Bluetooth Low Energy jsme si v našem seriálu už ukázali na desce Arduino Nano 33 BLE s mikrokontrolerem nRF52840, takže jen stručně:

Když skrze BLE zapisujeme, nebo čteme nějaké hodnoty – třeba právě stav baterie v naší blikačce – říkáme tomu BLE charakteristiky. Charakteristiky nicméně nepoletují vzduchem jen tak nazdařbůh, ale jsou zapouzdřené v BLE službách, které slouží pro jejich snadnou organizaci. Služby i charakteristiky zároveň mají svůj jedinečný 128bitový identifikátor UUID, který se zpravidla zapisuje v textové hexadecimální podobě.

d9458fd7-6d3a-49bb-97aa-293c49849083
Architektura obvyklé stavové komunikace BLE organizovaná do služeb a charakteristik

Některé jsou standardní a rozumějí jim aplikace (třeba právě UUID pro službu a charakteristiku stavu baterie), jiné slouží jen pro vaše interní organizační účely a náhodný UUID si pak můžete vygenerovat třeba na webu guidgenerator.com.

Ještě jednou a pro jistotu: Nejedná se o žádnou centrální registraci, není to žádná evidovaná SPZka. Je to jen vaše naprosto anonymní ID, které by ale mělo být vždy unikátní, aby nedošlo ke kolizi, kdyby si někdo zvolil to samé.

Náš firmware bude používat tyto služby a jejich vnořené charakteristiky:

  • Světlo (Vlastní UUID: 19B10000-E8F2-537E-4F6C-D104768A1214)
    • Režim (Vlastní UUID: 19B10001-E8F2-537E-4F6C-D104768A1214)
    • Jas (Vlastní UUID: 19B10002-E8F2-537E-4F6C-D104768A1214)
  • Baterie (Standardní UUID: 180F)
    • Stav nabití (Standardní UUID: 2A19)

Ahoj, jsem cyklolampička a nabízím tyto služby

Díky této architektuře může náš firmware po zapnutí počítače začít hulákat do okolí (tzv. advertising) své jméno, BLE MAC a také údaje o jedné ze služeb. Už z ní lze pak přečíst různé informace i bez oboustranného spojení.

V případě oboustranného spojení pak druhá strana získá kompletní seznam služeb a jejích charakteristik, které naše cyklolampa nabízí.

READ, WRITE, NOTIFY

Každá z charakteristik může kombinovat různé příznaky, které stanovují, jestli se bude jednat o čtení, zápis, anebo notifikaci (včetně). Naše charakteristiky pro nastavení režimu blikání a jas tedy budou mít příznaky READ a WRITE, aby druhá strana zjistila aktuální stav (READ) a mohla jej změnit (WRITE), no a charakteristika stavubaterie bude mít příznaky READ a NOTIFY. Stav baterie tedy dokáže druhá strana na vyžádání přečíst, ale stejně tak může zareagovat pokaždé, když vyšleme notifikaci.

Naše charakteristiky budou mít velikost jednoho bajtu:

Režim:

  • 0: Zhasnuto
  • 1: Svítím
  • 2-255: Blikám s prodlevou, která odpovídá násobku 50 ms a čísla-1. Takže hodnota 2 spustí blikání s prodlevou 50 ms, hodnota 3 s prodlevou 100 ms a tak dále

Jas:

  • 0-255: Jas pomocí obdélníkového PWM signálu. 0 odpovídá nejslabšímu jasu, 255 pak tomu nejsilnějšímu

Stav baterie:

  • 0-100: Standardní údaj v celých procentech. Jelikož mi nedorazil zmíněný měřící čip na lithiový akumulátor, budu posílat fiktivní hodnotu pro ověření funkce

Kdo bude druhou stranou?

Takže firmware bychom měli (jeho komentovaný kód najdete v závěru článku), ale kdo bude vlastně tou druhou stranou? Může to být třeba mobilní aplikace na vašem chytrém telefonu. Tu si ale naprogramujeme až příště, článek by se nám totiž nafouknul do obřích rozměrů.

9ec13dca-03b4-454a-a5bc-84a9217bcdf538834279-9376-446e-8dab-db062def62f25b4e61cc-cf00-45b9-879e-4c73b43dcba6024e41a8-dafc-48bd-9be5-37ff08ae6a59
Testovací spojení s BLE lampou skrze nRF Connect for Mobile

Namísto toho funkci ověříme pomocí mobilního BLE analyzátoru nRF Connect for Mobile od severského Nordicu. K dispozici je pro iOS i Android a dokáže zobrazit a ovládat libovolné Bluetooth Low Energy zařízení v dosahu.

9f0edd86-69c0-446c-b560-9b184259aac854a7debc-af4b-4206-b189-b682e169529d
Testovací spojení s lampou skrze nRF Connect for Desktop a speciální BLE přijímač

Stejný analyzátor nRF Connect for Desktop můžete spustit i na velkém počítači, v takovém případě ale budete potřebovat některou z BLE destiček od Nordicu – třeba nRF52840 Dongle – a speciální firmware, který ji promění v univerzální přijímač.

A to je vše. Cyklolampička funguje, po aktivaci bliká a stejně tak nabízí vzdálené ovládání pomocí Bluetooth Low Energy. V úvodním videu se podívejte na její ostrý test ve večerním Brně.

Zdrojový kód pro čip ESP32 a prostředí Arduino

Kód níže vyžaduje nainstalování podpory pro čipy ESP32 do vývojového prostředí Arduino.

// Knihovny pro praci s BLE na cipech ESP32
// Jsou soucasti podpory pro cipy ESP32 v Arduinu
// https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

// Knihovna DFRobot MAX17043 pro praci s cidlem akumulatoru
// https://github.com/DFRobot/DFRobot_MAX17043
// Lze doinstalovat skrze Projekt->Pridat knihovnu->Spravovat knihovny
#include <DFRobot_MAX17043.h>

// Pin, ktery bude ovladat radu LED
#define PIN_VYKONNA_LED 27

// 128b HEX UUID sluzby Svetlo a jeho charakterisrtik Rezim a Jas
// Muzete vygenerovat treba zde https://www.guidgenerator.com/online-guid-generator.aspx,
// nebo zvolit libovolny vlastni, ale mel by byt dostatecne unikatni, aby nedoslo ke kolizi
#define SVETLO_SLUZBA "19B10000-E8F2-537E-4F6C-D104768A1214"
#define REZIM_CHARAKTERISTIKA "19B10001-E8F2-537E-4F6C-D104768A1214"
#define JAS_CHARAKTERISTIKA "19B10002-E8F2-537E-4F6C-D104768A1214"

// Standardni zkracene 16bit HEX UUID pro sluzbu Baterie a charakteristiku Stav baterie
// Registr standaerdnizovanych 16bit HEX UID: https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf
#define BATERIE_SLUZBA "180F"
#define BATERIE_CHARAKTERISTIKA "2A19"

// Trida naseho BLE serveru, ktery pobezi na cipu ESP32
BLEServer* server = NULL;

// Tridy sluzeb a charakteristik pro ovladani svetla
BLEService *svetlo_sluzba = NULL;
BLECharacteristic* rezim_charakteristika = NULL;
BLECharacteristic* jas_charakteristika = NULL;

// Trida sluzby a charakteristiky pro stav baterie
BLEService *baterie_sluzba = NULL;
BLECharacteristic* baterie_charakteristika = NULL;

// O periodicke blikani a zasilani notifikaci se stavem baterie
// se postaraji dve asynchronni ulohy vrstvy FreeRTOS,
// na ktere je postavene SDK cipu ESP32
TaskHandle_t ulohaBlikani;
TaskHandle_t ulohaBaterie;

// Promenna se stavem svetla
bool stavLED = false;

// Promenna s rezimem svetla
// Ve vychozim stavu hodnota 3, takze blikani kazdych 100 ms
uint8_t rezimLED = 3;

// Promenna s jasem 0-255
// Ve vychozim stavu maximalni jas
uint8_t jasLED = 255;

// Trida cidla akumulatoru
DFRobot_MAX17043 max17043;

// Perioda zasilani notifikaci se stavem baterie
// Ve vychozim stavu 5 000 milisekund
uint32_t baterieProdlevaAktualizaceMs = 5000;

// Promenna se stavem baterie 0-100 %
// Protoze nam zatim nedorazil merici cip MAX17243,
// budeme zasilat fiktivni a postupne se snizujici hodnotu
uint8_t baterieStav = 100;

// Trida, ktera se zpracuje pri udalosti BLE serveru
class priUdalostiServeru: public BLEServerCallbacks {
    // Pri pripojeni klienta probudime ulohu,
    // ktera zasila notifikaxce se stavem baterie
    void onConnect(BLEServer* server) {
      Serial.println("PRIPOJENO");
      vTaskResume(ulohaBaterie);
    };

    // Pri odhlaseni klienta, uspime ulohu,
    // ktera zasila notifikace se stavem baterie
    // a zaroven aktivujeme advertising,
    // tedy hulakani do prostoru, ze jsme cyklosvetlo
    // a jsme k dispozici pro pripojeni
    void onDisconnect(BLEServer* server) {
      vTaskSuspend(ulohaBaterie);
      Serial.println("ODPOJENO");
      server->startAdvertising();
      Serial.println("Cekam na spojeni...");
    }
};

// Trida, ktera se zporacuje, pokud charaktrreristika pro rezim svetla
// obdrzi od klienta nova data
class bleNovaDataRezim: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *rezim_charakteristika) {
      // Precteme surove bajty, mel by to  byt jen jeden bajt 0-255
      uint8_t *bajty = rezim_charakteristika->getData();
      Serial.printf("NOVA DATA, REZIM: %d, 0x%X\r\n", *bajty, *bajty);
      // Podle hodnoty bajtu nastavim promennou rezimLED
      // Pozastavime ulohu s blikanim
      rezimLED = *bajty;
      vTaskSuspend(ulohaBlikani);

      // Pokud je rezim 0, zhasneme LED
      if (rezimLED == 0) {
        stavLED = false;
        Serial.println("Vypinam LED");
        analogWrite(PIN_VYKONNA_LED, 0);
      }

      // Pokud je rezim 1, rozsvitime LED
      else if (rezimLED == 1) {
        stavLED = true;
        Serial.println("Zapinam LED");
        analogWrite(PIN_VYKONNA_LED, jasLED);
      }

      // Pokud je rezim vetsi nez 1,
      // spustime ulohu blikani s prodlevou hdonota-1 * 50 ms
      else {
        Serial.printf("Spoustim blikani s prodlevou %d ms\r\n", ((rezimLED - 1) * 50));
        vTaskResume(ulohaBlikani);
      }
    };
};

// Analogicky zpracujeme nova data z charakteristiky Jas
class bleNovaDataJas: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *jas_charakteristika) {
      uint8_t *bajty = jas_charakteristika->getData();
      Serial.printf("NOVA DATA, JAS: %d, 0x%X\r\n", *bajty, *bajty);
      jasLED = *bajty;
    };
};

// Hlavni funkce setup se zpracuje hned na zacatku
void setup() {
  // Nastartujeme seriovou linku na USB pro ucely ladeni
  Serial.begin(115200);
  delay(100);
  // Nastavime pin pro LED na vystup
  pinMode(PIN_VYKONNA_LED, OUTPUT);

  // Nastartujeme meric lithiove baterie
  Serial.print("Startuji cidlo baterie MAX17043... ");
  if(max17043.begin() == 0) Serial.println("OK");
  else Serial.println("CHYBA");

  // Nastavime BLE zarizeni s nazvem "Cyklolampa"
  BLEDevice::init("Cyklolampa");
  // Vytvorime BLE server
  server = BLEDevice::createServer();
  // Pokud se na serveru neco stane, zavola se trida priUdalostiServeru
  server->setCallbacks(new priUdalostiServeru());

  // Zaregistrujeme BLE sluzbu Svetlo
  svetlo_sluzba = server->createService(SVETLO_SLUZBA);

  // Zaregistrujeme BLE charakteristiku Rezim sluzby Svetlo
  // Z charakteristiky muzeme cist a zapisovat do ni
  rezim_charakteristika = svetlo_sluzba->createCharacteristic(
                            REZIM_CHARAKTERISTIKA,
                            BLECharacteristic::PROPERTY_READ   |
                            BLECharacteristic::PROPERTY_WRITE
                          );
  // Pri udalosti charakterisrtiky zavolame tridu bleNovaDataRezim
  rezim_charakteristika->setCallbacks(new bleNovaDataRezim);

  // Analogicky zaregistrujeme charakteristiku Jas
  jas_charakteristika = svetlo_sluzba->createCharacteristic(
                          JAS_CHARAKTERISTIKA,
                          BLECharacteristic::PROPERTY_READ   |
                          BLECharacteristic::PROPERTY_WRITE
                        );
  jas_charakteristika->setCallbacks(new bleNovaDataJas);
  // Spustime sluzbu Svetlo
  svetlo_sluzba->start();

  // Analogicky zaregistrujeme BLE sluzbu a charakterisrtiku se stavem baterie
  // Lisi se v tom, ze ma priznak NOTIFY a muze posilat notifikace
  baterie_sluzba = server->createService(BATERIE_SLUZBA);
  baterie_charakteristika = baterie_sluzba->createCharacteristic(
                              BATERIE_CHARAKTERISTIKA,
                              BLECharacteristic::PROPERTY_READ   |
                              BLECharacteristic::PROPERTY_NOTIFY
                            );
  // Tady druhe strane rikame, ze muze pracovat s notifikacemi
  baterie_charakteristika->addDescriptor(new BLE2902());
  baterie_sluzba->start();

  // Nastavime vychozi hodnoty jednobajtovych charakteristik
  rezim_charakteristika->setValue((uint8_t*)&rezimLED, 1);
  jas_charakteristika->setValue((uint8_t*)&jasLED, 1);
  baterie_charakteristika->setValue((uint8_t*)&baterieStav, 1);

  // Nakonfigurujeme a spustime advertising,
  // tedy hulakani do prostoru, kdo jsme a co umime
  BLEAdvertising *oznamovani = BLEDevice::getAdvertising();
  oznamovani->addServiceUUID(SVETLO_SLUZBA);
  oznamovani->setScanResponse(false);
  oznamovani->setMinPreferred(0);
  BLEDevice::startAdvertising();

  // Vytvorime FreeRTOS ulohu, ktera spusti funkci loopBlikani
  // Uloha bude mit k dispozici 1000B RAM a velmi vysokou prioritu
  // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html#_CPPv411xTaskCreate14TaskFunction_tPCKcK8uint32_tPCv11UBaseType_tPC12TaskHandle_t
  xTaskCreate(loopBlikani, "BLIKANI", 1000, NULL, configMAX_PRIORITIES - 1, &ulohaBlikani);
  // Pokud je rezim LED mensi nez 2 (vypnuto/plny svit), ulohu pozastavime, protoze neblikame
  if (rezimLED < 2) vTaskSuspend(ulohaBlikani);

  // Je-li vychozi rezim roven 1,
  // rozsvitime LED
  if (rezimLED == 1) {
    stavLED = true;
    analogWrite(PIN_VYKONNA_LED, jasLED);
  }

  // Vytvorime druhou FreeRTOS ulohu, ktera nam tentokrat spusti funkci loopBaterie
  // Uloha ma 4000B RAM, protoze bude pracovat s BLE charakteristikami a cidlem baterie
  // Uloha ma velmi nizkou prioritu
  xTaskCreate(loopBaterie, "BATERIE", 4000, NULL, 1, &ulohaBaterie);
  // A hned ulohu pozastavime, dokud totiz neni pripojeny klient, nepotrtebujeme ji
  vTaskSuspend(ulohaBaterie);

  // Hotovo a cekame na spojeni
  // Podle vychoziho stavu promenne rezimLED lampa bud sviti, je zhasnuta, nebo blika
  Serial.println("Cekam na spojeni...");
}

// Smycka loop je povinna, ale je v nasem priapde prazdna
// veskera cyklicka logika se toti odehrava v uxlohach FreeROS
void loop() {
  ;
}

// Funkce s nekonecnou smyckou pro zasilani notifikaci se stavem baterie
// Zivotni cyklus funkce ridi FreeRTOS a jeho spravce uloh
// Pokud pzoastavime ulohu, pozastavi se i beh smycky
void loopBaterie(void *parametry) {
  while (true) {
    float baterieNapeti = max17043.readVoltage() / 1000.0f;
    baterieStav = (uint8_t)max17043.readPercentage();
    Serial.printf("Napeti baterie: %.2f V, nabiti baterie: %d %%\r\n", baterieNapeti, baterieStav);
    Serial.println("Posilam BLE notifikaci se stavem baterie");
    baterie_charakteristika->setValue((uint8_t*)&baterieStav, 1);
    baterie_charakteristika->notify(true);
    vTaskDelay(pdMS_TO_TICKS(baterieProdlevaAktualizaceMs));
  }
}

// Funkce s nekonecnou smyckou pro cyklicke spinani LED
// I tuto funkci ridi FreeRTOS, a tak ji muzeme pozastavit
// a znovu nastartovat, jka se nam to hodi
void loopBlikani(void *parametry) {
  while (true) {
    if (stavLED) analogWrite(PIN_VYKONNA_LED, jasLED);
    else analogWrite(PIN_VYKONNA_LED, 0);
    stavLED = !stavLED;
    vTaskDelay(pdMS_TO_TICKS((rezimLED - 1) * 50));
  }
}
Diskuze (27) Další článek: Než přišel WhatsApp. Top 5 kecálků z historie od ICQ po XChat

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,