Mirko Pavleski
Published © GPL3+

Unusual Led Ring Arduino Clock, Temperature, and Humidity me

Although this is a very simple Arduino project, it contains more different options, as well as a very intuitive way to show a lot of inform

BeginnerFull instructions provided2 hours189
Unusual Led Ring Arduino Clock, Temperature, and Humidity me

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1
DS3231M - ±5ppm, I2C Real-Time Clock
Maxim Integrated DS3231M - ±5ppm, I2C Real-Time Clock
×1
DHT11 Temperature & Humidity Sensor (4 pins)
DHT11 Temperature & Humidity Sensor (4 pins)
×1
Buzzer
Buzzer
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic diagram

...

Code

code

C/C++
...
#include <Wire.h>
//#include <Math.h>
#include <RTClib.h>
#include <Adafruit_NeoPixel.h>
#include <SimpleDHT.h>

//Pin setup
const int DHT_PIN = 2;
const int NEO_PIN = 6;
const int BUZZER_PIN = 9;
const int BTN_PIN = 4; //2.2kohm pullup

//Pushbutton
bool btn_down_short = false;
bool btn_down_long = false;
bool btn_ignore = false;
bool btn_was_down = false;
unsigned long btn_down_started = 0;

//Temp and Humidity Sensor
SimpleDHT11 dht11;
byte temperature = 0;
byte humidity = 0;

//Neopixel Ring (12 LEDs)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, 6, NEO_GRB + NEO_KHZ800);

//RTC (Real Time Clock Breakout)
RTC_DS3231 ds3231;

//Keep track of milliseconds between seconds
int last_sec_five = 0;
unsigned long last_sec_five_millis = 0;

//Needed to keep track of datetime editing
unsigned long edit_date_blink_millis = 0;
int edit_time_hour = 0;
int edit_time_min = 0;

//RTC time
int rtc[7]; //sec,min,hour,dow,day,month,year
unsigned long rtc_last_refreshed = 0;

//The state to keep track of navigation
String state = "";

//The default brightness level of the LEDs
int brightness = 20; //1 - 255
int edit_brightness_state = 1;

void setup()
{
    Serial.begin(9600);

    delay(3000);

    state = "setup_pins";
    setupPins();
    Serial.println("Pin setup done");
    
    state = "rtc_setup";
    setupRtc();
    Serial.println("RTC setup done.");

    setupNeo();
    Serial.print("LED brightness defaulted to: ");
    Serial.println(brightness);
    
    state = "led_test";
    ledTest();
    Serial.println("LED test done.");
    
    state = "show_time";
}

void loop()
{
    Serial.print(rtc[2]); Serial.print(":");
    Serial.print(rtc[1]); Serial.print(":");
    Serial.print(rtc[0]); Serial.print(" -- ");
    Serial.print((int)(((int)temperature * 9)/5) + 32); Serial.print(" -- ");
    Serial.print(humidity); Serial.print(" -- ");
    Serial.println(brightness);
  
    unsigned long current_millis = millis();
    
    checkButtonState();
    
    //Refresh RTC time buffer every second
    if(millis() - rtc_last_refreshed > 1000 || rtc_last_refreshed > millis())
    {
        rtc_last_refreshed = millis();
        getDateTime();
        getDhtData();
    }
    
    //Keep track of milliseconds between each multiples of 5 seconds
    if(rtc[0]/5 != last_sec_five)
    {
        last_sec_five = rtc[0]/5;
        last_sec_five_millis = millis();
    }
    
    if(state == "edit_time")
    {
        state = "edit_hour";
    }
    else if(state == "edit_hour")
    {
        if(btn_down_long)
        {
            state = "edit_minute";
        }
        else
        {
            editHour();
        }
    }
    else if(state == "edit_minute")
    {
        if(btn_down_long)
        {
            //User just finished editing the time, set the entered time in RTC
            setDateTime(0, edit_time_min, edit_time_hour, rtc[3], rtc[4], rtc[5], rtc[6]);
            printRtcToSerial();
            state = "show_time";
        }
        else
        {
            editMinute();
        }
    }
    else if(state == "show_temperature")
    {
        if(btn_down_short)
        {
            state = "show_humidity";
        }
        else if(btn_down_long)
        {
            
        }
        else
        {
            showTemperature();
        }
    }
    else if(state == "show_humidity")
    {
        if(btn_down_short)
        {
            state = "show_brightness";
        }
        else if(btn_down_long)
        {
            
        }
        else
        {
            showHumidity();
        }
    }
    else if(state == "edit_brightness")
    {
        if(btn_down_long)
        {
            state = "show_brightness";
        }
        else
        {
            editBrightness();
        }
    }
    else if(state == "show_brightness")
    {
        if(btn_down_short)
        {
            state = "show_time";
        }
        if(btn_down_long)
        {
            state = "edit_brightness";
        }
        else
        {
            showBrightness();
        }
    }
    else
    {
        if(btn_down_short)
        {
            state = "show_temperature";
        }
        else if(btn_down_long)
        {
            state = "edit_time";
        }
        else
        {
            showTime(rtc[2], rtc[1], rtc[0]);
        }
    }
}

void setupPins()
{
    pinMode(BUZZER_PIN, OUTPUT);
    pinMode(BTN_PIN, INPUT);
}

void setupRtc()
{
    if(!ds3231.begin())
    {
        Serial.println("Couldn't find RTC");
        while (1);
    }
    
    if(ds3231.lostPower())
    {
        Serial.println("RTC lost power, lets set the time!");
        ds3231.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }

    getDateTime();
    printRtcToSerial();
}

void printRtcToSerial()
{
    Serial.print("RTC set to: ");
    Serial.print(rtc[5]); Serial.print("/");
    Serial.print(rtc[4]); Serial.print("/");
    Serial.print(rtc[6]); Serial.print(" ");
    Serial.print(rtc[2]); Serial.print(":");
    Serial.print(rtc[1]); Serial.print(":");
    Serial.println(rtc[0]);
}

void setupNeo()
{
    strip.begin();
    strip.show();
    strip.setBrightness(brightness);
}

void ledTest()
{
    clearNeo();
    strip.setPixelColor(0, 255, 255, 255);
    strip.show();
    delay(1000);
  
    for(int p=0; p <= 11; p++)
    {
        clearNeo();
        strip.setPixelColor(p , 255, 0, 0);
        strip.show();
        delay(50);
    }
    
    for(int p=0; p <= 11; p++)
    {
        clearNeo();
        strip.setPixelColor(p , 0, 255, 0);
        strip.show();
        delay(50);
    }
    
    for(int p=0; p <= 11; p++)
    {
        clearNeo();
        strip.setPixelColor(p , 0, 0, 255);
        strip.show();
        delay(50);
    }
}

void getDateTime()
{
    DateTime now = ds3231.now();
    rtc[0] = now.second();
    rtc[1] = now.minute();
    rtc[2] = now.hour();
    rtc[3] = now.dayOfTheWeek();
    rtc[4] = now.day();
    rtc[5] = now.month();
    rtc[6] = now.year();
}

void setDateTime(int sec, int min, int hour, int dow, int day, int month, int year)
{
    ds3231.adjust(DateTime(year, month, day, hour, min, sec));
    
    getDateTime();
    
    edit_time_hour = 0;
    edit_time_min = 0;
}

void showTime(int show_hour, int show_minute, int show_second)
{
    int five_millis = millis() - last_sec_five_millis;
    five_millis = five_millis <= 5000 ? five_millis : 5000;
    int second_ratio = map(five_millis, 0, 5000, 0, 255);
    int second = (show_second/5)+1;
    
    int minute_ratio = map(((show_minute % 5)*10), 0, 50, 0, 255);
    int minute = (show_minute/5)+1;
    
    int hour_ratio = map(show_minute, 0, 60, 0, 255);
    int hour = (show_hour > 12 ? show_hour-12 : show_hour);
    
    for(int x=0; x < 13; x++)
    {
        int r = 0;
        int g = 0;
        int b = 0;
        
        if(second-1 == x)
        {
            g = 255-second_ratio;
        }
        if(second == x || (second == 12 && x == 0))
        {
            g = second_ratio;
        }
        if(minute-1 == x)
        {
            b = 255-minute_ratio;
        }
        if(minute == x)
        {
            b = minute_ratio;
        }
        if(hour-1 == x)
        {
            r = 255-hour_ratio;
        }
        if(hour == x)
        {
            r = hour_ratio;
        }
        
        strip.setPixelColor(x, r, g, b);
    }
    
    strip.show();
    delay(40);
}

void editHour()
{
    if(edit_time_hour == 0)
    {
        edit_time_hour = rtc[2];
        edit_time_hour = (edit_time_hour > 24 ? edit_time_hour-24 : edit_time_hour);
    }
    
    if(btn_down_short)
    {
        edit_time_hour = (edit_time_hour > 23 ? edit_time_hour = 1 : edit_time_hour + 1);
    }
    
    clearNeo();
    if(edit_time_hour > 12)
    {
        strip.setPixelColor(edit_time_hour-13 , 120, 0, 120);
    }
    else
    {
        strip.setPixelColor(edit_time_hour-1 , 255, 0, 0);
    }
    strip.show();
    delay(40);
}

void editMinute()
{
    if(edit_time_min == 0)
    {
        edit_time_min = rtc[1];
    }
    
    if(btn_down_short)
    {
        edit_time_min = (edit_time_min >= 59 ? edit_time_min = 1 : edit_time_min + 1);
    }
    
    int minute_ratio = map(((edit_time_min % 5)*10), 0, 50, 0, 255);
    int minute = (edit_time_min/5);

    clearNeo();
    strip.setPixelColor(minute-1, 0, 0, 255-minute_ratio);
    strip.setPixelColor(minute , 0, 0, minute_ratio);
    strip.show();
    delay(40);
}

void showBrightness()
{
    clearNeo();
    strip.setPixelColor(0, 255, 0, 0);
    strip.setPixelColor(1, 255, 0, 0);
    strip.setPixelColor(2, 255, 0, 0);
    strip.setPixelColor(3, 255, 0, 0);
    strip.setPixelColor(4, 0, 255, 0);
    strip.setPixelColor(5, 0, 255, 0);
    strip.setPixelColor(6, 0, 255, 0);
    strip.setPixelColor(7, 0, 255, 0);
    strip.setPixelColor(8, 0, 0, 255);
    strip.setPixelColor(9, 0, 0, 255);
    strip.setPixelColor(10, 0, 0, 255);
    strip.setPixelColor(11, 0, 0, 255);
    strip.show();
    delay(100);
}

void editBrightness()
{
    if(btn_down_short)
    {
        brightness = (brightness >= 236 ? brightness = 20 : brightness + 20);
        strip.setBrightness(brightness);
    }
    
    
    edit_brightness_state = (edit_brightness_state >= 12 ? edit_brightness_state = 1 : edit_brightness_state + 1);
    int p = edit_brightness_state;
    
    clearNeo();
    strip.setPixelColor(0, (p == 1 ? 255 : 50), 0, 0);
    strip.setPixelColor(1, (p == 2 ? 255 : 50), 0, 0);
    strip.setPixelColor(2, (p == 3 ? 255 : 50), 0, 0);
    strip.setPixelColor(3, (p == 4 ? 255 : 50), 0, 0);
    strip.setPixelColor(4, 0, (p == 5 ? 255 : 50), 0);
    strip.setPixelColor(5, 0, (p == 6 ? 255 : 50), 0);
    strip.setPixelColor(6, 0, (p == 7 ? 255 : 50), 0);
    strip.setPixelColor(7, 0, (p == 8 ? 255 : 50), 0);
    strip.setPixelColor(8, 0, 0, (p == 9 ? 255 : 50));
    strip.setPixelColor(9, 0, 0, (p == 10 ? 255 : 50));
    strip.setPixelColor(10, 0, 0, (p == 11 ? 255 : 50));
    strip.setPixelColor(11, 0, 0, (p == 12 ? 255 : 50));
    strip.show();
    delay(50);
}

void showTemperature()
{
    clearNeo();
    for(int x=0; x < 12; x++)
    {
        strip.setPixelColor(x, 15, 15, 0);
    }
    
    int temp = (int)(((int)temperature * 9)/5) + 32;
    
    if(temp > 0 && temp <= 12)
    {
        strip.setPixelColor(temp, 255, 0, 0);
    }
    else if(temp > 12 && temp <= 99)
    {
        int first_int = nthDigit(temp, 1)-1;
        int second_int = nthDigit(temp, 2)-1;
        if(first_int == second_int)
        {
            strip.setPixelColor(nthDigit(temp, 1)-1, 255, 0, 255);
        }
        else
        {
            strip.setPixelColor(nthDigit(temp, 1)-1, 255, 0, 0);
            strip.setPixelColor(nthDigit(temp, 2)-1, 0, 0, 255);
        }
    }
    strip.show();
    delay(40);
}

void showHumidity()
{
    clearNeo();
    for(int x=0; x < 12; x++)
    {
        strip.setPixelColor(x, 0, 0, 15);
    }
    
    int hum = (int)humidity;
    
    if(hum > 0 && hum <= 12)
    {
        strip.setPixelColor(hum, 255, 0, 0);
    }
    else if(hum > 12 && hum <= 99)
    {
        int first_int = nthDigit(hum, 1)-1;
        int second_int = nthDigit(hum, 2)-1;
        if(first_int == second_int)
        {
            strip.setPixelColor(first_int, 255, 0, 255);
        }
        else
        {
            strip.setPixelColor(first_int, 255, 0, 0);
            strip.setPixelColor(second_int, 0, 0, 255);
        }
    }
    strip.show();
    delay(40);
}

int nthDigit(int x, int n)
{
    if(x <= 0 || x >= 100)
    {
        return 0;
    }
    if(x > 0 && x < 10)
    {
        return x;
    }
    if(n == 2)
    {
        return x-floor(x/10)*10;
    }
    if(n == 1)
    {
        return floor(x/10);
    }
}

void clearNeo()
{
    strip.setPixelColor(0, 0, 0, 0);
    strip.setPixelColor(1, 0, 0, 0);
    strip.setPixelColor(2, 0, 0, 0);
    strip.setPixelColor(3, 0, 0, 0);
    strip.setPixelColor(4, 0, 0, 0);
    strip.setPixelColor(5, 0, 0, 0);
    strip.setPixelColor(6, 0, 0, 0);
    strip.setPixelColor(7, 0, 0, 0);
    strip.setPixelColor(8, 0, 0, 0);
    strip.setPixelColor(9, 0, 0, 0);
    strip.setPixelColor(10, 0, 0, 0);
    strip.setPixelColor(11, 0, 0, 0);
}

void checkButtonState()
{
    //Is the button being press right now
    bool btn_is_pressed = digitalRead(BTN_PIN) == LOW ? false : true;

    //If the button long press was registered in the last loop, stop it in this loop
    if(btn_down_long)
    {
        btn_down_long = false;
    }
    
    //If a long press was registered in the last loop, and user is still pushing, ignore it
    if(!btn_ignore)
    {
        //If the button is being pressed now and was not being pressed before, start counting press time
        if(btn_is_pressed && !btn_was_down)
        {
            btn_down_started = millis();
            tone(BUZZER_PIN, 2000); delay(200); noTone(BUZZER_PIN);
        }

        //If button is not pressed now and was pressed before, register short press
        if(!btn_is_pressed && btn_was_down)
        {
            btn_down_short = true;
        }
        else
        {
            btn_down_short = false;
        }
      
        //If button is pressed now and has been pressed for more than 2 sec, register long press
        if(btn_is_pressed && btn_was_down && (millis() - btn_down_started) > 2000)
        {
            btn_down_long = true;
            
            //Ignore the button press until the user lets go
            btn_ignore = true;
            tone(BUZZER_PIN, 2000); delay(200); noTone(BUZZER_PIN);
            tone(BUZZER_PIN, 2000); delay(200); noTone(BUZZER_PIN);
        }
        else
        {
            btn_down_long = false;
        }
    }
    
    //If the user last did a long press and now they have let go, do not ignore button press anymore
    if(btn_was_down && !btn_is_pressed)
    {
        btn_ignore = false;
    }
    
    //Let this loops button state be known in the next loop
    btn_was_down = btn_is_pressed;
}

void getDhtData()
{
    dht11.read(DHT_PIN, &temperature, &humidity, NULL);
}

Credits

Mirko Pavleski

Mirko Pavleski

121 projects • 1178 followers

Comments