#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define ENCODER_A 2 // Encoder pin A
#define ENCODER_B 3 // Encoder pin B
#define MENU_ITEMS 7 // Number of menu items
#define MAX_VISIBLE 3 // Maximum number of items visible on the display
#define ENCODER_MIN 0 // Minimum value for encoderValue
#define ENCODER_MAX 6 // Maximum value for encoderValue
#define COLOR_TEXT WHITE //Цвет текста
#define COLOR_DRAW WHITE
#define MAX_VOLT_BATTERY 4.2 //Максимальное напряжение батареи
#define OFF_VOLT_BATTERY 3.0 //Минимальное напряжение батареи, при котором она отключится
float DEFAULT_VOLT = 0;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Encoder encoder(ENCODER_A, ENCODER_B);
String menuItems[MENU_ITEMS] = {"Option 1", "Option 2", "Option 3", "Option 4", "Option 5", "Option 6", "Option 7"};
int selectedItem = 0;
int topItem = 0; // Index of the top item displayed
int lastEncoderValue = 0;
void setup() {
Serial.begin(9600);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop() {
int encoderValue = constrain(encoder.read() / 4, ENCODER_MIN, ENCODER_MAX);
if (encoderValue != lastEncoderValue) {
if (encoderValue > lastEncoderValue) {
selectedItem = (selectedItem + 1) % MENU_ITEMS;
if (selectedItem >= topItem + MAX_VISIBLE) {
topItem = (topItem + 1) % (MENU_ITEMS - MAX_VISIBLE + 1);
}
} else {
selectedItem = (selectedItem - 1 + MENU_ITEMS) % MENU_ITEMS;
if (selectedItem < topItem) {
topItem = (topItem - 1 + MENU_ITEMS) % (MENU_ITEMS - MAX_VISIBLE + 1);
}
}
lastEncoderValue = encoderValue;
}
BATTERY();
//display.clearDisplay();
display.setTextSize(1);
display.setCursor(40, 0);
display.setTextColor(WHITE);
display.print("Menu");
// Draw the menu items as a vertical list
int startY = 16;
int itemHeight = 16;
for (int i = topItem; i < min(topItem + MAX_VISIBLE, MENU_ITEMS); i++) {
if (i == selectedItem) {
display.setTextColor(BLACK, WHITE); // Invert colors for selected item
} else {
display.setTextColor(WHITE); // Reset text color
}
display.setCursor(8, startY + (i - topItem) * itemHeight);
display.print(menuItems[i]);
}
display.display();
}
unsigned long TIME_BATTERY = 0;
void BATTERY() {
display.clearDisplay();
//display.setCursor(0, 60);
display.fillRect(0, 1, 2, 3, COLOR_DRAW); //Рисуем заполненный квадрат
display.drawRect(2, 0, 10, 5, COLOR_DRAW); //Рисуем квадрат
if (millis() - TIME_BATTERY >= 1000) //Измеряем раз в 1 минуту = 60 секунд
{
//DEFAULT_VOLT = GET_DEFAULT_VOLT(); //Получаем внутренние напряжение
DEFAULT_VOLT = 3.6; //для симулятора
TIME_BATTERY = millis(); //Обновляем таймер
}
display.fillRect(map(DEFAULT_VOLT * 10, MAX_VOLT_BATTERY * 10, OFF_VOLT_BATTERY * 10, 20, 120) / 10 , 0, map(DEFAULT_VOLT * 10, MAX_VOLT_BATTERY * 10, OFF_VOLT_BATTERY * 10, 100, 0) / 10, 5, COLOR_DRAW); //Заполняем иконку батареи в зависимости от её заряда
}
float GET_DEFAULT_VOLT() { //Функция измеряет внутреннее напряжение Arduino
float RESULT = 0.0f; //Определяем переменную для получения результата.
byte COUNT_RESULT = 100; //Определяем сколько значений АЦП требуется получить для усреднения результата.
#if (defined( __AVR_ATmega640__ ) || defined( __AVR_ATmega328__ ) || defined( __AVR_ATmega1280__ ) || defined( __AVR_ATmega1281__ ) || defined( __AVR_ATmega2560__ ) || defined( __AVR_ATmega2561__ ) || defined( __AVR_ATmega32U4__ ) || defined( __AVR_ATmega32U4__ )) //Устанавливаем Uвх АЦП с источника ИОН на 1,1В, а в качестве ИОН АЦП используем Vcc ИОН - источник опорного напряжения.
ADCSRB &= ~(1<<MUX5); // У микроконтроллера «ATmega328» действительно нет такого бита, но он зарерервирован для совместимости
#endif
//Для Arduino Mega, Leonardo и Micro, сбрасываем бит «MUX5» регистра «ADCSRB», так как «MUX[5-0]» должно быть равно 011110 (см. регистр «ADMUX»).
ADMUX = (0<<REFS1)|(1<<REFS0)|(0<<ADLAR)|(1<<4)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(0<<MUX0); // Устанавливаем биты регистра «ADMUX»: «REFS»=01 (ИОН=VCC), «ADLAR»=0 (выравнивание результата по правому краю), «MUX[4-0]»=11110 или «MUX[3-0]»=1110 (источником сигнала для АЦП является напряжение ИОН на 1,1 В).
for(byte i=0; i<COUNT_RESULT; i++) //Получаем несколько значений АЦП
{
ADCSRA |= (1<<ADEN )|(1<<ADSC ); //Запускаем преобразования АЦП:Устанавливаем биты регистра «ADCSRA»: «ADEN»=1 (вкл АЦП), «ADSC» =1 (запускаем новое преобразование).
while(bit_is_set(ADCSRA,ADSC)){}; //Получаем данные АЦП:
RESULT += ADC; //Ждём завершения преобразования: о готовности результата свидетельствует сброс бита «ADSC» регистра «ADCSRA» и добавляем результат «ADC» к переменной «i».
}
RESULT /= COUNT_RESULT; //Делим результат «i» на «j», так как мы получили его «j» раз.
return (1.1f/RESULT) * 1024.0f; //Рассчитываем напряжение питания: // АЦП = (Uвх/Vcc)*1023. Напряжение Uвх мы брали с внутреннего ИОН на 1.1 В, значение которого возвращает функция analogSave_1V1(0).
}