/* Timescale Clock David Johnson-Davies - www.technoblogy.com - 8th November 2014 CC BY 4.0 Licensed under a Creative Commons Attribution 4.0 International license: http://creativecommons.org/licenses/by/4.0/ */ // 16x16 pixel bitmap definitions for each of the hours const int Blank = 12; PROGMEM prog_uchar ClockDigits[] = { // 12 0x08, 0x02, 0x08, 0x02, 0x1f, 0xfe, 0x1f, 0xfe, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x10, 0x06, 0x10, 0x0a, 0x10, 0x32, 0x10, 0x62, 0x1f, 0x82, 0x0e, 0x06, 0x00, 0x08, // 1 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x08, 0x02, 0x1f, 0xfe, 0x1f, 0xfe, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x10, 0x06, 0x10, 0x0a, 0x10, 0x32, 0x10, 0x62, 0x1f, 0x82, 0x0e, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x10, 0x82, 0x10, 0x82, 0x11, 0x82, 0x1e, 0xc4, 0x0c, 0xf8, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x70, 0x00, 0x90, 0x03, 0x12, 0x04, 0x12, 0x0f, 0xfe, 0x1f, 0xfe, 0x00, 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x07, 0x86, 0x18, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0xc4, 0x10, 0x78, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0xfc, 0x07, 0x86, 0x04, 0x82, 0x08, 0x82, 0x10, 0xc2, 0x10, 0x7c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x10, 0x02, 0x10, 0x0e, 0x10, 0x38, 0x10, 0xe0, 0x13, 0x80, 0x1e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x18, 0x0f, 0x3c, 0x11, 0xc6, 0x10, 0x82, 0x10, 0xc2, 0x0f, 0x62, 0x06, 0x7c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0f, 0x82, 0x10, 0xc2, 0x10, 0x44, 0x10, 0x4c, 0x18, 0x78, 0x0f, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 10 0x08, 0x02, 0x08, 0x02, 0x1f, 0xfe, 0x1f, 0xfe, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x0f, 0xfc, 0x18, 0x06, 0x10, 0x02, 0x10, 0x02, 0x18, 0x06, 0x0f, 0xfc, 0x03, 0xe0, // 11 0x08, 0x02, 0x08, 0x02, 0x1f, 0xfe, 0x1f, 0xfe, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x08, 0x02, 0x1f, 0xfe, 0x1f, 0xfe, 0x00, 0x02, 0x00, 0x02, // Blank 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Pins int const dc = PIN_A3; int const clk = PIN_A2; int const data = PIN_A1; int const cs = PIN_A5; int const SetHr = PIN_B2; int const SetMin = PIN_A7; unsigned long Time; // In seconds // Initialisation sequence for OLED module int const InitLen = 23; unsigned char Init[InitLen] = { 0xAE, // Display off 0xD5, // Set display clock 0x80, // Recommended value 0xA8, // Set multiplex 0x1F, 0xD3, // Set display offset 0x00, 0x40, // Zero start line 0x8D, // Charge pump 0x14, 0x20, // Memory mode 0x02, // Page addressing 0xA1, // 0xA0/0xA1 flip horizontally 0xC0, // 0xC0/0xC8 flip vertically 0xDA, // Set comp ins 0x02, 0x81, // Set contrast 0x7F, 0xD9, // Set pre charge 0xF1, 0xDB, // Set vcom detect 0x40, 0xA6 // Normal (0xA7=Inverse) }; // Write a data byte to the display void Data(uint8_t d) { digitalWrite(cs, LOW); for (uint8_t bit = 0x80; bit; bit >>= 1) { digitalWrite(clk, LOW); digitalWrite(data, d & bit); digitalWrite(clk, HIGH); } digitalWrite(cs, HIGH); } // Write a command byte to the display void Command(uint8_t c) { digitalWrite(dc, LOW); Data(c); digitalWrite(dc, HIGH); } void InitDisplay () { for (char c=0; c> 4)); // Column high for (int b = h ; b < 32; b+=2) Data(pgm_read_byte(&ClockDigits[offset+b])); Data(0); } } // Displays a fixed pointer in the centre of the bottom page of the display void DisplayPointer() { Command(0xB0); // Page 0 Command(0x0E); // Column low Command(0x13); // Column high Data(0b00100000); Data(0b01100000); Data(0b11111111); Data(0b01100000); Data(0b00100000); } // Draw timescale void DisplayTimescale (int minute) { Command(0xB1); // Page 1 Command(0x00); // Column low Command(0x10); // Column high for (int c=-64; c<64; c++) { int m = c + minute + 60; int h = (m-8)/60; if (m%60 == 0) Data(0b11111111); else if (m%30 == 0) Data(0b00111111); else if (m%15 == 0) Data(0b00001111); else if (m%5 == 0) Data(0b00000011); else Data(0b00000001); } } void setup() { // Turn off Timer1, USI, and ADC to save power PRR = 1< 0) DisplayDigit(x, Hour); else DisplayDigit(x0, Blank); x0 = x; // Next hour x = 124 - Minute - 8; if (x < 120) DisplayDigit(x, (Hour+1)%12); else DisplayDigit(x1, Blank); x1 = x; // Wait until the end of this second Start = Start + 1000; do ; while (millis() < Start); // Read set-time buttons if (!digitalRead(SetMin)) Time = ((Time / 60) + 1) * 60; else if (!digitalRead(SetHr)) Time = ((Time / 60) + 60) * 60; else Time = Time + 1; }