/* 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();
}