/* ATtiny85 as an I2C Master
 * SETUP:
 * ATtiny Pin 5 = SDA on SD1306
 * ATtiny Pin 7 = SCL on SD1306
 */

//#define DEBUG
#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI
#include <avr/pgmspace.h>
#define SSD1306_LCDWIDTH      128
#define SSD1306_LCDHEIGHT      64
#define SSD1306_SETCONTRAST   0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR   0x22
#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2

const static byte CHGEN[][8] PROGMEM  = {   // https://xantorohara.github.io/led-matrix-editor
{   // 0 A
  B00000000,
  B11111100,
  B11111110,
  B00010010,
  B00010010,
  B11111110,
  B11111100,
  B00000000,
},{ // 1 B
  B00000000,
  B11111110,
  B11111110,
  B10010010,
  B10010010,
  B11111110,
  B01101100,
  B00000000,
},{ // 2 C
  B00000000,
  B01111100,
  B11111110,
  B10000010,
  B10000010,
  B11000110,
  B01000100,
  B00000000,
},{ // 3 D
  B00000000,
  B11111110,
  B11111110,
  B10000010,
  B10000010,
  B11111110,
  B01111100,
  B00000000,
},{ // 4 E
  B00000000,
  B11111110,
  B11111110,
  B10010010,
  B10010010,
  B10010010,
  B10000010,
  B00000000,
},{   // 5 F
  B00000000,
  B11111110,
  B11111110,
  B00010010,
  B00010010,
  B00010010,
  B00000010,
  B00000000,
},{   // 6 G
  B00000000,
  B01111100,
  B11111110,
  B10000010,
  B10100010,
  B11100110,
  B01100100,
  B00000000,
},{ // 7 H
  B00000000,
  B11111110,
  B11111110,
  B00010000,
  B00010000,
  B11111110,
  B11111110,
  B00000000,
},{ // 8 I
  B00000000,
  B00000000,
  B10000010,
  B11111110,
  B11111110,
  B10000010,
  B00000000,
  B00000000,
},{ // 9 J
  B00000000,
  B01100000,
  B11100000,
  B10000010,
  B11111110,
  B01111110,
  B00000010,
  B00000000,
},{ //10 K
  B00000000,
  B11111110,
  B11111110,
  B00111000,
  B01101100,
  B11000110,
  B10000010,
  B00000000,
},{ // 11 L
  B00000000,
  B11111110,
  B11111110,
  B10000000,
  B10000000,
  B10000000,
  B10000000,
  B00000000,
},{ // 12 M
  B00000000,
  B11111110,
  B11111110,
  B00001100,
  B00011000,
  B00001100,
  B11111110,
  B11111110,
},{ // 13 N
  B00000000,
  B11111110,
  B11111110,
  B00001100,
  B00011000,
  B00110000,
  B11111110,
  B11111110,
},{ // 14 O
  B00000000,
  B01111100,
  B11111110,
  B10000010,
  B10000010,
  B11111110,
  B01111100,
  B00000000,
},{ // 15 P
  B00000000,
  B11111110,
  B11111110,
  B00100010,
  B00100010,
  B00111110,
  B00011100,
  B00000000,
},{ // 16 Q
  B00000000,
  B00111100,
  B01111110,
  B01000010,
  B01100010,
  B11111110,
  B10111100,
  B00000000,
},{ // 17 R
  B00000000,
  B11111110,
  B11111110,
  B00110010,
  B01110010,
  B11011110,
  B10001100,
  B00000000,
},{ //18 S
  B00000000,
  B01001100,
  B11011110,
  B10010010,
  B10010010,
  B11110110,
  B01100100,
  B00000000,
},{ // 19 T
  B00000000,
  B00000110,
  B00000010,
  B11111110,
  B11111110,
  B00000010,
  B00000110,
  B00000000,
},{ // 20 U
  B00000000,
  B01111110,
  B11111110,
  B10000000,
  B10000000,
  B11111110,
  B11111110,
  B00000000,
},{ // 21 V
  B00000000,
  B00111110,
  B01111110,
  B11000000,
  B11000000,
  B01111110,
  B00111110,
  B00000000,
},{ //  22 W
  B00000000,
  B11111110,
  B11111110,
  B01100000,
  B00110000,
  B01100000,
  B11111110,
  B11111110,
},{ // 23 X
  B00000000,
  B11000110,
  B11101110,
  B00111000,
  B00010000,
  B00111000,
  B11101110,
  B11000110,
},{ // 24 Y
  B00000000,
  B00001110,
  B00011110,
  B11110000,
  B11110000,
  B00011110,
  B00001110,
  B00000000,
},{ // 25 Z
  B00000000,
  B11000010,
  B11100010,
  B10110010,
  B10011010,
  B10001110,
  B10000110,
  B00000000,
},{ // 26 (BLANK)
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
},{ // 27 a ==== zona minuscole
  B00000000,
  B01000000,
  B11101000,
  B10101000,
  B10101000,
  B11111000,
  B11110000,
  B00000000,
},{ // 28 b
  B00000000,
  B11111110,
  B11111110,
  B10010000,
  B10010000,
  B11110000,
  B01100000,
  B00000000,
},{ // 29
  B00000000,
  B01110000,
  B11111000,
  B10001000,
  B10001000,
  B11011000,
  B01010000,
  B00000000,
},{ // 30
  B00000000,
  B01100000,
  B11110000,
  B10010000,
  B10010000,
  B11111110,
  B11111110,
  B00000000,
},{ // 31
  B00000000,
  B01110000,
  B11111000,
  B10101000,
  B10101000,
  B10111000,
  B00110000,
  B00000000,
},{ // 32
  B00000000,
  B00100000,
  B11111100,
  B11111110,
  B00100010,
  B00100110,
  B00000100,
  B00000000,
},{ // 33
  B00000000,
  B00011000,
  B10111100,
  B10100100,
  B10100100,
  B11111100,
  B01111100,
  B00000000,
},{ // 34
  B00000000,
  B11111110,
  B11111110,
  B00010000,
  B00010000,
  B11110000,
  B11100000,
  B00000000,
},{ // 35
  B00000000,
  B00000000,
  B10000000,
  B11110100,
  B11110100,
  B10000000,
  B00000000,
  B00000000,
},{ // 36
  B00000000,
  B01100000,
  B11100000,
  B10000000,
  B11111010,
  B01111010,
  B00000000,
  B00000000,
},{ // 37
  B00000000,
  B11111110,
  B11111110,
  B00100000,
  B01110000,
  B11011000,
  B10001000,
  B00000000,
},{ // 38
  B00000000,
  B00000000,
  B00000000,
  B11111110,
  B11111110,
  B00000000,
  B00000000,
  B00000000,
},{ // 39
  B00000000,
  B11111000,
  B11111000,
  B00110000,
  B11100000,
  B00110000,
  B11111000,
  B11111000,
},{ // 40
  B00000000,
  B11111000,
  B11111000,
  B00011000,
  B00011000,
  B11111000,
  B11110000,
  B00000000,
},{ // 41
  B00000000,
  B01110000,
  B11111000,
  B10001000,
  B10001000,
  B11111000,
  B01110000,
  B00000000,
},{ // 42
  B00000000,
  B11111100,
  B11111100,
  B00100100,
  B00100100,
  B00111100,
  B00011000,
  B00000000,
},{ // 43
  B00000000,
  B00011000,
  B00111100,
  B00100100,
  B11111100,
  B11111100,
  B10000000,
  B11000000,
},{ // 44
  B00000000,
  B11111000,
  B11111000,
  B00001000,
  B00001000,
  B00111000,
  B00110000,
  B00000000,
},{ // 45
  B00000000,
  B10010000,
  B10101000,
  B10101000,
  B10101000,
  B10101000,
  B01001000,
  B00000000,
},{ // 46
  B00000000,
  B00010000,
  B00010000,
  B11111100,
  B11111100,
  B00010000,
  B00010000,
  B00000000,
},{ // 47
  B00000000,
  B01111000,
  B11111000,
  B10000000,
  B10000000,
  B11111000,
  B11111000,
  B00000000,
},{ // 48
  B00000000,
  B00110000,
  B01110000,
  B11000000,
  B11000000,
  B01110000,
  B00110000,
  B00000000,
},{ // 49
  B00000000,
  B01111000,
  B11111000,
  B10000000,
  B11110000,
  B10000000,
  B11111000,
  B01111000,
},{ // 50
  B00000000,
  B10001000,
  B11011000,
  B01110000,
  B01110000,
  B11011000,
  B10001000,
  B00000000,
},{ // 51
  B00000000,
  B00011000,
  B10111000,
  B10100000,
  B10100000,
  B11111000,
  B01111000,
  B00000000,
},{ // 52 z
  B00000000,
  B00000000,
  B11001000,
  B11101000,
  B10111000,
  B10011000,
  B00000000,
  B00000000,
},{ // 53 0 ===================== Zona NUMERI
  B00000000,
  B01111100,
  B11111110,
  B10010010,
  B10001010,
  B11111110,
  B01111100,
  B00000000,
},{ // 1
  B00000000,
  B10000000,
  B10001000,
  B11111110,
  B11111110,
  B10000000,
  B10000000,
  B00000000,
},{ // 2
  B00000000,
  B11000100,
  B11100110,
  B10100010,
  B10010010,
  B10011110,
  B10001100,
  B00000000,
},{ // 3
  B00000000,
  B01000100,
  B11000110,
  B10010010,
  B10010010,
  B11111110,
  B01101100,
  B00000000,
},{ // 4
  B00000000,
  B00110000,
  B00101000,
  B00100100,
  B11111110,
  B11111110,
  B00100000,
  B00000000,
},{ // 5
  B00000000,
  B01001110,
  B11001110,
  B10001010,
  B10001010,
  B11111010,
  B01110010,
  B00000000,
},{ // 6
  B00000000,
  B01111100,
  B11111110,
  B10010010,
  B10010010,
  B11110110,
  B01100100,
  B00000000,
},{ // 7
  B00000000,
  B00000110,
  B00000110,
  B11100010,
  B11111010,
  B00011110,
  B00000110,
  B00000000,
},{ // 8
  B00000000,
  B01101100,
  B11111110,
  B10010010,
  B10010010,
  B11111110,
  B01101100,
  B00000000,
},{ // 9
  B00000000,
  B01001100,
  B11011110,
  B10010010,
  B10010010,
  B11111110,
  B01111100,
  B00000000,
}};
void ssd1306_cmd(byte c){
  TinyWireM.beginTransmission(0x3C); 
  TinyWireM.write(0x00);   // Indica segue CMD 
  TinyWireM.write(c);   // Indica Display ON
  TinyWireM.endTransmission(); 
}

void ssd1306_data(byte c){
  TinyWireM.beginTransmission(0x3C); 
  TinyWireM.write(0x40);   // Indica segue CMD 
  TinyWireM.write(c);   // Indica Display ON
  TinyWireM.endTransmission(); 
}

void ssd1306_init(){
    ssd1306_cmd(SSD1306_DISPLAYOFF);                    // 0xAE
    ssd1306_cmd(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
    ssd1306_cmd(0x80);                 // the suggested ratio 0x80
    ssd1306_cmd(SSD1306_SETMULTIPLEX);                  // 0xA8
    ssd1306_cmd(0x1F);
    ssd1306_cmd(SSD1306_SETDISPLAYOFFSET);              // 0xD3
    ssd1306_cmd(0x0);                                   // no offset
    ssd1306_cmd(SSD1306_SETSTARTLINE);// | 0x0);        // 0x40 line #0
    ssd1306_cmd(SSD1306_CHARGEPUMP);                    // 0x8D
    ssd1306_cmd(0x14);  // using internal VCC
    ssd1306_cmd(SSD1306_MEMORYMODE);                    // 0x20
    ssd1306_cmd(0x00);          // 0x00 horizontal addressing
    ssd1306_cmd(SSD1306_SEGREMAP | 0x1);  // A1 rotate screen 180
    ssd1306_cmd(SSD1306_COMSCANDEC);        // rotate screen 180
    ssd1306_cmd(SSD1306_SETCOMPINS);                    // 0xDA
//    ssd1306_cmd(0x12);
    ssd1306_cmd(0x02);
    ssd1306_cmd(SSD1306_SETCONTRAST);                   // 0x81
//    ssd1306_cmd(0xCF);
    ssd1306_cmd(0x8F);
    ssd1306_cmd(SSD1306_SETPRECHARGE);                  // 0xd9
    ssd1306_cmd(0xF1);
    ssd1306_cmd(SSD1306_SETVCOMDETECT);                 // 0xDB
    ssd1306_cmd(0x40);
    ssd1306_cmd(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
    ssd1306_cmd(SSD1306_NORMALDISPLAY);                 // 0xA6
    ssd1306_cmd(0x2E);                                  // DeActivate Hor scroll
//    ssd1306_cmd(0x2F);                                // Activate Hor scroll
    ssd1306_cmd(SSD1306_DISPLAYON);                     //switch on OLED
}

void setColAddress(byte c){
  ssd1306_cmd(SSD1306_COLUMNADDR); // 0x21 COMMAND
  ssd1306_cmd(c);                 // Column start address
  ssd1306_cmd(SSD1306_LCDWIDTH-1); // Column end address
}
/////////////////////////////////////////////////////
// Used when doing Horizontal or Vertical Addressing
void setPageAddress(byte c){
  ssd1306_cmd(SSD1306_PAGEADDR); // 0x22 COMMAND
  ssd1306_cmd(c); // Start Page address
  ssd1306_cmd((SSD1306_LCDHEIGHT/8)-1);// End Page address
}

void ssd1306_fill(char c){
  byte m;
  setColAddress(0);  // set the Column and Page addresses to 0,0
  m=(SSD1306_LCDHEIGHT/8);
  for(byte i=0; i < m; i++) {
    setColAddress(0);  // set the Column and Page addresses to 0,0
    setPageAddress(i);
    for(byte j=0; j < 128; j++) {
      ssd1306_data(c);   // fill display
    }
  }
}

byte CVT(byte c) {
  if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') c=26; // Blank per non stampabili
  else {
    if(c <= '9') c -= ('0' - 53);
    else if(c <= 'Z') c -= 'A';
    else c -= ('a'-27);
  }
  return(c);
}

void ssd1306_char(byte c){
  for(int i = 0; i<8; i++) ssd1306_data(pgm_read_byte_near(&(CHGEN[CVT(c)][i])));
}

void ssd1306_char2(byte c){ // stampa double L
  for(byte i = 0; i< 8 ; i++) {
    byte c1 = pgm_read_byte_near(&(CHGEN[CVT(c)][i]));
    ssd1306_data(c1);
    ssd1306_data(c1);
  }
}

void ssd1306_char4(byte k, byte c){ // stampa double H&L 
  for(byte i = 0; i< 8 ; i++) {
    byte c1 = pgm_read_byte_near(&(CHGEN[CVT(c)][i]));
    if (k==2) c1 = c1 <<4;    // Converto parte bassa, perché i byte sono capovolti ... 
    c1 = c1&0x80 | (c1 >> 1)&0x7F; // raddoppio bit a 2 a 2
    c1 = c1&0xE0 | (c1 >> 1)&0x1F;
    c1 = c1&0xF8 | (c1 >> 1)&0x7;
    c1 = c1&0xFE | (c1 >> 1)&0x1;
    ssd1306_data(c1);
    ssd1306_data(c1);
  }
}

void ssd1306_string(char *s, byte k) {
  if(k==0) {
    while(*s) ssd1306_char(*s++);
  } else { 
    if(k==1) {
      while(*s) ssd1306_char2(*s++);
    } else {
      while(*s) ssd1306_char4(k, *s++);
    }
  }
}

const char gse_0[] PROGMEM = "Lun";
const char gse_1[] PROGMEM = "Mar";
const char gse_2[] PROGMEM = "Mer";
const char gse_3[] PROGMEM = "Gio";
const char gse_4[] PROGMEM = "Ven";
const char gse_5[] PROGMEM = "Sab";
const char gse_6[] PROGMEM = "Dom";
const char *const DGSET[] PROGMEM = {gse_0, gse_1, gse_2, gse_3, gse_4, gse_5, gse_6};

const char mes_0[] PROGMEM = "Gen";
const char mes_1[] PROGMEM = "Feb";
const char mes_2[] PROGMEM = "Mar";
const char mes_3[] PROGMEM = "Apr";
const char mes_4[] PROGMEM = "Mag";
const char mes_5[] PROGMEM = "Giu";
const char mes_6[] PROGMEM = "Lug";
const char mes_7[] PROGMEM = "Ago";
const char mes_8[] PROGMEM = "Set";
const char mes_9[] PROGMEM = "Ott";
const char mes_10[] PROGMEM = "Nov";
const char mes_11[] PROGMEM = "Dic";
const char *const DMESI[] PROGMEM = {mes_0, mes_1, mes_2, mes_3, mes_4, mes_5, mes_6, mes_7, mes_8, mes_9, mes_10, mes_11};

byte mese, giorno, gset, ora, minuti;
int anno;
char buf[20];

void display_giorno(){  
  setColAddress(5);  // set the Column and Page addresses to 0,0
  setPageAddress(0);
  strcpy_P(buf, (char *)pgm_read_word(&(DGSET[gset])));
  ssd1306_string(buf,0);  // giorno settimana
  setColAddress(42);  // a colonna 53
  setPageAddress(0);
  sprintf(buf,"%02d", giorno+1);
  ssd1306_string(buf,0);  // giorno
}  
void display_mese(){  
  setColAddress(63);  // a colonna 72
  setPageAddress(0);
  strcpy_P(buf, (char *)pgm_read_word(&(DMESI[mese])));
  ssd1306_string(buf,0);  // descrizione mese
}
void display_anno(){  
  setColAddress(93);  // a colonna 98
  setPageAddress(0);
  sprintf(buf,"%04d", anno);
  ssd1306_string(buf,0);  // anno
}
void display_ora(){// ORARIO
  setColAddress(23);  // a colonna 23
  setPageAddress(2);  // Riga 2
  sprintf(buf,"%02d", ora);
  ssd1306_string(buf,2);  // ora, espanso doppia altezza
  setColAddress(23);  // a colonna 23
  setPageAddress(3);  // Riga 3
  ssd1306_string(buf,3);  // ora, espanso doppia altezza
}
void display_duep(){// duepunti
  setColAddress(64);  // 
  setPageAddress(2);  // 
  ssd1306_data(B00110000); // due punti grafico diretto
  ssd1306_data(B00110000);
  setColAddress(64);  // 
  setPageAddress(3);  // 
  ssd1306_data(B00001100);
  ssd1306_data(B00001100);
}
void display_nop(){// duepunti
  setColAddress(64);  // 
  setPageAddress(2);  // 
  ssd1306_data(B00000000); // due punti grafico diretto
  ssd1306_data(B00000000);
  setColAddress(64);  // 
  setPageAddress(3);  // 
  ssd1306_data(B00000000);
  ssd1306_data(B00000000);
}

void display_minuti(){
  setColAddress(73);  // 
  setPageAddress(2);  //
  sprintf(buf,"%02d", minuti);
  ssd1306_string(buf,2);  // minuti, espanso doppia altezza
  setColAddress(73);  // 
  setPageAddress(3);  // 
  ssd1306_string(buf,3);  // minuti, espanso doppia altezza
}
//
void display_all() {  
  display_giorno();
  display_mese();
  display_anno();
  display_ora();
  display_duep();
  display_minuti();
}

void setup(){
  TinyWireM.begin();                    // initialize I2C lib
  ssd1306_init();
  delay (2000);
  ssd1306_fill(0);  // prima clear

  anno=2021;
  mese=11;
  giorno=30;
  gset=4;
  ora=23;
  minuti=55;
  display_all();
  
}

byte giomax(byte mese) {
  switch (mese) {
    case 1: if(anno % 4) return 28; else return 29;
    case 3:
    case 5:
    case 8:
    case 10:
      return 30;
    default:  return 31;
  }
}
byte conta = 0;

void loop(){
  for (byte i = 0; i<30; i++) {
    delay(1000);
    display_nop();
    delay(1000);
    display_duep();
  }
  minuti++;
  if(minuti == 60) {
    minuti = 0;
    ora++;
    if(ora == 24) {
      ora=0;
      gset = ++gset % 7;
      giorno++;
      if(giorno == giomax(mese)) {
        giorno = 0;
        mese++;
        if(mese == 12) {
          mese=0;
          anno++;
          display_anno();
        }
        display_mese();
      }
      display_giorno();
    }
    display_ora();  
  }
  display_minuti();  
}
ATTINY8520PU