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

Pojďme programovat elektroniku: Bezdrátové Arduino Uno s baterií a Wi-Fi

  • Dnes si vyzkoušíme rozšiřující shieldy pro Arduino Uno
  • První dodá šťávu z dvojice lithiových článků
  • Arduino Uno bude zároveň vybavené Wi-Fi čipem

Po delší prodlevě mi opět dorazila hromada žlutých bublinkových obálek s lacinými cetkami z Asie, což si zaslouží pokračování našeho seriálu o programování elektroniky. V příštích několika dílech se vrátíme zase na začátek – k základní prototypovací destičce Arduino Uno, uvnitř nažloutlých balíků totiž byly jeho shieldy.

Jedná se o neoficiální rozšiřující desky s identickým půdorysem a řadami pinů, takže je na sebe můžete téměř libovolně vrstvit bez potřeby další kabeláže stejným způsobem, jako analogické haty ze světa Raspberry Pi nebo třeba moduly české stavebnice BigClown.

bd8f6dae-c5a8-48b9-a79d-32c089a96db3
Arduino Uno lze rozšířit hromadou nejrůznějších shieldů za pár dolarů. Promění destičku v MP3 přehrávač, displej, ovladač motorů, herní joystick, anebo přidají slot pro SD kartu.

Jediný háček zpravidla spočívá v tom, že pokud budete chtít použít více než jednu rozšiřující desku, snadno se vám stane, že budou obě pro své účely používat stejné piny GPIO a dojde ke kolizi. A jelikož mají asijské prototypovací desky už tradičně žalostnou dokumentaci, mnohdy se to ani nedozvíte dopředu a budete jen doufat.

4370cbf3-9c21-40b3-b976-c0dec7a944b029779e51-8838-47c7-9aab-62678ddf9400d2bfd266-19dc-4df3-af98-1cbae0546d1732d33021-a3d1-42e3-880b-4f527ae66263
Pomocí rozšiřujících desek můžete základní Arduino vylepšit o nejrůznější funkce. V tomto případě o modul RTC (přesný čas) a slot SD karty. Zatímco modul RTC hodin bude dostupný skrze sběrnici I2C, čtečka SD používá rychlejší sběrnici SPI. Ostatní piny GPIO jsou dále k dispozici.

Powerbanka pro Arduino Uno

Dnes si vyzkoušíme první takový rozšiřující shield. Na internetových e-shopech jej najdete pod různými názvy, nicméně pomůže spojení slov Arduino Uno, battery shield a 16340. Na Aliexpressu jej pořídíte za cenu něco málo přes dva dolary.

c2a7d63f-8c47-4534-a41a-97e95e490e07
Bateriový rozšiřující modul pro dva 3,7V lithiové články 16340

Rozšiřující destička obsahuje pouzdro na dva 3,7V Li-ion články typu 16340, který má zhruba poloviční délku mnohem rozšířenějších článků 18650. Na desku půdorysu Arduino Uno se takové články vejdou hned dva (můžete použit jeden, nebo oba naráz) a měnič se postará o to, aby vyrobil 5V pracovní napětí pro Arduino připojené níže. Nechybí ani velký USB port pro připojení externího spotřebiče, přičemž celkový vybíjecí odběr může dosáhnout nejvýše 1 A.

Battery shield pochopitelně obsahuje také elektroniku pro samotné dobíjení článků, k čemuž slouží konektor microUSB a o stavu dobíjení vás informují dvě LED. Červená se rozsvítí v průběhu dobíjení, zelená až bude hotovo.

8d3af6f2-9a1b-468e-9324-bc020f07c9b7d8aff0f7-c152-40ef-bcae-aef821f23b8c0f628ffc-7820-4d70-a2ed-8e839fcbd4cde44b79d9-2fd0-4c5e-a7d3-fc56dc29b57c
Baterie se dobíjejí skrze microUSB (500 mA), velký USB port pak nabízí 5V pro externí spotřebiče (dohromady včetně Arduina nejvýše 1 A). Popisky na lithiových článcích je třeba brát s rezervou, 2 500 mAh rozhodně nemají...

Bohužel, autoři vůbec nevyužili potenciálu a nepropojili skutečné napětí baterie třeba s některým z analogových pinů Arduina, abychom jej mohli přečíst a zavčas zjistit, že se brzy vybije. Jako základní powerbanka pro Arduino Uno ale rozšiřující deska se dvěma články funguje docela dobře.

Arduino Uno se sekundárním čipem ESP8266

Já dnes ale tuto baterii nepřipojím ke klasickému Arduinu Uno, ale k jeho modifikaci, která je vedle řídícího čipu ATmega328P vybavená ještě nám už dobře známým Wi-Fi čipem ESP8266. Opět, podobných hybridních desek je plný internet, já ale nakonec pořídil tento kousek opět z Aliexpressu. Cena je už pochopitelně vyšší – aktuálně okolo 12 amerických dolarů a výše.

c0e12989-d346-4c3a-8ae1-31a90070cb2d
Arduino Uno s klasickým čipem ATmega328P, ale zároveň s Wi-Fi obvodem ESP8266

Proč utrácet necelé tři stovky za desku s čipem ESP8266, když takové NodeMCU, Wemos D1 Mini a jejich klony seženete už za částku okolo padesátikoruny? Protože starý dobrý osmibitový čip ATmega328P a elektrické charakteristiky klasického Arudina jsou robustnější a pro prvotní experimentování i vhodnější.

Skrze piny GPIO klasického Arduina může téct mnohem vyšší proud než u citlivých Wi-Fi čipů, pro Arduino Uno existuje mnohem více knihoven a příkladů, k dispozici máme hromadu analogových pinů a tak dále a tak podobně. Těch důvodů je celý zástup.

5a708b93-ee98-4fa6-a53b-0104d928937c79444cfd-8e5a-4b1e-a2e0-e3a0d0e214f8373c139a-0175-4a13-ac86-20f22a85ed1e
Srovnání s klasickou deskou Arduino Uno a popis Wi-Fi obvodu

Sedm mechanických přepínačů

Takže zatímco hlavní program zpracuje Arduino, Wi-Fi čip ESP8266 se postará třeba jen o bezdrátovou komunikaci. Je to snadné, oba čipy totiž mají propojenou sériovou linku, přičemž toto propojení můžete na desce zapínat a vypínat pomocí mechanických přepínačů.

c6cde843-e600-4c8e-9b3e-17971591a63e
Pomocí sedmi mechanických přepínačů nastavíte chování sériového spojení skrze USB a vzájemné propojení čipů ATmega328P a ESP8266

Když přepínače překlopíte do polohy vzájemného propojení ATmega328P a ESP8266 a napíšete primitivní program, který otevře hlavní sériové spojení a pošle skrze něj nějaká data, ta dorazí do čipu ESP8266, na kterém může běžet váš druhý program, který tuto zprávu pošle třeba někam na internet.

Pomocí přepínačů na desce lze nastavit chování USB, respektive vzájemné propojení ESP8266 a ATmega328P pomocí sériové linky:

Režim USB/sériové linky 1 2 3 4 5 6 7 8
ESP8266 (režim flashování) OFF OFF OFF OFF ON ON ON -
ESP8266 OFF OFF OFF OFF ON ON OFF -
ATmega328P OFF OFF ON ON OFF OFF OFF -
ESP8266↔ATmega328P ON ON OFF OFF OFF OFF OFF -
Nepřipojeno OFF OFF OFF OFF OFF OFF OFF -

Abyste mohli do Wi-Fi čipu nahrát vlastní program z vývojového prostředí Arduina, přepínači na desce zároveň určujete, ke kterému z čipů bude přivedeno USB spojení z počítače. Buď bude připojené na ESP8266 a destička se bude pro váš počítač jevit jako jakákoliv jiná deska s čipem ESP8266, nebo na ATmega328P a deska se bude chovat jako běžné Arduino Uno.

06f251ad-a538-4862-9ca5-52f6936f2489
Jakmile přepínače nastavíme do polohy pro flashování čipu ESP8266, stačí v Arduinu jako cílovou desku zvolit generický modul ESP8266. Před flashováním je třeba stisknout sekundární tlačítko reset poblíž čipu ESP8266 na desce. Pokud to nebude fungovat, stačí desku odpojit od USB a zase připojit a čip ESP8266 bude opět připravený pro nové flashování.

Arduino Uno s bezdrátovou sériovou linkou přímo v prohlížeči

Tak, dost teorie. Abychom si ukázali, jak se takové Arduino Uno s Wi-Fi může chovat v praxi, napíšeme si bezdrátový sériový terminál! Jak to bude fungovat? Dejme tomu, že k destičce připojíme triviální ultrazvukový dálkoměr HC-SR04, se kterým jsme už v našem seriálu pracovali mnohokrát.

2f644468-b6c6-4d4e-ac63-6af89dbf22b3
Laciný ultrazvukový dálkoměr HC-SR04

Kdybychom měli klasické Arduino Uno, mohli bychom si napsat program, který ve smyčce loop stále dokola měří vzdálenost a tu poté posílá do sériového terminálu v počítači skrze USB kabel. Díky tomu, že naše Arduino Uno má zároveň Wi-Fi čip, který je s ním také propojený skrze sériovou linku, informace o vzdálenosti se namísto do USB kabelu odešlou identickým způsobem do čipu ESP8266.

8f7b3562-b3b6-4527-a676-1b475e9d055c
Dálkoměr po připojení k desce Arduino Uno se sekundárním Wi-Fi čipem ESP8266 a powerbankou. Arduino Uno se nyní obejde bez jakékoliv kabeláže, data z dálkoměru se totiž budou streamovat do webové sériové linky.

Optikou programu na čipu ATmega328P se tedy nic nemění. Může to být jakýkoliv program pro Arduino, který pracuje se sériovou linkou.

Tak, Arduino Uno odeslalo data skrze sériovou linku a teď konečně začne to nejzajímavější. Jakmile data dorazí do čipu ESP8266, ten je pomocí spuštěného webového serveru zobrazí ve formulářovém prvku na webové stránce.

f21e5223-eab5-4233-875a-dabf6dda1774
Identifikace IP adresy čipu ESP8266 v aplikaci Fing. Čip vyrábí čínský Espressif a Fing jedno takové zařízení v síti identifikoval podle jeho MAC adresy.

Když tedy naše internetové Arduino Uno spustíme, jeho čip ESP8266 se přihlásí k předdefinované síti Wi-Fi, my si dohledáme jeho lokální IP adresu (třeba pomocí skvělé aplikace Fing pro mobilní telefon) a načteme ji ve webovém prohlížeči.

Jakmile pak Arduino Uno cokoliv vypíše do sériové linky, data se s minimálním zpožděním zobrazí ve webovém prohlížeči, jako bychom před sebou měli sériový terminál Arduina a destička byla k počítači připojená skrze USB.

5ebfe9be-a01a-46b9-b534-c0337497366f
Pokud ATmega328P cokoliv pošle do sériové linky, během několika milisekund se to objeví v naši webové konzoli. Stejně tak cokoliv pošleme z konzole, se okamžitě přepošle skrze sériovou linku do Atmega328P.

No počkat, ale jak to tedy přesně uděláme? To po příchodu nové zprávy pokaždé vygenerujeme nový HTML kód a stránka v prohlížeči se bude třeba každou sekundu automaticky obnovovat? Ale kdeže, nemáme rok 1995! Tak použijeme AJAX a stránka se bude na pozadí pomocí Javascriptu stále dokola dotazovat serveru, jestli nedorazila nová zpráva? Ale kdeže, nemáme rok 2005.

Tak jak?

WebSockets API se postará o oboustranné streamování dat

Pokud náš seriál o programování elektroniky sledujete pravidelně, jistě si vzpomínáte na experiment s rezistivním dotykovým displejem – ajPed. Pomocí stylusu jsme tehdy na displej kreslili nejrůznější čmáranice a v prohlížeči se vše překreslovalo v živém přenosu.

Použili jsme standardizovanou technologii WebSockets, kterou už dnes podporují všechny relevantní prohlížeče a pro prostředí Arduino existuje celý zástup knihoven – třeba arduinoWebSockets. Díky této technologii můžete oběma směry mezi serverem a klientem (prohlížečem) v reálném čase streamovat prakticky libovolná data – podobně, jako když z Arduina komunikujeme s počítačem skrze sériovou linku.

Když tedy načteme v prohlížeči IP adresu našeho Arduina přihlášeného díky čipu ESP8266 k místní Wi-Fi, připojíme se na portu 80 k jeho primitivnímu HTTP serveru a do prohlížeče se stáhne HTML kód stránky.

Na čipu ESP8266 ale poběží na portu 81 ještě websocketový server, ke kterému se stránka v prohlížeči připojí pomocí Javascriptu. Jakmile do Wi-Fi čipu skrze sériovou linku dorazí nějaká data, websocketový server je předá prohlížeči a naše stránka vše vypíše do formulářového prvku a to s minimální latencí.

A stejně tak pokud z formulářového políčka v prohlížeči odešleme nějaká textová data, jako bychom byli v sériovém terminálu, dorazí do čipu ESP8266 a ten je přepošle skrze sériové spojení do čipu ATmega328P.

Tam už data zpracujeme libovolným způsobem, jako bychom je poslali skrze sériovou linku z počítače.

Arduino Uno s Wi-Fi a sériovou linkou v prohlížeči na videu:

Díky tomu, že jsme USB kabel nahradili rádiovými vlnami, můžeme nyní naši destičku umístit prakticky kamkoliv, kde by to za běžných okolností nebylo možné. Představte si například, že budete chtít testovat třeba některý z laciných čínských GPS modulů a napíšete si primitivní program, který bude jeho polohová data ve formátu NMEA vypisovat skrze USB do počítače.

3d9e5eba-2621-49a8-a137-60290b3bbe71
Powerbanka dodá šťávu na několikahodinovou zátěž při plném výkonu a testování

Jenže USB kabel bude příliš krátký, aby Arduino se satelitním přijímačem dosáhlo až k oknu. Když si vyrobíte podobnou bezdrátovou sériovou linku a po ruce budete mít zároveň náš bateriový shield s dvojicí lithiových článků, takto vybavené Arduino Uno budete moci klidně umístit na venkovní parapet, balkon, nebo na zahradu, pokud tam dosáhne signál Wi-Fi.

V okně prohlížeče se pak budou vypisovat textová data, jako by byla destička stále připojená skrze USB.

Nakonec jako vždy zdrojové kódy

Dnes jsme si tedy vyzkoušeli první rozšiřující desku pro Arduino Uno a základní Uno rozšířené o Wi-Fi čip ESP8266. Na závěr nesmí chybět zdrojový kód naší primitivní bezdrátové sériové linky.

Budou to vlastně dva programy. První firmware je jednoduchý ultrazvukový dálkoměr s modulem HC-SR04, který komunikuje skrze sériovou linku. Druhý firmware je už určený pro čip ESP8266 a postará se o to, aby se tato sériová data skrze technologii WebSockets dostala s minimální latencí do webového prohlížeče.

Zdrojové kódy pro ATmega328P (samotné měření vzdálenosti)

Tentokrát nepoužijeme pro práci s textovými řetězci oblíbený (ale paměťově potenciálně velmi nebezpečný) objekt String, ale na čipu s maličkou 2kB RAM budeme raději pracovat s polem znaků a nízkoúrovňovými funkcemi jazyka C pro práci s textovými řetězci.

// Piny ultrazvukoveho dalkomeru
#define TRIG 2
#define ECHO 3

// Pomocne promenne
bool prubezna_vzdalenost = false;
bool alarm = false;
uint16_t alarm_mm = 500;

// Prichozi zprava ze seriove linky
struct {
  char znaky[80];
  size_t delka;
  bool hotovo;
} zprava;

// Funkce pro ulozeni prichozi zpravy ze seriove linky
// ATmega328P ma malou RAM, pouziju tedy pole znaku namisto objektu String,
// se kterym se sice s textovymi retezci pracuje velmi snadno,
// ale pri spatnem pouziti muze zahltit malickou 2kB RAM
// Namisto toho budu cist znak po znaku a ukladat ho do pole znaku,
// dokud nenarazim na znak LF pro ukonceni radku.
// Pote oznacim cteni jako dokoncene a mohu celou zpravu analyzovat.
void ziskej_zpravu(char znak) {
  if (znak == 0xA) { // LF
    if (zprava.delka > 0) {
      zprava.hotovo = true;
    }
  }
  else {
    if (!zprava.hotovo) {
      if (znak != 0xD) { // CR
        zprava.znaky[zprava.delka++] = znak;
      }
    }
  }
}

// Funkce pro analyzu obsahu zpravy.
// Pomoci funkce strcmp porovnavam,
// zdali je obsah zpravy shodny s druhym parametrem
void analyzuj_zpravu() {
  if (strcmp(zprava.znaky, "LED 1") == 0) {
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.println("Zapinam svetylko");

  }
  else if (strcmp(zprava.znaky, "LED 0") == 0) {
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Vypinam svetylko");
  }
  else if (strcmp(zprava.znaky, "DST") == 0) {
    uint16_t vzdalenost = zmer_vzdalenost();
    Serial.println("Merim vzdalenost");
    Serial.print(vzdalenost);
    Serial.println(" mm");
  }
  else if (strcmp(zprava.znaky, "DST 1") == 0) {
    Serial.println("Kontinualni mereni vzdalenosti");
    prubezna_vzdalenost = true;
  }
  else if (strcmp(zprava.znaky, "DST 0") == 0) {
    Serial.println("Konec kontinualniho mereni vzdalenosti");
    prubezna_vzdalenost = false;
  }
  else if (strcmp(zprava.znaky, "ALARM 1") == 0) {
    Serial.println("Zapinam alarm pri zmereni vzdalenost < 500 mm");
    alarm = true;
  }
  else if (strcmp(zprava.znaky, "ALARM 0") == 0) {
    Serial.println("Vypinam alarm pri zmereni vzdalenost < 500 mm");
    alarm = false;
  }
  else {
    Serial.print("Neznamy dotaz: ");
    Serial.println(zprava.znaky);
  }
}

// Reset zpravy
// Vyplnim strukturu zprava nulami
void reset_zpravy() {
  memset(&zprava, 0, sizeof(zprava));
}

// Funkce pro zmereni vzdalenosti v milimetrech
uint16_t zmer_vzdalenost() {
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  return pulseIn(ECHO, HIGH) / 5.831;
}

// Hlavni funkce programu
void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(TRIG, OUTPUT);
  digitalWrite(TRIG, LOW);
  pinMode(ECHO, INPUT);
  digitalWrite(LED_BUILTIN, LOW);
  reset_zpravy();
}

// Funkce loop se zpracovava stale dokola
void loop() {
  // Pokud po seriove lince dorazila nejaka data,
  // ukladej je pomoci vyse popsane funkce
  if (Serial.available()) {
    ziskej_zpravu((char)Serial.read());
  }

  // Pokud uz mam celou zpravu (ukoncenou znakem LF pro zalomeni radku),
  // analyzuj jeji obsah
  if (zprava.hotovo) {
    analyzuj_zpravu();
    reset_zpravy();
  }

  // Pokud je aktivovane prubezne mereni vzdalenosti,
  // zmer ji a posli do seriove linky vysledek
  if (prubezna_vzdalenost) {
    uint16_t vzdalenost = zmer_vzdalenost();
    Serial.print(vzdalenost);
    Serial.println(" mm");
    delay(100);
  }

  // Pokud je aktivovany alarm,
  // zmer vzdalenost a zjisti, jestli je kratsi nez hodnota pro alarm
  if (alarm) {
    long vzdalenost = zmer_vzdalenost();
    if (vzdalenost < alarm_mm) {
      Serial.print("!!!! ALAAAAAAARM !!!!  (");
      Serial.print(vzdalenost);
      Serial.println(" mm)");
      delay(100);
    }
  }
}

Zdrojové kódy bezdrátové sériové linky skrze Wi-Fi a WebSockets pro ESP8266:

//https://github.com/Links2004/arduinoWebSockets
#include <WebSocketsServer.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include "html.h" // Viz posledni kod

// Autentizace Wi-Fi
char *ssid = "Kloboukovo";
char *password = "********";

// Webovy server pobezi na portu 80
ESP8266WebServer server(80);

// Websocketovy server pobezi na portu 81
WebSocketsServer websocket_server = WebSocketsServer(81);

// Pomocne promenne
bool websocket_pripojeno = false;
uint8_t websocket_pocet_spojeni = 0;
String message;

// Pokud skrze WebSocket dorazi textova data, preposli je skrze seriovou linku do ATmega328P
void websocketUdalost(uint8_t num, WStype_t type, uint8_t* data, size_t length) {
  if (type == WStype_CONNECTED) {
    websocket_pocet_spojeni++;
    websocket_pripojeno = true;
    websocket_server.broadcastTXT("Spojeni WebSocket funguje!");
    
  }
  else if (type == WStype_DISCONNECTED) {
    websocket_pocet_spojeni--;
    if (websocket_pocet_spojeni == 0)
      websocket_pripojeno = false;
  }
  else if (type == WStype_TEXT) {
    Serial.println((char*)data);
  }
}

// Hlavni funkce programu, ktera se zpracuje po jeho spusteni
void setup() {
  // Nastartovani seriove linky
  Serial.begin(115200);
  // Rezervuji pro tuto promennou 1024 B
  message.reserve(1024);

  // Pripojeni k Wi-Fi
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1);
  }

  // Pokud se klient pripoji k HTTP serveru
  // posli mu pormennou html v samostatnem souboru html.h
  server.on("/", []() {
    server.send_P(200, "text/html", html);
  });

  // Nastartovani WebSocket/HTTP serveru
  websocket_server.onEvent(websocketUdalost);
  websocket_server.begin();
  server.begin();
}

// Smycka loop, ktera se opakuje stale dokola
void loop() {
  // Pokud skrze seriovou linku dorazila nejaka data, posli je jako text vsem pripojenym WebSocket klientum
  if (Serial.available()) {
    if (websocket_pripojeno) {
      // Narozdil od ATmega328P tentokrat pro praci s textovymi retezci uz objekt String pouziji,
      // cip ESP8266 ma totiz o rad vetsi RAM a teto promenne jsem v uvodu vyhradil 1024 B
      // String je na cipech s malickou pameti RAM problematicky proto,
      // ze se vytvari v dynamicke casti RAM a pri jeho opakovanem vytvareni s ruznou delkou muze 
      // casem dochazet k fragmentaci RAM a postupnemu vycerpani – jako diry v ementalu
      message = Serial.readStringUntil('\n');
      message.trim();
      websocket_server.broadcastTXT(message.c_str());
    }
  }

  // Poslouchani na portech 80 a 81
  websocket_server.loop();
  server.handleClient();
  delay(1);
}

Zdrojový kód přiloženého hlavičkového souboru html.h s kódem webové stránky pro prohlížeč:

static const char PROGMEM html[] = R"surove_html(
<!DOCTYPE html>
<html>
<head>
<title>Konzole Arduino Uno</title>
<script>
var ws;
// Funkce pro formatovani cisel v casovem udaju - pripojeni uvodnich nul
function pad(n, ms=false){
 var zero = "";
 if(ms){
  if(n < 100){
   if(n < 10){
    zero = "00";
   }
   else{
    zero = "0"
   }
  }
 }
 else{
  if(n < 10) zero = "0";
 }
 return zero + n;
}
function start() {
  // Pokus o spojení s WebSocket serverem na portu 81
  ws = new WebSocket("ws://" + window.location.hostname + ":81/");
  // Pokud dorazi nejaka websocketova data, pridej cas a vypis je do formularoveho prvku
  ws.onmessage = function(evt) {
    console.log(evt.data);
    var d = new Date();
    var message = pad(d.getHours()) + ":" + 
                  pad(d.getMinutes()) + ":" + 
                  pad(d.getSeconds()) + "." + 
                  pad(d.getMilliseconds(),true) + " --> " + 
                  evt.data + "\n";
    document.getElementById("log").value += message;
    document.getElementById("log").scrollTop = document.getElementById("log").scrollHeight;
  };
}

// Funkce pro odeslani dat po klepnuti na enter
function send(event){
  if(event.keyCode == 13){
    console.log("Odesilam do Arduino Uno: " + document.getElementById("message").value);
    ws.send(document.getElementById("message").value);
    document.getElementById("message").value = "";
  } 
}
</script>
<style>
#message{
  width: 500px;
  border: 1px solid #666666;
  padding: 10px;
  box-sizing: border-box;
  font-size; 16px;
  font-weight: bold;
  font-family: Courier New, Courier, monotype;
}
#log{
  width: 500px;
  height: 500px;
  margin-top: 20px;
  border: 1px solid #666666;
  padding: 10px;
  box-sizing: border-box;
  font-size: 14px;
  font-weight: bold;
  font-family: Courier New, Courier, monotype;
}
</style>
</head>
<body onload="javascript:start();">
<h2>Konzole Arduino Uno</h2>
<input id="message" type="text" value="" onkeydown="javascript:send(event);" autofocus />
<textarea id="log" style=""></textarea>
</body>
</html>
)surove_html";
Diskuze (19) Další článek: Computer Stage na veletrhu Amper: Povídání o hlasových asistentech

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