#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "GyverEncoder.h"
//#include <BleGamepad.h> // при загрузці нa реально Arduino даний фрагмент розкоментувати a class BleGamepad закоментувати
//#include "esp32-hal-timer.h"///потрібно для зчитування даних гіроскопа по таймеру
//#include <EEPROM.h>
// #include "INA219_WE.h"
// INA219_WE ina219(0x40);
// Additional elements
// constants-variable-arrays
bool Vibrgyro;
// СПІЛЬНІ КОНСТАНТИ ПІД ХОД ДЛЯ БАГ ФУНК
#define X(x) (x)//Макрос повертає прийняти значення створено для кращого розуміння коду
#define Y(y) (y)
#define zero_void -1
#define reset 0
#define no_action 0 // НЕМА ДІЇ
#define no_data 0 // немає даних
#define no_indication 0 // немає вказівки
#define off 0
#define no 0
#define default_mode 0
#define function_interrupt_finish 0
#define by_default 0
#define data_ready 1
#define take_status 1
#define get_data 1
#define difference 1
#define yes 1
#define function_completed 1
#define save_settings 4 // зберехти настройки
#define reset_f 11
#define on 12
#define gyro 14
//#define profil 15
#define data_available 16// дані доступні
#define delete_data 17
#define start 18
//
// Піни карта
// #define b_mein 25
#define b_emul_dpad 1
#define b_handle 4
#define b_triger_r 23
#define b_analog_l 33
#define b_on_off 14
#define b_emul_dpad 26
#define b_sw 25 // кнопка енко // RotaryEncoder encoder(27, 14, RotaryEncoder::LatchMode::TWO03);
#define p_dt 32
#define p_clk 33
//
// Md_Display() {
#define info_display 20
#define menu 21
#define confirm_selection 22
#define lang_eng 25
#define lang_ukr 26
#define click_in_dis_on 27
//
// Md_MainDisplayENG();
#define menu_off 21
#define menu_start 22
//
// byte mdf_addres()
//#define click 13 ???
#define back 20
#define get_add 23
#define check_button_enc 24
#define mode_click_input 25
#define mode_click_back 26
#define back_add 27
#define mode_function_returns 28
#define address_not_entered 0
#define address_entered -1
#define address_deleted -2
//
// int mdf_cursor() // mdf_p_cursor()
#define get_pos 20
#define set_pos 21
#define cursor_read_encoder 24
#define cursor_type_one 0
#define cursor_type_two 1
//
// mdaf_confirm() // mdaf_p_confirm() // mdf_p_outputNotNeed()
//#define X(x) (x)
//#define Y(y) (y)
//#define reset 0
//#define no_data 0
//#define get_data 1
//#define function_completed 1
#define choice_no 20
#define choice_yes 21
#define accelerate_frame_blinking 22
#define print_not_need 23
#define offer_a_choice 24
#define output_by_coordinates 25
#define change_selection 26
//
// Mf_Buttons()
// #define reset_f 11
#define next_animation 20
//
// af_outputDisplay() //Timer class
#define ref_dis 20 // використовується для оновлення дисплею і в конструкторі класу таймер для того щоб надати дозвіл
#define clear_display 21
#define print_display 22
//
// af_autoShutdown()
// #define reset_f 11
#define change_time 20
#define get_time 21
//
// Md_movingButtons()
#define choice_bat_waiting 20
#define button_selection_complete 21
#define return_default 22
#define select_button 23
//
// Md_duplicateButtons()
// #define default_mode 0
#define select_button_r_s 20
#define select_button_r_m 21
#define duplicate_button_reset 22
#define show_duplicate_buttons 23
#define r_s_button_reset 24
#define r_m_button_reset 25
//
// Button gamepad class
#define touch_dead_zone 50
#define analog_dead_zone 2600
// ButGamepad(byte gBT, byte gBN, byte pRT, byte n)
#define button_gamepad 20
#define trigger_gamepad 21
#define tactile_read 22
#define touch_read 23
#define analog_read 24
// byte (*gamepadPressPtr)();
#define read_button_state 20
#define press_check 21
#define gamepad_button_pressed 22
#define gamepad_button_release 23
#define button_get_name 24
//byte buttonGamepadPressRelease();
#define press_button 20
#define release_button 21
#define dpad_check 22
#define dpad_center 23
//
// Profiles class
#define t_sp 1// не міняти число!
#define t_mp 2// не міняти число!
#define r_off 0 // = off
#define r_on 12 // = on
//Md_changeNameProfile()
#define cl 24
#define dl 25
#define tb 26
#define sb 27
#define g_s_n_i_n_l_a_r_s 28 // (give standard name if no letters and remove spaces ) дати стандартну назву, якщо немає букв
#define get_name 29
#define get_name_a_f 30
#define add_letter 31
#define delete_letter 32
// *mps()
#define gapn 22 //get active profile number-name
#define activate_p_sp 23
#define activate_p_mp 24
#define eeprom_initialization 25
#define activate_p 26
#define copy_profile 27
#define set_push_permit 28
#define get_push_permit 29
#define not_save 30
#define save_changes 31
#define active_copy_compare 32
#define active_copy_same 33 // зафіксовані зміни
//listButton[18]//даний масив має в собі номер-назву кнопки потрібно для порядкового опитування останні два елемента використовуються для дублювання кнопок
#define button_1 1
#define button_2 2
#define button_3 3
#define button_4 4
#define button_5 5
#define button_6 6
#define button_7 7
#define button_8 8
#define button_select 9
#define button_start 10
#define dpad_up 11
#define dpad_down 12
#define dpad_left 13
#define dpad_right 14
#define trigger_r 15
#define trigger_l 16
uint16_t tmrProf = 0;
//
// encoder module class
//turn()
#define get_press_end_turn 20
#define get_turn 21
#define rotated_r 22
#define rotated_l 23
#define rotated_r_fast 24
#define rotated_l_fast 25
#define reset_rotation 27
//bat()
#define click 28
#define press_b_e 29
#define hold_b_e 30
#define press_end_turn 22
//
// c-v-a
// Function prototypes
int af_clamp(int value = 0, int minVal = 0, int maxVal = 0);
void af_displayPrintLain(String text = "", byte x = 0, byte y = 0, byte scrollFlag = 0, byte cropText = 20);
byte mdaf_confirm(byte acceptedCommand = 0, byte x = 0, byte y = 0);
byte mdaf_p_confirm(byte acceptedCommand = 0, byte x = 0, byte y = 0);
void Md_p_duplicateButtons(byte acceptedCommand = 0);
int mdf_addres(byte acceptedCommand = 0, byte addNumb = 0, byte additionalCommand = 0);
byte Md_p_moveButtons(byte receivedData = 0, byte selectedButton = 0);
byte Md_Display(byte Receiving = 0);
void af_outputDisplay(byte acceptedCommand = 0);
byte af_autoShutdown(byte acceptedCommand = 0, byte acceptedData = 0);
byte buttonGamepadPressRelease(byte command = 0, byte buttonName = 0);
byte mdf_cursor(byte acceptedCommand = 0, byte data = 0, byte* arr = nullptr);
// Fp
// Ae
// (пуcті класи) Пустишки для того щоб не нагружати час компіляції
#define HAT_CENTERED 0
#define HAT_UP 1
#define HAT_UP_RIGHT 2
#define HAT_RIGHT 3
#define HAT_DOWN_RIGHT 4
#define HAT_DOWN 5
#define HAT_DOWN_LEFT 6
#define HAT_LEFT 7
#define HAT_UP_LEFT 8
class BleGamepadConfiguration {
public:
void setAutoReport(int value) {
// Нічого не робить
}
void setButtonCount(int count) {
// Нічого не робить
}
void setIncludeStart(bool include) {
// Нічого не робить
}
void setIncludeSelect(bool include) {
// Нічого не робить
}
void setHatSwitchCount(int count) {
// Нічого не робить
}
void setAxesmin(int minNam) {
// Нічого не робить
}
void setAxesmax(int maxNam) {
// Нічого не робить
}
void setWhichAxes(bool x, bool y, bool z, bool rx, bool ry, bool rz, bool slider1, bool slider2) {
// Нічого не робить
}
};
class BleGamepad {
public:
BleGamepad(const char* deviceName, const char* deviceManufacturer, uint8_t batteryLevel) {
// Нічого не робить
}
void press(uint8_t button) {
// Нічого не робить
}
void release(uint8_t button) {
// Нічого не робить
}
void setLeftTrigger(uint16_t value) {
// Нічого не робить
}
void setRightTrigger(uint16_t value) {
// Нічого не робить
}
bool isConnected() {
// Повертає false для симуляції відсутності з'єднання
return false;
}
void setZ(uint16_t value) {
// Нічого не робить
}
void setRZ(uint16_t value) {
// Нічого не робить
}
void sendReport() {
// Нічого не робить
}
void setHat1(uint8_t value) {
// Нічого не робить
}
void setLeftThumb(int16_t x, int16_t y) {
// Нічого не робить
}
void setRightThumb(int16_t x, int16_t y) {
// Нічого не робить
}
void pressSelect() {
// Нічого не робить
}
void releaseSelect() {
// Нічого не робить
}
void pressStart() {
// Нічого не робить
}
void releaseStart() {
// Нічого не робить
}
void begin(BleGamepadConfiguration* config) {
// Нічого не робить
}
void end() {
// Нічого не робить
}
// Додаткові методи-пустишки
void setButtonCount(uint8_t count) {
// Нічого не робить
}
void setHatSwitchCount(uint8_t count) {
// Нічого не робить
}
void setAxes(bool leftX, bool leftY, bool rightX, bool rightY, bool leftTrigger, bool rightTrigger, bool zAxis, bool rzAxis) {
// Нічого не робить
}
void setLeftThumbDeadzone(uint16_t deadzone) {
// Нічого не робить
}
void setRightThumbDeadzone(uint16_t deadzone) {
// Нічого не робить
}
void setRumble(uint16_t leftMotor, uint16_t rightMotor) {
// Нічого не робить
}
};
//
Adafruit_SSD1306 display(128, 64, &Wire, -1);
BleGamepadConfiguration bleGamepadConfig;//потрібно для настройок геймпада
BleGamepad bleGamepad("FS G36", "UA", 100);
Encoder enc(p_clk, p_dt);
// Byte size control(bsc)(використовується як унікальна зміна яка після досягнення потрібного нам значення повертає його і скидається на нуль)
class bsc {
private:
const byte maxValue; // Максимальне значення
byte val; // Накопичене значення
public:
// Конструктор
bsc(byte mv = 0) : maxValue(mv){
}
// Метод p() для додавання значення
void p(byte addValue) {
val += addValue;
val = af_clamp(val, 0, maxValue);// Скидання на нуль, якщо перевищено
}
// Метод m() для перевірки досягнення максимального значення
bool m() {
if (val >= maxValue) {
val = 0;
return true; // Повертаємо true, якщо досягнуто максимального значення
}
return false; // Максимальне значення не досягнуто
}
// Метод g() для повернення накопиченого значення
byte g() {
return val;
}
byte s(byte recordDatae) {
return val = recordDatae;
}
byte r() {//перед скиданням одноразового відправляємо получене число
byte temp = val; // Зберігаємо значення
val = 0; // Скидання на нуль
return temp; // Повертаємо збережене значення
}
};
// bsc
// Timer class (конструкція працює приблизно 49 днів після чого потрібен перезапуск мікроконтролера) (ДИВИТИСЬ ПОЯСНЕННЯ)
class Timer {
private:
uint32_t tmr;
int autoRestartTimer;
bool flag;
const byte displayRefreh;
public:
Timer(int aRT = 0, byte dR = 0) : displayRefreh(dR){//якщо є команда ref_dis то буде виконуватись оновлення при кожному виклику методів в яких є af_ outputDisplay(displayRefreh);
autoRestartTimer = aRT; //використовується для f() щоб був автоперезапуску якщо t != 0
}
bool e() {//таймер End по закінченню часу видасть тру
if (tmr < millis()) {
f();//вручну міняємо стан флажка також метод f(); викликається для того щоб два рази підряд получити правда e() == true f() == true
return true;
}
return false;
}
void b(int acceptedTime) {//почати відлік часу таймер Begin
af_outputDisplay(displayRefreh);
tmr = millis() + acceptedTime;
}
bool f() {// метод флажок періодично повертає правду брехню
if (tmr < millis()) {
af_outputDisplay(displayRefreh);
flag = !flag;
tmr = millis() + autoRestartTimer;
}
return flag;
}
void r() {// reset скидаємо таймер на нуль щоб можна було получити тру v методі e() при першому виклику після скидання
flag = tmr = 0;
}
};
// Tc
// Profiles class
class Profiles {
public:
Profiles(int deZoH, byte senHor, int deZoV, byte senVer, char* nameProf, byte tpProf, byte nabProf)
:deadZoneHor(deZoH),
sensitivityHor(senHor),
deadZoneVer(deZoV),
sensitivityVer(senVer),
nps(nameProf),
tape(tpProf),
namber(nabProf),
status(r_off)
{
listButtonsStandard();//даємо стандартні імена-номера кнопкам
}
//гіроскоп настройка
int setDeZoHor(int deZoH) {// set
if (deadZoneHor != deZoH) {
sensitivityHor = 0;
}
deadZoneHor = deZoH;
return 0;
}
byte setSenHor(byte senHor) {
sensitivityHor = senHor;
return 0;
}
int setDeZoVer(int deZoV) {
if (deadZoneVer != deZoV) {sensitivityVer = 0;}
deadZoneVer = deZoV;
return 0;
}
byte setSenVer(byte senVer) {
sensitivityVer = senVer;
return 0;
}
//
//робота з іменами профілів String nps;
String nameProfiles(byte acceptedCommand, char Letter = ' '){// функція працює з іменем профілю
static Timer tmr(400, ref_dis);
switch (acceptedCommand) {
case get_name://повертаємо ім'я
return nps;
break;
case add_letter://додаємо букву якщо є місце
if (nps.length() < 20) {nps += Letter;}
break;
case delete_letter://якщо є букви То видаляємо
if (nps.length()) {nps.remove(nps.length()-1, 1);}//nps.remove(кількість букв які потрібно оставити-1, кількість звуків які потрібно Видалити В кінці);
break;
case get_name_a_f://м'я активного профілю в списку буде мигати
if (status == r_on) {//test можливі баги чомусь не мигало
return tmr.f() ? nps:"";
}else{
return nps;
}
break;
case g_s_n_i_n_l_a_r_s://give standard name if no letters and remove spaces
checkForLetters();//стандартне ім'я якщо пустота
trimTrailingSpaces();//видаляємо пробіли в кінці
break;
}
return "";
}
byte centroName(){ // Центрує набране ім'я в рамці інфо-дисплея
for (int i = nps.length() - 1; i >= 0; i--) { // Перебираємо символи з кінця
if (nps.charAt(i) != ' ') { // Знаходимо останній ненульовий символ
return 61 - i * 3; // Розрахунок позиції центру
}
}
return 0; // Якщо всі символи пробіли, повертаємо 0
}
void checkForLetters() {//check for letters перевірити наявність букв якщо немає букв То дати стандартне ім'я
bool allSpaces = true; // Припускаємо, що всі символи — пробіли
// Перевіряємо кожен символ у рядку
for (int i = 0; i < nps.length(); i++) {
if (nps[i] != ' ') { // Якщо хоча б один символ не пробіл
allSpaces = false;
break; // Можна припинити перевірку, якщо знайдено символ, відмінний від пробілу
}
}
// Якщо рядок порожній або складається лише з пробілів
if (nps.length() == 0 || allSpaces) {
nps = "My profile " + String(namber); // Присвоюємо стандартне ім'я
letterIndicator = 12; // Встановлюємо індикатор на стандартне значення
}
}
void trimTrailingSpaces() { // trim Trailing Spaces видаляю пробіли в кінці речення
int i = nps.length() - 1;
while (i >= 0 && nps.charAt(i) == ' ') {
i--; // Знаходимо позицію останнього непустого символу
}
nps.remove(i + 1); // Видаляємо всі символи після останнього значущого
}
byte lettersTakeNumber(){//повертає кількість букв в реченні
return nps.length();
}
//
void listButtonsStandard() {
for (byte i = 0; i < 16; i++) {//стандартний порядок кнопок два останніх елемента використовуються для дублювання
listButton[i] = i + 1; //вказуємо кнопкам номер-ім'я
}
}
byte setStatus(byte st) {
status = st;
return 0;
}
bool operator==(const Profiles& other) const {//використовується для порівняння профілів
return (deadZoneHor == other.deadZoneHor) &&
(sensitivityHor == other.sensitivityHor) &&
(deadZoneVer == other.deadZoneVer) &&
(sensitivityVer == other.sensitivityVer) &&
(tape == other.tape) &&
(namber == other.namber) &&
(status == other.status) &&
(memcmp(listButton, other.listButton, sizeof(listButton)) == 0) &&
(nps == other.nps);
}
// profil[mps(gapn)].
String nps;//nps = name profile string
byte listButton[18];
int deadZoneHor;// (primary початкова) початок руху
byte sensitivityHor;
int deadZoneVer;
byte sensitivityVer;
byte tape;
byte namber;
byte status;
byte letterIndicator;
byte lblm;//list button located monitoring
private:
};
// Profile designers конструктори класу Profiles
Profiles profil[] = {// test
Profiles (0, 0, 0, 0, "0", 0, 0),// використовується для перевірки чи були зміни
Profiles (2000, 100, 2556, 92, "CodMW", t_sp, 1),
Profiles (166, 120, 1965, 112, "Batl3", t_sp, 2),
Profiles (2000, 100, 2556, 92, "SP 33", t_sp, 3),
Profiles (166, 120, 1965, 112, "SP 44", t_sp, 4),
Profiles (2000, 100, 2556, 92, "SP 55", t_sp, 5),
Profiles (166, 120, 1965, 112, "SP 66", t_sp, 6),
Profiles (2000, 100, 2556, 92, "SP 77", t_sp, 7),
Profiles (2000, 100, 2556, 92, "05461 ", t_mp, 1),
Profiles (166, 120, 1965, 112, "Xmen 74 ", t_mp, 2),
Profiles (2000, 100, 2556, 92, "Mp 3 ", t_mp, 3),
Profiles (166, 120, 1965, 112, "Mp 4 ", t_mp, 4),
Profiles (2000, 100, 2556, 92, "Mp 5 ", t_mp, 5),
Profiles (166, 120, 1965, 112, "Mp 6 ", t_mp, 6),
Profiles (2000, 100, 2556, 92, "Mp 7 ", t_mp, 7)
};
// Pd
byte mps(byte acceptedCommand, byte number = 0 , byte acceptedValue = 0) {// mps= management profile setting
static byte active = 8;
static byte returnResult;
static bool pushPermit[18]; //загальний масив дозволів на натиск кнопки для всіх профілів
if (acceptedCommand == gapn) { // Скеровування до активного профілю
return active;
}
else if (acceptedCommand == activate_p) {// вибраний профіль активується
for (int i = 1; i < 15; i++) {
profil[i].setStatus(r_off);
}
active = number;
profil[active].setStatus(r_on);
}
else if (acceptedCommand == copy_profile) { //виконується одноразово при наборі адресу при заході в настройки див. адрес 2
profil[0] = profil[active];
}
else if (acceptedCommand == active_copy_compare) {
return (profil[0] == profil[active]) ? active_copy_same : 0;
}
else if (acceptedCommand == not_save) {
profil[active] = profil[0];
}
else if (acceptedCommand == get_push_permit) {//значення всередині елементу несе в собі дозвіл на натиск кнопки
// Повертаємо значення елемента масиву pushPermit дізнаємось по значенню чи є дозвіл на натиск кнопки
return !pushPermit[number];
}
else if (acceptedCommand == set_push_permit) {
pushPermit[number] = acceptedValue; // Можете змінити значення
}
return 0;
}
// P
// Button debounce class
class DebButton {
private:
const byte pin;
//Timer tmr;
bool buttonState;
public:
// Конструктор класу, ініціалізація піна, номера кнопки геймпада та часу захисту від випадкових спрацьовувань
DebButton(int p, int dt = 300) : pin(p) {
//tmr = dt;
pinMode(pin, INPUT_PULLUP);
}
// Метод для обробки натискання кнопки
bool press() {
if (!digitalRead(pin) && !buttonState) {// після натську кнопки буде повернення true
buttonState = true; // тепер переміна буде правда
}
else if (buttonState && digitalRead(pin)) {// пісня відпущення кнопки буде повернення брехня
buttonState = false;// тепер переміна буде брехня
}
return buttonState;// постійно повертаємо стан кнопки
}
};
//
// Button gamepad class
class ButGamepad {
private:
byte gamepadButtonName;
byte pinReadType;
byte pushPermit;
byte numb;
byte flagPress;
byte buttonCheckFlag;
DebButton* debButton = nullptr;
//вказівник вказує на одну з вибраних функцій які ми вибираємо конструкторi
typedef byte (ButGamepad::*GamepadPressMethod)(byte);
GamepadPressMethod gamepadPressPtr;
//функції використовуються всередині класу обробка натиску кнопки
bool tactilePinButtonRead() {
return debButton && debButton->press(); // Перевірка та виклик методу
}
bool touchPinButtonRead() {
return touchRead(numb) < touch_dead_zone;
}
bool analogPinButtonRead() {
return analogRead(numb) > 2600;
}
//
public:
ButGamepad(byte gBN, byte pP, byte pRT, byte n){ //конструктор проводимо настройку при першому запуску
gamepadButtonName = gBN;
pinReadType = pRT;
pushPermit = pP;
numb = n;
//створюється новий debounce екземпляр якщо будемо натиснути звичайну кнопку
if (pinReadType == tactile_read) {
debButton = new DebButton(numb);
}
if (gamepadButtonName == trigger_r || gamepadButtonName == trigger_l) {// вибір яким способом буде натиснути курок(R L)геймпада. курок(R L) == tactile touch analog
if (gamepadButtonName == trigger_r) {
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerRightPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerRightPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerRightPress;
}
}
else if (gamepadButtonName == trigger_l) {
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerLeftPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerLeftPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerLeftPress;
}
}
}
else {// вибір яким способом методом буде натиснути кнопки геймпада 1-8. depad. Select Start. варіанти tactile touch analog
if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactilePress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchPress;
}
else if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadPress;
}
}
}
~ButGamepad() {//GPT пише що помилки не буде що це потрібно
if (debButton) {
delete debButton;
debButton = nullptr;
}
}
// функції які викликаються через вказівник зроблено для того щоб в циклі фор викликати через gamepadBatton різні функції по черзі
// Bluetooth gamepad push button
byte tactilePress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (tactilePinButtonRead()) {
if (!flagPress) {
buttonGamepadPressRelease(press_button, gamepadButtonName);
flagPress = 1;
}
}
else if (flagPress){
flagPress = 0;
buttonGamepadPressRelease(release_button, gamepadButtonName);
}
}
else if (acceptedCommand == press_check) {
if (tactilePinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !tactilePinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte touchPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
buttonGamepadPressRelease(press_button, gamepadButtonName);
}
}
else if (flagPress){
flagPress = 0;
buttonGamepadPressRelease(release_button, gamepadButtonName);
}
}
else if (acceptedCommand == press_check) {
if (touchPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !touchPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte analogReadPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (analogPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
buttonGamepadPressRelease(press_button, gamepadButtonName);
}
}
else if (flagPress){
flagPress = 0;
buttonGamepadPressRelease(release_button, gamepadButtonName);
}
}
else if (acceptedCommand == press_check) {
if (analogPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !analogPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
//
// Bluetooth gamepad right trigger pressure
byte analogReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
int temp = analogRead(numb);
Serial.print("---"); Serial.print(temp);
Serial.print("---"); Serial.println(numb);
if (temp > analog_dead_zone) { // test
temp = map(temp, analog_dead_zone, 4095, 0, 32767);
temp = constrain(temp, 0, 32767);
bleGamepad.setRightTrigger(temp);
flagPress = 1;
}
else if (flagPress) {
flagPress = 0;
bleGamepad.setRightTrigger(0);// test
}
}
else if (acceptedCommand == press_check) {
if (analogPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !analogPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte touchReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
bleGamepad.setRightTrigger(32767);
}
}
else if (flagPress){
flagPress = 0;
bleGamepad.setRightTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (touchPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !touchPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte tactileReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (tactilePinButtonRead()) {
bleGamepad.setRightTrigger(32767);
flagPress = 1;
}
else if (flagPress){
flagPress = 0;
bleGamepad.setRightTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (tactilePinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !tactilePinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
//
// Bluetooth gamepad left trigger pressure
byte analogReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
int temp = analogRead(numb);
if (temp > analog_dead_zone) { // test
temp = map(temp, analog_dead_zone, 4095, 0, 32767);
temp = constrain(temp, 0, 32767);
bleGamepad.setLeftTrigger(temp);
flagPress = 1;
}
else if (flagPress) {
flagPress = 0;
bleGamepad.setLeftTrigger(0);// test
}
}
else if (acceptedCommand == press_check) {
if (analogPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !analogPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte touchReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
bleGamepad.setLeftTrigger(32767);
}
}
else if (flagPress){
flagPress = 0;
bleGamepad.setLeftTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (touchPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !touchPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte tactileReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (tactilePinButtonRead()) {
bleGamepad.setLeftTrigger(32767);
flagPress = 1;
}
else if (flagPress){
flagPress = 0;
bleGamepad.setLeftTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (tactilePinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !tactilePinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
//
//використовується для переінціалізації вказівника на функцію заміна кнопок місцями
byte buttonReinstall(byte newButton){
/*
Тарас з майбутнього Ну і в нас погана пам'ять то капець мусимо так писати.
В даній ситуації піни вже є настроєними в конструкторі на види зчитування аналогове цифрове і дотик розглянемо приклад Дана зміна gamepadButtonName = newButton несе в собі номер-ім'я кнопки геймпада (віртуальне ім'я) Наприклад якщо вона несе в собі номер-ім'я 16 то це номер-ім'я відповідає правому курку геймпада, якщо від 1 до 8 то це є звичайні кнопки геймпада щось краще зрозуміти дивитись //listButton[18]//.Якщо наприклад в нас Дана нова кнопка gamepadButtonName
яку ми хочемо перемістити на це місце яке вже є настроєне в конструкторі на один з трьох способів зчитування піну
відповідає назві правого курка if (gamepadButtonName == trigger_r) в залежності від способу зчитування даних з піна pinReadType відбудеться підбір метода класу який буде натискати кнопку одним з трьох методів класу, якщо це наприклад звичайна кнопка else if (pinReadType == tactile_read) {gamepadPressPtr = &ButGamepad::tactileReadTriggerRightPress; } хоче натискати правий курок то після натиску фізичної кнопки відбудеться миттєве натискання правого курка без вимірювання степені нахилу решту можна зрозуміти по даній підкасті.
*/
gamepadButtonName = newButton;
if (gamepadButtonName == trigger_r || gamepadButtonName == trigger_l) {// вибір яким способом буде натиснути курок(R L)геймпада курок(R L) = tactile touch analog
if (gamepadButtonName == trigger_r) {
//підбір метода яким будемо натискати правий курок опираючись на настроєний в конструкторі тип зчитування піна
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerRightPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerRightPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerRightPress;
}
}
else if (gamepadButtonName == trigger_l) {
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerLeftPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerLeftPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerLeftPress;
}
}
}
else {// вибір яким способом буде натиснути кнопка геймпада кнопка(1...10) = tactile touch analog
if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactilePress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchPress;
}
else if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadPress;
}
}
return 0;
}
//функція повертає вказівник який вказує на функцію яка була вибрана через конструктор
byte gamepadBatton(byte acceptedCommand) {
if (pushPermit) {
return (this->*gamepadPressPtr)(acceptedCommand);
}
return 0;
}
};// Button gamepad
//створюємо екземпляри класу настроюємо вказівник на необхідну функцію
//ButGamepad(1(reinstall!), 2, 3) 1-вибираємо курок чи кнопка 2-вибираємо чим будемо натискати сенсорна-звичайна кнопка або аналог 3-номер піна
ButGamepad butGamepad[] = {// test конструктор класів вказуємо номера пінів і способи зчитування які відповідають за кнопки
ButGamepad(profil[mps(gapn)].listButton[0], mps(get_push_permit, 0), tactile_read, 13), //
ButGamepad(profil[mps(gapn)].listButton[1], mps(get_push_permit, 1), tactile_read, 14),
ButGamepad(profil[mps(gapn)].listButton[2], mps(get_push_permit, 2), tactile_read, 18),
ButGamepad(profil[mps(gapn)].listButton[3], mps(get_push_permit, 3), tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[4], mps(get_push_permit, 4), tactile_read, 17),
ButGamepad(profil[mps(gapn)].listButton[5], mps(get_push_permit, 5), tactile_read, 5),
ButGamepad(profil[mps(gapn)].listButton[6], mps(get_push_permit, 6), tactile_read, 19),
ButGamepad(profil[mps(gapn)].listButton[7], mps(get_push_permit, 7), tactile_read, 13),
ButGamepad(profil[mps(gapn)].listButton[8], mps(get_push_permit, 8), tactile_read, 23), // button_select
ButGamepad(profil[mps(gapn)].listButton[9], mps(get_push_permit, 9), tactile_read, 26), // button_start
ButGamepad(profil[mps(gapn)].listButton[10], mps(get_push_permit, 10), tactile_read, 13 + false), // dpad_up
ButGamepad(profil[mps(gapn)].listButton[11], mps(get_push_permit, 11), tactile_read, 12), // dpad_down
ButGamepad(profil[mps(gapn)].listButton[12], mps(get_push_permit, 12), tactile_read, 13 + false), // dpad_left
ButGamepad(profil[mps(gapn)].listButton[13], mps(get_push_permit, 13), tactile_read, 27), // dpad_right
ButGamepad(profil[mps(gapn)].listButton[14], mps(get_push_permit, 14), analog_read, 15), // trigger_r
ButGamepad(profil[mps(gapn)].listButton[15], mps(get_push_permit, 15), analog_read, 4), // trigger_l
ButGamepad(profil[mps(gapn)].listButton[16], mps(get_push_permit, 16), analog_read, 13 + false), //дублікатори
ButGamepad(profil[mps(gapn)].listButton[17], mps(get_push_permit, 17), analog_read, 13 + false)
};
//butGamepad[i].mps(set_push_permit, 0, true);
byte buttonGamepadPressRelease(byte command, byte buttonName) {//функція натискає кнопки 1-8 Select Start і d-пад викликається в кожному екземплярі класу через метод gamepadBatton
static byte x = 1;
static byte y = 1;
byte dPad[3][3] = {
{HAT_UP_LEFT, HAT_UP, HAT_UP_RIGHT},
{HAT_LEFT, HAT_CENTERED, HAT_RIGHT},
{HAT_DOWN_LEFT, HAT_DOWN, HAT_DOWN_RIGHT}
};
if (command == press_button) {
if (buttonName <= button_8 ) {//звичайні кнопки
bleGamepad.press(buttonName);//номер кнопки є одночасно і назвою кнопки (buttonName = число-назва)
}
else if (buttonName == button_select || buttonName == button_start) {//кнопки Старт Select
if (buttonName == button_select) {
bleGamepad.pressSelect();
}
else if (buttonName == button_start) {
bleGamepad.pressStart();
}
}
else{//кнопки Dpad
if (dpad_up == buttonName || dpad_down == buttonName){//якщо жодна умова не виконується X Y буде 1 == центер Dpad, інакше виконається скерування по координатах
x = map(buttonName, 13, 14, 0, 2);
}
else if (dpad_left == buttonName || dpad_right == buttonName) {
y = map(buttonName, 15, 16, 0, 2);
}
bleGamepad.setHat1(dPad[x][y]);
}
}
else if (command == release_button) {
if (buttonName <= button_8 ) {
bleGamepad.release(buttonName);
}
else if (buttonName == button_select || buttonName == button_start) {
if (buttonName == button_select) {
bleGamepad.releaseSelect();
}
else if (buttonName == button_start) {
bleGamepad.releaseStart();
}
}
else{//кнопки Dpad//в залежності від попередніх натисків відбудеться центровка або перезапис старих даних
if (dpad_up == buttonName || dpad_down == buttonName) {
x = 1;
}
else if (dpad_left == buttonName || dpad_right == buttonName) {
y = 1;
}
bleGamepad.setHat1(dPad[x][y]);//старі дані перезаписуються новими
}
}
return 0;
}
void buttonGamepadReinstallAllButtons() {
for (int i = 0; i < 16; i++) {
butGamepad[i].buttonReinstall(profil[mps(gapn)].listButton[i]); //аналог перезапуску всіх(16ти) конструкторів екземплярів класу
}
}
// Bdc
// encoder module class
class encoderModule{
private:
DebButton butEnc;// b_ sw pin = 25 оголошуємо екземпляр класу а конструктор викликаємо нижче так потрібно робити всередині класу
bsc bepes; //bepes = button encoder program execution stage(енкодер кнопка етап виконання програми)
Timer tmr;
public:
encoderModule() : butEnc(b_sw) { // Ініціалізуємо у списку ініціалізації в такому випадку не можна створювати конструктор класу при оголошенні екземпляра класу
}
int8_t turn(int8_t acceptedCommand){
if (acceptedCommand == get_press_end_turn && butEnc.press() && enc.isTurn()) {
bepes.s(press_end_turn);
return readTurn();
}
else if (acceptedCommand == get_turn && !butEnc.press() && enc.isTurn()) {
return readTurn();
}
else if (acceptedCommand == reset_rotation) {//скидання накопичених даних дані можуть мінятися Навіть якщо ми не викликаємо тому що обробка відбувається в перериванні
return readTurn();
}
return 0;
}
int8_t readTurn(){
af_outputDisplay(ref_dis);//оновлення дисплею при обертанні екватора
if (enc.isFastR()) {// якщо енкодер обертається з достатньою швидкістю
return rotated_r_fast;
}
else if (enc.isFastL()) {
return rotated_l_fast;
}
else if (enc.isRight()) {
return rotated_r;
}
else if (enc.isLeft()) {
return rotated_l;
}
return no_data;
}
byte but(){
if (butEnc.press()){
if (bepes.g() != press_end_turn && tmr.e()) {
if (!bepes.g()) {
tmr.b(200);
return bepes.s(press_b_e);
}
else if (bepes.g() == press_b_e) {
return bepes.s(hold_b_e);
}
}
}
else if (bepes.g()) {
af_outputDisplay(ref_dis); //оновлення дисплею при кліку
tmr.r();//таймер завжди потрібно скидати щоб можна було при потребі клікати кнопкою Напряги мозги Якщо хочеш зрозуміти
if (bepes.r() == press_b_e) {
return click;
}
}
return no_data;
}
byte gbs(){// get state button
return butEnc.press();
}
};
encoderModule em;
// emc
// Additional functions
void af_displayPrintLain(String text, byte x, byte y, byte linePosition, byte cropText) {// (ДИВИТИСЬ ПОЯСНЕННЯ)
// Статичні змінні для збереження стану між викликами
static Timer tmr(0); // Таймер для контролю автоматичної прокрутки також включаємо оновлення дисплея при кожному зміщенні тексту
static byte lps; // Змінна для збереження line Position seve
static int stx; // Позиція прокрутки по горизонталі
byte stxt; // Тимчасова змінна для прокрутки stxt =crolling text x temporary
static int tlip; // Довжина тексту в пікселях text tlip = Length In Pixels
if (mdf_cursor(get_pos) != lps) {//тут відбувається скидання прокрутки якщо курс змінив позицію
tlip = text.length() * 6; // Довжина тексту у пікселях (6 пікселів на символ)
stx = 0; // Початкова позиція прокрутки
lps = linePosition; // Оновлення
}
if (mdf_cursor(get_pos) == linePosition) {//якщо номер строки співпадає з позицією пурсора відбувається прокрутка тексту
// Якщо натиснута кнопка енкодера разом з обертом енкодера
if (em.gbs()) {
switch (em.turn(get_press_end_turn)) {
case rotated_r:
case rotated_r_fast:
stx += 6; // Прокрутка п
break;
case rotated_l:
case rotated_l_fast:
stx -= 6; // Прокрутка л
break;
}
stx = constrain(stx, 0, tlip);// Обмеження позиції прокрутки в межах розміру тексту
}
// Автоматична прокрутка за таймером, якщо кнопка не натиснута
else if (tmr.f()) {
if (tlip > stx) {
stx += 2; // Збільшення позиції прокрутки
} else {
stx = 0; // Скидання прокрутки до початку тексту
}
}
stxt = stx; // Присвоєння тимчасової змінної значення прокрутки
}
// Встановлення курсора з урахуванням прокрутки та виведення тексту
display.setCursor(x - stxt, y);
display.print(text);
//чорні прямокутники для закриття потрібні лише тоді коли даний параметр приймає любе значення окрім нуля
//якщо даний параметр брехня То це означає що текст поміщається виділену область
if (linePosition) {
display.fillRect(0, y, x, 7, 0); // Ліва область
display.fillRect(x + cropText * 6, y, 128, 7, 0); // Права область відповідно до обрізаного тексту
display.fillRect(0, y + 7, 128, 64, 0); // Очищення області під текстом
}
}
void af_curvedArrow(int x, int y) {//крива стрілка Вверх
display.setCursor(x, y);
display.println("_");
display.setCursor(x + 3, y); // Зсув на 3 пікселі для другого символу
display.write(24);
}
int af_clamp(int value, int minVal, int maxVal) {//якщо вийти за рамки то поверне в початок або кінець
if (value < minVal) {
return maxVal;
}
else if (value > maxVal) {
return minVal;
}
return value;
}
byte af_autoShutdown(byte acceptedCommand, byte acceptedData){
static uint16_t timerMagnifier = 3 * 60000;
static Timer tmr(timerMagnifier, ref_dis);
if (acceptedCommand) {
if (acceptedCommand == reset_f) {
tmr.b(timerMagnifier);
}
else if (acceptedCommand == change_time) {
timerMagnifier = acceptedData * 60000;
}
else if (acceptedCommand == get_time) {
return timerMagnifier / 60000;
}
}
else if (tmr.e()){
delay(400);
esp_deep_sleep_start();
}
return 0;
}
void af_buttonOnOff(){//test
if (!digitalRead(13)) {
delay(400);
esp_deep_sleep_start();
}
delay(100);
}
void af_outputDisplay(byte acceptedCommand){//функція оновлює дисплей тоді коли є дані з енкодера або екземпляр класу Timer є з параметром ref_dis
static bsc stageExecution(2);
if(stageExecution.g()) {//очищення і вивід зображення якщо є дозвіл
if(acceptedCommand == clear_display && stageExecution.g() == 1) {//
stageExecution.p(1);
display.clearDisplay();
}
else if(acceptedCommand == print_display && stageExecution.g() == 2) {
stageExecution.r();
display.display();
}
}
else if(!stageExecution.g()){//даємо дозвіл на оновлення дисплею всередині функцій! які можуть змінювати картинку
if(acceptedCommand == ref_dis){
stageExecution.p(1);
}
}
}
void startAnimation(){
display.clearDisplay();
display.invertDisplay(0);
display.setCursor(-2, 28);
display.println(" FS G36 ");
display.display();
delay(500);
display.clearDisplay();
display.display();
}
void isr() {//функція викликається в пририванні і обробляє дані з енкодера
enc.tick(); // Оновлюємо стан енкодера в перериванні
}
// Af
// Main display
// add1-----------------------------------------------------------------------------------------------------------
void Md_MainDisplay() {// головна фунція виклик решта функцій дисплея
switch (mdf_addres(get_add, 1, check_button_enc)) {
case address_not_entered:
Md_itemOne();
MdId_bluetoothPrint();
mdaf_typeNumberPrint();
MdId_battery();
break;//
case 1:
profil[mps(gapn)].tape == t_sp ? Md_ProfileSp() : Md_ProfileMp();//відображаємо меню залежності від типу профіля
break;
case 2:
//Md_GeneralSettings();
break;
case 3:
// qr-код ссылка на інструкцію
break;
case 4: //back
Md_Display(info_display);
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 1);
break;
case address_entered:
mdf_cursor(set_pos, 1);
break;
}
}
void Md_itemOne(){
byte Lines[] = {22, 32, 42, 52};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 22);
display.println(" Profile");
display.setCursor(0, 32);
display.println(" General settings");
display.setCursor(0, 42);
display.println(" Instruction (!)");
display.setCursor(0, 52);
display.println(" Exit menu ");
}
//
// add2-----------------------------------------------------------------------------------------------------------
// Profile Sp
void Md_ProfileSp() {
switch (mdf_addres(get_add, 2, check_button_enc)) {
case address_not_entered:
Md_p_ProfileSp();
break;
case 1:
Md_profileSettingsSp();
break;
case 2: // QR code
// Додайте код для обробки QR-коду тут, якщо потрібно
break;
case 3:
Md_selectProfile();
break;
case 4: //back
mdf_cursor(set_pos, mdf_addres(get_add, 1));
mdf_addres(back_add, 1);
break;
case address_entered:
mdf_addres(get_add, 2) == 1 ? mps(copy_profile) : [](){return 0;}();//копіюємо профіль щоб перевірити зміну настройок перевірка виконується при зміні настройок при зміні профілю перевірка не виконується
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_ProfileSp() {
byte LinesSp[] = {31, 39, 49, 57};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
display.setCursor(3, 1);// стрілки по боках активний профіль
display.write(25);
display.setCursor(118, 1);
display.write(25);
display.drawRoundRect(0, 0, 127, 10, 2, WHITE);// РАМКА АКТИВ ПРОФ НАЗВА ГРИ
display.drawRect(0, 9, 127, 19, WHITE);
display.drawRect(10, 29, 51, 19, WHITE);// РАМКА НАСТРОЙКИ КЮАР КОД
display.setCursor(21, 1);
display.print("Active profile");
display.setCursor(98, 36); // РИСКА В лыво РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.println("_" );
display.setCursor(93, 36);
display.println("_" );
display.setCursor(91, 36);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.write(24);
display.setCursor(91, 34);
display.write(30);
display.drawCircle(113, 42, 11, WHITE);// КРУГ НАВК Sp
display.setCursor(105, 39);
display.print("Sp");
display.println(profil[mps(gapn)].namber);
display.setCursor(profil[mps(gapn)].centroName(), 15);// функція centroName() в залежності від кількості ім'я вранці
display.println(profil[mps(gapn)].nameProfiles(get_name));
display.setCursor(0, 32); // РИСКА В ПРАВО РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.println(" _" );
display.setCursor(4, 32);
display.println(" _" );
display.setCursor(66, 32);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.write(24);
display.setCursor(66, 30);
display.write(30);
display.setCursor(0, 31);
display.println(" Settings " );
display.println(" QR code");
display.setCursor(0, 49);
display.println(" Select profile ");
display.println(" Back" );
}
//
// Profile Mp
void Md_ProfileMp() {
switch (mdf_addres(get_add, 2, check_button_enc)) {
case address_not_entered:
Md_p_ProfileMp();
break;
case 1:
Md_profileSettingsMp();
break;
case 2:
Md_selectProfile();
break;
case 3: //back
mdf_cursor(set_pos, mdf_addres(get_add, 1));
mdf_addres(back_add, 1);
break;
case address_entered:
mdf_addres(get_add, 2) == 1 ? mps(copy_profile) : [](){return 0;}(); //копіюємо профіль щоб перевірити зміну настройок перевірка виконується при зміні настройок при зміні профілю перевірка не виконується
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_ProfileMp() {
byte LinesMp[] = {35, 49, 57};
mdf_cursor(cursor_read_encoder, sizeof(LinesMp), LinesMp);
display.setCursor(3, 1);// стрілки по боках активний профіль
display.write(25);
display.setCursor(118, 1);
display.write(25);
display.drawRoundRect(0, 0, 127, 10, 2, WHITE);// РАМКА АКТИВ ПРОФ НАЗВА ГРИ
display.drawRect(0, 9, 127, 19, WHITE);
display.drawRect(10, 29, 51, 19, WHITE);// РАМКА НАСТРОЙКИ КЮАР КОД
display.setCursor(21, 1);
display.print("Active profile");
display.setCursor(98, 36); // РИСКА В лыво РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.println("_" );
display.setCursor(93, 36);
display.println("_" );
display.setCursor(91, 36);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.write(24);
display.setCursor(91, 34);
display.write(30);
display.drawCircle(113, 42, 11, WHITE);// КРУГ НАВК Sp
display.setCursor(105, 39);
display.print("Mp");
display.println(profil[mps(gapn)].namber);
display.setCursor(profil[mps(gapn)].centroName(), 15);// функція centroName() в залежності від кількості ім'я вранці
display.println(profil[mps(gapn)].nameProfiles(get_name));
display.setCursor(0, 32); // РИСКА В ПРАВО РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.println(" _" );
display.setCursor(4, 32);
display.println(" _" );
display.setCursor(66, 32);// СТРЫЛКА ВВЕРХ РАЗОМ ПОЛУЧАЭТСЯ КРИВА СТРЫЛКА
display.write(24);
display.setCursor(66, 30);
display.write(30);
display.setCursor(0, 35);
display.println(" Settings " );
display.setCursor(0, 49);
display.println(" Select profile ");
display.println(" Back" );
}
//
// General settings
/*
void Md_GeneralSettings(){
int seveDataTemp;// використовую щоб було більш зрозуміліше
if (mdf_addres(get_add, 2) <= 3){
Md_p_GeneralSettings();
}
if (mdf_cursor(get_pos) <= 3){
switch (mdf_addres(2, input_last)) {
case 1:
seveDataTemp = mdf_magnifier(af_autoShutdown(get_time), 3, 15, 1);
af_autoShutdown(change_time, seveDataTemp);
break;
case 2:
break;
case 3:
break;
}
}
else{
switch (mdf_addres(get_add, 2, check_button_enc)) {
case 4:
break;
case 5:
break;
case 6:
mdf_cursor(set_pos, 2);
mdf_addres(back_add, 1);
break;
}
}
}
void Md_p_GeneralSettings(){
int seveDataTemp = 0;
byte Lines[] = {0, 8, 16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 0);
display.println(" Auto shutdown- m.");
display.println(" Shutter-imitation ");
display.println(" Battery-saving");
display.println(" Gyroscope ");
display.println(" Buttons");
display.println(" Back" );
if (af_autoShutdown(get_time) < 10){
seveDataTemp = 3;
}
Serial.println(seveDataTemp);
display.setCursor(15*6+seveDataTemp, 0);
display.print(af_autoShutdown(get_time));
*/
// Gs
//
// add3-----------------------------------------------------------------------------------------------------------
// Profile settings Sp
void Md_profileSettingsSp() {
switch (mdf_addres(get_add, 3, check_button_enc)) {
case address_not_entered:
Md_p_profileSettingsSp();
break;
case 1:
Md_Buttons();
break;// Buttons
case 2:// back //якщо були зміни то повернення відбудеться через функцію prof mdf_profileStorage
if (mdaf_savingSettingsDisplay() == function_completed){//якщо були зміни попадаємо сюди виконуємо підтвердження або повернення настройок профілю
mdf_cursor(set_pos, mdf_addres(get_add, 2)); //повернення
mdf_addres(back_add, 2);
}
break;
case address_entered:
mdf_cursor(set_pos, 1);
if (mdf_addres(get_add, 3) == 2 && mps(active_copy_compare) == active_copy_same){//при виході Перевіряємо чи були настройки якщо ні Просто повертаємося якщо так запуститься підтвердження
mdf_cursor(set_pos, mdf_addres(get_add, 2));//повернення
mdf_addres(back_add, 2);
}
break;
}
}
void Md_p_profileSettingsSp() {
byte LinesSp[] = {16, 24};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
display.setCursor(0, 16);
display.println(" Buttons" );
display.println(" Back" );
}
//
// Profile settings Mp
void Md_profileSettingsMp(){
switch (mdf_addres(get_add, 3, check_button_enc)) {
case address_not_entered:
Md_p_profileSettingsMp();// відображення пунктів настройки
break;
case 1:// Gyroscope
Md_gyroscopeSettings();
break;
case 2:// Buttons
Md_Buttons();
break;
case 3:// Name
Md_changeNameProfile();
break;
case 4:// back
if (mdaf_savingSettingsDisplay() == function_completed){//якщо були зміни попадаємо сюди виконуємо підтвердження або повернення настройок профілю
mdf_cursor(set_pos, mdf_addres(get_add, 2)); //повернення
mdf_addres(back_add, 2);
}
break;
case address_entered:
mdf_cursor(set_pos, 1);
if (mdf_addres(get_add, 3) == 4 && mps(active_copy_compare) == active_copy_same){//при виході Перевіряємо чи були настройки якщо ні Просто повертаємося якщо так запуститься підтвердження
mdf_cursor(set_pos, mdf_addres(get_add, 2));//повернення
mdf_addres(back_add, 2);
}
break;
}
}
void Md_p_profileSettingsMp() {
byte LinesMp[] = {16, 24, 32, 50};
mdf_cursor(cursor_read_encoder, sizeof(LinesMp), LinesMp);
display.drawRoundRect(9, 14, 59, 28, 0, WHITE);
display.setCursor(0, 16);
display.println(" Gyroscope" );
display.println(" Buttons" );
display.println(" Name" );
display.setCursor(0, 50);
display.println(" Back" );
}
//
// Select profile
void Md_selectProfile() {
switch (mdf_addres(get_add, 3, check_button_enc)) {
case address_not_entered:
Md_p_selectProfile();
break;
case 1:
Md_standardProfiles();
break;
case 2:
Md_myProfiles();
break;
case 3: //back
mdf_cursor(set_pos, mdf_addres(get_add, 2));
mdf_addres(back_add, 2);
break;
case address_entered:
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_selectProfile() {
byte Lines[] = {16, 24, 32};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 16);
display.println(" Standard profiles" );
display.println(" My profiles");
display.println(" Back" );
}
// Sp
//
// add4-----------------------------------------------------------------------------------------------------------
// Standard profiles
void Md_standardProfiles(){
switch (mdf_addres(get_add, 4, check_button_enc)) {
case address_not_entered:
Md_p_standardProfiles();// відображення списку профілів вибір
break;
case 8: //back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
if(mdf_addres(get_add, 4) != 8){
mps(activate_p, mdf_addres(get_add, 4));
buttonGamepadReinstallAllButtons();// аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 2);//повертаємося через один пункт
}
break;
}
}
void Md_p_standardProfiles(){
byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
for (int i = 0; i < 7; i++) {// виводим імена профілів діючий профіль мигає
display.setCursor(6, i*8);
display.print(profil[i+1].nameProfiles(get_name_a_f));
}
display.setCursor(0, 56);
display.println(" Back" );
}
// Sp
// My profiles
void Md_myProfiles(){
switch (mdf_addres(get_add, 4, check_button_enc)){// третій параметр забороняє вибирати діючий профіль
case address_not_entered:
Md_p_MyProfiles();
break;
case 8: //back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
if (mdf_addres(get_add, 4) != 8) {
mps(activate_p, mdf_addres(get_add, 4)+7);
buttonGamepadReinstallAllButtons();// аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 2);// не міняти двоєчку будуть глюки
}
break;
}
}
void Md_p_MyProfiles() {
byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
for (int i = 0; i < 7; i++) {
display.setCursor(6, i*8);
display.print(profil[i+8].nameProfiles(get_name_a_f));
}
display.setCursor(6, 56);
display.println("Back" );
}
// Mp
// Gyroscope settings
void Md_gyroscopeSettings(){
int seveDataTemp;// використовую щоб було більш зрозуміліше
Md_p_gyroscopeSettings();//відображаємо пункти меню постійно
switch (mdf_addres(get_add, 4, check_button_enc)) {
case address_not_entered:
break;
case 1:
seveDataTemp = mdf_magnifier(profil[mps(gapn)].deadZoneHor, 0, 9999, 100);
profil[mps(gapn)].setDeZoHor(seveDataTemp);
break;
case 2:
seveDataTemp = mdf_magnifier(profil[mps(gapn)].sensitivityHor, 0, 100, 5);
profil[mps(gapn)].setSenHor(seveDataTemp);
break;
case 3:
seveDataTemp = mdf_magnifier(profil[mps(gapn)].deadZoneVer, 0, 9999, 100);
profil[mps(gapn)].setDeZoVer(seveDataTemp);
break;
case 4:
seveDataTemp = mdf_magnifier(profil[mps(gapn)].sensitivityVer, 0, 100, 5);
profil[mps(gapn)].setSenVer(seveDataTemp);
break;
case 5: //back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
mdf_addres(mode_click_back);
/*
if (mdf_addres(get_add, 4) == 1 || mdf_addres(get_add, 4) == 3) {// test зупиняємо виконання функції Gf_GyroAnalogR для того щоб настроїти мертву зону
timerAlarmDisable(timer);
}
*/
break;
}
}
void Md_p_gyroscopeSettings(){
static Timer tmr(500, ref_dis);
byte Lines[] = {5, 15, 34, 44, 55};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(8, 5);
display.println("Dead zone ");
display.setCursor(8, 15);
display.println("Sensitivity");
display.setCursor(8, 34);
display.println("Dead zone ");
display.setCursor(8, 44);
display.println("Sensitivity");
display.setCursor(8, 55);
display.println("Back");
display.setCursor(84, 10);
display.println("H");
display.setCursor(84, 39);
display.println("V");
display.setCursor(102, 4);
display.println(profil[mps(gapn)].deadZoneHor);
display.setCursor(104, 17);
display.println(profil[mps(gapn)].sensitivityHor);
display.setCursor(102, 33);
display.println(profil[mps(gapn)].deadZoneVer);
display.setCursor(104, 46);
display.println(profil[mps(gapn)].sensitivityVer);
display.drawRoundRect(6, 2, 69, 23, 3, WHITE); // РАМКА НАВКОЛО Dead zone Sensitivity H
display.drawRoundRect(6, 31, 69, 23, 0, WHITE);// РАМКА НАВКОЛО Dead zone Sensitivity V
// H
if((mdf_cursor(get_pos) == 1 || mdf_cursor(get_pos) == 2) && !mdf_addres(get_add, 4)){// РАМКА НВКОЛО H
if(tmr.f()){display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
}
else{display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
// V
if((mdf_cursor(get_pos) == 3 || mdf_cursor(get_pos) == 4) && !mdf_addres(get_add, 4)){// РАМКА НВКОЛО V
if(tmr.f()){display.drawCircle(86, 42, 7, WHITE);}
}
else{display.drawCircle(86, 42, 7, WHITE);}
if(mdf_addres(get_add, 4) == 1){ // число рамка DZ H
if(tmr.f()){display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
}
else{display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
if(mdf_addres(get_add, 4) == 2){ // число рамка S H
if(tmr.f()){display.drawRect(101, 15, 23, 11, WHITE);}
}
else{display.drawRect(101, 15, 23, 11, WHITE);}
if(mdf_addres(get_add, 4) == 3){ // число рамка DZ V
if(tmr.f()){display.drawRect(99, 31, 29, 11, WHITE);}
}
else{display.drawRect(99, 31, 29, 11, WHITE);}
if(mdf_addres(get_add, 4) == 4){ // число рамка S V
if(tmr.f()){display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
}
else{display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
}
// Gs
// Buttons
void Md_Buttons(){
switch (mdf_addres(get_add, 4, check_button_enc)) {
case address_not_entered: // address_not_entered == 0
Md_p_Buttons();
break;
case 1:
Md_moveButtons();
break;
case 2:
Md_duplicateButtons();
break;
case 3:
break;
case 4:// back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered: // address_not_entered == 21
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_Buttons(){
byte LinesSp[] = {16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
display.setCursor(0, 16);
display.println(" Move button");
display.println(" Duplicate buttons");
display.println(" Buttons tester");
display.println(" Back" );
}
// B
// changeNameProfile
// Оголошення змінних для позиції курсора у зміні імені профілю
int8_t cnpaX, cnpaY; // Змінні для координат X та Y
char letterBig[6][10] = { // Масив символів для великих букв
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
{'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'},
{'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', '_'},
{'\0', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '.','\0'},
{',', ':', '&', '!', ' ', ' ', '(', ')', '?','\0'}
};
char letterSmall[5][10] = {// Масив символів для малих букв
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'},
{'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '_'},
{'\0', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '.','\0'},
{',', ':', '&', '!', ' ', ' ', '(', ')', '?','\0'}
};
char (*ptrLetter)[10] = letterSmall;// // Вказівник на поточний масив символів (великі або малі букви)
void Md_changeNameProfile(){
static int8_t magnitude = 1;// костилі використовується для вибору пробілу і виходу
static int8_t fl;// Перемикач між великими та малими літерами костилі вказівник не працює належним чином
if (em.gbs()) {//залежності від позиції кнопки переміщення відбувається по х або Y
switch (em.turn(get_press_end_turn)) {
case rotated_r:
case rotated_r_fast:
cnpaY--;
break;
case rotated_l:
case rotated_l_fast:
cnpaY++;
break;
}
cnpaY = af_clamp(cnpaY, 0, 4);//скидання на мінімальне і наоборот якщо перевищили значення
}
else{
switch (em.turn(get_turn)) {
case rotated_r:
cnpaX--;
break;
case rotated_l:
cnpaX++;
break;
}
if(cnpaX != af_clamp(cnpaX, 0, 9)) {//якщо X вийшло за межі допустимих чисел виконуємо пробірку і збільшення або зменшення Y
cnpaY += cnpaX == 10 ? 1:-1;//зменшуємо або збільшуємо y для того щоб перемістити курсор вниз після досягнення максимуму по X
cnpaY = af_clamp(cnpaY, 0, 4);//додатково перевіряємо y щоб не Вийти за межі
}
cnpaX = af_clamp(cnpaX, 0, 9);//скидання на мінімальне і наоборот якщо перевищили значення
}
if (em.but() == click) {//Клік виконує зміну бук видалення пробіл додавання букви вихід
if (getActionSelection() == cl) {//міняємо величину бук
ptrLetter = (ptrLetter == letterSmall) ? letterBig : letterSmall;
}
else if (getActionSelection() == dl) {//видаляємо букву
profil[mps(gapn)].nameProfiles(delete_letter);
if (!profil[mps(gapn)].lettersTakeNumber()) {ptrLetter = letterBig;}//якщо видаляємо останню букву робимо всі букви великими одноразово
}
else if (getActionSelection() == tb) {//вихід плюс скидання і перевірка набраного тексту
profil[mps(gapn)].nameProfiles(g_s_n_i_n_l_a_r_s); //(give standard name if no letters and remove spaces ) TEST якщо Видалити всі букви то заміниться ім'я на стандартне
cnpaX = cnpaY = 0;
ptrLetter = letterSmall;
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
}
else{ //доплюсовуємо до речення вибрано букву
profil[mps(gapn)].nameProfiles(add_letter, ptrLetter[cnpaY][cnpaX]);
if (profil[mps(gapn)].lettersTakeNumber() == 1) {ptrLetter = letterSmall;}//речення починається з великої букви і автоматично скидається на Малі
}
}
Md_p_changeNameProfile();// віалізуємо програму
}
byte Md_p_changeNameProfile(){
static Timer tmr(300, ref_dis);
display.setCursor(2, 1);// виводи ім'я профіля
display.print(profil[mps(gapn)].nameProfiles(get_name));
if(tmr.f()){// вертикальна риска біля букви
display.drawRect(2+6*profil[mps(gapn)].lettersTakeNumber(), 0, 1, 9, WHITE);
}
if(!getActionSelection()){//відображаємо рамку навколо букви якщо навелись на стандартний символ
display.drawRect(4+cnpaX*12, 12+cnpaY*10, 9, 11, WHITE);// рамка курса 10 вверх вниз, вліво вправо 12
}
for (int i = 0; i <= 4; i++) {// виводим масив букв на дисплей
for (int j = 0; j < 10; j++) {
display.setCursor(6+j*12, 14+i*10);
display.print(ptrLetter[i][j]);
}
}
if(getActionSelection() != cl){//якщо координата вказує на спеціальний символ починаємо ним мигати
ptrLetter == letterSmall ? arrowLowercaseLetters(4, 42) : arrowCapitalLetters(4, 42);
}
else if(getActionSelection() == cl && !tmr.f()){
ptrLetter == letterSmall ? arrowLowercaseLetters(4, 42) : arrowCapitalLetters(4, 42);
}
if(getActionSelection() != dl){
letterDeletionSymbol(110, 44);
}
else if(getActionSelection() == dl && !tmr.f()){
letterDeletionSymbol(110, 44);
}
if(getActionSelection() != sb){
display.fillRect(50, 55, 25, 6, WHITE);
}
else if(getActionSelection() == sb && !tmr.f()){
display.fillRect(50, 55, 25, 6, WHITE);
}
if(getActionSelection() != tb){
arrowReturn(110, 54);
}
else if(getActionSelection() == tb && !tmr.f()){
arrowReturn(110, 54);
}
return 0;
}
int8_t getActionSelection() {//якщо навелись то функція поверне координату яка відповідає символу
if (cnpaY == 3 && cnpaX == 0) return cl;
if (cnpaY == 3 && cnpaX == 9) return dl;
if (cnpaY == 4 && cnpaX == 9) return tb;
if ((cnpaY == 4) && (cnpaX == 4 || cnpaX == 5)) return sb;
return 0; // Значення за замовчуванням
}
//функції відображають власні створені символи
void arrowLowercaseLetters(int xOffset, int yOffset) {//символ малі букви
// Масив, який описує символ "стрілка"
uint8_t pixelArray[10][9] = {
{0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 0, 0, 0, 1, 1, 1},
{0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0, 0}
};
for (int y = 0; y < 10; y++) { // 10 рядків у масиві
for (int x = 0; x < 9; x++) { // 9 стовпців у масиві
if (pixelArray[y][x] == 1) {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_WHITE); // Малюємо білий піксель
} else {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_BLACK); // Малюємо чорний піксель (опціонально)
}
}
}
}
void arrowCapitalLetters(int xOffset, int yOffset) {//символ великі букви
// Масив, який описує заповнену стрілку
uint8_t pixelArray[10][9] = {
{0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 0},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0, 0}
};
for (int y = 0; y < 10; y++) { // 10 рядків у масиві
for (int x = 0; x < 9; x++) { // 9 стовпців у масиві
if (pixelArray[y][x] == 1) {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_WHITE); // Малюємо білий піксель
} else {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_BLACK); // Малюємо чорний піксель (опціонально)
}
}
}
}
void letterDeletionSymbol(int xOffset, int yOffset) {//символи видалення
uint8_t pixelArray[7][9] = {
{0, 0, 0, 1, 1, 1, 1, 1, 1},
{0, 0, 1, 0, 0, 0, 0, 0, 1},
{0, 1, 0, 0, 1, 0, 1, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 1},
{0, 1, 0, 0, 1, 0, 1, 0, 1},
{0, 0, 1, 0, 0, 0, 0, 0, 1},
{0, 0, 0, 1, 1, 1, 1, 1, 1}
};
for (int y = 0; y < 7; y++) { // 7 рядків у масиві
for (int x = 0; x < 9; x++) { // 9 стовпців у масиві
if (pixelArray[y][x] == 1) {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_WHITE); // Малюємо білий піксель
} else {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_BLACK); // Малюємо чорний піксель (опціонально)
}
}
}
}
void arrowReturn(int xOffset, int yOffset) {//символ повернення
// Масив, який описує символ "стрілка повернення"
uint8_t pixelArray[9][12] = {
{0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0},
{1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1},
{0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0}
};
for (int y = 0; y < 9; y++) { // 9 рядків у масиві
for (int x = 0; x < 12; x++) { // 12 стовпців у масиві
if (pixelArray[y][x] == 1) {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_WHITE); // Малюємо білий піксель
} else {
display.drawPixel(x + xOffset, y + yOffset, SSD1306_BLACK); // Малюємо чорний піксель (опціонально)
}
}
}
}
// cNP
//
// add5-----------------------------------------------------------------------------------------------------------
// Moving buttons
void Md_moveButtons(){
Md_p_moveButtons();
switch (mdf_addres(get_add, 5, check_button_enc)) {
case address_not_entered:
break;
case 1:
Md_moveButtonsExecute(select_button) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 2:
Md_moveButtonsExecute(return_default) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 3: //back
mdf_cursor(set_pos, mdf_addres(get_add, 4));
mdf_addres(back_add, 4);
break;
case address_entered:
mdf_addres(mode_function_returns);
mdf_addres(get_add, 5) == 1 ? Md_p_moveButtons(next_animation) : [](){return 0;}();
mdf_addres(get_add, 5) == 2 ? mdaf_confirm(output_by_coordinates, X(37), Y(46)) : [](){return 0;}();
break;
}
}
byte Md_p_moveButtons(byte receivedData, byte selectedButton){
// бегуща стрілка. анімація. кнопка один збереження. крапка 2 збереження. анімація кнопка ліва. анімація кнопка права
static byte runningArrow, animation, buttonOne, buttonTwo, accumulationL, accumulationR;
static Timer tmr; // таймер для виконання анімації
const char** buttonNames = getButtonNames();
display.drawRect(6, 5, 31, 31, WHITE); // квадрат L
display.drawRect(90, 5, 31, 31, WHITE);// квадрат R
display.drawCircle(21, 16, 8, WHITE);// кружки над кнопками
display.drawCircle(105, 16, 8, WHITE);
display.drawRect(12, 26+accumulationL, 19, 5, WHITE);// ліва кнопка accumulationL збільшення зменшення анімація кнопки
display.fillRect(9, 30, 25, 4, WHITE);
display.drawRect(96, 26+accumulationR, 19, 5, WHITE);// ПРАВА кнопка accumulationR збільшення зменшення анімація кнопки
display.fillRect(93, 30, 25, 4, WHITE);
byte Lines[] = {40, 48, 56};// передаємо координати переміщення функції
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 40);// понятно
display.println(" Select-move");
display.println(" Reset");
display.println(" Back");
if (!receivedData && !animation) {// якщо фаlс то закінчуємо роботу функції економія ресурсів
return 0;
}
if (receivedData == delete_data) {
animation = accumulationL = accumulationR = runningArrow = 0;
}
else if (receivedData == next_animation) {// в коді викликаємо функцію переключаємо анімацію
accumulationL = accumulationR = runningArrow = 0;
if (animation != 3) {
animation++;
}
if (animation == 2) {
buttonOne = selectedButton;
}
else if (animation == 3) {
buttonTwo = selectedButton;
tmr.b(2000);
}
}
if (animation == 1 && tmr.e()) { // анімація кнопок за допомогою перемінної
accumulationL++;
if (accumulationL == 4) {
accumulationL = 0;
}
}
else if (animation == 2 && tmr.e()) {
accumulationR++;
runningArrow++;
if (accumulationR == 4 ) {
accumulationR = 0;
}
if (runningArrow == 8 ) {
runningArrow = 0;
}
}
else if (animation == 3 && tmr.e()) {// відображення другої кнопки завершення роботи з запізнення
animation = 0;
}
if (animation == 1 ) {
if (accumulationL){// знак запитання мигає якщо кнопка не натиснута
display.setCursor(19, 13);
display.println('?');
}
}
else if (animation == 2 ) {
display.setCursor(22-strlen(buttonNames[buttonOne])*3, 13);// strlen автоцентровка в колі може бути одна або дві букви
display.println(buttonNames[buttonOne]);
if(accumulationR){// знак запитання мигає якщо кнопка не натиснута
display.setCursor(103, 13);
display.println('?');
}
for (int i = 0; i < 8; ++i) {// відображаємо кутові скобки
display.setCursor(40+i*6, 13);
display.write(62);
display.setCursor(40+i*6, 23);
display.write(60);
}
display.setCursor(40+runningArrow*6, 13);// трикутник переміщається по углових скобках анімація
display.write(16);
display.setCursor(83-runningArrow*6, 23);// трикутник переміщається по углових скобках анімація
display.write(17);
}
else if (animation == 3 ) {// завершення програми відображення натиснутих кнопок
display.setCursor(22-strlen(buttonNames[buttonOne])*3, 13);
display.println(buttonNames[buttonOne]);
display.setCursor(106-strlen(buttonNames[buttonTwo])*3, 13);
display.println(buttonNames[buttonTwo]);
}
if (tmr.e()) {// всі дії виконуються по таймеру
tmr.b(400);
}
return 0;
}
int Md_moveButtonsExecute(int acceptedCommand){// test
static int8_t sequenceNumber;
switch (acceptedCommand) {
case select_button:
if (em.but() == click){//функція може завершитися по кліку один з варіантів
Md_p_moveButtons(delete_data);
sequenceNumber = 0;
return function_completed;
}
for (int i = 0; i < 16; i++) {
if (butGamepad[i].gamepadBatton(press_check) == gamepad_button_pressed) {
Md_p_moveButtons(next_animation, butGamepad[i].gamepadBatton(button_get_name));
if (!sequenceNumber) {
sequenceNumber = i+1;
}
else {
int savedButton1 = profil[mps(gapn)].listButton[sequenceNumber-1];//зберігаємо елементи масиву в тимчасовій переміні
int savedButton2 = profil[mps(gapn)].listButton[i];
profil[mps(gapn)].listButton[i] = savedButton1; //міняємо елементи масиву (номер-назви) місцями
profil[mps(gapn)].listButton[sequenceNumber-1] = savedButton2;
sequenceNumber = 0;
if (savedButton1 != savedButton2) {
profil[mps(gapn)].lblm = difference;// test ?
}
buttonGamepadReinstallAllButtons();//виконуємо перенастройку всіх екземплярів класу buttonGamepad тому що listButton получив зміни
return function_completed;
}
break;
}
}
break;
case return_default:////виконується reset або якщо нема потреби відображення надпису not_need
if (profil[mps(gapn)].lblm == by_default) {
if (mdaf_confirm(print_not_need) == function_completed) {
return function_completed;
}
}
else{
if (mdaf_confirm(offer_a_choice) == data_ready) {
if (mdaf_confirm(get_data) == choice_yes) {
profil[mps(gapn)].lblm = by_default;
profil[mps(gapn)].listButtonsStandard(); //повертаємо стандартну номер-назву
buttonGamepadReinstallAllButtons();//перенастройка всіх екземплярів класу тому що значення в listButton[] помінялись
}
return function_completed;
}
}
break;
}
return 0;
}
//
// Duplicate buttons
void Md_duplicateButtons(){//вибираємо Яку кнопку будемо дублювати або яку кнопку потрібно відключити від дублюючої кнопки
byte regime[] = {default_mode, select_button_r_s, default_mode, select_button_r_m, default_mode};
Md_p_duplicateButtons(regime[mdf_addres(get_add, 5)]);//змінюємо анімацію в залежності від набраного адресу
switch (mdf_addres(get_add, 5, check_button_enc)) {
case address_not_entered:
break;
case 1:
Md_duplicateButtonsExecute(select_button_r_s) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 2:
Md_duplicateButtonsExecute(r_s_button_reset) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 3:
Md_duplicateButtonsExecute(select_button_r_m) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 4:
Md_duplicateButtonsExecute(r_m_button_reset) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 5: //back
mdf_cursor(set_pos, mdf_addres(get_add, 4));
mdf_addres(back_add, 4);
break;
case address_entered:
mdf_addres(get_add, 5) % 2 == 0 ? mdf_addres(mode_function_returns) : mdf_addres(mode_click_back);//якщо получимо один або три виконається вибір другого варіанту якщо два або чотири вибір першого
mdf_addres(get_add, 5) == 2 ? mdaf_confirm(output_by_coordinates, X(72), Y(16)) : [](){return 0;}();//одноразово вказує координати функції для відображення вибору
mdf_addres(get_add, 5) == 4 ? mdaf_confirm(output_by_coordinates, X(72), Y(47)) : [](){return 0;}();//одноразово вказує координати функції для відображення вибору
break;
}
}
void Md_p_duplicateButtons(byte acceptedCommand){
static Timer tmr(300, ref_dis);
const char** buttonNames = getButtonNames();
af_displayPrintLain("Rifle stock", X(10), Y(4));
af_displayPrintLain("Rs.reset", X(6), Y(16));
af_displayPrintLain("Rifle magazine", X(10), Y(31+4), 3, 9);
af_displayPrintLain("Rm.reset", X(6), Y(31+16));
af_displayPrintLain("Back", X(6), Y(56));
for (int y = 0; y <= 31; y += 31) {//відображаємо прямокутники і Квадрати і символи
af_curvedArrow(56, y+16);//крива стрілка Вверх
display.drawRect(6, y, 72, 15, WHITE); // прямокутник
display.drawRect(103, y, 15, 15, WHITE);//квадрат ? - ім'я кнопки
display.setCursor(88, y+4);
display.println("=");
}
byte LinesSp[] = {4, 16, 35, 47, 56};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
if (acceptedCommand == default_mode) {//якщо немає режиму відображається вибрані кнопки або знак ? (buttonNames[0] = ?) якщо нема зробленого вибору
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[16]], X(108) , Y(4));//відображаємо і центруємо кнопку в залежності від кількості букв одна або дві
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[17]], X(108) , Y(35));
}
else if (acceptedCommand == select_button_r_s ) { //вибираємо кнопку яку будемо дублювати
if (tmr.f()) { //виконуємо мигання за вказаною вище періодичністю
display.setCursor(108, 4);
display.println(buttonNames[0]);//відображаємо знак запитання з таблиці символів
}
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[17]], X(108) , Y(35));//відображаємо знизу вибрану кнопку або знак запитання якщо нема вибору Тому що прийнята команда не є default_mode
}
else if (acceptedCommand == select_button_r_m) {//майже та сама схема що і вище
if (tmr.f()) {
display.setCursor(108, 35);
display.println(buttonNames[0]);
}
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[16]], X(108) , Y(4));
}
}
int Md_duplicateButtonsExecute(int acceptedCommand){
if (acceptedCommand == select_button_r_s || acceptedCommand == select_button_r_m) {
for (int i = 0; i < 16; i++) {
if (butGamepad[i].gamepadBatton(press_check) == gamepad_button_pressed) {//очікуємо натиску кнопки яку будемо дублювати
if (acceptedCommand == select_button_r_s) { // в залежності від вибраної кнопки для дублювання виконуємо копіювання
profil[mps(gapn)].listButton[16] = profil[mps(gapn)].listButton[i];
}
else{
profil[mps(gapn)].listButton[17] = profil[mps(gapn)].listButton[i];
}
return function_completed;
}
}
}
else if (acceptedCommand == r_s_button_reset){//в залежності від вибору вибираємо кнопку яку будемо скидати
if (!profil[mps(gapn)].listButton[16]) {// якщо кнопка для дубляжу не вибрана відображаємо повідомлення що не потрібно робити Reset
return mdaf_confirm(print_not_need) == function_completed ? function_completed : 0;
}
if (mdaf_confirm(offer_a_choice) == data_ready) {//якщо виконати скидання дублюючі кнопки тобто при натисканні фізичної кнопки буде натискатися кнопка номер 0 якої не існує
mdaf_confirm(get_data) == choice_yes ? profil[mps(gapn)].listButton[16] = 0 : 0;//якщо підтверджуємо скидання вказуємо нульову кнопку виконуємо return function_completed;
return function_completed;
}
}
else if (acceptedCommand == r_m_button_reset) {//логіка Та ж сама що і описано зверху
if (!profil[mps(gapn)].listButton[17]) {
return mdaf_confirm(print_not_need) == function_completed ? function_completed : 0;
}
if (mdaf_confirm(offer_a_choice) == data_ready) {//якщо виконати скидання дублюючі кнопки буду натискати не існуючу кнопку геймпада номер 0
mdaf_confirm(get_data) == choice_yes ? profil[mps(gapn)].listButton[17] = 0 : 0;
return function_completed;
}
}
return 0;
}
//
//Buttons tester
//
//buttons additional functions
void Md_printButtonNameCenteringX(const char* buttonName, byte x, byte y) {
x -= (strlen(buttonName) == 2) ? 3 : 0; // Використовуємо тернарний оператор
display.setCursor(x, y);
display.println(buttonName);
}
const char** getButtonNames() {//функція яка повертає масив з іменами кнопок (ДИВИТИСЬ ПОЯСНЕННЯ)
static const char* buttonNames[] = {
"?",//для дублікатів кнопки
"A",
"B",
"X",
"Y",
"A1",
"A2",
"RB",
"LB",
"Se",
"St",
"dU",
"dD",
"dL",
"dR",
"RT",
"LT"
};
return buttonNames;
}
//
//
// Main display functions
// Cursor
byte mdf_cursor(byte acceptedCommand, byte data, byte* arr) {
static int8_t point = 1;
static int8_t cursor = cursor_type_one;
switch (acceptedCommand) {
case cursor_read_encoder:
//переміщаємо курсор якщо тип стрілки є cursor_type_one
//і якщо кнопка нкодра не натиснута при натиску і обертання використовується для прокрутки тексту Тому потрібно заборона на переміщення
if(cursor == cursor_type_one){
switch (em.turn(get_turn)) {
case rotated_l:
Serial.println("--");
point--;
break;
case rotated_r:
Serial.println("++");
point++;
break;
}
point = af_clamp(point, 1, data);
}
break;
case cursor_type_one:
em.turn(reset_rotation);//енкодер опрацьовується в прериванні і тому можуть появлятися непотрібні дані стрілка може скакнути
cursor = cursor_type_one;
break;
case cursor_type_two:
cursor = cursor_type_two;
break;
case get_pos:
return point;
break;
case set_pos: //вказуємо позицію курс при переходах вперед або назад по адресу
point = data;
cursor = cursor_type_one;
break;
}
mdf_p_cursor(cursor, arr[point-1]);
return 0;
}
byte mdf_p_cursor(byte cursorPrint, byte cursorPosition) {
const byte cursorTypes[] = {26, 16}; //cursor_type_one == 0 cursor_type_two == 1
display.setCursor(0, cursorPosition);
display.write(cursorTypes[cursorPrint]);//вибираємо один з двох видів курсора з таблиці символів
return 0;
}
//
int mdf_addres(byte acceptedCommand, byte addNumb, byte additionalCommand) {//N
static byte add[10];//для коректної роботи можна використати тільки вісім адресів 1-8
static byte inputMode = mode_click_input;
switch (acceptedCommand) {
case back_add:
mdf_cursor(cursor_type_one);
inputMode = mode_click_input;
for (int i=9; i >= addNumb; i--){// команда back виконує видалення всіх адресів до числа вказаного в addNumb
add[i] = address_not_entered;
}
return address_deleted;//????
break;
case mode_click_back:
mdf_cursor(cursor_type_two);
inputMode = mode_click_back;
break;
case mode_function_returns:
mdf_cursor(cursor_type_two);
inputMode = mode_function_returns;
break;
case get_add:
if (additionalCommand == check_button_enc){
if (inputMode == mode_click_input){
if (add[addNumb] == address_not_entered && em.but() == click){// набір адреса
add[addNumb] = mdf_cursor(get_pos);
return address_entered;
}
}
else if (inputMode == mode_click_back){
if (add[addNumb+1] == address_not_entered && em.but() == click){//якщо попадемо на нуль повертаємося
Serial.println("if (mode_click_back)");
mdf_addres(back_add, addNumb);
return address_deleted;
}
}
}
return add[addNumb];
break;
}
return 0;
}
int mdf_magnifier(int numeric, int minNam, int maxNam, int Speed) {
switch (em.turn(get_turn)) {
case rotated_r:
numeric++;
break;
case rotated_l:
numeric--;
break;
case rotated_r_fast:
numeric+=Speed;
break;
case rotated_l_fast:
numeric-=Speed;
break;
}
numeric = af_clamp(numeric, minNam, maxNam);
return numeric;
}
// mdf
// Menu display additional functions -------------------------------------------------------------------------------------------
// Saving Settings Display
byte mdaf_savingSettingsDisplay(){ //коротко копіювання mps(copy profile) порівняння mps(active_copy_compare) якщо == active_copy_same просто виходимо якщо != active_copy_sameрізні викликаються Дана функція
mdaf_confirm(output_by_coordinates, X(36), Y(40));
mdaf_p_savingSettingsDisplay();// візуальна частина коду
if (mdaf_confirm(offer_a_choice) == data_ready) {
if (mdaf_confirm(get_data) == choice_no) {//якщо вибір ні повертаємо настройки
mps(not_save);
}
return function_completed;//незалежно від вибору функція поверне function_completed
}
return 0;
}
void mdaf_p_savingSettingsDisplay(){
display.drawRoundRect(20, 8, 87, 22, 0, WHITE); // рамка навколо слова зверху
display.setCursor(25, 15);
display.println("Save settings");
}
//
void mdaf_typeNumberPrint(){//функція відображає номер і тип профілю в інфодисплей і першому пункті меню
char name[3][3] = {" ", "Sp", "Mp"};
display.setCursor(49, 0);
display.print("( )");
display.setCursor(55, 0);
display.print(name[profil[mps(gapn)].tape]);//tape може бути t_sp = 1 t_mp = 2
display.print(profil[mps(gapn)].namber);
}
// Confirm
byte mdaf_confirm(byte acceptedCommand, byte x, byte y){//функція надає варіант для вибору та каву ні або виводить повідомлення що не потрібно якщо нема потреби
static bsc selection(1);//вибір так або ні
static bsc clicksCount(3);//зміна накопичує вказані ліки при досягненні виконається дія
if (acceptedCommand == output_by_coordinates) {// кожен раз перед основним викликом одноразово функція приймає координати
mdaf_p_confirm(output_by_coordinates, x, y);// переправляє координати в mdaf_p_confirm для збереження
}
else if (acceptedCommand == print_not_need) {//після перевірки можна подати команду вивести повідомлення якщо підтвердження не потрібне
if ( mdaf_p_confirm(print_not_need) == function_completed || em.but() == click) {
mdaf_p_confirm(reset);
return function_completed;
}
}
else if (acceptedCommand == offer_a_choice) {//якщо підтвердження потрібне запропоновуємо вибір виводимо так або ні
if (em.turn(get_turn)) {//при обертах енкодера в залежності від накопичених значень відбудеться різні дії
selection.p(1);//якщо вже є одиничка і ми доплюсуємо один змінна скинеться на нуль
mdaf_p_confirm(change_selection); // міняємо позицію відображення рамки так або ні
clicksCount.r();//якщо було накопичення ліків відбудеться скидання із-за того що позиція рамки змінилася
}
if (em.but() == click) {//робимо кліки і накопичуємо їх якщо максимум повідомляємо що дані готові
clicksCount.p(1);
if (clicksCount.m()) {
mdaf_p_confirm(reset);
return data_ready;
}
mdaf_p_confirm(accelerate_frame_blinking);
}
mdaf_p_confirm(offer_a_choice);
}
else if (acceptedCommand == get_data) {//якщо получили data_ready беремо дані-вибір
byte variants[] = {choice_no, choice_yes};
return variants[selection.r()]; //метод .r() одноразово повертає накопичне число і скидає його
}
return 0;
}
byte mdaf_p_confirm(byte acceptedCommand, byte x, byte y){
const byte frameSizeWidth[] = {15, 21};
const byte framePosition[] = {2, 33};
const int frameBlink[] = {1000, 500, 200};//test
static byte frequency;//frequency ЧАСТОТА
static byte flashFlag;
static Timer tmr(1000, ref_dis);
static byte selection;
static byte azixX;
static byte azixY;
switch (acceptedCommand) {
case output_by_coordinates://зберігаємо координати для виведення
azixX = x;
azixY = y;
break;
case change_selection://якщо ми міняємо позицію рамки то відбувається скидання
selection = !selection;
frequency = 0;
tmr.r();
break;
case accelerate_frame_blinking://при накопиченні кліків збільшується частота блимання рамки навколо вибору
frequency++;
break;
case reset://якщо накопичили необхідну кількість ліків відбудеться скидання до початкового стану
selection = frequency = 0;
tmr.r();
break;
case print_not_need://якщо настройки не мінялись виводимо повідомлення що не потрібне скидання
if (tmr.f()){
display.setCursor(azixX+4,azixY+3);
display.print("not need");
display.drawRect(azixX, azixY, 56, 15, WHITE);// велика рамка навколо так ні
}
else if (!tmr.f()) {
tmr.r();
return function_completed;
}
break;
case offer_a_choice://якщо настройки мінялись пропонується вибір
display.setCursor(azixX+4, azixY+4);
display.println("NO");
display.setCursor(azixX+35, azixY+4);
display.println("YES");
if (tmr.e()) {
tmr.b(frameBlink[frequency]);
}
if (tmr.f()) {//рамка світилась одразу після запуску або змінні вибору
display.drawRect(azixX + framePosition[selection], azixY + 2, frameSizeWidth[selection], 11, WHITE);//рамка навколо так або ні
}
display.drawRect(azixX, azixY, 56, 15, WHITE);// велика рамка навколо так ні
break;
}
return 0;
}
//
//
// Md
// MdId
void MdId_bluetoothPrint() {
const byte bluArr[9] = {
0b00100,
0b00110,
0b10101,
0b01110,
0b00100,
0b01110,
0b10101,
0b00110,
0b00100
};
static Timer tmr(1000, ref_dis);
display.drawCircle(6, 6, 6, WHITE);// кружок
if (bleGamepad.isConnected()) {
display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
}
else{
if (tmr.f()) {
display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
}
}
}
void MdId_battery() {
const float batLevels[] = {4.15, 4.0, 3.82, 3.76, 3.65, 3.6, 3.5, 3.4, 3.3, 3.0};
/*
float shuntVoltage_mV = ina219.getShuntVoltage_mV();
float busVoltage_V = ina219.getBusVoltage_V();
float loadVoltage_V = busVoltage_V + (shuntVoltage_mV / 1000);
*/
static float loadVoltage_V = 3.0;// test
static byte chargeLevel;
static Timer tmr;
if (tmr.e()) {
tmr.b(60000);
for (int i = 0; i <= 9 ; i++) {
if (loadVoltage_V >= batLevels[i]) {
if (chargeLevel != map(i, 0, 9, 0, 11)) {
//af_outputDisplay(ref_dis);
}
chargeLevel = map(i, 0, 9, 0, 11);
break;
}
}
}
MdId_p_battery(chargeLevel);
}
byte MdId_p_battery(byte receivedData) {
display.drawRect(111, 2, 1, 4, WHITE);// картинка батареї
display.drawRect(112, 0, 16, 8, WHITE);
display.fillRect(114+receivedData, 2, 12-receivedData, 4, WHITE);// шкала заряду батареї
return 0;
}
// MI
// info Display
void infoDisplay() {
MdId_bluetoothPrint();
mdaf_typeNumberPrint();
MdId_battery();
display.drawRect(0, 14, 127, 19, WHITE);// РАМКА НАВКОЛО НАЗВИ
display.setCursor(profil[mps(gapn)].centroName(), 20);// функція centroName() в залежності від кількості сентрує ім'я вранці
display.print(profil[mps(gapn)].nameProfiles(get_name));
display.setCursor(44,44);
display.write(26);
display.drawRect(50, 42, 27, 11, WHITE);
display.setCursor(52,44);
display.print("Menu");
}
// Id
// Display
byte Md_Display(byte Receiving) {
static byte Regime = info_display;
// static byte Regime = menu;//test
display.clearDisplay();//test
//af_outputDisplay(clear_display);
if (Receiving) {
Regime = Receiving;
}
switch (Regime) {
case info_display:
if (em.but() == click) {
Md_Display(menu);
}
/*
else if (em.but() == hold_b_e) {// по плану має виключати дисплей при утриманні ?
display.clearDisplay();
display.display();
}
*/
else{
infoDisplay();
}
break;
case menu:
Md_MainDisplay();
break;
}
display.display();
//af_outputDisplay(print_display);
return 0;
}
// D
// Gamepad function
/*//test
void IRAM_ATTR Gf_GyroAnalogR() {
// static Timer tmr;
volatile static int GiroDedZone;
volatile static int rightStickX;
volatile static int rightStickY;
static int x; // x
static int z; // z
// Serial.println(" 11111");
// if (af_BatR(b_handle) && tmr.e()) {
// if (af_BatR(b_handle) && bleGamepad.isConnected()) {
if (af_BatR(b_handle)) {
af_autoShutdown(reset_f);
Quaternion q;
VectorFloat gravity;
VectorInt16 gyro;
volatile static float ypr[3];
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGyro(&gyro, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
// tmr = millis() + 11;
}
if (abs(x) > 1) {
GiroDedZone = map(z, -150, 150, -30, 30);
GiroDedZone = abs(GiroDedZone);
if (x < -GiroDedZone) {
x = map(x, -1, -profil[mps(gapn)].sensitivityHor, -profil[mps(gapn)].deadZoneHor, -32767);
x = constrain(x, -32767, -1);
bleGamepad.setZ(x);
}
else if (x > GiroDedZone) {
x = map(x, 1, profil[mps(gapn)].sensitivityHor, profil[mps(gapn)].deadZoneHor, 32767);
x = constrain(x, 1, 32767);
bleGamepad.setZ(x);
}
}
else{
bleGamepad.setZ(0);
}
if (abs(z) > 1) {
GiroDedZone = map(x, -150, 150, -30, 30);
GiroDedZone = abs(GiroDedZone);
if (z < -GiroDedZone) {
z = map(z, -1, -profil[mps(gapn)].sensitivityHor, profil[mps(gapn)].deadZoneHor, 32767);
z = constrain(z, -32767, -1);
bleGamepad.setRZ(z);
}
else if (z > GiroDedZone) {
z = map(z, 1, profil[mps(gapn)].sensitivityHor, -profil[mps(gapn)].deadZoneHor, -32767);
z = constrain(z, 1, 32767);
bleGamepad.setRZ(z);
}
}
else{
bleGamepad.setRZ(0);
}
bleGamepad.sendReport();
}
else if (rightStickX || rightStickY){
rightStickX = rightStickY = 0;
bleGamepad.setRightThumb(0, 0);
bleGamepad.sendReport();
}
}
*/
void Gf_gamepadButtons(){
for (byte i = 0; i < 18; i++) {//Що ти всі кнопки разом з дублікатами
butGamepad[i].gamepadBatton(read_button_state); //виконується натискання кнопки за допомогою необхідного методу вказаному при створенні через конструктор
}
}
// Gf
void setup() {
Serial.begin(9600);// test В ФІНАЛІ НЕЗАБУТИ ЗАКОМЕНТУВАТИ!!!
Serial.println(" Serial.println("");");
//виконуємо настройки геймпада
bleGamepadConfig.setAutoReport(0);
bleGamepadConfig.setButtonCount(8);
bleGamepadConfig.setIncludeStart(true);
bleGamepadConfig.setIncludeSelect(true);
bleGamepadConfig.setHatSwitchCount(1);
bleGamepadConfig.setAxesmin(-32767);
bleGamepadConfig.setAxesmax(32767);
bleGamepadConfig.setWhichAxes(1, 1, 1, 1, 1, 1, 0, 0);
bleGamepad.begin(&bleGamepadConfig);
//
//EEPROM.begin(512);
//mps(eeprom_initialization);// ініціалізація з енергонезалежної пам'яті профілів
// pinMode(3, OUTPUT);// подаємо високий сигнал на транзистор щоб заживити модулі
// digitalWrite(3, HIGH);
// pin 2 глючить встроєний резистор
// Налаштування типу енкодера та таймауту на швидкі повороту
enc.setType(TYPE2); // 2шаговий енкодер (TYPE2)
enc.setFastTimeout(20); // Таймаут для швидкого повороту (в мс)
//
//обробка даних енкодера за допомогою приривання
attachInterrupt(digitalPinToInterrupt(p_clk), isr, CHANGE); //attachInterrupt(пін, функція для виклику, зміна з 0-1 або 1-0);
attachInterrupt(digitalPinToInterrupt(p_dt), isr, CHANGE);
//
// ----------Display--------------
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextSize(1);
display.setTextColor(WHITE);
// gyroStart();// при потребі проводимо калібровку якщо калібровка виконана виконуємо старт функції гіроскопа
// startAnimation();// анімація при включенні. до робити в кінці!
}
void loop() {
display.clearDisplay();
Md_duplicateButtons();
display.display();
}// void loop()