Rainclock

Udruga Međimurski informatički klub

Rainclock je pametni sat koji automatski saznaje na kojoj je lokaciji upaljen, te pokazuje točno vrijeme i vremensku prognozu za tu lokaciju bez podešavanja korisnika. Korisniku nudi različite prikaze podataka dostupne preko web sučelja.
Ciljevi ovog projekta su produbiti znanje učenika vezano uz Arduino MKR1000 s naglaskom na njegove sposobnosti spajanja na internet, te naučiti razradu teme od ideje do gotovog proizvoda.
Projekt će se koristiti kao ogledni primjer u udruzi za edukaciju polaznika IOT tečajeva u cjelinama koje obrađuju komunikaciju između sustava na internetu (http, https/ssl, udp protokol, REST servisi, JSON, itd.). Upoznavanjem s aktualnim tehnologijama smanjujemo ulazne barijere naših članova u svijet IT-a i developmenta kroz zanimljive projekte i gadgete koje bi svatko poželio imati.
Željeli bi napomenut vanjske servise koje smo koristili za izradu projekta: NTP server – dobivanje podataka o trenutnom točnom vremenu, Google Maps Geolocation API – dobivanje geolokacije uređaja na temelju “vidljivih WiFI access pointova”, Google Maps Time Zone API – dobivanje podataka o vremenskoj zoni na temelju geolokacijskih podataka Open Weather Map API – podaci o trenutnoj vremenskoj prognozi an temelju geolokacijskih podataka

Video snimka

Izrada projekta

Projekt sa svim spojenim komponentama spreman za “ugradnju” u kućište. Možemo vidjeti sve glavne komponente – Arduino, LCD ekran, RGB led diodu i servo motor s pločicom na kojoj su prikazane vremenske prilike.

Prikazuje na koji način smo projekt ugradili u kućište koje je malena kartonska kutija. Ovakav dizajn finalnog rješenja omogućit će nadogradnju i dodavanje novi dijelova, te štiti komponente i prezentira projekt kao jedinstven uređaj.

Mobilna aplikacija

Mobilna aplikacija zapravo je web stranica poslužena od strane Arduina. Brine o zahtjevima korisnika, te Arduino prikazuje jednostavno sučelje s 4 linka putem kojih postavljamo različit raspored informacija na ekranu. Za prikaz rada pogledajte video.

Shema projekta

Na Arduino spojite RGB modul koji na sebi već ima otpornike.0.

Arduino program

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


/***************************
* CONSTANTS
***************************/
const int status = WL_IDLE_STATUS;
const unsigned int NTP_LOCAL_PORT = 2390;
const int NTP_PACKET_SIZE = 48;
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

/***************************
* WiFi 
***************************/
String ssid = "leglo virusa";
String pass = "#NeD1rajM11nternet!";
String googleApiKey = "AIzaSyDvBb8EKrcGDYshtyv0vkQEddEQ9mOUNqM";
//char ssid[] = "TICM-UCIONA";
//char pass[] = "ticmuciona";
//char ssid[] = "Dexter's Laboratory";
//char pass[] = "TICM_Lab2017!";
IPAddress ip;
WiFiServer server(80);

/***************************
* NTP 
***************************/
IPAddress timeServer(129, 6, 15, 28);
byte packetBuffer[NTP_PACKET_SIZE];
WiFiUDP Udp;
unsigned long epoch = 0;

/***************************
* DISPLAY  
***************************/
hd44780_I2Cexp lcd;

/**************************
* LOCATION
**************************/
WifiLocation location(googleApiKey);
location_t locationData;
String locationCountry;
String locationCity;

/***************************
* TIMEZONE
***************************/
int dstOffset = 0;
int rawOffset = 0;
String timezoneStatus = "";
String timeZoneId = "";
String timeZoneName = "";

String timezoneServer = "maps.googleapis.com";
String timezoneApi = "/maps/api/timezone/";
String timezoneReturnType = "json";
String timezoneLocation = "?location=";
String timezoneTimestamp = "×tamp=1458000000";
String timezoneKey = "&key=";

/***************************
* OWM
***************************/
String owmApiKey = "27604a3ec8c43638c2ed800f47f8c5d5";
String owmServer = "api.openweathermap.org";

String weatherId;
String weatherMain;
String weatherTemp;
String weatherHumidity;
String weatherPressure;

/***************************
* RAINCLOCK
***************************/
int time_h;
int time_m;
int time_s;

unsigned long ntpCalldelay = 57000;
unsigned long t1;

int servoPin = 5;
Servo servo;
int rgbPins[3] = {2, 3, 4};

String displayVersion = "DEFAULT";
/***************************
* SETUP - start
***************************/
void setup()
{
    echoBrand("SETUP");

    //upali Serial Monitor - koristimo za debug
    Serial.begin(9600);

    //postavke za servo motor koji pokazuje vremenske prilike
    servo.attach(servoPin);
    servo.write(180);

    //povezivanje lcd zaslona
    int lcdStatus = lcd.begin(LCD_COLS, LCD_ROWS);
    if (lcdStatus)
    {
        lcdStatus = -lcdStatus;
        hd44780::fatalError(lcdStatus);
    }

    //provjeri da li uredaj na koji smo skinuli program ima mogucnost spajanja na wifi
    if (WiFi.status() == WL_NO_SHIELD)
    {
        echoBrand("WiFi nedostupan na uredaju");
        while (true);
    }

    //spoji se na wifi
    //ime mreze je u varijabli ssid, a lozinka u varijabli pass
    int wifiStatus;
    while (wifiStatus != WL_CONNECTED)
    {
        echoBrand("WiFi spajanje...");
        Serial.println(ssid);
        wifiStatus = WiFi.begin(ssid, pass);
        delay(10000);
    }
    echoBrand("WiFi spojen");
    //ispisi informacije o wifi vezi na serial monitor
    printWiFiStatus();

    //dobavi trenutnu lokaciju uredaja s google servisa
    //na temelju vidljivih wifi tocaka
    locationData = location.getGeoFromWiFi();
    echoBrand("lokacija:");
    delay(1000);
    echoBrand("Lat: " + String(locationData.lat, 7));
    delay(1000);
    echoBrand("Lon: " + String(locationData.lon, 7));
    delay(1000);

    //dobavi informacije o vremenskoj zoni u 
    //kojoj je uredaj upaljen na temelju koordinata iz proslog koraka
    getTimeZone();
    echoBrand("Vremenska zona:");
    delay(1000);
    echoBrand(timezoneStatus);
    delay(1000);
    echoBrand(timeZoneId);
    delay(1000);
    echoBrand(timeZoneName);
    delay(1000);

    //dobavi informacije o vrmenskoj progrnozi
    //sa servisa open weather map
    getCurrentWeather(locationData);

    //dobavi trenutno vrijeme s NTP servera
    echoBrand("NTP spajanje...");
    Udp.begin(NTP_LOCAL_PORT);
    getNTPTime();

    //pokreni lokalni server da arduino
    //moze primati zahtjeve korisnika
    server.begin();

    //pohrani vrijeme za timer koji 
    //odreduje interval pozivanja NTP servisa
    t1 = millis();
}
/***************************
* SETUP - end
***************************/

/***************************
* LOOP - start
***************************/
void loop()
{
    if (millis() - t1 > ntpCalldelay)
    {
        getNTPTime();
        t1 = millis();
    }

    enableServer();
}
/***************************
* LOOP - end
***************************/

/**
* metoda enableServer() brine o zahtjevima korisnika
* arduino prikazuje jednostavno sucelje sa 4 linka
* putem kojih postavljamo razlicit raspored informacija  
* na ekranu
*
* podrzane http metode: GET
* podrzani http pozivi:
*   - /DEFAULT
*   - /WEATHER
*   - /LOCATION
*   - /TIMEZONE
*/
void enableServer()
{
    WiFiClient client = server.available();
    if (client)
    {
        String currentLine = "";

        while (client.connected())
        {
            if (client.available())
            {
                char c = client.read();
                Serial.write(c);
                if (c == '\n')
                {
                    //kada se primi http zahtjev od klijenta
                    //posalji odgovor koji prikazuje izbornik ekrana
                    if (currentLine.length() == 0)
                    {
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-type:text/html");
                        client.println();

                        client.println("
"); client.println("

RAINCLOCK

"); client.println("

ODABERI PRIKAZ NA EKRANU:


"); client.println("Standardno
"); client.println("Vremenska prognoza
"); client.println("Lokacija uredaja
"); client.println("Vremenska zona
"); client.println("
"); client.println(); break; } else { currentLine = ""; } } else if (c != '\r') { currentLine += c; } //ovisno o zahtjevu klijenta pozovi odgovarajucu //metodu za drugaciji raspored informacija na ekranu if (currentLine.endsWith("GET /DEFAULT")) { displayVersion = "DEFAULT"; echoCurrentTime(); } if (currentLine.endsWith("GET /WEATHER")) { displayVersion = "WEATHER"; echoWeather(); } if (currentLine.endsWith("GET /LOCATION")) { displayVersion = "LOCATION"; echoLocation(); } if (currentLine.endsWith("GET /TIMEZONE")) { displayVersion = "TIMEZONE"; echoTimezone(); } } } delay(1); client.stop(); } } /** * metoda getNTPTime() sluzi da dobavljanje podataka * o trenutnom vremenu sa NTP servera */ void getNTPTime() { sendNTPpacket(timeServer); delay(1000); if (Udp.parsePacket()) { Udp.read(packetBuffer, NTP_PACKET_SIZE); unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); unsigned long secsSince1900 = highWord << 16 | lowWord; const unsigned long seventyYears = 2208988800UL; epoch = secsSince1900 - seventyYears; getCurrentTime(rawOffset); } } /** * metoda sendNTPpacket() salje UDP zahtjev * NTP serveru na sto NTP server odgovara * podacima koje obradujemo u metodi getNTPTime() */ unsigned long sendNTPpacket(IPAddress &address) { memset(packetBuffer, 0, NTP_PACKET_SIZE); packetBuffer[0] = 0b11100011; packetBuffer[1] = 0; packetBuffer[2] = 6; packetBuffer[3] = 0xEC; packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; Udp.beginPacket(address, 123); Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } /** * metoda printWiFiStatus() ispisuje podatke * o wifi vezi */ void printWiFiStatus() { Serial.print("SSID: "); Serial.println(WiFi.SSID()); //spremi ip adresu u varijablu ip ip = WiFi.localIP(); Serial.print("IP Adresa: "); Serial.println(ip); long rssi = WiFi.RSSI(); Serial.print("jacina signala (RSSI):"); Serial.print(rssi); Serial.println(" dBm"); } /** * metoda getCurrentTime() na temelju * podataka od NTP servera racuna * sate, minute i sekunde. * * metodu pozivamo na kraju svakog intervala osvjezavanja * pa tako ovdje provjeravamo koji ekran je trenutno aktivan * kako bi korisniku prikazali tocne podatke */ void getCurrentTime(int rawOffset) { time_h = ((epoch % 86400L) / 3600) + (rawOffset / 3600) - 12; time_m = ((epoch % 3600) / 60); time_s = (epoch % 60); if (displayVersion == "DEFAULT") { echoCurrentTime(); } if (displayVersion == "WEATHER") { echoWeather(); } if (displayVersion == "LOCATION") { echoLocation(); } if (displayVersion == "TIMEZONE") { echoTimezone(); } else { echoCurrentTime(); } } /** * metoda echoCurrentTime() */ void echoCurrentTime() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(time_h); lcd.print(':'); if (time_m < 10) { lcd.print('0'); } lcd.print(time_m); //lcd.print(':'); //if (time_s < 10) //{ // lcd.print('0'); //} //lcd.print(time_s); lcd.print(" "); int dow = ((epoch / 86400) + 4) % 7; switch (dow) { case 0: lcd.print("NED"); break; case 1: lcd.print("PON"); break; case 2: lcd.print("UTO"); break; case 3: lcd.print("SRI"); break; case 4: lcd.print("CET"); break; case 5: lcd.print("PET"); break; case 6: lcd.print("SUB"); break; } lcd.print(" "); lcd.print(weatherTemp); lcd.print((char)223); lcd.setCursor(0, 1); lcd.print(ip); } /** * metoda echoWeather() */ void echoWeather() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(weatherTemp); lcd.print((char)223); lcd.print(" "); lcd.print(weatherMain); lcd.setCursor(0, 1); lcd.print("H:"); lcd.print(weatherHumidity); lcd.print("% "); lcd.print("P:"); lcd.print(weatherPressure); lcd.print("hPa"); } /** * metoda echoLocation() */ void echoLocation() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(locationCity); lcd.print(","); lcd.print(locationCountry); lcd.setCursor(0, 1); lcd.print(String(locationData.lat, 5)); lcd.print(" "); lcd.print(String(locationData.lon, 5)); } /** * metoda echoTimezone() */ void echoTimezone() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(timeZoneId); lcd.setCursor(0, 1); lcd.print(timeZoneName); } /** * metoda echo() pomaze u ispisu na lcd ekran * prvi parametar ispisuje u prvi red, a drugi u drugi * red ekrana * * metoda parametre biljezi i na serial monitor */ void echo(String msg1, String msg2) { lcd.clear(); lcd.print(msg1); lcd.setCursor(0, 1); lcd.print(msg2); Serial.println(msg1); Serial.println(msg2); } /** * metoda echoBrand() pomaze u ispisu na lcd ekran * parametar ispisuje u drugi red, a u prvi * ispisuje naziv projekta "rainkclock" * * metoda parametar biljezi i na serial monitor */ void echoBrand(String msg) { lcd.clear(); lcd.print("RAINCLOCK"); lcd.setCursor(0, 1); lcd.print(msg); Serial.println(msg); } /** * metoda getTimeZone() sluzi za komunikaciju s google servisom * za vremenske zone * * komunikaciju je bilo potrebno odraditi "rucno" jer * nismo pronasli odgovarajuci library */ void getTimeZone() { String lat = String(locationData.lat, 7); String lon = String(locationData.lon, 7); String timeZoneLocationParams = lat + ',' + lon; String timeZoneApiCommand = "GET " + timezoneApi + timezoneReturnType + timezoneLocation + timeZoneLocationParams + timezoneTimestamp + timezoneKey + googleApiKey + " HTTP/1.1"; Serial.println(timeZoneApiCommand); //google api koristi ssl(https) WiFiSSLClient sslClient; //ssl defaultni port je 443, za rayliku od http-a gdje je to port 80 if (sslClient.connect(timezoneServer.c_str(), 443)) { sslClient.println(timeZoneApiCommand); sslClient.println("Host: " + timezoneServer); sslClient.println("User-Agent: ArduinoWiFi/1.1"); sslClient.println("Connection: close"); sslClient.println(); } String response; delay(1000); while (sslClient.available()) { char c = sslClient.read(); response += c; Serial.write(c); } int index = response.indexOf("dstOffset"); if (index != -1) { String tempStr = response.substring(index); tempStr = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); dstOffset = tempStr.toInt(); Serial.println(dstOffset); } index = response.indexOf("rawOffset"); if (index != -1) { String tempStr = response.substring(index); tempStr = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); rawOffset = tempStr.toInt(); Serial.println(rawOffset); } index = response.indexOf("status"); if (index != -1) { String tempStr = response.substring(index); timezoneStatus = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); timezoneStatus.trim(); timezoneStatus = timezoneStatus.substring(1, timezoneStatus.length() - 1); Serial.println(timezoneStatus); } index = response.indexOf("timeZoneId"); if (index != -1) { String tempStr = response.substring(index); timeZoneId = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); timeZoneId.trim(); timeZoneId = timeZoneId.substring(1, timeZoneId.length() - 1); Serial.println(timeZoneId); } index = response.indexOf("timeZoneName"); if (index != -1) { String tempStr = response.substring(index); timeZoneName = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); timeZoneName.trim(); timeZoneName = timeZoneName.substring(1, timeZoneName.length() - 3); Serial.println(timeZoneName); } sslClient.stop(); } /** * metoda getCurrentWeather() sluzi za komunikaciju s * open wather map servisom za vremensku prognozu * * iz dobivenog rezultata koji je u JSON formatu * izvlacimo podatke pomocu ArduinoJson library-ja */ void getCurrentWeather(location_t location) { WiFiClient owmClient; Serial.println("\nStarting connection to OWM..."); if (owmClient.connect(owmServer.c_str(), 80)) { Serial.println("Connected to OWM!"); owmClient.print("GET /data/2.5/weather?"); owmClient.print("lat=" + String(location.lat, 7)); owmClient.print("&lon=" + String(location.lon, 7)); owmClient.print("&appid=" + owmApiKey); owmClient.println("&units=metric"); owmClient.println("Host: api.openweathermap.org"); owmClient.println("Connection: close"); owmClient.println(); } delay(1000); String response = ""; while (owmClient.connected()) { response = owmClient.readStringUntil('\n'); Serial.println(response); StaticJsonBuffer<5000> jsonBuffer; JsonObject &root = jsonBuffer.parseObject(response); if (!root.success()) { Serial.println("JSON parse failed!"); return; } weatherId = (const char *)root["weather"][0]["id"]; weatherMain = (const char *)root["weather"][0]["main"]; weatherTemp = (const char *)root["main"]["temp"]; weatherHumidity = (const char *)root["main"]["humidity"]; weatherPressure = (const char *)root["main"]["pressure"]; locationCountry = (const char *)root["sys"]["country"]; locationCity = (const char *)root["name"]; int temperature = weatherTemp.toInt(); int weatherCode = weatherId.toInt(); Serial.println(weatherId); Serial.println(weatherMain); Serial.println(weatherTemp.toInt()); Serial.println(weatherHumidity); Serial.println(weatherPressure); showWeather(temperature, weatherCode); } owmClient.stop(); } /** * metoda showWeather() sluzi za prikaz * temperature (rgb ledica) i vrmenena (servo motor) */ void showWeather(int temperature, int weatherCode) { if (weatherCode >= 200 && weatherCode <= 299) { servo.write(0); } else if ((weatherCode >= 500 && weatherCode <= 599) || weatherCode == 906 || (weatherCode >= 300 && weatherCode <= 399)) { servo.write(30); } else if (weatherCode >= 600 && weatherCode <= 699) { servo.write(60); } else if (weatherCode == 741 || weatherCode == 701) { servo.write(90); } else if (weatherCode == 905) { servo.write(120); } else if (weatherCode > 800 && weatherCode <= 899) { servo.write(150); } else { servo.write(180); } if (temperature > -10) { setColor(0x0000FF); } else if (temperature > 0) { setColor(0xFFFF00); } else if (temperature > 10) { setColor(0x00FF00); } else if (temperature > 20) { setColor(0x003300); } else if (temperature > 30) { setColor(0xFF0000); } } /** * metoda setColor() postavlja boju RGB ledice * prema prosljedenoj hex vrijednosti */ void setColor(long color) { analogWrite(rgbPins[0], color >> 16); analogWrite(rgbPins[1], color >> 8 & 0xFF); analogWrite(rgbPins[2], color & 0xFF); }

Arduino program ovoga projekta možete preuzeti ovdje.

Autori

Projekt su izradili Leo Stričak i Andrija Palašek uz mentorstvo Viktora Lazara iz Udruge Međimurski informatički klub.

Projekt je prijavljen na temu: Internet of Things: Pametna rasvjeta.

Drugi projekti

Pametna rasvjeta – Zastora pomoću mobitela

Pametna rasvjeta – Zastora pomoću mobitela

Sustav pametne rasvjete baziran na prisutnosti korisnika

Sustav pametne rasvjete baziran na prisutnosti korisnika

Vanjska pametna rasvjeta – sensor

Vanjska pametna rasvjeta – sensor

Zvučna rasvjeta

Zvučna rasvjeta

Pametna rasvjeta za stan

Pametna rasvjeta za stan

Ambijetalno svjetlo

Ambijetalno svjetlo

Pametna rasvjeta

Pametna rasvjeta

Solarna energija

Solarna energija

Svjetlo u tami

Svjetlo u tami

Pametna rasvjeta (detektor zvuka)

Pametna rasvjeta (detektor zvuka)

Policijska sirena i oznaka Starta

Policijska sirena i oznaka Starta

Daljinsko upravljanje rasvjetom

Daljinsko upravljanje rasvjetom

Pali – gasi

Pali – gasi

Pametna rasvjeta – Music reacting circuit

Pametna rasvjeta – Music reacting circuit

Anti-theft

Anti-theft

Connect IT Pametni hodnik

Connect IT Pametni hodnik

Protuprovalni sustav

Protuprovalni sustav

Pametni stan

Pametni stan

Pametni tanjurić za šalicu

Pametni tanjurić za šalicu

Pametna kuća s Arduinom

Pametna kuća s Arduinom

Pametna kuća

Pametna kuća

Protuprovalno svjetlo u ormaru

Protuprovalno svjetlo u ormaru

Pametna rasvjeta

Pametna rasvjeta

Pametne osvijetljene stube

Pametne osvijetljene stube

Svijetliš u noći

Svijetliš u noći

Careduino

Careduino

Rainclock

Rainclock

Pljeskalica

Pljeskalica

Zebra

Zebra

Pametna rasvjeta informatika

Pametna rasvjeta informatika

Pametna rasvjeta

Pametna rasvjeta

Signalizacija

Signalizacija

Pametna rasvjeta

Pametna rasvjeta

Svjetlo za ugođaj

Svjetlo za ugođaj

Pametna rasvjeta

Pametna rasvjeta

Snimanje filma s mobitelom uz pomoć pametne rasvjete

Snimanje filma s mobitelom uz pomoć pametne rasvjete

Pametna ulična rasvjeta

Pametna ulična rasvjeta

Neka bude svjetlo

Neka bude svjetlo

Emotion light

Emotion light

Gasi letriku!

Gasi letriku!

Svjetlosni komunikator

Svjetlosni komunikator

iHouse

iHouse

Pametna rasvjeta s Arduinom

Pametna rasvjeta s Arduinom

Pametna rasvjeta kuće

Pametna rasvjeta kuće

Model kuće s pametnom rasvjetom

Model kuće s pametnom rasvjetom

Pametna rasvjeta – Zaštita kuće od provalnika

Pametna rasvjeta – Zaštita kuće od provalnika

Pametna rasvjeta na pljeskalački pogon

Pametna rasvjeta na pljeskalački pogon

Pametna rasvjeta

Pametna rasvjeta

Uređaj za mjerenje količine svjetlosti, vlage i temperature

Uređaj za mjerenje količine svjetlosti, vlage i temperature

Pametna budilica

Pametna budilica

Ambijentalna rasvjeta

Ambijentalna rasvjeta

Pametna garaža

Pametna garaža

Laserski brojač ljudi

Laserski brojač ljudi

Pametna vanjska rasvjeta

Pametna vanjska rasvjeta

Pametna rasvjeta

Pametna rasvjeta

Blynk pametna rasvjeta sa ESP8266

Blynk pametna rasvjeta sa ESP8266

Rasvjeta u igraonici

Rasvjeta u igraonici

Svjetionik

Svjetionik

IoT pametna rasvjeta ovisno o vremenu dana

IoT pametna rasvjeta ovisno o vremenu dana

Arduino smart light switch

Arduino smart light switch

Slabo pokretne osobe

Slabo pokretne osobe

RGB mixer

RGB mixer

Pametna rasvjeta

Pametna rasvjeta

Zujalica i svjetlo na senzor

Zujalica i svjetlo na senzor

Žuta i zelena soba

Žuta i zelena soba

Pametna rasvjeta

Pametna rasvjeta

Ako upališ, ugasi!

Ako upališ, ugasi!

Upozorenje na upaljena svjetla

Upozorenje na upaljena svjetla

GPS rasvjeta

GPS rasvjeta

RGB svjetlo

RGB svjetlo

Pametan parking

Pametan parking

Priprema, pozor, sad

Priprema, pozor, sad

Disco light

Disco light

Aurora Borealis

Aurora Borealis

Pametni hladnjak

Pametni hladnjak

Pametna rasvjeta

Pametna rasvjeta

Pametna rasvjeta u kući

Pametna rasvjeta u kući

Noćna rasvjeta

Noćna rasvjeta

Noćni prozor

Noćni prozor

Otvori me, zatvori me

Otvori me, zatvori me

House and garden lights

House and garden lights

IOT_Rasvjeta_4

IOT_Rasvjeta_4

Smart rasvjeta

Smart rasvjeta

IOT-Stubišni automat

IOT-Stubišni automat

Lego Arduino pametna rasvjeta

Lego Arduino pametna rasvjeta

IOT_Rasvjeta_2

IOT_Rasvjeta_2

IOT_Rasvjeta_1

IOT_Rasvjeta_1

Svjetlo za bicikl

Svjetlo za bicikl