P4A – PHP dla Arduino cz. 1

czwartek, maj 5, 2011 0:45 by netmaniac | Filled in arduino, dla początkujących, howto

Wygląda na to, że jesteś tutaj nowy(a) jeśli zapiszesz się do mojego RSS feed w prosty sposób otrzymasz informacje o nowych materiałach tutaj.

Gdy poznamy już trochę Arduino w głowie każdego prędzej czy później pojawi się pomysł na projekt, który wymaga aby Arduino mogło połączyć się z siecią. Czy to będzie automatyka domowa dostępna przez sieć, czy zestaw czujników raportujący odczyty do bazy danych – trzeba jakoś połączyć Arduino do Internetu. I tu z pomocą przychodzi nam Ethernet Shield.

Najpierw trochę historii. Ethernet Shield był początkowo kompatybilny z małym Arduino. Dlaczego nie z Mega? Otóż do komunikacji z układem W5100 będącym sercem shielda wykorzystywany jest protokół SPI – na cyfrowych wejściach nr 10, 11, 12 i 13. W Arduino Mega SPI jest na innych wejściach. Można to obejść wyginając nóżki shielda i podłączając je do właściwych wejść cyfrowych kabelkiem.

Ale to dotyczy starszych shieldów. Obecnie sprzedawane przez Nettigo Ethernet Shieldy są kompatybilne zarówno z małymi Arduino (UNO, Duemilanove) jaki i Mega (te z procesorami ATmega1280 i ATmega2560) . Po czym poznać takiego shielda? Jeżeli jest na nim gniazdo kart microSD – znaczy to, że to jest nowsza wersja.

Ethernet Shield z gniazdem kart microSD

Ethernet Shield z gniazdem kart microSD

 

 

Wspomniany już został W5100 – układ scalony koreańskiej firmy WIZnet, napędzający Ethernet Shielda. Różni się tym od wielu innych kontrolerów Ethernet, że stos TCP/IP jest zaimplementowany bezpośrednio w układzie scalonym.  Co to znaczy dla użytkownika? Że biblioteka, którą musisz wykorzystać aby komunikować się ze światem potrzebuje mniej pamięci RAM i zajmuje mniej pamięci flash w porównaniu z układami nie mającymi na sobie stosu TCP/IP.

Jak korzystać z Ethernet Shielda?

Jest wiele przykładów w sieci jak tworzyć strony WWW wyświetlające dane z Arduino. Jednak, jeżeli masz już większe  doświadczenie z programowaniem Arduino to pewnie wiesz, że wszystkie łańcuchy znakowe, nawet te zdefiniowane w kodzie zajmują RAM, którego w ATmedze zawsze mało.

Weźmy oficjalny przykład z Arduino Tutoriala. Kod:

 client.println("HTTP/1.1 200 OK");
 client.println("Content-Type: text/html");
 client.println();

Zajmie nam 40 bajtów RAMu (15 znaków w HTTP… + kończące zero i 23 w Conte… + kończące zero). Łatwo sobie wyobrazić co to znaczy gdy mamy 2kB do dyspozycji w ogóle. Strona HTML nie może być zbyt rozbudowana.

Istnieje pewne rozwiązanie, które może nam pomóc – czyli przechowywanie stringów w pamięci Flash. Pozwala to zmniejszyć użycie pamięci RAM, ale często kosztem dodatkowego kodu. Dostęp do stringów tak definiowanych wymaga użycia specjalnego makra i kompilator nie pozwoli nam korzystać z tego makra w wywołaniu funkcji oczekującej char * jako argumentu. Na dodatek – każda zmiana w kodzie HTML, który chcemy wysłać oznacza, że trzeba zmodyfikować szkic i wgrać go w Arduino.

Zaraz, przecież Ethernet Shield od dwóch wydań ma na sobie gniazda na karty microSD – nie można jakoś wykorzystać przestrzeni jaką dają karty SD? Można, ale trzeba się trochę postarać.

Najpierw – Ethernet Shield musi zostać skonfigurowany do pracy w sieci – oznacza to ustawienie adresu MAC oraz IP. Można to zrobić tak:

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1, 177 };

Ethernet.begin(mac, ip);

Adres MAC najlepiej, zamiast wymyślać samemu odczytać z naklejki jaka znajduje się na spodzie shielda. Adres IP zależy od konfiguracji sieci. Powyższa sekwencja Ethernet.begin będzie działać tylko w sieci lokalnej – tzn gdy wszystkie adresy IP znajdują się w jednej podsieci. Jeżeli shield ma się łączyć z hostami w innych sieciach (zarówno jako klient lub gdy ma służyć jako serwer) musicie podać jeszcze jeden argument – tablice z 4 liczbami – adres IP domyślnej bramki (default gateway). Więcej w opisie biblioteki Ethernet.

Większość klocków mamy prawie gotowych. Biblioteka Ethernet, wchodząca w skład Arduino IDE, nie jest tym co nam najbardziej będzie pasować. Ułatwia ona tworzenie m.in. serwera TCP, ale do serwera HTTP jeszcze trzeba kawałek. Dlatego przyda się nam Webduino – na bazie biblioteki Ethernet, ktoś za nas wykonał spory kawałek pracy budując serwer HTTP.

Normalnie ściągnęlibyśmy ze strony Downloads bibliotekę i rozpakowalibyśmy w sketchbook/libraries. Na nasze potrzeby będziemy modyfikować trochę Webduino,  będzie do ściągnięcia na końcu tego mini-tutoriala, więc na razie nic nie trzeba instalować.

Webduino – jak zaczać?

Na początek musimy wiedzieć, że za pomocą

void addCommand(const char *verb, Command *cmd);

możemy zarejestrować dowolną funkcję wywoływaną gdy URL będzie pasował do verb. Przykład:

webserver.addCommand("blob.htm", &blob);

Jeżeli URL będzie się zaczynał (tzn porcja po adresie Arduino) od blob.htm, wówczas wywołana zostanie funkcja blob, która musi przyjmować argumenty zgodnie ze zdefiniowanym typem Command:

typedef void Command(WebServer &server, ConnectionType type,

                       char *url_tail, bool tail_complete);

server to obiekt WebServer, dla którego nastąpiło wywołanie metody, type to rodzaj połaczenia (INVALID, GET, HEAD, POST), url_tail to jest to co zostało w URLu po dopasowanym blob.htm. Jeżeli URL został obcięty ze względu na niewielki bufor używany przez Webduino (ehh ta pamięć), to ostatni parametr tail_complete bedzie miał wartość false.

OK, ale to mało wygodne musieć rejestrować każdą funkcję, zwłaszcza, że chcemy serwować dane z karty SD, której zawartości nie znamy. Przyda się nam teraz setFailureCommand, która pozwoli zarejestrować funkcję w naszym kodzie wywoływaną, gdy nie nastąpiło żadne inne dopasowanie do zarejestrowanych funkcji przez addCommand.

Czyli jeżeli URL nie pasuje do żadnej zgłoszonej wcześniej funkcji, wówczas zostanie wywołana funkcja podana do setFailureCommand.

Teraz wystarczy w niej sprawdzić czy url_tail jest nazwą pliku na karcie SD (ponieważ nie nastąpiło żadne dopasowanie, url_tail zawiera pełny URL włącznie z pierwszym znakiem / za adresem Arduino). Gdy pliku nie ma – wyświetlamy HTTP 404, jeżeli jest – wystarczy go wysłać do klienta.

Jak odczytać plik z karty SD?

Podobnie jak z serwerem HTTP nie musimy robić wszystkiego sami. Dobrą pomocą do stworzenia tego szkicu był tutorial przygotowany przez Limor Fried czyli – LadyAda. Korzysta on z biblioteki SdFatLib, która ma wsparcie dla systemów plików FAT16 i FAT32 (czyli tego co zwykle na kartach SD i SDHC jest).

Kod inicjalizacji i obsługi plików w zasadzie został wzięty z tego tutoriala. Omówię tutaj funkcję fetchSD, która została zarejestrowana przez setFailureCommand. Jej zadaniem jest znaleźć plik na karcie i wysłać go do przeglądarki:

P(CT_PNG) = "image/png\n";
P(CT_JPG) = "image/jpeg\n";
P(CT_HTML) = "text/html\n";
P(CT_CSS) = "text/css\n";
P(CT_PLAIN) = "text/plain\n";
P(CT) = "Content-type: ";
P(OK) = "HTTP/1.0 200 OK\n";
void fetchSD(WebServer &server, WebServer::ConnectionType type, char *urltail, bool){
	char buf[32];
	int16_t  readed;

	++urltail;
	char *dot_index; //Where dot is located
	if (! file.open(&root, urltail, O_READ)) {
		//Real 404
		webserver.httpNotFound();
  } else {
	if (dot_index = strstr(urltail, ".")) {
		++dot_index;
		server.printP(OK);
		server.printP(CT);
		if (!strcmp(dot_index, "htm")) {
				server.printP(CT_HTML);

		} else if (!strcmp(dot_index, "css")) {
				server.printP(CT_CSS);

		} else if (!strcmp(dot_index, "jpg")) {
				server.printP(CT_JPG);

		} else {
				server.printP(CT_PLAIN);
		}
		server.print(CRLF);
	}
	readed = file.read(buf,30);
	while( readed > 0) {
		buf[readed] = 0;
		bufferedSend(server,buf,readed);
		readed = file.read(buf,30);
	}
	flushBuffer(server);
	file.close();
	}
}

Na samym początku mamy zarejestrowanych kilka stałych znakowych przechowywanych w pamięci flash.

P(CT_PNG) = "image/png\n";

Makro P jest częścią Webduino, które służy do zapisywania stringów na pamięci flash a nie w RAM. A stałe te to są nazwy różnych formatów danych, tzw MIME Type, jakie chcemy obsługiwać. O co chodzi? Przeglądarka nie wie jakie dane zostaną wysłane przez serwer. Czy to będzie HTML czy obrazek dowiaduje się ona właśnie z nagłówka Content-Type, o którym za chwilę.

Następnie ‘pozbywamy’ się wiodącego ukośnika: ++urltail;, potem próbujemy otworzyć plik na karcie SD – jeżeli nie udaje się – to wyświetlamy błąd HTTP 404 (Not Found):

	if (! file.open(&root, urltail, O_READ)) {
		//Real 404
		webserver.httpNotFound();
  } else {

jeżeli się udało otworzyć plik, to w else spróbujemy go odczytać i wysłać do klienta.

Teraz kilka uwag. Po pierwsze – SdFatLib obsługuje tylko krótkie nazwy w formacie 8.3. Jeżeli spróbujesz użyć dłuższych nazw (które FAT32 dopuszcza) to pamiętaj, że nazwa widziana przez SdFatLib będzie inna od tej, którą zobaczysz po podmonotwaniu karty w twoim komputerze. I jeżeli zrobisz plik index.html (cztery znaki w rozszerzeniu), wówczas nazwa będzie dla SdFatLib ind~1.htm. Cóż, nawet jeżeli teraz w komputerze zmienisz nazwę na index.htm, wpis w katalog będzie w rozszerzonej formie. Musisz skasować plik i utworzyć go na nowo z nazwą w formacie 8.3.

Druga uwaga jest taka – z oczywistych względów (FISI :) ) nie będziemy się przejmować katalogami i zakładamy że wszystkie pliki są w głównym katalogu. Może w późniejszych wersjach kodu dodam obsługę trochę bardziej skomplikowanych struktur.

OK, wracamy do kodu fetchSD. Skoro udało się nam otworzyć plik, to szukamy kropki w nazwie pliku i jeżeli ją znajdziemy to sprawdzamy czy pozostała część (w domyśle – rozszerzenie) będzie pasować do znanych nam rozszerzeń. Bo nie wystarczy nam wysłać danych do klienta HTTP – musimy wysłać nagłówek z informacją o właściwym Content-Type (mówiliśmy o tym już wcześniej), inaczej dane mogą zostać błędnie zinterpretowane przez przeglądarkę.

Słowo o tym jak wygląda odpowiedź serwera WWW. Podzielona ona jest na dwie części. Pierwsza to tak zwane nagłówki. Przeglądarka jako nagłówek traktuje wszystko to co na początku, aż natrafi na pustą linię tekstu (linie są oddzielane znakami CR LF). Reszta to właściwa odpowiedź. Jak co ona zostanie zinterpretowana, będzie zależało od nagłówków. Serwer może pomóc przeglądarce przez ustawienie nagłówka określającego typ danych:

Content-type: text/html

Do pierwszego dwukropka jest nazwa nagłówka (tutaj Content-Type) potem wartość nagłówka. Tutaj używane są tak zwane typy MIME. I tak może to być np image/png dla obrazka PNG, image/jpg dla JPG czy text/html dla pliku HTML.

Obowiązkowym nagłówkiem jest status – czyli czy żądanie klienta zostało obsłużone, czy wystąpił błąd a może przekierowanie. HTTP/1.0 200 OK znaczy – jest dobrze, będzie żądna treść. Najpierw jest protokół i jego wersja (HTTP w wersji 1.0) a następnie sam kod 200 – w świecie HTTP znaczy to że jest dobrze. Inne częste kody to 404 – nie znaleziono zasobu (sławne Not Found), 301 i 302 – przekierowania.

Wiedząc to, staramy się rozpoznać rozszerzenie pliku i wysłać odpowiedni nagłówek:

	if (dot_index = strstr(urltail, ".")) {
		++dot_index;
		server.printP(OK);
		server.printP(CT);
		if (!strcmp(dot_index, "htm")) {
				server.printP(CT_HTML);

		} else if (!strcmp(dot_index, "css")) {
				server.printP(CT_CSS);

		} else if (!strcmp(dot_index, "jpg")) {
				server.printP(CT_JPG);

		} else {
				server.printP(CT_PLAIN);
		}
		server.print(CRLF);

Funkcje z dużym P na końcu oczekują nie char * ale const prog_uchar *.

Mamy już wysłany nagłówek HTTP (zakończony pustą linią server.print(CRLF)), więc wyślemy same dane:

	readed = file.read(buf,30);
	while( readed > 0) {
		buf[readed] = 0;
		bufferedSend(server,buf,readed);
		readed = file.read(buf,30);
	}
	flushBuffer(server);
	file.close();

Czytamy po 30 bajtów, wysyłamy do klienta przez funkcję buforującą wysyłane dane. Po co? Otóż jeżeli użyjemy najprostszego rozwiązania i będziemy wysyłać dane znak po znaku, wówczas każdy znak będzie w odrębnym pakiecie TCP. Bardzo (naprawdę, uwierz, naprawdę) nieefektywne rozwiązanie. Po prostu server.write wysyła od razu dane.

Dlatego napisałem funkcję bufferedSend, która jako argumenty bierze obiekt serwera WWW, wskaźnik na bufor z danymi i rozmiar bufora. Czemu nie korzystamy z funkcji określających rozmiar bufora znakowego takich jak strlen? Bo działać to może tylko gdy dane są tekstowe. Jeżeli dane są binarne (obrazki) to znacznik końca łańcucha może pojawić się w legalnym strumieniu danych.

W C i C++ znakiem końca łańcucha jest znak 0 (nie cyfra, tylko bajt o wartości 0). Jeżeli w naszym strumieniu danych mogą pojawić się zera, wszystkie funkcje związane z łańcuchami znakowymi, a oferowane przez standardową bibliotekę nie przydadzą się nam

Z tego powodu musimy wprost określić ilość danych wysyłanych do bufora.

I w zasadzie to tyle. Mamy na Arduino serwer WWW wysyłający dane z karty SD.

Czy to ma sens?

Wystarczy kilka testów z bardziej złożoną stroną WWW (nie jeden plik HTML ale do tego jakieś CSS i obrazki), żeby przekonać się, że rozwiązanie to ma swoje ograniczenia. Arduino jest jednowątkowe, więc każdy element z naszego serwera WWW jest ściągany po kolei. Oznacza to, że z punktu widzenia użytkownika strona się wolno ładuje.

Więc po co to? Arduino może prezentować dane zbierane z czujników w przyjaźniejszej formie jeżeli nie będzie ograniczeniem ilość pamięci RAM potrzebnej bardziej rozbudowanej stronie WWW. Trzymając kod HTML na karcie SD pozbywamy się tego ograniczenia. Ale jak w HTMLu trzymanym na karcie SD umieścić dane zebrane z czujników przez Arduino?

Potrzebujemy czegoś, co pozwoli nam wstrzyknąć dane do HTMLa pomiędzy ‘odczytem’ a ‘wysłaniem’. Czyli coś jak:

PHP dla Arduino

OK, to jest na wyrost :) potrzebujemy czegoś co bardziej przypomina szablony niż pełne PHP, ale na początku PHP też nie powalało funkcjonalnością :)

O tym jak zrobić taki parser (i pełny kod szkicu) – w następnym odcinku. Stay tuned.

Kręć, się kręć czyli serwo HXT900 z continuous rotation

czwartek, kwiecień 14, 2011 20:30 by netmaniac | Filled in arduino, dla początkujących

Serwomechanizmy takie jak mały HXT 900 są sterowane szerokością impulsu PWM. Czyli podłączamy zasilanie, masę i wyjście cyfrowe PWM do serwa i jeżeli skorzystamy z biblioteki Servo wchodzącej w skład Arduino IDE wystarczy napisać do serwa wartość od 0 do 180 określającą jak daleko ma się wychylić.

Serwomechanizm HXT900

Serwomechanizm HXT900

Dzięki niewielkiej przeróbce HXT900 można zmienić w silniczek którego prędkość obrotową można regulować w prosty sposób przez bibliotekę Servo, bez konieczności korzystania z układów elektronicznych do zasilania i sterowania silnikiem. Nazywane to jest continuous rotation.

Zacznijmy od demontażu serwa, przy okazji zobaczymy jak serwo działa. Najpierw – co będzie potrzebne do tej operacji?

Narzędzia potrzebne do operacji

Narzędzia potrzebne do operacji

Czyli – mały śrubokręt, gwóźdź do poszerzenia na gorąco otworu na ośkę (może być śrubka o odpowiedniej średnicy), pilnik do metalu (podejrzewam, że papier ścierny w zupełności też wystarczy), szczypce boczne, lutownica i topnik.

Bierzemy serwo i odkręcamy śrubki na dole obudowy:

Odkręcamy śrubki...

Odkręcamy śrubki...

Read the rest of this entry »

Czujnik ciśnienia BMP085 z Arduino

środa, marzec 30, 2011 10:00 by netmaniac | Filled in arduino, dla początkujących, howto

Od już dłuższego czasu w ofercie Nettigo dostępny jest adapter do BMP085 – czujnika ciśnienia atmosferycznego z wbudowanym czujnikiem temperatury. Jest on jak wszystkie adaptery ze SparkFun łatwy w użyciu dzięki wyprowadzeniom w rozstawie 0.1″.

Kilka razy byłem już pytany jak wykorzystać ten czujnik, więc krótka instrukcja. Najpierw podłączenie – czujnik ma interfejs I2C, więc wykorzystamy na ten cel wejścia analogowe A4 i i A5. Po włączeniu I2C te dwa wejścia zmieniają się w SDA i SCL odpowiednio – czyli szynę I2C, którą w Arduino IDE obsługuje biblioteka Wire.

Czujnik może być zasilany napięciem od 1.8 do 3.6V, więc nie podłączajcie 5V bo może skończyć się to zniszczeniem czujnika. Czyli poza A4 i A5 do SDA i SCL na adapterze podłączamy 3.3 V do VCC na adapterze i masę do GND. Wygląda to na przykład tak:

Podłączenie BMP085 do Arduino

Podłączenie BMP085 do Arduino

Teraz szkic… Czujnik nie podaje wprost wartości ciśnienia ani temperatury. Wg karty katalogowej trzeba to wyliczyć, tyle że algorytm jest dokładnie opisany. Ja jednak z wrodzonego lenistwa wolałem skorzystać z gotowych, dostępnych rozwiązań :) I tak na tym blogu znalazłem gotowy szkic. Po minimalnych modyfikacjach, zweryfikowaniu z kartą katalogową oraz kilku testach przybrał taką postać:

// BMP08 with Arduino

// DANGER: The BMP08 accepts 1.8 to 3.6 Volts – so no chance to connect it directly to 5 Volts.

// Connect VCC to VCC and GND to GND, SCL goes to analogue pin 5, SDA to analogue pin4.
// Notice! Sparkfun breakoutboard contains already 4.7K pull ups,
// If not using pre-built pull-ups:
// --> Add some pull up resistors (1K to 20K, most often something like 4.7K) between SDA, SCL and VCC finishes the setup.

// References: http://interactive-matter.org/2009/12/arduino-barometric-pressure-sensor-bmp085/ and http://news.jeelabs.org/2009/02/19/hooking-up-a-bmp085-sensor/
// Specification: http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf
// SparkFun breakout board: http://www.sparkfun.com/commerce/product_info.php?products_id=9694

#include "Wire.h"

#define I2C_ADDRESS 0x77

const unsigned char oversampling_setting = 3; //oversamplig for measurement
const unsigned char pressure_waittime[4] = { 5, 8, 14, 26 };

//just taken from the BMP085 datasheet
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;

void setup()
{
Serial.begin(9600); // start serial for output
Serial.println("Setting up BMP085");
Wire.begin();
bmp085_get_cal_data();
}
void bmp085_read_temperature_and_pressure(int& temperature, long& pressure);
void loop()
{
int temperature = 0;
long pressure = 0;

bmp085_read_temperature_and_pressure(&temperature,&pressure);
Serial.print(temperature/10.0);
Serial.print(" ");
Serial.print(pressure/100.0);
Serial.println();
delay(100);
}

void bmp085_read_temperature_and_pressure(int* temperature, long* pressure) {
int ut= bmp085_read_ut();
long up = bmp085_read_up();
long x1, x2, x3, b3, b5, b6, p;
unsigned long b4, b7;

//calculate the temperature
x1 = ((long)ut - ac6) * ac5 >> 15;
x2 = ((long) mc << 11) / (x1 + md);
b5 = x1 + x2;
*temperature = (b5 + 8) >> 4;

//calculate the pressure
b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = ac2 * b6 >> 11;
x3 = x1 + x2;

//b3 = (((int32_t) ac1 * 4 + x3)<> 2;

if (oversampling_setting == 3) b3 = ((int32_t) ac1 * 4 + x3 + 2) << 1;
if (oversampling_setting == 2) b3 = ((int32_t) ac1 * 4 + x3 + 2);
if (oversampling_setting == 1) b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 1;
if (oversampling_setting == 0) b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 2;

x1 = ac3 * b6 >> 13;
x2 = (b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;
b7 = ((uint32_t) up - b3) * (50000 >> oversampling_setting);
p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;

x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
*pressure = p + ((x1 + x2 + 3791) >> 4);

}

unsigned int bmp085_read_ut() {
write_register(0xf4,0x2e);
delay(5); //longer than 4.5 ms
return read_int_register(0xf6);
}

void bmp085_get_cal_data() {
Serial.println("Reading Calibration Data");
ac1 = read_int_register(0xAA);
Serial.print("AC1: ");
Serial.println(ac1,DEC);
ac2 = read_int_register(0xAC);
Serial.print("AC2: ");
Serial.println(ac2,DEC);
ac3 = read_int_register(0xAE);
Serial.print("AC3: ");
Serial.println(ac3,DEC);
ac4 = read_int_register(0xB0);
Serial.print("AC4: ");
Serial.println(ac4,DEC);
ac5 = read_int_register(0xB2);
Serial.print("AC5: ");
Serial.println(ac5,DEC);
ac6 = read_int_register(0xB4);
Serial.print("AC6: ");
Serial.println(ac6,DEC);
b1 = read_int_register(0xB6);
Serial.print("B1: ");
Serial.println(b1,DEC);
b2 = read_int_register(0xB8);
Serial.print("B2: ");
Serial.println(b1,DEC);
mb = read_int_register(0xBA);
Serial.print("MB: ");
Serial.println(mb,DEC);
mc = read_int_register(0xBC);
Serial.print("MC: ");
Serial.println(mc,DEC);
md = read_int_register(0xBE);
Serial.print("MD: ");
Serial.println(md,DEC);
}

long bmp085_read_up() {
write_register(0xf4,0x34+(oversampling_setting<<6));
delay(pressure_waittime[oversampling_setting]);

unsigned char msb, lsb, xlsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(0xf6); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 3); // read a byte
while(!Wire.available()) {
// waiting
}
msb = Wire.receive();
while(!Wire.available()) {
// waiting
}
lsb |= Wire.receive();
while(!Wire.available()) {
// waiting
}
xlsb |= Wire.receive();
return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting);
}

void write_register(unsigned char r, unsigned char v)
{
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r);
Wire.send(v);
Wire.endTransmission();
}

char read_register(unsigned char r)
{
unsigned char v;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 1); // read a byte
while(!Wire.available()) {
// waiting
}
v = Wire.receive();
return v;
}

int read_int_register(unsigned char r)
{
unsigned char msb, lsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r); // register to read
Wire.endTransmission();

Wire.requestFrom(I2C_ADDRESS, 2); // read a byte
while(!Wire.available()) {
// waiting
}
msb = Wire.receive();
while(!Wire.available()) {
// waiting
}
lsb = Wire.receive();
return (((int)msb<<8) | ((int)lsb));
}

Jako rezultat na porcie szeregowym powinniśmy dostać ciąg wartości – temperatura i ciśnienie w hektoPascalach.

Jest wersja alternatywna (z której nie korzystałem jeszcze ale wygląda bardzo dobrze, zresztą jest intensywnie przetestowana) – biblioteka do ArduCoptera od DIYDrones ma w sobie obsługę tego czujnika. Kod jest na Google Code.

A jeżeli ktoś chciałby podłączyć ten czujnik do OpenWrt to tutaj może znaleźć informację jak podłączyć urządzenie po I2C.

Film dokumentalny o Arduino

poniedziałek, marzec 14, 2011 19:53 by markal | Filled in arduino, przegląd sieci

Pojawił się niedawno w sieci ciekawy film dokumentalny poświęcony Arduino, nakręcony w zeszłym roku. W większości składa się z rozmów z twórcami tego projektu oraz osobami współpracującymi z zespołem. Dowiemy się m. in. jak powstała całą idea i dlaczego twórcy zdecydowali się udostępnić ją darmowo. Nie zabrakło też prezentacji paru bardzo interesujących urządzeń wykonanych w oparciu o Arduino.

Film po angielsku, momentami hiszpańsku, z angielskimi napisami.

Nettigo keypad – jak podłączyć prostą klawiaturkę do Arduino

środa, marzec 9, 2011 0:58 by netmaniac | Filled in arduino, dla początkujących

Nettigo Keypad jest prostą klawiaturą złożoną z pięciu klawiszy. Pomyślana została ona jako część interfejsu do Twojego urządzenia – pięć klawiszy jest ułożonych tak, że można je wykorzystać jako cztery klawisze kierunku i jeden wyboru.

Całość jest odczytywana przez jeden pin – aby odczytać, który klawisz został naciśnięty trzeba zarezerwować jedno wejście analogowe. Keypad powstał jako uzupełnienie LCD Kitu, tak aby razem można było zbudować jakiś prosty interfejs do Arduino.

LCD Kit razem z keypadem ma funkcjonalność zbliżoną do LCD Shielda. Jaka jest różnica? Dzięki LCD kitowi (albo innemu wyświetlaczowi – nie 2×16 a np. 4×20) i keypadowi, Arduino może znajdować się w innym miejscu niż wyświetlacz/klawiatura.

Jak jest zbudowany fizycznie keypad? Są to połączone szeregowo rezystory. W zależności od miejsca w którym zostaną zwarte do masy, rezystancja jednej z gałęzi utworzonego dzielnika napięcia zmienia się. Znaczy to, że napięcie mierzone między dwoma gałęziami również ulega zmianie. I to cała filozofia.

Skutek takiego rozwiązania jest taki, że klawisz który jest zwiera rezystor bliżej punktu pomiaru ma większą siłę przebicia i wciskając dwa klawisze jednocześnie zawsze tylko jeden będzie odczytany.

Montaż keypada jest bardo prosty – przyciski i rezystory należy przylutować, goldpina do podłączenia można ale nie trzeba (można przylutować kabelki). Wszystkie rezystory mają jednakową wartość (10k) więc nie ma znaczenia w które miejsce który rezystor zostanie przylutowany.

Po zmontowaniu całość wygląda mniej więcej tak:

Zmontowany keypad

Zmontowany keypad

Co dalej?

Read the rest of this entry »

Arduino relay shield – jak zmontować

niedziela, marzec 6, 2011 11:07 by netmaniac | Filled in arduino, dla początkujących

Arduino relay shield jest w ofercie Nettigo już od pewnego czasu, jednak dotąd nie pojawił się na naszym blogu opis jak go wykorzystywać. Czas to zmienić :)

Zanim się go wykorzysta, najpierw trzeba go zmontować, gdyż sprzedawany jest jako zestaw do montażu. Oprócz płytki drukowanej (PCB) w opakowaniu znajduje się trochę różnych elementów:

Elementy wchodzące w skład relay shielda
Elementy wchodzące w skład relay shielda

Read the rest of this entry »

Gra w zapamiętywanie kolorów

czwartek, luty 3, 2011 21:30 by markal | Filled in arduino, dla początkujących, howto, projekty

Niniejszy artykuł został napisany w oparciu o ten tekst zamieszczony na blogu Newtona. Wszystkie ilustracje, filmik oraz kod programu pochodzą właśnie stamtąd.

Celem projektu było wykorzystanie jak największej ilości elementów zawartych w Starter Kicie. A czym ów projekt jest? Jak sam tytuł wskazuje, to gra polegająca na zapamiętaniu sekwencji kolorów wyświetlanych za pomocą diody LED RGB, a następnie odtworzeniu jej. Na początku sekwencja składa się z 3 kolorów, potem stopień trudności wzrasta – za każdym razem jest dodawany kolejny kolor do zapamiętania. Pewnym ułatwieniem jest fakt, że do każdego koloru jest przypisany dźwięk o innym tonie – podczas wyświetlania danego koloru odpowiadający mu dźwięk jest emitowany z dołączonego głośniczka, tak więc gra angażuje dwa zmysły: wzrok i słuch. Interfejs użytkownika stanowi wspomniana już dioda RGB, linijka diod LED informująca, który z kolei kolor ustawiamy oraz dwa przyciski typu microswitch. Wciskając oba naraz uruchamiamy grę, lewym wybieramy kolor, a prawym zatwierdzamy. Są jeszcze dwie diody LED informujące, czy udało nam się poprawnie odtworzyć sekwencję. Jeśli zaświeci się lewa – znaczy, że tak, w przeciwnym wypadku zaświeci się prawa dioda.

Zmontowany na płytce stykowej układ prezentuje się tak:

Zmontowany układ

Zmontowany układ

A jak działa, można zobaczyć (i usłyszeć) na tym filmiku. Poniżej natomiast jest schemat tego układu wykonany we Fritzingu:

Schemat układu

Schemat układu

W tym miejscu kilka słów komentarza – przy rysowaniu takiego schematu warto uruchomić program Fritzing podając w linii komend (bądź we właściwościach skrótu) parametr -graphicssystem raster – będzie działał szybciej. Kolejna rzecz, którą warto wiedzieć – jeśli podczas rysowania połączeń między elementami przytrzymamy klawisz Shift, uzyskamy ładne, prostokątne linie. Jak widać, autor powyższego obrazka nie wiedział o tym ;)
Co do kwestii technicznych, wszystkie rezystory umieszczone na płytce mają wartość 220Ω, zaś układ scalony w centralnej części płytki to rejestr przesuwający 74HC595, dzięki któremu możemy sterować ośmioma diodami, zużywając na to jedynie trzy cyfrowe wyprowadzenia Arduino. Dokładniej zostało to opisane w artykule “Przesuwane diody”.

Jeśli już mamy wszystko podłączone, teraz należałoby to jakoś oprogramować. Na początku trzeba zadeklarować, co gdzie podłączamy (do których wejść/wyjść). Odpowiada za to ten fragment kodu:

int latchPin = 8, clockPin = 12, dataPin = 3; //rejestr przesuwający
byte data = 0; //dane do wysłania na rejestr

int led8 = 7, led9 = 6; //dodatkowe LEDy
int ledR = 11, ledG = 10, ledB = 9; //LED RGB
int buttonLeft = 5, buttonRight = 4; //przyciski
int speakerPin = 13; //głośniczek

Teraz następuje inicjalizacja – ustawienie funkcji wyprowadzeń, włączenie rezystorów podciągających, itd.:

void setup()
{
  Serial.begin(57600);
  for(int i=3;i<=13;i++) // ustawienie wszystkich pinów
    pinMode(i, OUTPUT); // od 3 do 13 jako wyjścia
  pinMode(buttonLeft, INPUT); // ustawienie pinów przycisków
  pinMode(buttonRight, INPUT); // jako wejścia

  digitalWrite(buttonLeft, HIGH); // włączenie wewnętrznych
  digitalWrite(buttonRight, HIGH); // rezystorów podciągających
  randomSeed(analogRead(0)); // zainicjalizowanie generatora liczb pseudolosowych
  // wartością odczytaną z niepodłączonego analogowego pinu 0 - przy każdym 
  // uruchomieniu będzie ona inna
  clearLeds();
}

W tym miejscu chciałbym wtrącić pewne wyjaśnienie, otóż w paru miejscach w kodzie programu pojawia się taki dziwny zapis:

 a ? b : c

a jeśli ktoś na co dzień nie programuje w języku C, może być tym nieco zdezorientowany, tym bardziej, że w “Language Reference” na oficjalnej stronie Arduino nie wyjaśniono tego. Jest to tak zwany operator wyrażenia warunkowego. Jeśli warunek podstawiony w miejscu “a” jest spełniony (lub zmienna w miejscu “a” ma wartość TRUE), zostanie zwrócona liczba “b”. W przeciwnym wypadku – “c”. Ot i cała filozofia.

Wróćmy do właściwego programu. Główna pętla wygląda następująco:

void loop()
{
  effect();
  clearLeds();
  if(waitForButton() == 3)
    return;

  for(int j=3;j<=8;j++)
    if(!playLevel(7, j))
      return;
}

Kolejno: na początku, funkcją effect() uruchamiane są dźwiękowe i świetlne “fajerwerki”, a funkcja clearLeds gasi wszystkie diody. Następnie, o ile użytkownik nie wciśnie dwóch przycisków naraz, co spowoduje reset, funkcja playLevel rozpoczyna kolejne poziomy gry, za każdym obiegiem pętli for z drugim parametrem (ilość świateł do zapamiętania) zwiększonym o 1. Pierwszy parametr natomiast jest na sztywno ustawiony jako 7, czyli kolory do zapamiętania losowane są spośród ośmiu (od 0 do 7). Można oczywiście zmniejszyć tę ilość, ale wtedy gra stanie się zbyt prosta. Zdecydowanie lepiej pomanipulować funkcją setColor, dodając kolejne kolory (wystarczy zmienić parametry w setRGB). Mając kilka dodanych kolorów, możemy ich liczbę (mniejszą o 1, bo numeracja jest od 0!) wpisać jako pierwszy parametr funkcji playLevel. Wtedy rozgrywka stanie się trudniejsza, czyli ciekawsza ;).

Pełny kod programu opatrzony komentarzami można pobrać tutaj. Myślę, że jest on na tyle jasny, że nie ma potrzeby go szczegółowo wyjaśniać.

Programowanie Arduino Pro Mini za pomocą USB Mini

wtorek, styczeń 25, 2011 0:56 by netmaniac | Filled in arduino, dla początkujących, howto

Każdy, kto robił projekt oparty na Arduino, wie, że przychodzi taki pomysł do głowy, że chciałoby się, aby Arduino było znacznie mniejsze niż podstawowa płytka.

Szybko powstały różne płytki, które oferują zgodność z Arduino ale są znacznie mniejsze. Jedną z takich jest produkowany przez SparkFun Arduino Pro Mini. Obecnie ma ono na pokładzie ATmega328 a rozmiar całej płytki to 33 mm x 18 mm. Występuje ono w dwóch wersjach. Pierwsza jest działa na obniżonym napięciu 3.3V i kosztem niższego taktowania (8 MHz) oferuje mniejsze zużycie prądu. Druga jest kopią funkcjonalną Arduino – działa na 5V i jest taktowana 16 MHz.

Jeśli rozmiar płytki jest taki niewielki to czego brakuje? Otóż brakuje konwertera USB/Serial. Dlatego do programowania musimy mieć dodatkowe urządzenie – konwerter USB/Serial TTL (dla osłody – brak tego układu na płytce oznacza niższą cenę samego Mini).

Jednym z konwerterów USB Serial w ofercie Nettigo jest Arduino Mini USB. Ponieważ połączenie i programowanie Pro Mini sprawia czasem na początku kłopoty – przedstawiam małe HOWTO:

Manualne programowanie

Po resecie urządzenia mniej więcej przez jedną sekundę aktywny jest bootloader Arduino. Oczekuje on na dane na Serialu i jeżeli rozpozna dane binarne – transmisja zostanie zapisana w pamięci Flash i uruchomiona. Dlatego najprostszy sposób to podłączyć Arduino Mini USB i Pro Mini w następujący sposób:

  • połączyć masy USB Mini z Pro Mini (GND z GND)
  • Zasilanie z USB Mini (+5V) do Vcc na Pro Mini
  • RX z USB Mini do RX1 na Pro Mini
  • TX z USB Mini do TX0 na Pro Mini

Wyjścia na USB Mini nie są opisane, więc tutaj jest schemat poglądowy wyprowadzeń (gniazdo USB jest z lewej strony rysunku):

Wyprowadzenia USB Mini

Wyprowadzenia USB Mini

Rzeczywiste podłączenie można podejrzeć na tym zdjęciu:

Układ do ręcznego resetu przy programowaniu

Układ do ręcznego resetu przy programowaniu

Szkic należy najpierw skompilować (ikonka z trójkącikiem) a następnie nacisnąć przycisk reset na Arduino Pro Mini i jednocześnie przycisk upoloadu w Arduino IDE (trzeba pamiętać o wyborze właściwej płytki w menu Tools/Board) . Diody TX i RX na USB Mini powinny zacząć migotać i szkic po chwili powinien już być na Pro Mini.

Automatyczny reset (Automatic Reset lub Software Reset)

Jednak zwykłe Arduino już od długiego czasu można programować naciskając Upload w IDE bez konieczność ręcznego resetowania płytki. W wypadku płytek Mini, które ze względu na swoje małe rozmiary często są w trudno dostępnych miejscach, takie rozwiązanie ma niewątpliwą przewagę nad ręcznym resetowaniem.

Aby skopiować to zachowanie większego Arduino wystarczy wyjście RTS na USB Mini (oznaczone 3) połączyć z którymś wejść RST na Pro Mini:

Automatyczny (programowy) reset Arduini Pro Mini

Automatyczny (programowy) reset Arduini Pro Mini

Tutaj użyłem sond z Bus Pirate, jako że ani do Pro Mini ani do USB Mini nie miałem przylutowanych goldpinów. Od teraz wystarczy tylko nacisnąć Upload w Arduino IDE i już Pro Mini po krótkiej chwili ma na sobie nowy szkic.

Modem komunikacji radiowej XBee XB24B

czwartek, styczeń 13, 2011 0:55 by sprae | Filled in arduino, howto

W życiu każdego entuzjasty elektroniki i programowania (zwanego z angielska geekiem) najwięcej radości dają gadgety, mające potencjalnie ogromne możliwości, których wykorzystanie zależy tylko od nas.
Po godzinach spędzonych na zabawie i testowaniu możliwości XBee, mogę szczerze powiedzieć, że jest on jednym z takich urządzeń.

Czym jest XBee?

XBee to modem radiowy z interfejsem szeregowym i dodatkowymi pinami, które możesz wykorzystać we własnym zakresie. Modem taki pozwala przesyłać dane bez pomocy kabla z prędkościami od 1200 do 230400 bitów na sekundę w paśmie 2.4 Ghz (czyli tym samym co WiFi). Modem zasilany jest napięciem 3.3 V. Dostarczany jest w postaci małej płytki drukowanej.

Read the rest of this entry »

Hallotron – czujnik pola magnetycznego

czwartek, grudzień 2, 2010 11:36 by markal | Filled in arduino

Hallotron to inaczej czujnik pola magnetycznego wykorzystujący efekt Halla. Ten efekt polega w skrócie na występowaniu różnicy potencjałów w przewodniku znajdującym się w polu magnetycznym.

CS3144E dostępny w sklepie Nettigo jest hallotronem w obudowie TO-92UA. Składa się ze stabilizatora napięcia, generatora napięcia Halla, wzmacniacza różnicowego i przerzutnika Schmitta. Wyjście typu otwarty kolektor. Dokładną specyfikację można znaleźć w karcie katalogowej, w sekcji Pliki.

Budowa wewnętrzna czujnika:

Budowa wewnętrzna czujnika Halla

Z zewnątrz natomiast wygląda to tak:

Wyprowadzenia czujnika

Nie sugerujcie się oznaczeniami – to całkiem inny układ, ale w tej samej obudowie. Po prostu nie miałem możliwości zrobienia porządnego zdjęcia w trybie makro ;) .

Czujnik podaje na wyjściu dwie wartości: VOH bliską napięciu zasilania i VOL bliską zeru. Stan wysoki występuje na wyjściu, gdy nie ma pola magnetycznego, a ściślej mówiąc, gdy pole wokół czujnika jest zbyt słabe, żeby przekroczyć wartość progową. Kiedy natężenie pola magnetycznego odpowiednio wzrośnie, napięcie na wyjściu spadnie prawie do zera. Układ wróci do poprzedniego, wysokiego stanu dopiero, gdy natężenie pola zmniejszy się, przy czym wartość progowa dla zmiany L->H jest niższa, niż dla H->L. Najlepiej zilustrować to wykresem:

Zmiana napięcia wyjściowego

Taki przebieg napięcia wyjściowego to tzw. histereza. A dlaczego nie ma po prostu jednego progu przełączania? W takim wypadku, gdyby natężenie pola było akurat bliskie temu progowi napięcie wyjściowe migotałoby podczas niewielkich jego zmian.

A tak, gdy pole magnetyczne osiągnie wartość BOP, wówczas nastąpi przełączenie ze stanu wysokiego na niski. Aby nastąpiło przełączenie w stan wysoki wartość natężenia pola magnetycznego musi spaść poniżej BHP, które jest mniejsze od BOP.

Dlatego przy natężeniu o zbliżonej do BOP wartość, następuje przełączenie w stan niski i nie ma efektu migotania, który miałby miejsce gdyby przełączenie między stanami odbywało się niezależnie od kierunku przy wartości BOP.

Najprostsza aplikacja

Hallotron ma oczywiście wiele ambitnych zastosowań, chociażby w obrotomierzu, niemniej ja ograniczę się do przedstawienia najprostszego z możliwych – diody LED włączanej za pomocą magnesu.

W związku z tym, że czujnik daje na wyjściu dwa poziomy napięć, aż się prosi, żeby podłączyć go do jednego z cyfrowych pinów Arduino. W tym celu jak zwykle trzeba zadeklarować pin jako wejście, w funkcji setup() wpisując:

pinMode(hallPin, INPUT)

gdzie hallPin oznacza numer cyfrowego pinu Arduino – ja wybrałem 8. Jeśli już jesteśmy przy konfiguracji wejścia, kolejną konieczną do zrobienia rzeczą będzie podciągnięcie go do zasilania przez wewnętrzny rezystor. Jeśli tego nie zrobimy, wejście będzie wisiało w powietrzu, zbierając zakłócenia objawiające się migotaniem diody. A więc zaraz po poprzedniej linijce dodajemy:

digitalWrite(hallPin, HIGH);

Jako wyjście wybieramy inny cyfrowy pin, przy czym najlepiej 13-ty, bo tam większość modeli Arduino ma dołączoną diodę LED. Reszta kodu nie wymaga chyba dłuższego komentowania – wejście jest odczytywane w pętli, a wyjście ustawiane w zależności od jego wartości. Cały program prezentuje się następująco:

const int hallPin = 8;
const int ledPin = 13;

int hallState = 0;

void setup(){
  pinMode(hallPin, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(hallPin, HIGH);
}

void loop(){
  hallState = digitalRead(hallPin);

  if (hallState == HIGH){
    digitalWrite(ledPin, LOW);
  }
  else{
    digitalWrite(ledPin, HIGH);
  }
}

Schemat połączenia (bardziej dla zasady, bowiem nie ma tu raczej żadnych niejasności):

Schemat połączeń

Zewnętrzna dioda LED jest opcjonalna, o ile nasz model Arduino ma wbudowaną. W przeciwnym wypadku musi być podłączona, jak pokazano. Rezystor ograniczający prąd można zupełnie bezpiecznie pominąć.

Tak wygląda złożony w praktyce, jakże skomplikowany, układ:

Układ zmontowany w praktyce

A tak działa:

Jak widać, wypróbowałem dwa magnesy i różnica w zasięgu czujnika jest widoczna. Nie miałem niestety magnesu neodymowego, ale podejrzewam, że wtedy czujnik reagowałby z dużo większej odległości. Co do strony czujnika, do której należy zbliżać magnes – nie ma to znaczenia, a przynajmniej nie jest zauważalne.