12 dubna 2015

Arduino a SD karty

Mám tu teď zajímavý Arduinový projekt: senzor, který sbírá každou milisekundu data o zrychlení a k tomu je bezdrátový (napájitelný z baterie). Zatímco kilohertzový sample rate není pro 16MHz Atmel žádný problém, tak otázka, jak ty data dostat z mikrokontroleru někam ven už zajímavá je.

Zpočátku jsem si naivně věřil na bezdrátovou komunikací, ostatně tohle 2,4GHz rádio slibuje megabitové přenosové rychlosti. Že to nebude až taková prča jsem si uvědomil, už když jsem mezi VCC a GND pájel kondíky, protože si ve špičkách říkají o hodně víc než 30mA, které jim dá Arduino. I poté se ale packet loss pohyboval v desítkách procent a já si řekl, že bezdrát ještě chvíli nechám u ledu.

Další technologickou volbou tedy byly SD karty. SDčka umí komunikovat po SPI sběrnici, “čtečky” pro Arduino jsou tak jen málo víc, než plechový držáček. Z toho plyne nízká cena a číňani je prodávají rovnou v párech. Samotné paměťovky pak stojí pár stovek za desítky gigabajtů prostoru. A ani rychlostně nebude problém, i ty nejlevnější jsou Class 6, tedy zápis minimálně 6 MB za sekundu. Při požadovaném datovém toku 4 kBps mám tři řády rezervy.

Zatímco jsem čekal, až mi ve schránce přistane obálka s SD sloty, věnoval jsem se teorii. SD karty, podobně jako všechny non-RAM storage mají rády, když se zapíše jednou za čas hodně dat, což je přímo opačné defaultnímu chování přečtu bajt–zapíšu bajt. Napsal jsem tedy kolem čtecího sketche další logiku se dvěma volatile byte[196] buffery (data se čtou v časovaném přerušení), přičemž je vždy je určen jeden, do kterého se data sekvenčně ukládají. Po jeho zaplnění se pro zápis označí druhý buffer a ten zaplněný se začne ukládat na paměťovku. Protože každý sample jsou 4 bajty (2B data + 2B timestamp), musím stihnout data uložit během 49 ms, což by na flashovou kartu neměl být problém. Navíc mám buffery dva, takže jednorázově zvládnu i dvojnásobnou, 98ms latenci.

Poté, co čínská a česká pošta vše doručily a já zapojil všechny SPI kabely jsem nadšeně vyndal SDčko z Raspberry (LTLM 32GB class 10, papírový zápis 10MBps, reálně i víc) a očekával bezproblémový logging. Velmi svižně na mě ale začaly po sériové konzoli vypadávat chyby, když se data nestihla uložit. Neuběhlo 10 sekund v kuse, aby Arduino nenadávalo na overflow. A z analýzy sebraných dat vyšlo, že se rozhodně nejednalo o nějaké “těsné” nestihnutí, ztraceny byly desítky datapointů. Karta tedy nestíhá, ale jak to, když i v PC stabilně vykazovala 20+MBps write rate?

Bloumal jsem chvíli po fórech, až jsem narazil na benchmark SD karet. Autor tam nadává na čínskou SDHC kartu, která má maximální latence přes 140 ms, přičemž se značkovými Sandisky se stabilně pohybuje pod 10 ms. Po jeho vzoru jsem tedy benchmark aplikoval i na svoji “Class 10” kartu a dosáhl ještě horších výsledků:

Free RAM: 1009
Type is FAT16
File size 5MB
Buffer size 100 bytes
Starting write test.  Please wait up to a minute
Write 98.07 KB/sec
Maximum latency: 246060 usec, Minimum Latency: 84 usec, Avg Latency: 1014 usec

Starting read test.  Please wait up to a minute
Read 223.09 KB/sec
Maximum latency: 4488 usec, Minimum Latency: 84 usec, Avg Latency: 442 usec

Maximální latence kolem čtvrt sekundy, to je více než dvojnásobek mojí 98ms tolerance. Přitom je vidět, že se jedná o pár ojedinělých extrémů, průměrná latence je pohodová 1 ms. Najednou dává smysl i vysoká přenosová rychlost na PC – většina dat se na kartu zapíše rychle a příležitostné “zaseknutí” plynně vykryjou gigabajty RAMky.

V prostředí Arduina ale máme k dispozici 2kB RAM, a i o ty probíhá boj s dalšími knihovnami, takže zmíněných 2 x 196kB bylo stabilní maximum. Nezbývalo než jít znovu na nákupy, tentokrát pro Sandisk Extreme 32GB. Cena skoro dvojnásobná, co výkon? Hurá na benchmark:

Free RAM: 1009
Type is FAT32
File size 5MB
Buffer size 100 bytes
Starting write test.  Please wait up to a minute
Write 159.98 KB/sec
Maximum latency: 14788 usec, Minimum Latency: 84 usec, Avg Latency: 619 usec

Starting read test.  Please wait up to a minute
Read 248.08 KB/sec
Maximum latency: 3384 usec, Minimum Latency: 84 usec, Avg Latency: 397 usec

Nejvyšší latence 15 ms, o řád pod levnou LTLM. Nadšeně jsem nahrál sketch s logováním dat z akcelerometru a skutečně – všechna data se bez problémů uložila, žádný buffer overflow se nekonal.

Ještě vás mohlo napadnout, že se v případě LTLM jednalo o vadnou kartu, případně nějaký čínský zmetek neznámé značky. Doma se mi ale válela stará 2GB karta od věhlasného Kingstonu, tak jsem ji taky prohnal benchem a i její maximum bylo 235 ms. Byť to mohlo být jejím pokročilým věkem, mám spíš dojem, že velké latetence budou neduhem všech levných karet, bez ohledu na značku.

Je to ostatně logické – všechny Class 10 a další certifikace se vztahují na dlouhodobou průměrnou rychlost zápisu, ne na jednorázovou reakční dobu. A protože “velké” počítače se spoustou paměti dovedou “zamaskovat” neduhy paměťových zařízení, tak toho výrobci využívají.

Paradoxně tak do nejlevnějších mikrokontrolerů za pár dolarů musíte připlatit stovky korun za nízkolatenční storage. Nebo po nich nechtít kilohertzový sample rate, ale to je pak dost nuda, že.

Doplnění: o měsíc a půl později

Nic není jednoduchý. Přístup k SDčkám přes SPI je ze strany výrobců karet spíš opomíjenou technikou. S dalšími benchmarky se LTLM vzpamatovala a začala házet maximální latence kolem 20 ms, naopak Sandisk se občas rozkopnul k 80 ms. Na závěr vše korunovalo, když se LTLM zamkla pro zápis a umřela. Takže berte závěry s rezervou a začněte od levnýho, třeba budete mít štěstí.

Taky jsem zjistil, že šaškování s buffery je zbytečné, protože SdFatLib si bufferuje sám a zapisuje každých 512 B.

Naopak 2,4 GHz od Nordicu jsem rozchodil napájením 47 µF kondíku místo původního 4,7 µF. Packet loss spadl hluboko pod 1 % a dá se z něj stabilně ždímat 20 kBps.

Reference

  1. BenchSD v SD FAT knihovně
  2. Sketch ukládacího programu v Gistu