// simple project using ATTINY85 board and SSD1306 128x64pxIIC
// OLED Display to show turbo gauge indicator
// Links:
// SSD1306xled library: https://github.com/tinusaur/ssd1306xled
// Additional boards json file: http://digistump.com/package_digistump_index.json
// 128x64 SSD1306 OLED Display 1.54": https://s.click.aliexpress.com/e/_DCYdWXb
// Colorful breadboards: https://s.click.aliexpress.com/e/_DmQH8V9
// Potentiometer 10K: https://s.click.aliexpress.com/e/_DkU6R5D
// Colorful knobs: https://s.click.aliexpress.com/e/_DlOIaoj
// Image2cpp (convert array to image): https://javl.github.io/image2cpp/
// Photopea (online graphics editor like Photoshop): https://www.photopea.com/
#include <PinChangeInterrupt.h>
#include <TinyDebug.h>
//#include <font6x8.h> - fonts for the ssd1306xled library, but we are not drawing any strings
//#include <font8x16.h> - fonts for the ssd1306xled library, but we are not drawing any strings
#include <ssd1306xled.h>
#include "image.h" // fullscreen image for the oven gauge, generated by image2cpp website
// default PIN configuration for OLED display and ATTINY85 is:
// VCC ---- vcc
// GND ---- gnd
// SCL ---- pb2
// SDA ---- pb0
// ----CONSTANTS
// pin number for the button
const int button1Pin = 1;
const int button2Pin = 3;
const int button3Pin = 5;
const int potentiometerPin = A2; // set pin A2 (pin PB4) potentiometer value
// animated piece of the fan icon (3 frames)
const unsigned char epd_bitmap_icon_anim_01 [] PROGMEM = { 0x42, 0xc3, 0x3c, 0x3c, 0x3c, 0x3c, 0xc3, 0x42};
const unsigned char epd_bitmap_icon_anim_02 [] PROGMEM = { 0x42, 0xa9, 0x1a, 0x7c, 0x3e, 0x58, 0x95, 0x42};
const unsigned char epd_bitmap_icon_anim_03 [] PROGMEM = { 0x42, 0x95, 0x58, 0x3e, 0x7c, 0x1a, 0xa9, 0x42};
const unsigned char* icon_anim_array[3] = {
epd_bitmap_icon_anim_01,
epd_bitmap_icon_anim_02,
epd_bitmap_icon_anim_03
};
//------- VARIABLES
byte fanIconAnimationActive = false;
byte icon_anim_frame = 0; // current frame
const uint8_t gauge_fill [] PROGMEM = { B11111111 }; // image for fully filled gauge
const uint8_t gauge_empty [] PROGMEM = { B00000000 }; // image for fully empty gauge
const uint8_t gauge_red_a [] PROGMEM = { B01010101 }; // image for red area (checkerboard)
const uint8_t gauge_red_b [] PROGMEM = { B10101010 }; // image for red area (checkerboard)
int gauge_width = 25; // width of the gauge - size could be from 0-122px
void setup() {
Debug.begin();
Debug.print(F("CTC1: "));
Debug.print(CTC1);
Debug.print(CTC1 & (1 << CTC1));
Debug.print(F(" CS10: "));
Debug.print(CS10);
Debug.print(CS10 & (1 << CS10));
Debug.print(F(" CS11: "));
Debug.print(CS11);
Debug.print(CS11 & (1 << CS11));
Debug.print(F(" TCCR0A: "));
Debug.print(TCCR0A);
Debug.print(F(" TCCR1: "));
Debug.print(TCCR1);
Debug.print(F(" = "));
Debug.print(1<<CTC1);
Debug.print(F(" | "));
Debug.print(2<<CS10);
Debug.print(F(" ("));
Debug.print(1<<CTC1 | 2<<CS10);
Debug.println(F(") "));
_delay_ms(40);
SSD1306.ssd1306_init(); // display initialization
SSD1306.ssd1306_draw_bmp(0, 0, 128, 8, epd_bitmap_oven_gauge_attiny_128x64px); //turbo_gauge_128x64_image); // draw fullscreen 128x64 image, only once
pinMode(potentiometerPin, INPUT); // set pin as input to measure voltage - potentiometer value
// set the buttons pins as input with a pullup resistor to ensure they defaults to HIGH
pinMode(button1Pin, INPUT_PULLUP);
pinMode(button2Pin, INPUT_PULLUP);
pinMode(button3Pin, INPUT);
enablePinChangeInterrupt();
}
/*
Enable pin (change) interrupts
*/
void enablePinChangeInterrupt() {
Debug.print(F("enablePinChangeInterrupt: "));
Debug.println(digitalRead(button1Pin));
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(button1Pin), button1Down, CHANGE);
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(button2Pin), button1Down, CHANGE);
//attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(button3Pin), button1Down, CHANGE);
}
void button1Down() {
Debug.print(F("button1Down"));
Debug.print(digitalRead(button1Pin));
Debug.print(digitalRead(button2Pin));
Debug.print(F(" - "));
Debug.println(analogRead(button3Pin));
if (digitalRead(button1Pin) == LOW) {
// Disable all interrupts
noInterrupts();
fanIconAnimationActive = !fanIconAnimationActive;
// Enable all interrupts
interrupts();
}
}
void loop() {
int pot_value = analogRead(potentiometerPin); // read potentiometer value, 0-1023
gauge_width = map(pot_value, 0, 1023, 0, 122); // remap the potentiometer value between values 0-122px (max gauge width)
drawGauge(); // gauge_width = (gauge_width + 1) % 112; // increase the gauge_width between 0-112px
drawIcons();
/*
Debug.print(analogRead(button3Pin));
Debug.print(F(" - "));
Debug.println(analogRead(potentiometerPin));
*/
}
void drawGauge() {
for (int i = 0; i < 122; i++) { // go over entire width of the gauge
if (i < gauge_width) { // if the current position is smaller than gauge width
SSD1306.ssd1306_draw_bmp(3 + i, 2, 3 + i + 1, 3, gauge_fill); // draw filled gauge
} else { // not the filled gauge - could be the empty gauge or checkerboard
if (i > 88) { // draw checkerboard
SSD1306.ssd1306_draw_bmp(3 + i, 2, 3 + i + 1, 3, i % 2 ? gauge_red_a : gauge_red_b); // draw checkerboard, toggle between two images based on the x position
} else { // draw empty gauge
SSD1306.ssd1306_draw_bmp(3 + i, 2, 3 + i + 1, 3, gauge_empty); // draw empty gauge
}
}
}
}
void drawIcons() {
if(fanIconAnimationActive) {
SSD1306.ssd1306_draw_bmp(1 * 8 + 3, 5, 2 * 8 + 3, 6, icon_anim_array[icon_anim_frame]); // draw the animation part of the turbo gauge
icon_anim_frame = (icon_anim_frame + 1) % 3; // increase the current frame, go over values 0-2
} else {
SSD1306.ssd1306_draw_bmp(1 * 8 + 3, 5, 2 * 8 + 3, 6, icon_anim_array[1]);
}
}