edison science corner
Published © CC BY-NC-SA

Simple RGB Desktoclock using ATTINY85

RGB Desktop clock using ATtiny85 and neopixel

IntermediateFull instructions provided426
Simple RGB Desktoclock using ATTINY85

Things used in this project

Story

Read more

Schematics

circuit diagram

Code

code

C/C++
#include <Adafruit_NeoPixel.h>
#include <avr/eeprom.h>
#include <RTClib.h>
#include "characters.h"

#define NUM_LEDS 54 // number of NeoPixels
#define BUTTON 4 // button pin


uint8_t hours = 0; //keep the hours to display
uint8_t minutes = 0;//keep the minutes to display
uint8_t color = 1; // store color value Red, blue, green, white
uint8_t brightLevel = 0;
uint8_t lastMinute = 60; // used to update the digits only once per minute
uint16_t arrayNumberValue[1]; // stores one array pointer to array number in characters.h
bool displayOn = true; // if display is on or off
bool sqwOn = false; // SQW pin of the RTC ds1307
unsigned long buttonPressTimeStamp; // button press time

RTC_DS1307 rtc;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, 1, NEO_GRB + NEO_KHZ800);

void setup() {
  delay(360);

  pinMode(BUTTON, INPUT_PULLUP); // use internal pullup of attiny 85
  //Write to EEPROM
  if (eeprom_read_byte((uint8_t*)511) != 131) { // prevents excessive flash write and default values if first time used or no colors are choose
    eeprom_write_byte((uint8_t*)511, 131);
    eeprom_write_byte((uint8_t*)2, 1);//color
    eeprom_write_byte((uint8_t*)3, 5);//brightness level
    eeprom_write_byte((uint8_t*)4, true);//Display mode
    eeprom_write_byte((uint8_t*)5, false);//RTC SQW Pin Mode
  };

  strip.begin();
  brightLevel = eeprom_read_byte((uint8_t*)3); // read brightness from eeprom
  strip.setBrightness(brightLevel * 4);// brightness select from 0 to 255

  while (!rtc.begin()) { // wait RTC to begin
    strip.setPixelColor(52, 100, 0, 0);
    strip.setPixelColor(53, 100, 0, 0);
    strip.show();
    rtc.begin();
    delay(500);
  }

  if (rtc.readnvram(0) != 0x01) {// if there isnt a 1 in rtc at adress 0 the rtc wasnt initialized
    while ((millis() - buttonPressTimeStamp) < 2000) { // display batt 0% menssage
      drawDigits(0, letterB);
      drawDigits(1, letterA);
      drawDigits(2, letterT);
      drawDigits(3, letterT);
    }
    strip.clear();
    while ((millis() - buttonPressTimeStamp) >= 2000 && (millis() - buttonPressTimeStamp) < 4000) {
      drawDigits(0, number0);
      drawDigits(1, simbolPercent);
    }
    //This line sets the RTC with an explicit date & time.
    rtc.adjust(DateTime(0, 0, 0, 0, 0, 0));
    rtc.writenvram(0, 0x01); // write a 1 to RTC SRAM adress 8 to know the rtc is already initialized
    rtc.writeSqwPinMode(DS1307_OFF);// Rtc SQW pin off
    buttonPressTimeStamp = millis();
  }
  //read from EEPROM
  color = eeprom_read_byte((uint8_t*)2); //read stored color
  displayOn = eeprom_read_byte((uint8_t*)4);// read display mode
  sqwOn = eeprom_read_byte((uint8_t*)5);// read alarmMode

  while ((millis() - buttonPressTimeStamp) < 1500) { // Greeting message
    drawDigits(0, letterG);
    drawDigits(1, letterE);
    drawDigits(2, letterM);
    drawDigits(3, letterA);
  }
  while ((millis() - buttonPressTimeStamp) >= 1500 && (millis() - buttonPressTimeStamp) < 3000) {
    drawDigits(0, letterL);
    drawDigits(1, letterG);
    drawDigits(2, letterH);
    drawDigits(3, letterT);
  }
  strip.clear();
  buttonPressTimeStamp = millis();


  if (digitalRead(BUTTON) == LOW) { // if the button is pushed while connecting the device alternates SQW pin
    sqwOn = !sqwOn;
    while ((millis() - buttonPressTimeStamp) < 1500) {
      drawDigits(0, letterS);
      drawDigits(1, letterQ);
      if (sqwOn) {
        drawDigits(2, number1);
        drawDigits(3, letterH);
        rtc.writeSqwPinMode(DS1307_SquareWave1HZ);// Rtc SQW pin 1Hz pwm
        eeprom_write_byte((uint8_t*)5, sqwOn);
      }
      if (!sqwOn) {
        drawDigits(2, letterN);
        drawDigits(3, letterO);
        rtc.writeSqwPinMode(DS1307_OFF);// Rtc SQW pin off
        eeprom_write_byte((uint8_t*)5, sqwOn);
      }
    }
    strip.clear();
    buttonPressTimeStamp = millis();
  }
}

void loop() {

  if (digitalRead(BUTTON) == LOW) { // checks if the button is pressed to enter options or turn on display if the display off option is selected
    buttonPressTimeStamp = millis();
    if (!displayOn) {
      readTimeBlinkColon();
      showTime(2, minutes);
      showTime(0, hours);
      while (digitalRead(BUTTON) == LOW) {// if the button is pressed for 5seg or longer the display is turned on indefinitely
        while ((millis() - buttonPressTimeStamp) > 5000 && (millis() - buttonPressTimeStamp) < 7000) {
          displayOn = true;
          eeprom_write_byte((uint8_t*)4, true);//Display mode
          drawDigits(0, letterD);
          drawDigits(1, letterI);
          drawDigits(2, letterO);
          drawDigits(3, letterN);
        }
      }
      if (displayOn) {
        showTime(2, minutes);
        showTime(0, hours);
      } else {
        strip.clear();
        strip.show();
      }
    } else {
      lastMinute = 60; // lastMinute needs to be set to 60 to force time update
      strip.clear();
      setOptions();
    }
  }

  readTimeBlinkColon();

  if (minutes != lastMinute) { // time update occurs only once every minute
    lastMinute = minutes;
    if (displayOn) {
      showTime(2, minutes);
      showTime(0, hours);
    }
  }
}
// OPTIONS MENU
void setOptions() {// Al options are time based using millis (you need to be careful to time each function accordingly or the code will be unstable.
  while ((millis() - buttonPressTimeStamp) < 4000) {// each option remains for 4 second using millis function.
    drawDigits(0, letterC); // color selection
    drawDigits(1, letterO);
    showTime(2, color);
    if (digitalRead(BUTTON) == LOW  && (millis() - buttonPressTimeStamp) > 900) {// debounce function to wait for 900 millis
      buttonPressTimeStamp = (millis() - 100);
      color++;
      if (color > 4) {
        color = 1;
      }
    }
  }
  while ((millis() - buttonPressTimeStamp) >= 4000 && (millis() - buttonPressTimeStamp) < 8000) {
    drawDigits(0, letterB);
    drawDigits(1, letterR);
    showTime(2, brightLevel);
    if (digitalRead(BUTTON) == LOW  && (millis() - buttonPressTimeStamp) > 4700) {
      buttonPressTimeStamp = (millis() - 4000); // returns to 4000 millis of diferece to stay in this loop
      brightLevel++;
      if (brightLevel > 10) {
        brightLevel = 1;
      }
      strip.setBrightness((brightLevel * 4));
    }
  }
  while ((millis() - buttonPressTimeStamp) >= 8000 && (millis() - buttonPressTimeStamp) < 13000) {
    drawDigits(0, letterH);
    drawDigits(1, letterR);
    showTime(2, hours);
    if (digitalRead(BUTTON) == LOW  && (millis() - buttonPressTimeStamp) > 8400) {
      buttonPressTimeStamp = (millis() - 8000);
      if (hours < 23) {
        hours++;
      } else {
        hours = 0;
      }
    }
  }
  while ((millis() - buttonPressTimeStamp) >= 13000 && (millis() - buttonPressTimeStamp) <= 16000) {
    drawDigits(0, letterM);
    drawDigits(1, letterI);
    showTime(2, minutes);
    if (digitalRead(BUTTON) == LOW && (millis() - buttonPressTimeStamp) > 13400) {
      buttonPressTimeStamp = (millis() - 13000);
      if (minutes < 59) {
        minutes++;
      } else {
        minutes = 0;
      }
    }
  }
  rtc.adjust(DateTime(0, 0, 0, hours, minutes, 0));// rtc adjusts time
  
  while ((millis() - buttonPressTimeStamp) >= 16000 && (millis() - buttonPressTimeStamp) <= 20000) {
    drawDigits(0, letterD);
    drawDigits(1, letterI);
    if (displayOn) {
      drawDigits(2, letterO);
      drawDigits(3, letterN);
    }
    if (!displayOn) {
      drawDigits(2, letterN);
      drawDigits(3, letterO);
    }
    if (digitalRead(BUTTON) == LOW && (millis() - buttonPressTimeStamp) > 16600) {
      buttonPressTimeStamp = (millis() - 16000);
      displayOn = !displayOn;
    }
  }
  if (!displayOn) {
    strip.clear();
    strip.show();
  }
  eeprom_write_byte((uint8_t*)2, color); // values are store in EEPROM
  eeprom_write_byte((uint8_t*)3, brightLevel);
  eeprom_write_byte((uint8_t*)4, displayOn);
}

void drawDigits (uint8_t digitNumber, uint8_t arrayName[] ) {// draws on digit form 0 to 3 taking values from array in characters
  //digit number from 0 left to 3 right
  uint8_t firstLed = 0;
  uint8_t lastLed = 0;
  uint8_t led = 0;
  uint8_t ledValue = 0;;

  switch (digitNumber) { // each digit NeoPixel LED number from first to last
    case 0: // digit 0 from left to right
      firstLed = 0;
      lastLed = 12;
      break;
    case 1: // digit 1
      firstLed = 13;
      lastLed = 25;
      break;
    case 2: //digit 2
      firstLed = 26;
      lastLed = 38;
      break;
    case 3: //digit 3
      firstLed = 39;
      lastLed = 51;
      break;
  }

  for (uint8_t x = firstLed; x <= lastLed ; x++) { // all arryas with caracters are progmem to save ram, in Micros like the ATTiny 85 have few ram
    ledValue = pgm_read_byte_near (arrayName + led);// read values from progmem
    switch (color) { // this code selects the color to draw the characters in colors RED,BLUE,GREEN,WHITE
      case 1:
        strip.setPixelColor(x, ledValue, 0, 0);
        break;
      case 2:
        strip.setPixelColor(x, 0, ledValue, 0);
        break;
      case 3:
        strip.setPixelColor(x, 0, 0, ledValue);
        break;
      case 4:
        strip.setPixelColor(x, ledValue, ledValue, ledValue);
        break;
    }
    led++;
  }
  strip.show();
}

void showTime (uint8_t firstDigit, uint8_t value) {// this function takes the digit and value from characters.h and draws it without the 0 to the left in hours
  uint8_t valueUnits = value;                      // always draws 2 digits.
  if (value < 10) {
    if (firstDigit == 0) {
      for (uint8_t x = 0; x < 13; x++) {
        strip.setPixelColor(x, 0, 0, 0);
      }
    } else {
      drawDigits(firstDigit, number0);
    }
    arrayNumberValue[0] = pgm_read_word_near(number + value);
    drawDigits((firstDigit + 1), arrayNumberValue[0]);
  } else {
    value /= 10; // some math to substract the 0 from the left in hours digits
    arrayNumberValue[0] = pgm_read_word_near(number + value);
    drawDigits(firstDigit, arrayNumberValue[0]);
    value *= 10;

    for (uint8_t x = 0; x < value ; x++) {
      valueUnits--;
    }
    arrayNumberValue[0] = pgm_read_word_near(number + valueUnits);
    drawDigits((firstDigit + 1), arrayNumberValue[0]);
  }
}
void readTimeBlinkColon() { // read time from rtc and blink colon every second
  if ((millis() - buttonPressTimeStamp) <= 1000 ) {
    //Read time from RTC
    DateTime now = rtc.now();
    minutes = now.minute();
    hours = now.hour();
  }
  if ((millis() - buttonPressTimeStamp) > 1000 && (millis() - buttonPressTimeStamp) < 2000) {
    //colon turn off you cand delete this to keep colons on
    strip.setPixelColor(52, 0, 0, 0);
    strip.setPixelColor(53, 0, 0, 0);
  }
  if ((millis() - buttonPressTimeStamp) >= 2000 && displayOn) { // colons color select to match digits
    buttonPressTimeStamp = millis();
    switch (color) {
      case 1:
        strip.setPixelColor(52, 100, 0, 0);
        strip.setPixelColor(53, 100, 0, 0);
        break;
      case 2:
        strip.setPixelColor(52, 0, 100, 0);
        strip.setPixelColor(53, 0, 100, 0);
        break;
      case 3:
        strip.setPixelColor(52, 0, 0, 100);
        strip.setPixelColor(53, 0, 0, 100);
        break;
      case 4:
        strip.setPixelColor(52, 100, 100, 100);
        strip.setPixelColor(53, 100, 100, 100);
        break;
    }
  }
  strip.show();
}

Credits

edison science corner

edison science corner

52 projects • 62 followers
Electronic enthusiast watch videos from here https://www.youtube.com/channel/UCVcOsUBmH4hzVSnmJA_i5SA

Comments