Koronavirus | Programování | Pojďme programovat elektroniku

Pojďme programovat elektroniku: Inteligentní mýdlo napojené na ministerstvo zdravotnictví

  • V březnu jsme si namíchali dezinfekci
  • Tentokrát si vyrobíme její bezdotykový rozprašovač na ruce
  • Bude napojený na ministerstvo zdravotnictví

Před třemi týdny jsme si na Živě.cz namíchali litr dezinfekce na ruce s přídavkem D-panthenolu, který zjemnil následky agresivního isopropylalkoholu – hlavního zabijáka virů našeho tekutého Koronakillu.

Dezinfekci jsme slili do nádoby, a jelikož měla tekutina relativně nízkou viskozitu, spíše než gel se hodila do rozprašovače, který zároveň posloužil jako vhodný dávkovač. Má to ale jeden háček.

c8b4cd07-dbed-4294-b3d6-ae1fe8c37273
Ať už komerční, nebo amatérská dezinfekce v lahvičce má jednu vadu na kráse. Není bezkontaktní a nemá Wi-Fi. To tak nemůžeme nechat!

Pokaždé, když jej chceme použít, musíme stisknout páku rozprašovače. Stručně řečeno, nádoba, ve které kdysi bývala dle etikety okena na leštění zrcadel, není bezkontaktní. A to je chyba, dezinfekce totiž tím pádem nesplňuje ty nejpřísnější normy hygienického použití.

Rozprašovač s Wi-Fi a spojením na ministerstvo

A tak si dnes nebohou plastovou nádobu vezmeme pěkně do parády, vybavíme ji motorem a bezkontaktním ovládáním. Aby toho nebylo málo, náš dezinfekční sprej bude zároveň inteligentní, bude si totiž průběžně stahovat údaje z Ministerstva zdravotnictví ČR.

2c92234b-9a24-48ae-ab29-f4180b18fbe9
Na webu ministerstva zdravotnictví najde strojově zpracovatelná data o koronaviru v Česku ve formátech CSV a JSON

Na webu věnovaném aktuální pandemii COVID-19 najdete otevřené datové sady, tedy jednoduché API, pomocí kterého si můžete stáhnout kumulované počty nakažených, jednotlivé anonymizované případy včetně pohlaví a příslušnosti k hygienické stanici, a nakonec i počty provedených testů po jednotlivých dnech.

Naše chytrá dezinfekce si z webu ministerstva stáhne statistiků pozitivních případu po dnech ve formátu CSV. Je to tento soubor.

484e3a80-13fa-497d-bf61-acf3256ff52c
Nás bude zajímat tento CSV soubor

Data jsou k dispozici i ve formátu JSON, my je ale budeme stahovat na drobném čipu s titěrnou operační pamětí v řádu několika desítek kilobajtů RAM. Je tedy zbytečné pracovat s celou historií.

Namísto načtení kompletního a opravdu rozměrného JSONu proto zvolíme soubor CSV, který stáhneme jen po jednotlivých a mnohem menších řádcích, přičemž nás bude zajímat až ten poslední pro včerejší den.

Každý řádek je formátovaný v tomto pořadí:

datum,pocet_den,pocet_celkem

Takže jeden z posledních vypadá takto:

2020-04-06,235,4822

Pro náš experiment potřebujeme znát dvě poslední ztučnělé hodnoty. První obsahuje počet pozitivních případů identifikovaných v daný den, no a ta druhá celkový počet zjištěných infikovaných od počátku věků.

Mikročip ESP8266 na prototypovací destičce NodeMCU soubor stáhne každých pět hodin a obě klíčové hodnoty zobrazí na připojeném 0,96“ OLED displeji, se kterým komunikuje po sběrnici I²C. Kdykoliv si tedy přijdeme dezinfikovat ruce, budeme vědět, kolik nových pozitivních případů hygienici odhalili za poslední celý den a jaký je celkový počet (opět za poslední celý den, čili za ten včerejší). 

Kolik pozitivních případů, tolik dezinfekce

Chytrost naší dezinfekce ale nespočívá jen v tom, že na maličkém displeji zobrazí drobnou statistiku, ale hlavně v tom, že se jí bude řídit.

V příkladu výše hygienici odhalili 235 pozitivních případů. Podělíme je 100, zaokrouhlíme nahoru a získáme počet stisků dezinfekce: 3.

Pokud tedy bude počet denních zjištěných případů stoupat, bude se zvyšovat i dávka dezinfekce. A když bude klesat, bude svoji aktivitu tlumit i náš rozprašovač. 

Podívejte se na video, jak náš bizarní rozprašovač funguje v praxi:

Pohyb odhalí infračervené čidlo

Fajn, základní logiku komunikace a interpretace dat z ministerstva zdravotnictví bychom měli, ale jak vlastně docílíme bezkontaktního spuštění a samotného stlačení páky rozprašovače?

Rozprašovač se musí aktivovat v okamžiku, kdy před ním mávneme rukou. Mohli bychom tedy použít některý z drobných laserových nebo ultrazvukových dálkoměrů, nakonec jsme se ale rozhodli pro primitivní a hlavně laciný modul infračerveného detektoru překážek.

066bbde0-1014-4a39-b8c2-d679ed92d57a
Primitivní infračervený detektor překážky

Vedle napájení má jen jeden digitální pin, na kterém se bude měnit logický stav podle toho,  zdali bude čidlo detekovat překážku, nebo ne. Citlivost lze přitom nastavit pomocí otočného trimru přímo na tištěném spoji.

Infračervený detektor je naprosto primitivní. Obsahuje jednu infračervenou diodu, která v neviditelném spektru ozařuje nejbližší okolí. A dále obsahuje infračervený detektor. Když se k modulu přiblížíme rukou nebo jiným předmětem dostatečně blízko, IR dioda jej ozáří, detektor zachytí odraz a na signálním vodiči se změní logický stav.

Podobný laciný modul sice lehce zmate svit Slunce, ale to nám nevadí, rozprašovač totiž umístíme do předsíně nebo koupelny bez oken. Takže máme internetovou komunikaci a také spouštěč, který iniciuje stisknutí páčky, chybí nám ale ještě nějaký motor.

Páku stiskne silné 5V servo

Pro co nejsnazší instalaci jsme zvolili běžné analogové modelářské servo z Aliexpressu K-Power M1500 s dostatečnou silou k tomu, aby dokázalo stáhnout páku rozprašovače.

c6aab2a7-4571-4ae9-8bca-37a8962f02ca2149dfdf-cbff-4f20-abb9-37da59a317365b1c270a-329a-4ccc-b36f-5cfafc8a6cbb
Analogové servo a montáž na rozprašovači s dezinfekcí v jednoduchém úchytu, který jsme si vyrobili na 3D tiskárně

U servomotorů můžeme na rozdíl od běžných DC motorů ovládat úhel natočení pomocí signálního vodiče a pulzně šířkové modulace (PWM). Serva mají zpravidla rozsah 180 stupňů, což nám bude bohatě stačit. V Arduinu je práce se servomotory poměrně jednoduchá, k dispozici je totiž předinstalovaná knihovna s několika příklady, která si rozumí s většinou z nich.

A to je celé

Takže si to shrňme. Jakmile před rozprašovačem mávneme rukou, servo provede zhruba 140stupňové otočení, což bude stačit k plnému stlačení páky rozprašovače.

905fa73b-f7f8-4bff-b5b7-cd4ad6559124eaf4b525-d605-45a6-a3bc-ed0843fbd021e4d73f10-253b-4c9d-a759-797de4bb19ca1d235390-5331-43ee-aabd-d725003834ef
Kompletní prototyp automatizovaného rozprašovače s dezinfekcí. Všimněte si, že řídící deska s čidly a servo mají separátní USB napájení. Silné servo by totiž během zátěže mohlo způsobit podpětí (brownout) a naše deska by se cyklicky restartovala.

Poté se servo vrátí do výchozí pozice a celé kolečko se zopakuje podle již známé informace o počtu nových nakažených z webu ministerstva zdravotnictví. My si to všechno budeme moci zároveň zkontrolovat na drobném OLED displeji, na kterém se budou zobrazovat podrobnosti.

Když by vás dezinfekce nebavila...

Mimochodem, na konci pandemie, až už nebude potřeba tolik dezinfekce, si můžete ze serva a dvou  čipů poskládat něco zábavnějšího. Třeba primitivního robůtka, který bude pomocí luxmetru ovládat skrytou hru s dinosaurem v prohlížeči Chrome.

Robot na hru s dinosaurem v Chromu:

Aktivujete ji načtením adresy chrome://dino a stisknutím mezerníku.

Zdrojový kód

Na úplný závěr nesmí chybět komentovaný kód celého programu chytré dezinfekce, který je určení pro čip ESP8266 na desce NodeMCU a vývojové prostředí Arduino.

// Potrebne knihovny na ovladnuti serva, sitovou komunikaci a OLED displej
#include <Servo.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
// Knihovnu nize najdete zde: https://github.com/ThingPulse/esp8266-oled-ssd1306
#include <SSD1306Wire.h> 

// Objekty serva a displeje
Servo servo;
// Displej je pripojny skrze sbernici I2C (piny D1 a D2 desky NodeMCU)
SSD1306Wire display(0x3c, SDA, SCL);

// Pomocne promenne pro aktualizaci udaju kazdych 5 hodin
uint64_t periodaAktualizace = 1000 * 3600 * 5;
uint64_t posledniAktualizace = 0;

// Aktualni pozice serva
uint8_t servoPozice = 0;
// Prodleva v milisekundach mezi jednotlivymi kroky serva
uint8_t servoRychlost = 5; 

// Promenne novych nakaz a celkoveho poctu nakazenych
uint16_t pripadyNove = 0;
uint32_t pripadyCelkem = 0;

// Fingerprint certifikatu domeny onemocneni-aktualne.mzcr.cz
// Zjistite jej na teto adrese https://www.grc.com/fingerprints.htm
// Bez fingerprintu nebude fungovat overeni certifikatu a tedy i cele HTTPS spojeni
char fingerprint[] = "21 73 BF 83 74 E6 E3 4D 23 CB 1E 10 1E CB A8 BA 96 0A F7 14";
// Domena a port ciloveho webu, se kterym budeme komunikovat skrze HTTPS
char domena[] = "onemocneni-aktualne.mzcr.cz";
int httpsPort = 443;

// Udaje pirpojeni k Wi-Fi
char ssid[] = "Klobouk";
char password[] = "123456789";

// Funkce pro nastaveni uhlu servomotoru
// Funkce jde krok po kroku od posledni zname pozice k te nove
void servoUhel(int uhel) {
  if (servoPozice > uhel) {
    for (int i = servoPozice; i >= uhel; i--) {
      servo.write(i);
      delay(servoRychlost);
    }
  }
  else if (servoPozice < uhel) {
    for (int i = servoPozice; i <= uhel; i++) {
      servo.write(i);
      delay(servoRychlost);
    }
  }
  servoPozice = uhel;
}


// Funkce pro nizkourovnove HTTPS spojeni se serverem mzcr.cz
void aktualizujDataZMinisterstva() {
  // Vypis hlasku o stahovani dat na displej
  display.clear();
  display.drawString(0, 0, "Stahuji");
  display.drawString(0, 26, "data!");
  display.display();

  // Inicializace sifrovaneho spojeni
  WiFiClientSecure httpsClient;
  httpsClient.setFingerprint(fingerprint);
  httpsClient.setTimeout(5000);

  // Pokus o spojeni se serverem
  // V pripade chyby se bude opakovat nejvyse 30x
  uint8_t opakovani = 0;
  while ((!httpsClient.connect(domena, httpsPort)) && (opakovani < 30)) {
    delay(100);
    opakovani++;
  }

  // Sifrovane spojeni je otevreno,
  // a tak pomoci protokolu HTTP GET pozadame o CSV soubor
  httpsClient.print(String("GET ") + "/api/v1/covid-19/nakaza.csv" + " HTTP/1.1\r\n" +
                    "Host: " + domena + "\r\n" +
                    "Connection: close\r\n\r\n");

  // Server by nam mel nejprve odpovedet HTTP hlavickou, kterou zahodime
  String radek;
  while (httpsClient.connected()) {
    radek = httpsClient.readStringUntil('\n');
    if (radek == "\r") {
      break;
    }
  }

  // Po hlavicce prijdou na radu samotna textova data CSV souboru
  // cteme je po jednotlivych radcich, ktere prepisujeme
  while (httpsClient.available()) {
    radek = httpsClient.readStringUntil('\n');
  }

  // Vycistime posledni radek od pripadneho smeti na krajich
  radek.trim();
  // Radek by mel nyni mit podobny obsah: 2020-04-06,235,4822
  // Vytahneme z nej prostredni a posledni hodnotu oddelenou carkou
  pripadyCelkem = radek.substring(radek.lastIndexOf(',') + 1).toInt();
  pripadyNove = radek.substring(radek.indexOf(',') + 1, radek.lastIndexOf(',')).toInt();

  // Vypisme pocty nakaz za den a celkove na OLED displej
  char txtCelkem[20];
  char txtNove[20];
  sprintf(txtCelkem, "Celkem: %d", pripadyCelkem);
  sprintf(txtNove, "Nove: %d", pripadyNove);
  display.clear();
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 0, txtCelkem);
  display.drawString(0, 18, txtNove);
  display.display();
}

// Hlavni funkce setup se spusti jen jednou po startu programu
void setup() {
  // Servo je pripojene na pin D6 desky NodeMCU
  servo.attach(D6);
  // Resetuj servo na vychozi polohu 160 stupnu (smerem k pace nadoby)
  servoUhel(160);
  // Na pinu D5 desky NodeMCU je IR detektor, nastavime jej tedy na vstup
  pinMode(D5, INPUT);

  // Nastartujeme displej a napiseme na nej velkym pismem hlasku o pripojovani k Wi-Fi
  display.init();
  display.flipScreenVertically();
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_24);
  display.drawString(0, 0, "Pripojuji");
  display.drawString(0, 26, "k Wi-Fi!");
  display.display();

  // Pripojeni k Wi-Fi
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  WiFi.begin(ssid, password);
  Serial.println("");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  // Spust funkci pro stazeni dat ze serveru MZCR
  aktualizujDataZMinisterstva();
}


// Smycka loop se opakuje stale dokola a spusti se po zpracovani funkce setup
void loop() {
  // Pokud IR detektor zachytil prekazku
  if (digitalRead(D5) == LOW) {
    // Z poctu dennich nakaz vypocitej pocet davek pro servo
    // a vypis atualni statistiku na displej
    uint8_t pocetStisku = ceil(pripadyNove / 100.0f);
    char txtCelkem[20];
    char txtNove[20];
    char txtPocet[20];
    sprintf(txtCelkem, "Celkem: %d", pripadyCelkem);
    sprintf(txtNove, "Nove: %d", pripadyNove);
    sprintf(txtPocet, "Stisk: %d", pocetStisku);
    display.clear();
    display.drawString(0, 0, txtCelkem);
    display.drawString(0, 18, txtNove);
    display.drawString(0, 36, txtPocet);
    display.display();

    // Podle poctu davek opakuj pohyb servem:
    // nejprve na 20 stupnu (stlaceni paky rozprasovace)
    // zpet na 160 stupnu (navrat paky do vychozi pozice)
    for (uint8_t i = 0; i < pocetStisku; i++) {
      servoUhel(20);
      servoUhel(160);
    }

    // Pockej pet sekund, a vycisti displej
    delay(5000);
    display.clear();
    display.display();
  }

  // Pokud uz uplynulo pet hodin, znovu stahni data z webu MZCR
  if (abs(posledniAktualizace - millis()) > periodaAktualizace) {
    aktualizujDataZMinisterstva();
    posledniAktualizace = millis();
  }
}
Diskuze (11) Další článek: Android Auto a Apple CarPlay představují v autech větší nebezpečí než alkohol v těle řidiče

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