Fyzikální kabinet FyzKAB

Načtení Vernier Go!Temp modulem Arduino

USB teploměr Go!Temp společnosti Vernier asi není třeba představovat. Nejedna škola jím disponuje, a to asi především díky úvodní marketingové akci, kdy bylo možné tento teploměr zakoupit za krásných 500 Kč. Pravda je, že současná cena skoro tři tisíce už zas tak zajímavá není.

Vernier USB Go
Obr. č. 1 – USB teploměr Go!Temp společnosti Vernier

USB teploměr lze připojit k počítači a pomocí bezplatného programu Logger Lite docela dobře využívat pro řadu základních pokusů. Pokud chcete využívat teploměr aspoň trochu mobilně, například v přírodě, musíte jej použít společně s přenosným netbookem (cena 4–7 tisíc Kč) nebo dokoupit od společnosti Vernier originální Datalogger LabQuest (cena 25 tis Kč). Nadále se zde nebudeme zabývat tím, která z výše uvedených variant je lepší.

My se v tomto článku vrhneme na možnost připojení USB teploměru Go!Temp k modulu Arduino. Původní motivace k vyřešení tohoto úkolu byly hned dvě:

  • První vycházela z toho, co tu bylo naznačeno. Jedna škola si pořídila tyto USB teploměry za příznivou zaváděcí cenu a až později zjistila, že je bez PC nebo LabQuestu nemůže používat. Bylo tedy třeba vyrobit nějakou (nejlépe levnou) zobrazovací jednotku teploty. Trochu zvrhlé řešení, ve kterém se z poměrně zajímavého měřicího čidla stává normální digitální teploměr, ale což… i takové úkoly občas stojí za řešení.
  • Druhou motivací, kvůli které jsem se především do řešení problému vrhl, byla možnost zkusit si připojit obecné USB zařízení k modulu Arduino. To by se třeba mohlo jednou využít při tvorbě nové úlohy do naší Vzdálené laboratoře.

Připojení čidel Vernier k modulu Arduino

Společnost Vernier si uvědomila, že připojení měřicích čidel k modulu Arduino otevírá určité možnosti pro další využití. To není vůbec špatná strategie. Díky tomu ve spolupráci s firmou SparkFun byl vyvinut zásuvný shield pro modul Arduino, do kterého lze zapojovat až dvě analogová a dvě digitální čidla.

Arduino Interface Shield
Obr. č. 2 – Shield pro modul Arduino „Arduino Interface Shield“

Bohužel pro naše účely se tento díl použít nemůže, neboť nedisponuje připojením USB čidla. To je trochu škoda. Za cenu kolem dvou tisíc by si tento shield, který jinak obsahuje minimum elektronických součástek, určitě nějakou přidanou hodnotu zasloužil.

Pro připojení USB teploměru k modulu Arduino využijeme shield přímo vyvinutý pro připojování USB zařízení. Najdeme jej pod označením „Arduino USB Host Shield“ a cenově se pohybuje okolo tří set korun (dle prodejce). K shieldu budeme využívat knihovnu USB Host Shield Library 2.0, která je dostupná přímo v manažeru knihoven prostředí Arduino IDE nebo na stránkách: https://github.com/felis/USB_Host_Shield_2.0

USB Host Shield
Obr. č. 3 – Hardwarové rozšíření Arduino USB Host Shield

USB Shield je doporučen pro modul Arduino Mega, ale lze jej použít i na modulu Arduino UNO, kde jej budeme používat my, a taktéž na modulu Arduino Leonardo. Doporučení modulu Arduino Mega vychází zejména z nároků na paměť, neboť doprovodná knihovna je opravdu neskutečně rozsáhlým dílem. Pro náš jednoduchý projekt však bohatě postačí Arduino UNO.

Je možné, že při hledání USB Host Shield narazíte nejen na výše zobrazený shield (obr. 3) pro modul Arduino UNO/Mega apod., ale i mnohem menší Host Shield označený Mini. Oba shieldy jsou postaveny na stejném čipu a pro oba lze využít stejné ovládací knihovny. My se zde budeme zabývat zapojením postaveným na klasickém modulu Arduino a jemu příslušnému shieldu.

Řešení připojení USB teploměru k modulu Arduino Mini a jemu příslušnému shieldu USB Host Mini budeme věnovat celý zvláštní článek někdy příště, neboť si to svou pozornost jistě zaslouží.

Připojení Vernier Go!Temp k USB Host Shield

Hardwarová část

Spojení USB Host Shieldu a modulu Arduino není na první pohled žádný problém. Oba moduly stačí do sebe zasunout připravenými konektory podobně jako každý jiný shield. Také zapojení USB teploměru do USB konektoru na rozšiřujícím shieldu není nic zásadního. Teoreticky bychom mohli říci: „Sláva, hardwarovou stránku projektu máme vyřešenou 😊.“

Zde bychom ale neměli podlehnout falešné představě, že vše je ve světě elektroniky, zejména té levné, takto jednoduché!

Tak jako moduly Arduino lze koupit od několika výrobců v podobě precizních i jiných klonů, podobně lze narazit na shield od různých výrobců. Z tohoto důvodu je třeba zkontrolovat na shieldu propájení napájecích spojek. USB komunikace je z hlediska napětí na komunikačních linkách třívoltová, ale řada USB periférií vyžaduje napájení 5 V. Ale dokonce některé USB periférie mohou být napájeny napětím 3,3 V. Jiné periférie vyžadují poměrně velký napájecí proud, který dodává separátní zdroj, pak by bylo propojení napájení periférie a shieldu nevhodné. A tak dále… Zkrátka, aby si vývojář sám mohl nakonfigurovat potřebné napájení, disponuje USB shield pro napájení propojovacími můstky. Před použitím je tedy potřeba zkontrolovat, zda jsou správně propájené. Někteří výrobci a prodejci dodávají shield bez propájených propojek, jiní již propájený.

Následující obrázek (obr. 4) ukazuje, jak by měly být napájecí propojky propájeny (vyznačeno zeleně). V oblasti USB konektoru by měla být spojena propojka 5 V, která přivádí na USB konektor pětivoltové napájení z modulu Arduino. Zde jen pozor na některé levné klony modulu Arduino, které jsou osazeny levnými a tedy i proudově nedostatečnými stabilizátory. Při připojení proudově náročnější periférie by se mohl tento stabilizátor na modulu Arduino přehřát nebo jen nedodat potřebné napájení. Dalším místem, které je třeba zkontrolovat a případně propájet, jsou můstky v oblasti u pinového konektoru, kde pro naše účely musí být propojeno jak napětí 5 V, tak 3,3 V.

USB Host Shield - pajecí můstky
Obr. č. 4 – Propájení napájecích můstků na shieldu USB Host Shield

Softwarová část

Máme-li vyřešenou část hardwarovou, můžeme se pustit do části softwarové. Zde si pochopitelně musíme vše naprogramovat sami.

USB teploměr Go!Temp je zařízení pracující na základě USB protokolu, který je označován HID, čili Human Interface Device. Protokol HID je komunikační standard pro připojená zařízení, která díky tomu běžně nevyžadují pro svou funkci proprietární ovladače. To je naše štěstí, neboť implementace nějakého speciálního ovladače, který by společnost Vernier jen tak pro naše účely uvolnila, by asi byla nemožná. Díky standardu HID a jeho podpory ze strany podpůrné knihovny USB_Host_Shield_2.0, by se načítání aktuální teploty (snad) dalo zvládnout.

Na Githubu knihovny pro USB Host Shield knihovny pro USB Host Shield je i několik ukázkových příkladů. Jedna sekce je speciálně věnována i načítání dat ze zařízení podporujících HID protokol. Najdeme v ní hned několik ukázek. Všechny však mají jeden problém – dokáží zobrazit na sériový monitor seznam předem stanovených údajů, aniž by bylo možné tyto údaje jakkoliv načíst do nějaké proměnné nebo s nimi nadále pracovat. Je to způsobeno asynchronním způsobem komunikace s USB periférií, kdy hlavní smyčka programu vlastně nedělá nic a výstup na sériový port obhospodaří procedura Usb.Task().

Tohoto problému si všimlo hned několik vývojářů, kteří se pokoušeli připojit k modulu Arduino joystick. Ukázkový program umožňuje vypisovat údaje o poloze joysticku, ale ani zde jakékoliv načtení těchto údajů primárně nejde. Ale právě díky úsilí těchto vývojářů řešícím načítání polohy joysticku, se nám nakonec podařilo načíst data z USB teploměru Go!Temp.

USB teploměr je naštěstí poměrně „hloupé“ zařízení, které se chová podobně jako joystick. Na obecný dotaz odpoví předem stanovenou sérií výstupních hodnot. Zatímco u joysticku jde o sérii bytů odpovídající poloze páky a konfiguraci stisknutých tlačítek, zde jde o sérii, ve které lze hodnotu teploty dohledat. Zde je třeba dodat, že dále popsaný postup načtení hodnoty teploty z USB teploměru není zcela ideální, ale je funkční.

Následující ukázkový program ukazuje načtení sekvence výstupních dat z USB teploměru Go!Temp. Jedná se o upravenou verzi ukázkového programu načtení joysticku. Původní projekt je složen ze tří souborů. Zde pro ucelenost celého kódu byly všechny části dány do jednoho bloku programu (původní jednotlivé soubory jsou odděleny danými komentáři).

Důležité části, které budou vysvětleny pod kódem, jsou zvýrazněny:

// knihovny pro USB Host Shield
#include <usbhid.h>
#include <hiduniversal.h>
// #include <SPI.h>     // pridani knihovny je nadbytecne, protoze si ji knihovny pro USB HOST zavolaji samy

// nasledujici cast GoTemp.h a GoTemp.cpp je prevzata z ovladace joysticku a upravena dle potreby

// ------ GoTemp.h --------------
struct GoTempData {        // struktura pro nacitani hodnot z USB
   uint8_t Ready;          // 1. Byte je stav 0/1 urcujici, zda je cidlo jiz ready
   uint8_t cisloVzorku;    // 2. Byte je poradove cislo vzorku (0-255)
   uint16_t Temp_RAW;      // 3. a 4. Byte jsou LO a HI hodnota odpovidajici teplote
   // zbyle Byte jsou ignorovany (nejsou pro nas potreba)
};

class GoTempEvent {
public:
   virtual void OnGoTempChanged(const GoTempData *evt);
   static GoTempData mostRecentEvent;
};

#define RPT_GOTEMP_LEN sizeof(GoTempData)/sizeof(uint8_t)

class GoTempReportParser : public HIDReportParser {
   GoTempEvent *GoTempEvents;
   uint8_t oldTemp[RPT_GOTEMP_LEN];
public:
   GoTempReportParser(GoTempEvent *evt);
   virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

// ------ GoTemp.cpp --------------
GoTempReportParser::GoTempReportParser(GoTempEvent *evt) :
   GoTempEvents(evt) {}

void GoTempReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
   bool match = true;
   // Checking if there are changes in report since the method was last called
   for (uint8_t i=0; i<RPT_GOTEMP_LEN; i++) {
    if( buf[i] != oldTemp[i] ) {
     match = false;
     break;
    }
   }
   // Calling Game Pad event handler
   if (!match && GoTempEvents) {
    GoTempEvents->OnGoTempChanged((const GoTempData*)buf);
    for (uint8_t i=0; i<RPT_GOTEMP_LEN; i++) oldTemp[i] = buf[i];
   }
}

GoTempData GoTempEvent::mostRecentEvent; // potreba pro pristup z hlavniho programu (bez tohoto to nejde)

void GoTempEvent::OnGoTempChanged(const GoTempData *evt) {
   // ulozeni struktury evt, aby byla pristupna stylem: GoTempEvent::mostRecentEvent.JMENO (DULEZITE!)
   mostRecentEvent = *evt;
}

// ------ GoTemp.ino --------------
// hlavni cast programu
USB                   Usb;
HIDUniversal          Hid(&Usb);
GoTempEvent           GoTempEvents;
GoTempReportParser    GoTemp(&GoTempEvents);

void setup() {
   Serial.begin(115200); // nastaveni sériového portu
#if !defined(__MIPSEL__)
   while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif

   if (Usb.Init() == -1) Serial.println("USB Shield se nespustil."); // nahozeni USB HOST Shield
   delay(200);
   if (!Hid.SetReportParser(0, &GoTemp)) ErrorMessage(PSTR("SetReportParser"), 1 ); // nastaveni pro cteni USB ve stylu HID
   GoTempEvent::mostRecentEvent.Ready = 0;
}

void loop() {
   Usb.Task(); // probehne asynchronni pozadavek na USB
   if (GoTempEvent::mostRecentEvent.Ready != 0) { // USB cidlo se uz ozvalo, muzeme merit
     // nacteni hodnot z reportu USB
     Serial.print("cislo vzorku: ");
     Serial.println(GoTempEvent::mostRecentEvent.cisloVzorku);
     Serial.print("(skoro) teplota: ");
     Serial.println(GoTempEvent::mostRecentEvent.Temp_RAW);
     GoTempEvent::mostRecentEvent.Ready = 0;
   }
}

Důležitou částí kódu je datová struktura GoTempData, do které budou načítána výstupní data z USB teploměru. V článku zabývajícím se připojením USB teploměru Vernier Go!Temp k linuxu bylo zmíněna a vysvětlena struktura návratových bytů teploměru. To se nám teď hodí!

vystupní data Vernier Go!Temp
Obr. č. 5 – Struktura výstupních dat USB teploměru Vernier Go!Temp

První byte je jakýmsi stavovým hlášením, které udává, zda je teploměr již připraven k měření a tedy, zda jsou již dále uvedená data validní. My tento byte budeme ukládat do proměnné nazvané Ready. Další byte určuje pořadové číslo naměřené hodnoty (proměnná cisloVzorku). Vzhledem k tomu, že byte může nabývat maximální hodnoty 255, nelze tento byte při delším měření moc využívat jako pořadové číslo měření, ale spíše jako indikátor toho, že proběhlo další měření. Ale to je již na samotném řešení konkrétního programu. Následují dva byte odpovídají dvojici hodnot LO a HI šestnáctibitového čísla, ze kterého lze dopočítat teplotu. My je nebudeme načítat odděleně, a pak z nich dopočítávat potřebnou hodnotu, ale rovnou je načteme jako dvoubytové číslo (proměnná Temp_RAW). Případné další hodnoty, které by teploměr ještě mohl zasílat, již nepotřebujeme a budeme je ignorovat.

Datová struktura GoTempData v podobě různých proměnných nadále vstupuje do potřebných procedur pro načtení připojeného USB zařízení, zejména pomocí protokolu HID.

Další důležitou částí kódu je vytvoření globálně dostupné datové struktury GoTempEvent::mostRecentEvent, do které v proceduře GoTempEvent::OnGoTempChanged uložíme načtené výstupní hodnoty z USB zařízení. Jednotlivé hodnoty struktury GoTempData, tím pádem budou již dostupné v hlavní části programu (Trik od „hledačů“ řešení načítání polohy joysticku).

Princip načítání USB teploměru pak vidíme v hlavní části programu, tedy ve smyčce Loop. Pochopitelně musíme zmínit i úvodní proceduru Setup, ve které dochází k nastavení USB Shieldu a k nastavení HID komunikace s parametry pro načítání datové struktury GoTempData. Závěrečný příkaz GoTempEvent::mostRecentEvent.Ready = 0 bude ještě zmíněn dále.

V hlavní nekonečné smyčce Loop dochází k volání procedury Usb.Task(), která spustí asynchronní komunikaci s připojenou USB periférií. V případě teploměru Go!Temp trvá měření asi 0,5 s. Nekonečná smyčka však zatím běží dále, takže je třeba otestovat, zda již je čidlo připraveno a zda proběhlo měření. K tomu slouží podmínka:

if (GoTempEvent::mostRecentEvent.Ready != 0) { …

Aby byla zaručena počáteční nulovost této proměnné, než ji samo čidlo změní po asynchronním zápisu do dané proměnné, je dobré tuto proměnnou na samém začátku vynulovat. K tomu slouží příkaz GoTempEvent::mostRecentEvent.Ready = 0 na konci procedury Setup. Podobně vynulujeme tuto proměnnou po načtení hodnoty teploty, aby nedošlo k výpisu teploty předešlé, dokud nebude naměření příští.

Jakmile se stavový byte Ready změní, je v dané větvi hlavní podmínky načtena série hodnot odpovídající číslu měření a hodnotě odpovídající teplotě. Jakmile máme tyto hodnoty buď načtené, nebo zde jen vypsané na sériový port, vynulujeme hodnotu proměnné GoTempEvent::mostRecentEvent.Ready, aby se hodnota opět načetla, až USB čidlo skutečně odpoví.

Výsledkem našeho programu by měl být následující výstup na sériovém monitoru.

čistá data na sériovém monitoru - data Vernier Go!Temp
Obr. č. 6 – Výpis výstupních hodnot z USB teploměru Go!Temp – výpis načtených hodnot

Na výpisu vidíme, že výstupní hodnota 2976 na první pohled moc neodpovídá reálné teplotě. Z tohoto důvodu je zde tato hodnota označena „(skoro) teplota“. Jak si nadále ukážeme, lze z této hodnoty teplotu určit, ale zároveň se trochu musíme zamyslet nad druhou stranu našeho načítání USB zařízení.

Měříme nebo hackujeme?

Protože k USB teploměru Go!Temp není žádná technická dokumentace týkající se USB komunikace, je otázkou, zda opravdu čidlo načítáme správně. USB zařízení mají několik možností komunikace. Liší se pochopitelně rychlostí i zvoleným formátem. Aby se tato čidla mohla s řídicím počítačem domluvit na správné komunikaci, mají stanovaný standardní způsob, na kterém komunikují vždy. Zpravidla tato komunikace proběhne po zasunutí USB periférie do počítače. Počítač se dotáže na typ výrobce, popř. ID číslo periférie, a dle toho připraví potřebný ovladač. Další komunikace pak probíhá v režii daného ovladače nebo řídicího programu, který již ví, s jakou periférií má tu čest.

Podobně to probíhá i v případě USB teploměru Go!Temp. Jakmile jej zasunete do USB portu počítače, nejdříve se stavová LED rozsvítí červeně, pak se barva změní na oranžovou. Při spuštění programu Logger Lite se stavová LED rozsvítí zeleně a začíná měření. Po vypnutí programu Logger Lite se barva stavové LED změní na oranžovou a po chvíli nečinnosti se USB teploměr odpojí a LED úplně zhasne.

Proč to zde zmiňujeme? Protože je třeba si uvědomit, co se v tomto případě děje a zda dokážeme s modulem Arduino dosáhnout všech těchto fází.

Po připojení a úvodní aktivaci USB čidla (červená barva LED) teploměr přechází do stavu komunikace v „technickém“ režimu (oranžová barva LED), ve které čidlo komunikuje na úvodní standardní bázi stejně jako každé jiné USB zařízení. V případě zapnutí programu Logger Lite začíná počítač komunikovat s čidlem přesně stanoveným způsobem, tak jak je dáno výrobcem (LED to signalizuje zelenou barvou). Na správné dotazy dává čidlo správné výsledky. Vzájemný systém dotazů a odpovědí je oběma stranám znám. Je možné, že čidlo skutečně v tuto chvíli odpovídá již v hodnotách nějakých teplotních stupňů, které vycházejí z kalibrační křivky daného čidla. To je věc, kterou bez znalosti konkrétní komunikace dotaz-odpověď nikdy nezjistíme a nikdy ji dosáhneme. Zkrátka nevíme, jak má plnohodnotná komunikace tohoto zařízení vypadat.

Výsledkem našeho snažení je tedy stav, kdy načítáme hodnoty v režimu úvodní komunikace USB zařízení a jejího „mastera“. Poznáme to tak, že po celou dobu naší komunikace stavová LED teploměru svítí oranžově nikoliv zeleně. Také je třeba neustále čidlo „dráždit“ dotazy v hlavní nekonečné smyčce programu pomocí příkazu Usb.Task().

Zde psaný způsob načítání hodnot lze tedy spíše považovat za určité hacknutí čidla než korektní načítání. Ale pokud nám nevadí, že jediným omezením je oranžově svíticí LED, místo zelené, tak se vlastně nic neděje. Zvolený způsob komunikace čidlo nijak neohrožuje a je v rámci naší použitelnosti zcela spolehlivý.

Konečně získání teploty

Jak jsme viděli na obrázku z výstupu na sériový monitor teploměr ponechaný v pokojové teplotě, vrací hodnotu asi 2900. Pokud teploměr zahřejeme, hodnota se zvyšuje. Při ochlazení naopak klesá. Například teplotě asi 37 °C odpovídá hodnota kolem 4500. Nezbývá nám nic jiného, než provést kalibraci – tedy najít vztah mezi načtenou hodnotou z čidla a reálnou teplotou. Asi nejlepší je vzít dvojici USB teploměrů, vložit je do hrníčku a přilévat různě teplou vodu. První USB teploměr zapojit na Arduino a vypisovat výstupní hodnoty na sériový monitor, druhý teploměr připojit k USB portu počítače a aktuální teplotu měřit pomocí programu Logger Lite. V libovolném tabulkovém kalkulátoru (MS Excel, LibreOffice Calc…) si pak vytvořit tabulku odpovídajících hodnot. Vzájemná závislost je lineární, takže potřebné konstanty získáme pomocí lineární regrese. Vzájemnou závislost lze pak zapsat v podobě:

Teplota  [°C] = Temp_RAW SKLON POSUN

kde Temp_RAW vyjadřuje hodnotu načtenou z USB teploměru, konstanty SKLON a POSUN jsou kalibrační konstanty a výsledná proměnná Teplota již bude obsahovat výslednou teplotu ve stupních Celsia.

I když proces kalibrace je milé fyzikální cvičení, takže se o něj jistě nikdo nechce nechat ošidit, hledanou závislost zde uvedeme. A to včetně námi získaných kalibračních konstant SKLON a POSUN. Postačí přidat do kódy následující řádky, které definují kalibrační konstanty a deklarují globální proměnněnou Teplota pro uložení hodnoty teploty. Též je třeba upravit načtení hodnoty GoTempEvent::mostRecentEvent.Temp_RAW, kterou je třeba přepočítat na teplotu.

// kalibracni konstanty, Teplota = (hodnota z cidla) / SKLON - POSUN
#define SKLON 130.19
#define POSUN 0.899
:
float Teplota;     // promenna pro teplotu
:
Teplota = GoTempEvent::mostRecentEvent.Temp_RAW / SKLON - POSUN; :

Pochopitelně, kdo si chce stanovit konstanty SKLON a POSUN dle svého vlastního čidla, může provést kalibraci výše popsaným způsobem.

Předchozí kód programu pro načtení teploty z USB teploměru Vernier Go!Temp tedy doplníme o finální převod načtené hodnoty na teplotu a pro lepší další práci s hodnotou teploty ji uložíme do globální proměnné Teplota.

Celkový kód by pak mohl vypadat následujícím způsobem: (úpravy pro výpočet teploty jsou v kódu vyznačeny tučně)

// kalibracni konstanty, Teplota = (hodnota z cidla) / SKLON - POSUN
#define SKLON 130.19
#define POSUN 0.899

// knihovny pro USB Host Shield
#include <usbhid.h>
#include <hiduniversal.h>
// #include <SPI.h>     // pridani knihovny je nadbytecne, protoze si ji knihovny pro USB HOST zavolaji samy

// nasledujici cast GoTemp.h a GoTemp.cpp je prevzata z ovladace joysticku a upravena dle potreby

// ------ GoTemp.h --------------
struct GoTempData {        // struktura pro nacitani hodnot z USB
   uint8_t Ready;          // 1. Byte je stav 0/1 urcujici, zda je cidlo jiz ready
   uint8_t cisloVzorku;    // 2. Byte je poradove cislo vzorku (0-255)
   uint16_t Temp_RAW;      // 3. a 4. Byte jsou LO a HI hodnota odpovidajici teplote
   // zbyle Byte jsou ignorovany (nejsou pro nas potreba)
};

class GoTempEvent {
public:
   virtual void OnGoTempChanged(const GoTempData *evt);
   static GoTempData mostRecentEvent;
};

#define RPT_GOTEMP_LEN sizeof(GoTempData)/sizeof(uint8_t)

class GoTempReportParser : public HIDReportParser {
   GoTempEvent *GoTempEvents;
   uint8_t oldTemp[RPT_GOTEMP_LEN];
public:
   GoTempReportParser(GoTempEvent *evt);
   virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

// ------ GoTemp.cpp --------------
GoTempReportParser::GoTempReportParser(GoTempEvent *evt) :
   GoTempEvents(evt) {}

void GoTempReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
   bool match = true;
   // Checking if there are changes in report since the method was last called
   for (uint8_t i=0; i<RPT_GOTEMP_LEN; i++) {
    if( buf[i] != oldTemp[i] ) {
     match = false;
     break;
    }
   }
   // Calling Game Pad event handler
   if (!match && GoTempEvents) {
    GoTempEvents->OnGoTempChanged((const GoTempData*)buf);
    for (uint8_t i=0; i<RPT_GOTEMP_LEN; i++) oldTemp[i] = buf[i];
   }
}

GoTempData GoTempEvent::mostRecentEvent; // potreba pro pristup z hlavniho programu (bez tohoto to nejde)

void GoTempEvent::OnGoTempChanged(const GoTempData *evt) {
   // ulozeni struktury evt, aby byla pristupna stylem: GoTempEvent::mostRecentEvent.JMENO (DULEZITE!)
   mostRecentEvent = *evt;
}

// ------ GoTemp.ino --------------
// hlavni cast programu
USB                   Usb;
HIDUniversal          Hid(&Usb);
GoTempEvent           GoTempEvents;
GoTempReportParser    GoTemp(&GoTempEvents);

float Teplota;     // promenna pro teplotu

void setup() {
   Serial.begin(115200); // nastaveni sériového portu
#if !defined(__MIPSEL__)
   while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif

   if (Usb.Init() == -1) Serial.println("USB Shield se nespustil."); // nahozeni USB HOST Shield
   delay(200);
   if (!Hid.SetReportParser(0, &GoTemp)) ErrorMessage(PSTR("SetReportParser"), 1 ); // nastaveni pro cteni USB ve stylu HID
   GoTempEvent::mostRecentEvent.Ready = 0;
}

void loop() {
   Usb.Task(); // probehne asynchronni pozadavek na USB
   if (GoTempEvent::mostRecentEvent.Ready != 0) { // USB cidlo se uz ozvalo, muzeme merit
     // nacteni hodnot z reportu USB
     Teplota = GoTempEvent::mostRecentEvent.Temp_RAW / SKLON - POSUN;
     Serial.print("cislo vzorku: ");
     Serial.println(GoTempEvent::mostRecentEvent.cisloVzorku);
     Serial.print("teplota: ");
     Serial.print(Teplota);
     Serial.println(" *C");
     GoTempEvent::mostRecentEvent.Ready = 0;
   }
}

Následující obrázek již zobrazuje výpis naměřené teploty na sériovém monitoru. Zobrazená hodnota teploty je zaokrouhlena na dvě desetinná místa díky převodu, ke kterému dochází při standardním výpisu reálného čísla na sériový výstup.

teplota na sériovém monitoru - data Vernier Go!Temp
Obr. č. 7 – Výpis hodnoty teploty z USB teploměru Go!Temp – po výpočtu teploty

S ohledem na výchozí přesnost USB teploměru ±0,5 °C (informace výrobce) by byla vhodnější prezentace jen na jedno desetinné místo:

:
Serial.print(String(Teplota, 1));
:

Načtení teploty z USB teploměru do proměnné Teplota, která je dále využitelná v programovém kódu, otevírá možnost dalšího využití. Může se jednat o pouhé zobrazení na displeji, spínání řídicího relé v termostatickém systému, nebo možná i odeslání prostřednictvím nějaké komunikační sítě. To je již na samotném vývojáři a možném technickém řešení.

Závěr

V dnešním článku jsme si ukázali, jak modulem Arduino s pomocí USB Host Shield načítat teplotu z USB teploměru Vernier Go!Temp. Jak bylo zmíněno, tak jednou z motivací bylo vytvoření přenosné zobrazovací jednotky, která by zobrazovala aktuální teplotu. Toto řešení ukazují následující obrázky 8 a 9, kde je výše uvedené řešení doplněno o LCD nebo OLED displej a pochopitelně dále i o potřebné bateriové napájení.

Vernier Go!Temp + Arduino a LCD displej
Obr. č. 8 – Načítání USB teploměru modulem Arduino s využitím I²C LCD displeje

Oběma těmto řešením bychom se rádi časem postupně věnovali v některém z následujících článků.

Vernier Go!Temp + Arduino a OLED displej
Obr. č. 9 – Návrh bateriové zobrazovací jednotky k USB teploměru
(Arduino mini, USB Host Shield Mini, I²C OLED displej)

Možnost načítání dat z USB teploměru nás přivedlo na další možné využití USB Host Shieldu ve spojitosti s měřicím systémem Vernier. Existuje i několik dalších USB čidel, namátkově: USB sonar Go!Motion nebo USB rozhraní pro připojení senzorů Go!Link.

Určitě do budoucna bude stát za pokus připojit tyto přístroje na USB konektor USB Host Shieldu a pomocí právě prezentované metody zkusit načíst data z těchto USB periférií. Nejdříve ta zcela obecná data, pak v nich zkusit najít sekvence, které odpovídají hledaným hodnotám, jako se nám to podařilo najít ve 3. a 4. byte USB teploměru Go!Temp.

A to je možnost, které se může věnovat i někdo z dalších zvídavých bastlířů. Nenechávejte všechno jen na nás! Naopak budeme velmi rádi, když se s námi pak podělíte o svá zjištění.

Autor článku: Miroslav Panoš

Článek je součástí následující série článků:
UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!