#ifndef __TM1637DISPLAY__
#define __TM1637DISPLAY__
#include <inttypes.h>
#define SEG_A 0b00000001
#define SEG_B 0b00000010
#define SEG_C 0b00000100
#define SEG_D 0b00001000
#define SEG_E 0b00010000
#define SEG_F 0b00100000
#define SEG_G 0b01000000
#define DEFAULT_BIT_DELAY 100
class TM1637Display {
public:
//! Initialize a TM1637Display object, setting the clock and
//! data pins.
//!
//! @param pinClk - The number of the digital pin connected to the clock pin of the module
//! @param pinDIO - The number of the digital pin connected to the DIO pin of the module
//! @param bitDelay - The delay, in microseconds, between bit transition on the serial
//! bus connected to the display
TM1637Display(uint8_t pinClk, uint8_t pinDIO, unsigned int bitDelay = DEFAULT_BIT_DELAY);
//! Sets the brightness of the display.
//!
//! The setting takes effect when a command is given to change the data being
//! displayed.
//!
//! @param brightness A number from 0 (lowes brightness) to 7 (highest brightness)
//! @param on Turn display on or off
void setBrightness(uint8_t brightness, bool on = true);
//! Display arbitrary data on the module
//!
//! This function receives raw segment values as input and displays them. The segment data
//! is given as a byte array, each byte corresponding to a single digit. Within each byte,
//! bit 0 is segment A, bit 1 is segment B etc.
//! The function may either set the entire display or any desirable part on its own. The first
//! digit is given by the @ref pos argument with 0 being the leftmost digit. The @ref length
//! argument is the number of digits to be set. Other digits are not affected.
//!
//! @param segments An array of size @ref length containing the raw segment values
//! @param length The number of digits to be modified
//! @param pos The position from which to start the modification (0 - leftmost, 3 - rightmost)
void setSegments(const uint8_t segments[], uint8_t length = 4, uint8_t pos = 0);
//! Clear the display
void clear();
//! Display a decimal number
//!
//! Dispaly the given argument as a decimal number.
//!
//! @param num The number to be shown
//! @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are
//! blank. NOTE: leading zero is not supported with negative numbers.
//! @param length The number of digits to set. The user must ensure that the number to be shown
//! fits to the number of digits requested (for example, if two digits are to be displayed,
//! the number must be between 0 to 99)
//! @param pos The position of the most significant digit (0 - leftmost, 3 - rightmost)
void showNumberDec(int num, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0);
//! Display a decimal number, with dot control
//!
//! Dispaly the given argument as a decimal number. The dots between the digits (or colon)
//! can be individually controlled.
//!
//! @param num The number to be shown
//! @param dots Dot/Colon enable. The argument is a bitmask, with each bit corresponding to a dot
//! between the digits (or colon mark, as implemented by each module). i.e.
//! For displays with dots between each digit:
//! * 0.000 (0b10000000)
//! * 00.00 (0b01000000)
//! * 000.0 (0b00100000)
//! * 0.0.0.0 (0b11100000)
//! For displays with just a colon:
//! * 00:00 (0b01000000)
//! For displays with dots and colons colon:
//! * 0.0:0.0 (0b11100000)
//! @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are
//! blank. NOTE: leading zero is not supported with negative numbers.
//! @param length The number of digits to set. The user must ensure that the number to be shown
//! fits to the number of digits requested (for example, if two digits are to be displayed,
//! the number must be between 0 to 99)
//! @param pos The position of the most significant digit (0 - leftmost, 3 - rightmost)
void showNumberDecEx(int num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0);
//! Display a hexadecimal number, with dot control
//!
//! Dispaly the given argument as a hexadecimal number. The dots between the digits (or colon)
//! can be individually controlled.
//!
//! @param num The number to be shown
//! @param dots Dot/Colon enable. The argument is a bitmask, with each bit corresponding to a dot
//! between the digits (or colon mark, as implemented by each module). i.e.
//! For displays with dots between each digit:
//! * 0.000 (0b10000000)
//! * 00.00 (0b01000000)
//! * 000.0 (0b00100000)
//! * 0.0.0.0 (0b11100000)
//! For displays with just a colon:
//! * 00:00 (0b01000000)
//! For displays with dots and colons colon:
//! * 0.0:0.0 (0b11100000)
//! @param leading_zero When true, leading zeros are displayed. Otherwise unnecessary digits are
//! blank
//! @param length The number of digits to set. The user must ensure that the number to be shown
//! fits to the number of digits requested (for example, if two digits are to be displayed,
//! the number must be between 0 to 99)
//! @param pos The position of the most significant digit (0 - leftmost, 3 - rightmost)
void showNumberHexEx(uint16_t num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0);
//! Translate a single digit into 7 segment code
//!
//! The method accepts a number between 0 - 15 and converts it to the
//! code required to display the number on a 7 segment display.
//! Numbers between 10-15 are converted to hexadecimal digits (A-F)
//!
//! @param digit A number between 0 to 15
//! @return A code representing the 7 segment image of the digit (LSB - segment A;
//! bit 6 - segment G; bit 7 - always zero)
uint8_t encodeDigit(uint8_t digit);
protected:
void bitDelay();
void start();
void stop();
bool writeByte(uint8_t b);
void showDots(uint8_t dots, uint8_t* digits);
void showNumberBaseEx(int8_t base, uint16_t num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = 4, uint8_t pos = 0);
private:
uint8_t m_pinClk;
uint8_t m_pinDIO;
uint8_t m_brightness;
unsigned int m_bitDelay;
};
#endif // __TM1637DISPLAY__
extern "C" {
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
}
#include <Arduino.h>
#define TM1637_I2C_COMM1 0x40
#define TM1637_I2C_COMM2 0xC0
#define TM1637_I2C_COMM3 0x80
//
// A
// ---
// F | | B
// -G-
// E | | C
// ---
// D
const uint8_t digitToSegment[] = {
// XGFEDCBA
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
0b01110111, // A
0b01111100, // b
0b00111001, // C
0b01011110, // d
0b01111001, // E
0b01110001 // F
};
static const uint8_t minusSegments = 0b01000000;
TM1637Display::TM1637Display(uint8_t pinClk, uint8_t pinDIO, unsigned int bitDelay)
{
// Copy the pin numbers
m_pinClk = pinClk;
m_pinDIO = pinDIO;
m_bitDelay = bitDelay;
// Set the pin direction and default value.
// Both pins are set as inputs, allowing the pull-up resistors to pull them up
pinMode(m_pinClk, INPUT);
pinMode(m_pinDIO,INPUT);
digitalWrite(m_pinClk, LOW);
digitalWrite(m_pinDIO, LOW);
}
void TM1637Display::setBrightness(uint8_t brightness, bool on)
{
m_brightness = (brightness & 0x7) | (on? 0x08 : 0x00);
}
void TM1637Display::setSegments(const uint8_t segments[], uint8_t length, uint8_t pos)
{
// Write COMM1
start();
writeByte(TM1637_I2C_COMM1);
stop();
// Write COMM2 + first digit address
start();
writeByte(TM1637_I2C_COMM2 + (pos & 0x03));
// Write the data bytes
for (uint8_t k=0; k < length; k++)
writeByte(segments[k]);
stop();
// Write COMM3 + brightness
start();
writeByte(TM1637_I2C_COMM3 + (m_brightness & 0x0f));
stop();
}
void TM1637Display::clear()
{
uint8_t data[] = { 0, 0, 0, 0 };
setSegments(data);
}
void TM1637Display::showNumberDec(int num, bool leading_zero, uint8_t length, uint8_t pos)
{
showNumberDecEx(num, 0, leading_zero, length, pos);
}
void TM1637Display::showNumberDecEx(int num, uint8_t dots, bool leading_zero,
uint8_t length, uint8_t pos)
{
showNumberBaseEx(num < 0? -10 : 10, num < 0? -num : num, dots, leading_zero, length, pos);
}
void TM1637Display::showNumberHexEx(uint16_t num, uint8_t dots, bool leading_zero,
uint8_t length, uint8_t pos)
{
showNumberBaseEx(16, num, dots, leading_zero, length, pos);
}
void TM1637Display::showNumberBaseEx(int8_t base, uint16_t num, uint8_t dots, bool leading_zero,
uint8_t length, uint8_t pos)
{
bool negative = false;
if (base < 0) {
base = -base;
negative = true;
}
uint8_t digits[4];
if (num == 0 && !leading_zero) {
// Singular case - take care separately
for(uint8_t i = 0; i < (length-1); i++)
digits[i] = 0;
digits[length-1] = encodeDigit(0);
}
else {
//uint8_t i = length-1;
//if (negative) {
// // Negative number, show the minus sign
// digits[i] = minusSegments;
// i--;
//}
for(int i = length-1; i >= 0; --i)
{
uint8_t digit = num % base;
if (digit == 0 && num == 0 && leading_zero == false)
// Leading zero is blank
digits[i] = 0;
else
digits[i] = encodeDigit(digit);
if (digit == 0 && num == 0 && negative) {
digits[i] = minusSegments;
negative = false;
}
num /= base;
}
if(dots != 0)
{
showDots(dots, digits);
}
}
setSegments(digits, length, pos);
}
void TM1637Display::bitDelay()
{
delayMicroseconds(m_bitDelay);
}
void TM1637Display::start()
{
pinMode(m_pinDIO, OUTPUT);
bitDelay();
}
void TM1637Display::stop()
{
pinMode(m_pinDIO, OUTPUT);
bitDelay();
pinMode(m_pinClk, INPUT);
bitDelay();
pinMode(m_pinDIO, INPUT);
bitDelay();
}
bool TM1637Display::writeByte(uint8_t b)
{
uint8_t data = b;
// 8 Data Bits
for(uint8_t i = 0; i < 8; i++) {
// CLK low
pinMode(m_pinClk, OUTPUT);
bitDelay();
// Set data bit
if (data & 0x01)
pinMode(m_pinDIO, INPUT);
else
pinMode(m_pinDIO, OUTPUT);
bitDelay();
// CLK high
pinMode(m_pinClk, INPUT);
bitDelay();
data = data >> 1;
}
// Wait for acknowledge
// CLK to zero
pinMode(m_pinClk, OUTPUT);
pinMode(m_pinDIO, INPUT);
bitDelay();
// CLK to high
pinMode(m_pinClk, INPUT);
bitDelay();
uint8_t ack = digitalRead(m_pinDIO);
if (ack == 0)
pinMode(m_pinDIO, OUTPUT);
bitDelay();
pinMode(m_pinClk, OUTPUT);
bitDelay();
return ack;
}
void TM1637Display::showDots(uint8_t dots, uint8_t* digits)
{
for(int i = 0; i < 4; ++i)
{
digits[i] |= (dots & 0x80);
dots <<= 1;
}
}
uint8_t TM1637Display::encodeDigit(uint8_t digit)
{
return digitToSegment[digit & 0x0f];
}
#define CLK 2 // Пин для подключения CLK дисплея
#define DIO 7 // Пин для подключения DIO дисплея
#define PLUS_BUTTON 9 // Пин для кнопки "+"
#define MINUS_BUTTON 10 // Пин для кнопки "-"
#define MODE_BUTTON 12 // Пин для кнопки "Метод обжарки"
#define START_BUTTON 13 // Пин для кнопки "Старт/отмена"
#define LIFT_BUTTON 11 // Пин для кнопки "Поднять и посмотреть"
#define LONG_PRESS_DURATION 2000 // Длительность для определения долгого нажатия (в миллисекундах)
#define FREEZE_DURATION 5000 // Длительность заморозки таймера при нажатии на кнопку "Поднять и посмотреть" (в миллисекундах)
#define MAX_TIME_SECONDS 180 // Максимальное время в минутах (03:00)
#define MIN_TIME_ 15 // Минимальное время в минутах (00:15)
#define MIN_TIME_SECONDS 15 // Минимальное время в минутах (00:15)
#define TIME_STEP 15 // Шаг изменения времени в секундах
TM1637Display display(CLK, DIO); // CLK, DIO
class ToasterModes {
private:
uint8_t data[4];
public:
ToasterModes(uint8_t value1, uint8_t value2, uint8_t value3, uint8_t value4) {
data[0] = value1;
data[1] = value2;
data[2] = value3;
data[3] = value4;
}
uint8_t getValue(int index) const {
return data[index];
}
const uint8_t* getData() const {
return data;
}
};
#define NUMBER_OF_MODES 4 // Кол-во модов
ToasterModes modes[] = {
{
0b00110000, // I
0b00000110, // I
0b10110000, // I
0b00000110, // I
},
{
0b00110000, // I
0b00000000,
0b10110000, // I
0b00000110, // I
},
{
0b00110000, // I
0b00000110, // I
0b10000000,
0b00000110, // I
},
{
0b00110000, // I
0b00000000,
0b10000000,
0b00000110, // I
},
};
// Переменные для таймера
int timeSeconds = MIN_TIME_SECONDS;
bool timerRunning = false;
int toastMethod = 0;
// Переменные для режима и метода обжарки
enum Mode { TIME_SELECTION, TOAST_METHOD };
Mode currentMode = TIME_SELECTION;
uint8_t data[] = {
0b00110000, // I
0b00000110, // I
0b10110000,
0b00000110, // I
};
const char* toastMethods[] = {"||:||", "|:||", "||:|", "|:|"};
// Переменные для управления заморозкой и блокировкой кнопок
bool frozen = false;
bool locked = false;
// Последнее время нажатия на кнопку
unsigned long lastButtonPress = 0;
void setup() {
// Настройка пинов для кнопок
pinMode(PLUS_BUTTON, INPUT_PULLUP);
pinMode(MINUS_BUTTON, INPUT_PULLUP);
pinMode(MODE_BUTTON, INPUT_PULLUP);
pinMode(START_BUTTON, INPUT_PULLUP);
pinMode(LIFT_BUTTON, INPUT_PULLUP);
// Установка яркости дисплея
display.setBrightness(0x0a);
// Отображение начального времени
display.showNumberDecEx(timeSeconds, 0b11100000);
}
void loop() {
// Проверка нажатия кнопок
checkButtons();
// Обновление таймера, если он запущен
if (timerRunning && !frozen) {
updateTimer();
}
}
void checkButtons() {
// Проверка кнопки "+"
if (digitalRead(PLUS_BUTTON) == LOW) {
if (millis() - lastButtonPress >= LONG_PRESS_DURATION) {
// Долгое нажатие кнопки
locked = !locked;
if (locked) {
display.showNumberDecEx(0xB, 0b00010000); // Отображение "B" с точкой
} else {
display.showNumberDecEx(timeSeconds, 0b11100000);
}
delay(1000); // Задержка для избежания ложного срабатывания
} else {
// Короткое нажатие кнопки
if(currentMode == TOAST_METHOD){
changeMethod(1);
}
else
{
changeTime(TIME_STEP);
}
delay(200);
}
lastButtonPress = millis();
}
// Проверка кнопки "-"
if (digitalRead(MINUS_BUTTON) == LOW) {
if(currentMode == TOAST_METHOD){
changeMethod(1);
}
else{
changeTime(-TIME_STEP);
}
delay(200);
lastButtonPress = millis();
}
// Проверка кнопки "Метод обжарки"
if (digitalRead(MODE_BUTTON) == LOW) {
switchMode();
lastButtonPress = millis();
delay(200);
}
// Проверка кнопки "Старт/отмена"
if (digitalRead(START_BUTTON) == LOW) {
toggleTimer();
lastButtonPress = millis();
delay(200);
}
// Проверка кнопки "Поднять и посмотреть"
if (digitalRead(LIFT_BUTTON) == LOW && timerRunning) {
freezeTimer();
lastButtonPress = millis();
delay(200);
}
}
void changeTime(int deltaSeconds) {
int totalSeconds = timeSeconds + deltaSeconds;
if (totalSeconds < MIN_TIME_SECONDS) {
timeSeconds = MIN_TIME_SECONDS;
} else if (totalSeconds > MAX_TIME_SECONDS) {
timeSeconds = MAX_TIME_SECONDS;
} else {
timeSeconds = totalSeconds;
}
display.showNumberDecEx(timeSeconds, 0b11100000);
}
void switchMode() {
if (currentMode == TIME_SELECTION) {
currentMode = TOAST_METHOD;
display.clear();
display.setSegments(modes[toastMethod].getData());
// display.showNumberDecEx(0xC, 0b00010000); // Отображение "C" с точкой
} else {
currentMode = TIME_SELECTION;
display.showNumberDecEx(timeSeconds, 0b11100000);
}
}
void toggleTimer() {
if (timerRunning) {
timerRunning = false;
} else {
timerRunning = true;
}
}
void updateTimer() {
if (millis() - lastButtonPress >= 1000) {
if (timeSeconds == 0) {
timerRunning = false;
return;
}
timeSeconds--;
display.showNumberDecEx(timeSeconds, 0b11100000);
lastButtonPress = millis();
}
}
void freezeTimer() {
frozen = true;
delay(FREEZE_DURATION);
frozen = false;
}
void changeMethod(int number){
if(number == 1){
toastMethod += 1;
if(toastMethod >= NUMBER_OF_MODES){
toastMethod = 0;
}
display.clear();
display.setSegments(modes[toastMethod].getData());
}
if(number == 0){
toastMethod -= 1;
if(toastMethod < 0){
toastMethod = NUMBER_OF_MODES - 1;
}
display.clear();
display.setSegments(modes[toastMethod].getData());
}
}