#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "GyverEncoder.h"
//#include <MPU6050.h>
//#include <BleGamepad.h> // при загрузці нa реально Arduino даний фрагмент розкоментувати a пуcті класи закоментувати
//#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 function_not_completed 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
//
//Effects
//Md_triggerReadEffects();
#define tre_analog_dead_zone 2600
//
//Md_vibration();
//#define p_vt 18
#define vibration_start 21
#define vibration_stop 22
#define vt_get_permission 23
#define vt_pin_activate 24
#define vt_change 25
//
// Md_shotgunBolt()
//#define p_led_trigger_red 27
//#define p_led_trigger_dreen 26
#define trigger_light_change 20
#define trigger_get_light 21
#define change_color 22
#define trigger_color_red 23
#define trigger_color_dreen 24
#define trigger_color_start 25
//
// Md_shotgunBolt()
//#define p_sb_trigger 12
#define bolt_start 21
#define bolt_stop 22
#define bolt_get_permission 23
#define bolt_pin_activate 24
#define bolt_change 25
//
//
// Піни карта
#define p_led_trigger_red 27
#define p_led_trigger_dreen 26
#define p_sb_trigger 12
#define p_vt 18
#define b_emul_dpad 1
#define b_handle 4
#define p_triger_r 15
#define b_analog_l 33
#define b_on_off 14
#define b_emul_dpad 26
#define p_sw 25 // кнопка енко // RotaryEncoder encoder(27, 14, RotaryEncoder::LatchMode::TWO03);
#define p_dt 32
#define p_clk 33
//
//bgpr()
#define set_press_permit 20
#define get_press_permit 21
#define set_reset_permit 22
#define get_disconnected_number 23
//
// af_passwordEntry()
#define typing_a_password 20
#define password_reset 21
#define password_not_entered 22
#define password_entered 23
//
//Gf_gamepadButtonsDuplicators()
#define dbrs_change_mode 20
#define dbrs_get_mode 21
#define dbrs_reset 22
//
// 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 back 20
#define get_add 23
#define check_emb 24
#define check_emb_ptp 25
#define mode_click_input 26
#define mode_click_back 27
#define back_add 28
#define mode_function_returns 29
#define set_add 30
#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
//
// displayRefresh()
#define display_refresh 20 // використовується для оновлення дисплею і в конструкторі класу таймер для того щоб надати дозвіл
#define clear_display 21
#define print_display 22
#define permit_change_status 23
//
// Md_autoPowerOffa()
// #define reset_f 11
#define apo_change_time 20
#define apo_get_time 21
//
// Md_movingButtons()
#define choice_bat_waiting 20
#define button_selection_complete 21
#define return_default 22
#define select_button 23
//
//Md_displayTimeout()
//#define take_status 1
#define dt_get_tim_minutes 20
#define dt_get_tim_seconds 21
#define dt_change_time 22
//
//Timer class
#define ref_dis display_refresh // потрібно щоб було 20 там і там
#define wdu 0 // wdu = without display update без оновлення дисплею
//
// 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
#define select_regime_r_s 26
//
// Button gamepad class
#define touch_dead_zone 50
#define bgc_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
#define r_b_double_press 25
//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 not_save 30
#define save_changes 31
#define active_copy_compare 32
#define active_copy_same 33 // зафіксовані зміни
#define reset_giro 34
//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
#define rifle_stock 17
#define rifle_magazine 18
uint16_t tmrProf = 0;
#define enter_default_location 20
#define check_location 21
#define standard_location 22
#define non_standard_location 23
//
// 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 31
//control()
#define work_allowed 32
#define work_prohibited 33
#define em_check 34
#define em_worked 35
#define em_get_status 36
//
// c-v-a
// Function prototypes
byte bgpr(byte acceptedCommand = 0, byte acceptedNumber = 0, byte acceptedData = 0);
byte Gf_gamepadButtonsDuplicators(byte acceptedCommand = 0);
byte Md_triggerLight(byte acceptedCommand = 0);
byte Md_displayTimeout(byte acceptedCommand = 0);
uint32_t 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 Md_p_moveButtons(byte receivedData = 0, byte selectedButton = 0);
byte displayOutput(byte AcceptedCommand = 0);
void displayRefresh(byte acceptedCommand = 0);
byte Md_autoPowerOff(byte acceptedCommand = 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);
//MPU6050 mpu;
// Byte saving data (bsd) використовується як унікальна зміна яка після досягнення потрібного нам значення повертає його і скидається на нуль
class bsd {
private:
const byte maxValue; // Максимальне значення
byte val; // Накопичене значення
public:
// Конструктор
bsd(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; // Повертаємо збережене значення
}
};
// bsd
// Timer class (конструкція працює приблизно 49 днів після чого потрібен перезапуск мікроконтролера) (ДИВИТИСЬ ПОЯСНЕННЯ)
class Timer {
private:
uint32_t tmr;
byte timerFlag;
unsigned int autoRestartTimer;
byte displayRefreh;
public:
Timer(unsigned int aRT = 0, byte dR = wdu) {//якщо є команда ref_dis то буде виконуватись оновлення при кожному виклику методів в яких є af_ outputDisplay(displayRefreh);
autoRestartTimer = aRT;
displayRefreh = dR;
}
bool fa() {//при першому запуску повертає правда дальше потрібно скористатися часом який встановлений через конструктор обов'язково
if (tmr) {//2) тепер якщо вказали час через один з двох способів виконуємо дії
if (tmr < millis()){//по завершенню часу змінюємо стан флажка стан флажка повертається постійно
displayRefresh(displayRefreh);
tmr = millis() + autoRestartTimer;
timerFlag = !timerFlag;
}
}
else if (!tmr) {//1) перший запуск повертаємо правда
tmr = millis() + autoRestartTimer;//якщо не вказали час через конструктор потрібно викликати ft(вказуємо час);
return true;//робимо інверсію повертаємо правда
}
return timerFlag;//весь час повертаємо стан флажка
}
bool fm() {////при першому запуску повертає правда дальше потрібно скористатися ft(вказати час)
if (tmr && tmr < millis()) {
timerFlag = !timerFlag;
displayRefresh(displayRefreh);
tmr = 0;
return true;
}
else if (!tmr) {
timerFlag = !timerFlag;
return true;
}
return false;
}
bool fmg() {//беремо стан флажка який змінює метод fm()
return timerFlag;
}
void ft(unsigned int rT = 0){//вказуємо час роботи таймера
displayRefresh(displayRefreh);
tmr = millis() + rT;
}
void fr(unsigned int rT = 0){//коли перестаємо викликати функцію завжди потрібно викликати
tmr = timerFlag = 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)
{
malb(enter_default_location);//даємо стандартні імена-номера кнопкам
}
//
//гіроскоп настройка
void giroReset(){
deadZoneHor = sensitivityHor = deadZoneVer = sensitivityVer = 0;
}
bool giroStatus(){
return deadZoneHor||sensitivityHor||deadZoneVer||sensitivityVer;
}
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.fa() ? 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();
}
//
//функція працює з порядковими номерами кнопок геймпада
byte malb(byte acceptedCommand) {//malb = management array list buttons
if (acceptedCommand == enter_default_location) { // Скеровування до активного профілю
for (byte i = 0; i <= 15; i++) {//стандартний порядок кнопок два останніх елемента використовуються для дублювання
listButton[i] = i + 1; //вказуємо кнопкам номер-ім'я (i + 1 не чіпати Все правильно)
}
}
else if (acceptedCommand == check_location) {
for (byte i = 0; i <= 15; i++) {//якщо розташування кнопок помінялось місцями функція видасть брехня(стандарт від 1-15)
if (listButton[i] != i+1) {
return non_standard_location;
}
}
return standard_location;
}
return 0;
}
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) &&
(rrsps == other.rrsps);
}
// 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
byte rrsps = 1; //rrsps = regime rifle stock profile save
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 acceptedData = 0) {// mps= management profile setting
static byte active = 8;
static byte returnResult;
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 == reset_giro) {
if (!profil[mps(gapn)].giroStatus()) {
if (mdaf_confirm(print_not_need) == function_completed) {
return function_completed;
}
}
else if (mdaf_confirm(offer_a_choice) == data_ready) {//якщо виконати скидання дублюючі кнопки тобто при натисканні фізичної кнопки буде натискатися кнопка номер 0 якої не існує
if (mdaf_confirm(get_data) == choice_yes) {profil[mps(gapn)].giroReset();}
return function_completed;
}
}
//
return 0;
}
// P
// Button debounce class
class DebButton {// нагадування Не міняй сука нічого тут все добре
private:
const byte pin;
Timer tmr; //test
byte debounceTime;
byte buttonState;
public:
// Конструктор класу, ініціалізація піна, номера кнопки геймпада та часу захисту від випадкових спрацьовувань
DebButton(int p, int dT = 50) : pin(p) {
debounceTime = dT;
pinMode(pin, INPUT_PULLUP);
}
// Метод для обробки натискання кнопки
bool press() {
if (!digitalRead(pin) && !buttonState && tmr.fa()) {// після натську кнопки буде повернення true
buttonState = true;
tmr.ft(debounceTime);
}
else if (buttonState && digitalRead(pin) && tmr.fa()) {// пісня відпущення кнопки буде повернення брехня
buttonState = false;// тепер переміна буде брехня
tmr.ft(debounceTime);
}
return buttonState;// постійно повертаємо стан кнопки
}
};
//
// Button gamepad class
class ButGamepad {
private:
byte gamepadButtonName;
byte pinReadType;
byte pressPermit;
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;
pressPermit = 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 == r_b_double_press) {
if (tactilePinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 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 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 == r_b_double_press) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 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 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 == r_b_double_press) {
if (analogPinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 0;
}
}
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);
if (temp > bgc_analog_dead_zone) { // test
temp = map(temp, bgc_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 == r_b_double_press) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 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 == r_b_double_press) {
if (tactilePinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 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 > bgc_analog_dead_zone) { // test
temp = map(temp, bgc_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 == r_b_double_press) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 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 == r_b_double_press) {
if (tactilePinButtonRead()) {
if (!flagPress) {
flagPress++;
buttonGamepadPressRelease(press_button, gamepadButtonName);
bleGamepad.sendReport();
}
else if (flagPress == 1) {
flagPress++;
buttonGamepadPressRelease(release_button, gamepadButtonName);
bleGamepad.sendReport();
}
}
else if (flagPress){
flagPress = 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;
}
//
void pressPermitReinstall(byte newPermit){
pressPermit = newPermit;
}
//використовується для переінціалізації вказівника на функцію заміна кнопок місцями
byte buttonReinstall(byte newButton){
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 (pressPermit) {
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], bgpr(get_press_permit, 0), tactile_read, 13), //
ButGamepad(profil[mps(gapn)].listButton[1], bgpr(get_press_permit, 1), tactile_read, 14),
ButGamepad(profil[mps(gapn)].listButton[2], bgpr(get_press_permit, 2), tactile_read, 18),
ButGamepad(profil[mps(gapn)].listButton[3], bgpr(get_press_permit, 3), tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[4], bgpr(get_press_permit, 4), tactile_read, 17),
ButGamepad(profil[mps(gapn)].listButton[5], bgpr(get_press_permit, 5), tactile_read, 5),
ButGamepad(profil[mps(gapn)].listButton[6], bgpr(get_press_permit, 6), tactile_read, 19),
ButGamepad(profil[mps(gapn)].listButton[7], bgpr(get_press_permit, 7), tactile_read, 13),
ButGamepad(profil[mps(gapn)].listButton[8], bgpr(get_press_permit, 8), tactile_read, 23), // button_select
ButGamepad(profil[mps(gapn)].listButton[9], bgpr(get_press_permit, 9), tactile_read, 26), // button_start
ButGamepad(profil[mps(gapn)].listButton[10], bgpr(get_press_permit, 10), tactile_read, 13 + false), // dpad_up
ButGamepad(profil[mps(gapn)].listButton[11], bgpr(get_press_permit, 11), tactile_read, 12), // dpad_down
ButGamepad(profil[mps(gapn)].listButton[12], bgpr(get_press_permit, 12), tactile_read, 13 + false), // dpad_left
ButGamepad(profil[mps(gapn)].listButton[13], bgpr(get_press_permit, 13), tactile_read, 27), // dpad_right
ButGamepad(profil[mps(gapn)].listButton[14], bgpr(get_press_permit, 14), analog_read, p_triger_r), // trigger_r
ButGamepad(profil[mps(gapn)].listButton[15], bgpr(get_press_permit, 15), analog_read, 4), // trigger_l
ButGamepad(profil[mps(gapn)].listButton[16], bgpr(get_press_permit, 16), tactile_read, 13 + false), //дублікатори analog_read невказувати!
ButGamepad(profil[mps(gapn)].listButton[17], bgpr(get_press_permit, 17), tactile_read, 13 + false)
};
//butGamepad[i].mps(set_press_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 buttonGamepadReinstallAll() {
for (int i = 0; i <= 15; i++) {
butGamepad[i].buttonReinstall(profil[mps(gapn)].listButton[i]); //аналог перезапуску всіх(16ти) конструкторів екземплярів класу
}
}
byte bgpr(byte acceptedCommand, byte acceptedNumber, byte acceptedData) {//bgpr=button gamepad presspermit reinstal настройка заборон зберігається для всіх профілів
static bool pressPermit [18] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; //загальний масив дозволів на натиск кнопки для всіх профілів
if (acceptedCommand == get_press_permit) {//значення всередині елементу несе в собі дозвіл на натиск кнопки
return pressPermit[acceptedNumber]; // Повертаємо значення елемента масиву pressPermit дізнаємось по значенню чи є дозвіл на натиск кнопки
}
else if (acceptedCommand == set_press_permit) {
pressPermit[acceptedNumber] = acceptedData; // Можете змінити значення
butGamepad[acceptedNumber].pressPermitReinstall(acceptedData);
}
else if (acceptedCommand == set_reset_permit) {
for (int i = 0; i <= 13; i++) {
pressPermit[i] = 1;
butGamepad[i].pressPermitReinstall(true); //аналог перезапуску всіх(16ти) конструкторів екземплярів класу
}
}
else if (acceptedCommand == get_disconnected_number) {
byte numberDisconnected = 0;
for (int i = 0; i <= 13; i++) {
if (!pressPermit [i]) {
numberDisconnected++;
}
}
return numberDisconnected;
}
return 0;
}
// Bdc
// Encoder module class
class encoderModule{//зчитування даних кодера і кнопки в залежності від поєднань повертаються різні дані
private:
byte workPermission = work_allowed;
DebButton butEnc;// b_ sw pin = 25 оголошуємо екземпляр класу а конструктор викликаємо нижче так потрібно робити всередині класу
bsd bepes; //bepes = button encoder program execution stage(енкодер кнопка етап виконання програми)
Timer tmr;
public:
encoderModule() : butEnc(p_sw) { // Параметр p_sw передається через список ініціалізації
}
int8_t turn(int8_t acceptedCommand){//получаємо звичайні оберти або оберти з натиснутою кнопкою
if (control(em_get_status) == work_allowed && enc.isTurn()) {
if (acceptedCommand == get_press_end_turn && butEnc.press()) {
bepes.s(press_end_turn);
return readTurn();
}
else if (acceptedCommand == get_turn && !butEnc.press() ) {
return readTurn();
}
else if (acceptedCommand == reset_rotation) {//скидання накопичених даних дані можуть мінятися Навіть якщо ми не викликаємо тому що обробка відбувається в перериванні
return readTurn();
}
}
return 0;
}
int8_t readTurn(){//метод зчитує дані eодера використовуємо бібліотеку гайвер одер
displayRefresh(ref_dis);//оновлення дисплею при обертанні екватора
Md_displayTimeout(reset_f);//перезапускаємо затемнення дисплея
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(){//зчитуємо дані кнопки енкодера використовуємо метод DebButton
if (butEnc.press()){//якщо кнопка натиснута виконуємо дії в залежності від того що відбулося або відбудеться
if (bepes.g() != press_end_turn && tmr.fa()) {//якщо енкодер не обертався програма виконується по стандартному сценарію
if (!bepes.g()) {//повертаємо одноразово повідомлення про те щоб кнопка натиснута запускаємо таймер tmr.ft(200); утримання
tmr.ft(500);
return bepes.s(press_b_e);
}
else if (bepes.g() == press_b_e) {//якщо таймер вийшов і кнопка натиснута одноразово повідомляємо що є утримання
return bepes.s(hold_b_e);
}
}
}
else if (bepes.g()) {//після того як відпустили кнопку перевіряємо дані на натиск
displayRefresh(ref_dis); //оновлення дисплею при кліку
Md_displayTimeout(reset_f);//перезапускаємо затемнення дисплея
tmr.fr();//таймер завжди потрібно скидати щоб можна було при потребі клікати кнопкою Напряги мозги Якщо хочеш зрозуміти
if (bepes.r() == press_b_e) {Serial.println(11);return click;}
}
return no_data;
}
byte gbs(){// get state button
return butEnc.press();
}
byte control(byte acceptedCommand){
if (acceptedCommand == work_allowed) {//робота дозволена
displayRefresh(ref_dis);
Md_displayTimeout(reset_f);
workPermission = work_allowed;
}
else if (acceptedCommand == work_prohibited) {
workPermission = work_prohibited;
}
else if (acceptedCommand == em_check) {
if (bepes.r() == press_b_e || readTurn()) {
return em_worked;
}
}
else if (acceptedCommand == em_get_status) {
return workPermission;
}
return no_data;
}
};
encoderModule em;
// emc
// Utility functions (допоміжні функції)
//Additional functions
byte af_passwordEntry(byte acceptedCommand) {
static byte currentStage = 0; // Поточний етап (0 - клік, 1 - утримання, 2 - клік)
if (currentStage != 2 && acceptedCommand == typing_a_password){
switch (currentStage) {
case 0: // Етап 1: Очікування утримання
if (em.but() == hold_b_e) {
Serial.println(1); currentStage = 1; // Переходимо до наступного етапу
}
break;
case 1: // Етап 2: Очікування кліку
if (em.but() == click) {
Serial.println(2);currentStage = 2; // Переходимо до наступного етапу
return password_entered;
}
break;
}
return password_not_entered;
}
else if (acceptedCommand == password_reset){
currentStage = 0;
}
return 0;
}
void af_centerNumber(int x, int y, int number, byte minusNo = 0) {
String numberString = String(number);
int numDigits = numberString.length(); // Враховуємо кількість символів (цифри + знак)
// Визначаємо зміщення по X для числа, враховуючи ширину кожного символу
int offsetX = 0;
if (numDigits == 1) {
offsetX = 0; // Для одного символу зміщення не потрібно
} else if (numDigits == 2) {
offsetX = 6; // Для двох символів
} else if (numDigits == 3) {
offsetX = 12; // Для трьох символів
} else if (numDigits == 4) {
offsetX = 18; // Для чотирьох символів
}
if (minusNo && number < 0) {
offsetX += 6; // Додаємо ширину мінуса (приблизно 6 пікселів)
}
// Центруємо центральну цифру
int startX = x - (offsetX / 2);
// Встановлюємо курсор
display.setCursor(startX, y);
// Виводимо число
display.print(numberString);
}
void af_displayPrintLain(String text, byte x, byte y, byte linePosition, byte cropText) {// (ДИВИТИСЬ ПОЯСНЕННЯ)
// Статичні змінні для збереження стану між викликами
static Timer tmr(1, ref_dis); // Таймер для контролю автоматичної прокрутки також включаємо оновлення дисплея при кожному зміщенні тексту
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) {//якщо номер строки співпадає з позицією kурсора відбувається прокрутка тексту
// Якщо натиснута кнопка енкодера разом з обертом енкодера
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.fa()) {
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); // Очищення області під текстом
}
}
uint32_t af_clamp(int value, int minVal, int maxVal) {//якщо вийти за рамки то поверне в початок або кінець
if (value < minVal) {
return maxVal;
}
else if (value > maxVal) {
return minVal;
}
return value;
}
void af_buttonOnOffa(){//test
if (!digitalRead(13)) {
delay(400);
esp_deep_sleep_start();
}
delay(100);
}
void af_startAnimation(){
display.clearDisplay();
display.invertDisplay(0);
display.setCursor(-2, 28);
display.println(" FS G36 ");
display.display();
delay(500);
display.clearDisplay();
display.display();
}
void af_isr() {//функція викликається в пририванні і обробляє дані з енкодера
enc.tick(); // Оновлюємо стан енкодера в перериванні
}
//
//function symbols (функції відображають власні створені символи)
void fs_curvedArrow(int x, int y) {//крива стрілка Вверх
display.setCursor(x, y);
display.println("_");
display.setCursor(x + 3, y); // Зсув на 3 пікселі для другого символу
display.write(24);
}
void fs_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 fs_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 fs_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 fs_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); // Малюємо чорний піксель (опціонально)
}
}
}
}
//
// Uf
// Main display
// selecting point menu
void Md_MainDisplay() {// головна фунція виклик решта функцій дисплея
switch (mdf_addres(check_emb, 0)) {
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
displayOutput(info_display);
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 0);
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 ");
}
//
// point 1 profile setting
// add1-----------------------------------------------------------------------------------------------------------
// Profile Sp
void Md_ProfileSp() {
switch (mdf_addres(check_emb, 1)) {
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(check_emb, 0));
mdf_addres(back_add, 0);
break;
case address_entered:
mdf_addres(get_add, 1) == 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(check_emb, 1)) {
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(check_emb, 0));
mdf_addres(back_add, 0);
break;
case address_entered:
mdf_addres(get_add, 1) == 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" );
}
//
//
// add2-----------------------------------------------------------------------------------------------------------
// Profile settings Sp
void Md_profileSettingsSp() {
switch (mdf_addres(check_emb, 2)) {
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(check_emb, 1)); //повернення
mdf_addres(back_add, 1);
}
break;
case address_entered:
mdf_cursor(set_pos, 1);
if (mdf_addres(get_add, 2) == 2 && mps(active_copy_compare) == active_copy_same){//при виході Перевіряємо чи були настройки якщо ні Просто повертаємося якщо так запуститься підтвердження
mdf_cursor(set_pos, mdf_addres(check_emb, 1));//повернення
mdf_addres(back_add, 1);
}
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(check_emb, 2)) {
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(check_emb, 1)); //повернення
mdf_addres(back_add, 1);
}
break;
case address_entered:
mdf_cursor(set_pos, 1);
if (mdf_addres(get_add, 2) == 4 && mps(active_copy_compare) == active_copy_same){//при виході Перевіряємо чи були настройки якщо ні Просто повертаємося якщо так запуститься підтвердження
mdf_cursor(set_pos, mdf_addres(check_emb, 1));//повернення
mdf_addres(back_add, 1);
}
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(check_emb, 2)) {
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(check_emb, 1));
mdf_addres(back_add, 1);
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
//
//
// add3-----------------------------------------------------------------------------------------------------------
// Standard profiles
void Md_standardProfiles(){
switch (mdf_addres(check_emb, 3)) {
case address_not_entered:
Md_p_standardProfiles();// відображення списку профілів вибір
break;
case 8: //back
mdf_cursor(set_pos, mdf_addres(check_emb, 2));
mdf_addres(back_add, 2);
break;
case address_entered:
if(mdf_addres(get_add, 3) != 8){
mps(activate_p, mdf_addres(check_emb, 3));
buttonGamepadReinstallAll();// аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 1);//повертаємося через один пункт
}
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(check_emb, 3)){// третій параметр забороняє вибирати діючий профіль
case address_not_entered:
Md_p_MyProfiles();
break;
case 8: //back
mdf_cursor(set_pos, mdf_addres(check_emb, 2));
mdf_addres(back_add, 2);
break;
case address_entered:
if (mdf_addres(get_add, 3) != 8) {
mps(activate_p, mdf_addres(check_emb, 3)+7);
buttonGamepadReinstallAll();// аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 1);// не міняти двоєчку будуть глюки
}
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(check_emb, 3)) {
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:
if (mps(reset_giro) == function_completed) {mdf_addres(back_add, 3);}//скидаємо настройки гіроскопа після вибору так
break;
case 6: //back
mdf_cursor(set_pos, mdf_addres(check_emb, 2));
mdf_addres(back_add, 2);
break;
case address_entered:
if (mdf_addres(get_add, 3) <= 4) {
mdf_addres(mode_click_back);
}
else if (mdf_addres(get_add, 3) == 5 ) {
mdf_addres(mode_function_returns);
mdaf_confirm(output_by_coordinates, X(39), Y(46));
}
/*
if (mdf_addres(check_emb, 3) == 1 || mdf_addres(check_emb, 3) == 3) {// test зупиняємо виконання функції Gf_GyroAnalogR для того щоб настроїти мертву зону
timerAlarmDisable(timer);
}
*/
break;
}
}
void Md_p_gyroscopeSettings(){
static Timer tmr(500, ref_dis);
byte Lines[] = {2, 11, 26, 35, 46, 55};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(8, 2);
display.println("Dead zone ");
display.setCursor(8, 11);
display.println("Sensitivity");
display.setCursor(8, 26);
display.println("Dead zone ");
display.setCursor(8, 35);
display.println("Sensitivity");
display.setCursor(8, 46);
display.println("Reset");
display.setCursor(8, 55);
display.println("Back");
display.setCursor(84, 6);
display.println("H");
display.setCursor(84, 6+24);
display.println("V");
af_centerNumber(110, 2, profil[mps(gapn)].deadZoneHor);//виводимо настройки гіроскопа центруємо значення в рамці
af_centerNumber(110, 11, profil[mps(gapn)].sensitivityHor);
af_centerNumber(110, 26, profil[mps(gapn)].deadZoneVer);
af_centerNumber(110, 35, profil[mps(gapn)].sensitivityVer);
display.drawRoundRect(6, 0, 69, 20, 3, WHITE); // РАМКА НАВКОЛО Dead zone Sensitivity H
display.drawRoundRect(6, 0+24, 69, 20, 3, WHITE);// РАМКА НАВКОЛО Dead zone Sensitivity V
display.drawRect(128-30, 0, 29, 20, WHITE);//dzh значення показники
display.drawRect(128-30, 0+24, 29, 20, WHITE);//dzv значення показники
if(2 <= mdf_cursor(get_pos) || tmr.fa()){// РАМКА H
display.drawRoundRect(79, 2, 15, 15, 0, WHITE);
}
if((3 != mdf_cursor(get_pos) && 4 != mdf_cursor(get_pos)) || tmr.fa()){// РАМКА V
display.drawRoundRect(79, 2+24, 15, 15, 0, WHITE);
}
}
// Gs
// Buttons
void Md_Buttons(){
switch (mdf_addres(check_emb, 3)) {
case address_not_entered: // address_not_entered == 0
Md_p_Buttons();
break;
case 1:
Md_moveButtons();
break;
case 2:
Md_duplicateButtons();
break;
case 3:// back
mdf_cursor(set_pos, mdf_addres(check_emb, 2));
mdf_addres(back_add, 2);
break;
case address_entered: // address_not_entered == 21
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_Buttons(){
static Timer tmr(500, ref_dis);
byte LinesSp[] = {17, 28, 40};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
af_displayPrintLain("Move button", X(6), Y(17));
af_displayPrintLain("Duplicate buttons", X(6), Y(28));
af_displayPrintLain("Back", X(6), Y(40));
if (profil[mps(gapn)].malb(check_location) == non_standard_location) {//якщо кнопки мінялися місцями виводимо повідомлення про це
display.drawRect(77, 15, 45, 11, WHITE);
if (tmr.fa()) {af_displayPrintLain("Altered", X(79), Y(17));}
}
}
// 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(check_emb, 2));
mdf_addres(back_add, 2);
}
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.fa()){// вертикальна риска біля букви
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 ? fs_arrowLowercaseLetters(4, 42) : fs_arrowCapitalLetters(4, 42);
}
else if(getActionSelection() == cl && !tmr.fa()){
ptrLetter == letterSmall ? fs_arrowLowercaseLetters(4, 42) : fs_arrowCapitalLetters(4, 42);
}
if(getActionSelection() != dl){
fs_letterDeletionSymbol(110, 44);
}
else if(getActionSelection() == dl && !tmr.fa()){
fs_letterDeletionSymbol(110, 44);
}
if(getActionSelection() != sb){
display.fillRect(50, 55, 25, 6, WHITE);
}
else if(getActionSelection() == sb && !tmr.fa()){
display.fillRect(50, 55, 25, 6, WHITE);
}
if(getActionSelection() != tb){
fs_arrowReturn(110, 54);
}
else if(getActionSelection() == tb && !tmr.fa()){
fs_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; // Значення за замовчуванням
}
// cNP
//
// add4-----------------------------------------------------------------------------------------------------------
// Moving buttons
void Md_moveButtons(){
Md_p_moveButtons();
switch (mdf_addres(check_emb, 4)) {
case address_not_entered:
break;
case 1:
Md_moveButtonsExecute(select_button) == function_completed ? mdf_addres(back_add, 4) : [](){return 0;}();
break;
case 2:
Md_moveButtonsExecute(return_default) == function_completed ? mdf_addres(back_add, 4) : [](){return 0;}();
break;
case 3: //back
mdf_cursor(set_pos, mdf_addres(check_emb, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
mdf_addres(mode_function_returns);
mdf_addres(get_add, 4) == 1 ? Md_p_moveButtons(next_animation) : [](){return 0;}();
mdf_addres(get_add, 4) == 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.ft(2000);
}
}
if (animation == 1 && tmr.fa()) { // анімація кнопок за допомогою перемінної
accumulationL++;
if (accumulationL == 4) {
accumulationL = 0;
}
}
else if (animation == 2 && tmr.fa()) {
accumulationR++;
runningArrow++;
if (accumulationR == 4 ) {
accumulationR = 0;
}
if (runningArrow == 8 ) {
runningArrow = 0;
}
}
else if (animation == 3 && tmr.fa()) {// відображення другої кнопки завершення роботи з запізнення
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.fa()) {// всі дії виконуються по таймеру
tmr.ft(400);
}
return 0;
}
int Md_moveButtonsExecute(int acceptedCommand){// test
static bsd saesn; // saesn = порядковий номер елемента масиву збереження storage array element sequence number
switch (acceptedCommand) {
case select_button:
if (em.but() == click){//функція може завершитися по кліку один з варіантів
Md_p_moveButtons(delete_data);
saesn.r();
return function_completed;
}
for (int i = 0; i <= 15; i++) {
if (butGamepad[i].gamepadBatton(press_check) == gamepad_button_pressed) {
Md_p_moveButtons(next_animation, butGamepad[i].gamepadBatton(button_get_name));
if (!saesn.g()) {
saesn.s(i+1);
}
else {
int savedButton1 = profil[mps(gapn)].listButton[saesn.g()-1];//зберігаємо елементи масиву в тимчасовій переміні
int savedButton2 = profil[mps(gapn)].listButton[i];
profil[mps(gapn)].listButton[i] = savedButton1; //міняємо елементи масиву (номер-назви) місцями
profil[mps(gapn)].listButton[saesn.r()-1] = savedButton2;
buttonGamepadReinstallAll();//виконуємо перенастройку всіх екземплярів класу buttonGamepad тому що listButton получив зміни
return function_completed;
}
break;
}
}
break;
case return_default:////виконується reset або якщо нема потреби відображення надпису not_need
if (profil[mps(gapn)].malb(check_location) == standard_location) {
if (mdaf_confirm(print_not_need) == function_completed) {
return function_completed;
}
}
else{
if (mdaf_confirm(offer_a_choice) == data_ready) {//пропонуємо вибір якщо вибір зроблено получимо data_ready для подальшої перевірки
if (mdaf_confirm(get_data) == choice_yes) {//Перевіряємо чи вибір так Якщо так тоді скидаємо
profil[mps(gapn)].lblm = by_default;
profil[mps(gapn)].malb(enter_default_location); //повертаємо стандартну номер-назву
buttonGamepadReinstallAll();//перенастройка всіх екземплярів класу тому що значення в listButton[] помінялись
}
return function_completed;
}
}
break;
}
return 0;
}
//
// Duplicate buttons
void Md_duplicateButtons(){//вибираємо Яку кнопку будемо дублювати або яку кнопку потрібно відключити від дублюючої кнопки
byte regime[] = {default_mode, select_button_r_s, select_regime_r_s, r_s_button_reset, select_button_r_m, r_m_button_reset};
Md_p_duplicateButtons(regime[mdf_addres(get_add, 4)]);//змінюємо анімацію в залежності від набраного адресу
switch (mdf_addres(check_emb, 4)) {
case 1:
if (Md_duplicateButtonsExecute(select_button_r_s) == function_completed){mdf_addres(back_add, 4);}
break;
case 2:
Md_duplicateButtonsExecute(select_regime_r_s);
break;
case 3:
if (Md_duplicateButtonsExecute(r_s_button_reset) == function_completed){mdf_addres(back_add, 4);}
break;
case 4:
if (Md_duplicateButtonsExecute(select_button_r_m) == function_completed){mdf_addres(back_add, 4);}
break;
case 5:
if (Md_duplicateButtonsExecute(r_m_button_reset) == function_completed){mdf_addres(back_add, 4);}
break;
case 6: //back
mdf_cursor(set_pos, mdf_addres(back_add, 3));
break;
case address_entered:
Md_p_duplicateButtons();
if (mdf_addres(get_add, 4) == 1 || mdf_addres(get_add, 4) == 4 || mdf_addres(get_add, 4) == 2){
mdf_addres(mode_click_back);
}
else if (mdf_addres(get_add, 4) == 3 || mdf_addres(get_add, 4) == 5){
mdf_addres(mode_function_returns);
}
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("Mode", X(6), Y(14));
if (Gf_gamepadButtonsDuplicators(dbrs_get_mode)){
af_displayPrintLain("standard", X(40), Y(14));
}else{ af_displayPrintLain("double", X(40), Y(14));}
af_displayPrintLain("Reset", X(6), Y(22));
af_displayPrintLain("Rifle magazine", X(10), Y(35), 4, 9);
af_displayPrintLain("Reset", X(6), Y(45));
af_displayPrintLain("Back", X(6), Y(56));
for (int y = 0; y <= 31; y += 31) {//відображаємо прямокутники і Квадрати і символи
display.drawRect(6, y+2, 72, 11, WHITE); // прямокутник
display.drawRect(103, y, 15, 15, WHITE);//квадрат ? - ім'я кнопки
display.setCursor(88, y+4);
display.println("=");
}
byte LinesSp[] = {4, 14, 22, 35, 45, 56};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
if (acceptedCommand == select_button_r_m || acceptedCommand == select_button_r_s) {
if (acceptedCommand == select_button_r_s ) { //вибираємо кнопку яку будемо дублювати
if (tmr.fa()) { //виконуємо мигання за вказаною вище періодичністю
display.setCursor(108, 4);
display.println(buttonNames[0]);//відображаємо знак запитання з таблиці символів
}
}
else if (acceptedCommand == select_button_r_m) {//майже та сама схема що і вище
if (tmr.fa()) {
display.setCursor(108, 35);
display.println(buttonNames[0]);
}
}
if (acceptedCommand == select_button_r_m ){
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[16]], X(108) , Y(4));
}
else if (acceptedCommand == select_button_r_s ){
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[17]], X(108) , Y(35));
}
}
else {
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[16]], X(108) , Y(4));//відображаємо і центруємо кнопку в залежності від кількості букв одна або дві
Md_printButtonNameCenteringX(buttonNames[profil[mps(gapn)].listButton[17]], X(108) , Y(35));
}
}
byte Md_duplicateButtonsExecute(byte acceptedCommand){
if (acceptedCommand == select_button_r_s || acceptedCommand == select_button_r_m) {
for (byte i = 0; i <= 15; 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){//в залежності від вибору вибираємо кнопку яку будемо скидати
profil[mps(gapn)].listButton[16] = 0;
Gf_gamepadButtonsDuplicators(dbrs_reset);
return function_completed;
}
else if (acceptedCommand == r_m_button_reset) {//логіка Та ж сама що і описано зверху
profil[mps(gapn)].listButton[17] = 0;
return function_completed;
}
else if (acceptedCommand == select_regime_r_s) {//логіка Та ж сама що і описано зверху
switch (em.turn(get_turn)) {
case rotated_r:
Gf_gamepadButtonsDuplicators(dbrs_change_mode);
break;
case rotated_l:
Gf_gamepadButtonsDuplicators(dbrs_change_mode);
break;
}
}
return 0;
}
//
//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;
}
//
//
//
// point 2 General settings
// add1 point vialization-----------------------------------------------------------------------------------------------------------
void Md_GeneralSettings(){
switch (mdf_addres(check_emb, 1)) {
case 0:
Md_p_GeneralSettings();
break;
case 1:
Md_Various();
break;
case 2:
Md_Effects();
break;
case address_entered:
if (mdf_addres(get_add, 1) == 4){
mdf_cursor(set_pos, mdf_addres(back_add, 0));
}
else if (mdf_addres(get_add, 1) <= 2){
mdf_cursor(set_pos, 1);
}
break;
}
if (mdf_addres(get_add, 1) == 3){ //набравши пароль попадам в експерт настройки
switch (af_passwordEntry(typing_a_password)) {
case password_not_entered:
Md_p_GeneralSettings();
if (mdf_cursor(get_pos) != 3){
af_passwordEntry(password_reset);
mdf_addres(back_add, 1);
}
break;
case password_entered:
mdf_cursor(set_pos, 1);
break;
default:
Md_expertSettings();
break;
}
}
}
void Md_p_GeneralSettings(){
byte Lines[] = {16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 16);
display.println(" Various");
display.println(" Effects");
display.println(" Expert settings");
display.println(" Back" );
}
//
// add2 point vialization-----------------------------------------------------------------------------------------------------------
//Various
void Md_Various(){
switch (mdf_addres(check_emb, 2)) {
case 1:
break;
case 2:
Md_autoPowerOff(apo_change_time);
break;
case 3:
Md_displayTimeout(dt_change_time);
break;
case address_entered:
mdf_addres(get_add, 2) == 4 ? mdf_cursor(set_pos, mdf_addres(back_add, 1)) : mdf_addres(mode_click_back);
break;
}
Md_p_Various();
}
void Md_p_Various(){
byte Lines[] = {16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 16);
display.println(" Volume");
Md_p_autoPowerOffa();
Md_p_displayTimeout();
display.println(" Back");
}
//
//Effects
void Md_Effects(){
switch (mdf_addres(check_emb, 2)) {
case 1:
Md_triggerLight(trigger_light_change);
break;
case 2:
Md_shotgunBolt(bolt_change);
break;
case 3:
Md_vibration(vt_change);
break;
case address_entered:
mdf_addres(get_add, 2) == 4 ? mdf_cursor(set_pos, mdf_addres(back_add, 1)) : mdf_addres(mode_click_back);
break;
}
Md_p_Effects();
}
void Md_p_Effects(){
byte Lines[] = {16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
Md_p_triggerLight();
Md_p_shotgunBolt();
Md_p_vibration();
display.setCursor(0, 40);
display.println(" Back" );
}
//
//Expert settings
void Md_expertSettings(){
switch (mdf_addres(check_emb, 2)) {
case 1:
Md_giroCalibration();
break;
case 2:
Md_disableButton();
break;
case address_entered:
if (mdf_addres(get_add, 2) == 3) {
af_passwordEntry(password_reset);
mdf_cursor(set_pos, mdf_addres(back_add, 1));
}else{
mdf_addres(mode_click_back);
}
break;
}
if (mdf_addres(get_add, 2) != 2) {
Md_p_expertSettings();
}
}
void Md_p_expertSettings(){
af_displayPrintLain("Gyro calibration", X(6), Y(16));
af_displayPrintLain("Disable button ", X(6), Y(32));
af_displayPrintLain("Back", X(6), Y(40));
byte Lines[] = {16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
//
//
// add3 software realization-----------------------------------------------------------------------------------------------------------
//Various
// Auto power off
byte Md_autoPowerOff(byte acceptedCommand){
static uint32_t tmrRestartMillis = 3 * 60000;
static Timer tmr(tmrRestartMillis);
if (acceptedCommand) {
if (acceptedCommand == reset_f) {
tmr.ft(tmrRestartMillis);
}
else if (acceptedCommand == apo_change_time) {
switch (em.turn(get_turn)) {
case rotated_r:
tmrRestartMillis-=60000;
break;
case rotated_l:
tmrRestartMillis+=60000;
break;
}
tmrRestartMillis = af_clamp(tmrRestartMillis, 60000, 600000);
}
else if (acceptedCommand == apo_get_time) {
return tmrRestartMillis / 60000;
}
}
else if (tmr.fa()){
delay(400);
esp_deep_sleep_start();
}
return 0;
}
void Md_p_autoPowerOffa(){
display.setCursor(0, 24);
display.println(" Auto power off m.");
af_centerNumber(96, 24, Md_autoPowerOff(apo_get_time));
}
//
// Display timeout
byte Md_displayTimeout(byte acceptedCommand){
static Timer tmr;
static byte status = function_not_completed;
static int8_t oti = 1; //oti = off time indicator вказівник на час відключення
const unsigned int shutdownTime[] = {0, 5000, 15000, 30000, 60000, 120000, 300000, 600000};
if (acceptedCommand) {
if (oti && acceptedCommand == reset_f) {
tmr.ft(shutdownTime[oti]);
}
else if (acceptedCommand == dt_change_time) {
switch (em.turn(get_turn)) {
case rotated_r:
oti--;
break;
case rotated_l:
oti++;
break;
}
oti = af_clamp(oti, 0, 7);
if (!oti) {tmr.fr();}//захист від миттєвого згасання якщо перейдемо з off на вказаний час (5s.-10m.)
}
else if (acceptedCommand == dt_get_tim_minutes) {
if (4 <= oti) {
return shutdownTime[oti] / 60000;
}else{
return 0;
}
}
else if (acceptedCommand == dt_get_tim_seconds) {
if (oti <= 3 && oti) {
return shutdownTime[oti] / 1000;
}else{
return 0;
}
}
}
else if (oti){
if (tmr.fm()){//тут завжди буде брехня допоки ми не запустимо таймер tmr.ft(якщо вказати час почнеться перевірка);
displayRefresh(permit_change_status);//забороняємо оновляти дисплей
em.control(work_prohibited);//якщо час вийшов забороняємо модулю нкодера виконувати дії (обертання і кліки)
display.clearDisplay();//гасим дисплей
display.display();
}
else if (em.control(em_get_status) == work_prohibited && em.control(em_check) == em_worked){//тепер якщо encoder зупинено використовуємо його спрацювання для включення дисплею
displayRefresh(permit_change_status);//дозволяємо оновляти дисплей
em.control(work_allowed);
}
}
return 0;
}
void Md_p_displayTimeout(){
display.setCursor(0, 32);
display.println(" Display timeout");
if (Md_displayTimeout(dt_get_tim_minutes)){
af_centerNumber(104, 32, Md_displayTimeout(dt_get_tim_minutes));
display.setCursor(116, 32);
display.println("m.");
}
else if (Md_displayTimeout(dt_get_tim_seconds)){
af_centerNumber(104, 32, Md_displayTimeout(dt_get_tim_seconds));
display.setCursor(116, 32);
display.println("s.");
}
else {
display.setCursor(104, 32);
display.println("off");
}
}
//
//
// Effects
void Md_triggerReadEffects(){
static byte colorFlag = 1;
if (colorFlag && tre_analog_dead_zone < analogRead(p_triger_r)){
colorFlag = 0;
Md_triggerLight(trigger_color_red);
Md_shotgunBolt(bolt_start);
Md_vibration(vibration_start);
}
else if (!colorFlag && analogRead(p_triger_r) < tre_analog_dead_zone){
colorFlag = 1;
Md_triggerLight(trigger_color_dreen);
Md_shotgunBolt(bolt_stop);
Md_vibration(vibration_stop);
}
}
//Trigger light
byte Md_triggerLight(byte acceptedCommand){
static int ledBrightness = 100;
if (acceptedCommand) {
if (acceptedCommand == trigger_color_red && ledBrightness) {
analogWrite(p_led_trigger_dreen, 0);
analogWrite(p_led_trigger_red, ledBrightness);
}
else if (acceptedCommand == trigger_color_dreen) {
analogWrite(p_led_trigger_red, 0);
analogWrite(p_led_trigger_dreen, ledBrightness);
}
else if (acceptedCommand == trigger_light_change) {
switch (em.turn(get_turn)){
case rotated_r:
ledBrightness--;
break;
case rotated_l:
ledBrightness++;
break;
case rotated_r_fast:
ledBrightness-=10;
break;
case rotated_l_fast:
ledBrightness+=10;
break;
}
ledBrightness = af_clamp(ledBrightness, 0, 100);
if (!digitalRead(p_led_trigger_dreen)) {
analogWrite(p_led_trigger_red, ledBrightness);
}
else if (!digitalRead(p_led_trigger_red)) {
analogWrite(p_led_trigger_dreen, ledBrightness);
}
}
else if (acceptedCommand == trigger_get_light) {
return ledBrightness;
}
else if (acceptedCommand == trigger_color_start) {
pinMode(p_led_trigger_dreen, OUTPUT);
pinMode(p_led_trigger_red, OUTPUT);
analogWrite(p_led_trigger_dreen, ledBrightness);
}
}
return 0;
}
void Md_p_triggerLight(){
if (Md_triggerLight(trigger_get_light)){
af_displayPrintLain(" Trigger light %", 0, 16);
af_centerNumber(96, 16, Md_triggerLight(trigger_get_light));
}else{
display.setCursor(0, 16);
af_displayPrintLain(" Trigger light off", 0, 16);
}
}
//
//Shotgun bolt
byte Md_shotgunBolt(byte acceptedCommand){
static byte boltPermission = 1;
if (acceptedCommand){
if (acceptedCommand == bolt_start && boltPermission){
digitalWrite(p_sb_trigger, HIGH);
}
else if (acceptedCommand == bolt_stop){
digitalWrite(p_sb_trigger, LOW);
}
else if (acceptedCommand == bolt_change){
switch (em.turn(get_turn)){
case rotated_r:
boltPermission--;
break;
case rotated_l:
boltPermission++;
break;
}
boltPermission = af_clamp(boltPermission, 0, 1);
if (!boltPermission){digitalWrite(p_sb_trigger, LOW);}
}
else if (acceptedCommand == bolt_get_permission){
return boltPermission;
}
else if (acceptedCommand == bolt_pin_activate){
pinMode(p_sb_trigger, OUTPUT);
digitalWrite(p_sb_trigger, LOW);
}
}
return 0;
}
void Md_p_shotgunBolt(){
if (Md_shotgunBolt(bolt_get_permission)){
af_displayPrintLain(" Shotgun bolt on", 0, 24);
}else{
af_displayPrintLain(" Shotgun bolt off", 0, 24);
}
}
//
//Vibration
byte Md_vibration(byte acceptedCommand){
static byte vibrationPermission = 1;
if (acceptedCommand){
if (acceptedCommand == vibration_start && vibrationPermission){
digitalWrite(p_vt, HIGH);
}
else if (acceptedCommand == vibration_stop){
digitalWrite(p_vt, LOW);
}
else if (acceptedCommand == vt_change){
switch (em.turn(get_turn)){
case rotated_r:
vibrationPermission--;
break;
case rotated_l:
vibrationPermission++;
break;
}
vibrationPermission = af_clamp(vibrationPermission, 0, 1);
if (!vibrationPermission){digitalWrite(p_vt, LOW);}
}
else if (acceptedCommand == vt_get_permission){
return vibrationPermission;
}
else if (acceptedCommand == vt_pin_activate){
pinMode(p_vt, OUTPUT);
digitalWrite(p_vt, LOW);
}
}
return 0;
}
void Md_p_vibration(){
if (Md_vibration(vt_get_permission)){
af_displayPrintLain(" Vibration on", 0, 32);
}else{
af_displayPrintLain(" Vibration off", 0, 32);
}
}
//
//
//Expert settings
void Md_giroCalibration(){
displayRefresh(ref_dis);
Md_p_giroCalibration(mdf_addres(get_add, 3));
switch (mdf_addres(check_emb, 3)) {
case 1:
Md_giroCalibrationExecution();
break;
case 2:
mdf_cursor(set_pos, mdf_addres(back_add, 2));
break;
case address_entered:
if (mdf_addres(get_add, 3) == 1) {
mdf_addres(mode_click_back);
}
break;
}
}
void Md_p_giroCalibration(byte acceptedCommand){
display.drawRect(0, 0, 128, 33, WHITE);
switch (acceptedCommand) {
case 0:
display.setCursor(20, 13);
display.write(27); // Код символу "←"
af_centerNumber(38, 13, -10);
display.setCursor(56, 13);
display.write(26); // Код символу "→"
display.setCursor(91, 3);
display.write(24); // Код символу "↑"
af_centerNumber(91, 13, -22, true);
display.setCursor(91, 22);
display.write(25); // Код символу "↓"
break;
case 1:
display.setCursor(32, 12); // Текст по центру (приблизно)
display.print("Calibration");
break;
}
af_displayPrintLain("Calibration start", X(6), Y(36));
af_displayPrintLain("Back", X(6), Y(46));
byte Lines[] = {36, 46};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
void Md_giroCalibrationExecution() {
/*
// Скидання сум перед збором нових даних
float sumX = 0, sumY = 0, sumZ = 0;
int sampleCount = 100;
// Збір даних з гіроскопа
for (int i = 0; i < sampleCount; i++) {
// Читання кутової швидкості з гіроскопа (в градусах за секунду або радіанах)
Vector rawGyro = mpu.readGyro(); // Читання гіроскопа
sumX += rawGyro.XAxis; // Додавання до суми по осі X
sumY += rawGyro.YAxis; // Додавання до суми по осі Y
sumZ += rawGyro.ZAxis; // Додавання до суми по осі Z
delay(2); // Затримка між зчитуваннями
}
// Обчислення середнього значення для кожної осі
float avgX = sumX / sampleCount;
float avgY = sumY / sampleCount;
float avgZ = sumZ / sampleCount;
// Внесення корекційних значень у гіроскоп
mpu.setGyroOffsetX(avgX); // Встановлення корекції для осі X
mpu.setGyroOffsetY(avgY); // Встановлення корекції для осі Y
mpu.setGyroOffsetZ(avgZ); // Встановлення корекції для осі Z
*/
mdf_addres(back_add, 3);
}
void Md_disableButton(){
Md_p_disableButton();
switch (mdf_addres(check_emb, 3)) {
case 1:
if (mdaf_confirm(offer_a_choice) == data_ready) {// після вибору робимо дію відключення кнопок або нічого
if ( mdaf_confirm(get_data) == choice_yes) {
int maximum = 0;
for (int i = 0; i <= 13; i++) {
if (maximum >= 5 && butGamepad[i].gamepadBatton(press_check) == gamepad_button_pressed) {
maximum++;
bgpr(set_press_permit, i, false);
}
}
}
mdf_addres(back_add, 2);
}
break;
case 2:
bgpr(set_reset_permit);//скидаємо всі відключені кнопки
mdf_addres(back_add, 3);
break;
case address_entered:
if (mdf_addres(get_add, 3) == 1) {//перед виключенням Перевіряємо чи є зажаті кнопки
mdf_addres(mode_function_returns);
mdaf_confirm(output_by_coordinates, X(60), Y(30));
bool conditionMet = false;
for (int i = 0; i <= 13; i++) {
if (butGamepad[i].gamepadBatton(press_check) == gamepad_button_pressed) {//очікуємо натиску кнопки яку будемо дублювати
conditionMet = true; // Встановлюємо прапорець, якщо умова виконана
break;
}
}
if (!conditionMet) {mdf_addres(back_add, 2);}
}
else if (mdf_addres(get_add, 3) == 3) {mdf_cursor(set_pos, mdf_addres(back_add, 2));}
break;
}
}
void Md_p_disableButton(){
display.drawRect(0, 0, 127, 19, WHITE);
af_centerNumber(95, 4, bgpr(get_disconnected_number));
af_displayPrintLain("Deactivated( )", X(20), Y(4));
af_displayPrintLain("Deactivate faulty", X(6), Y(22));
af_displayPrintLain("Reset", X(6), Y(32));
af_displayPrintLain("Back", X(6), Y(42));
byte Lines[] = {22, 32, 42};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
//
//
// add4-----------------------------------------------------------------------------------------------------------
//
// 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) {//N
static byte add[10];//для коректної роботи можна використати тільки вісім адресів 1-8
static byte inputMode = mode_click_input;
switch (acceptedCommand) {
case back_add:
{
displayRefresh(ref_dis);
mdf_cursor(cursor_type_one);
inputMode = mode_click_input;
byte temp = add[addNumb];
for (int i=9; i >= addNumb; i--){// команда back виконує видалення всіх адресів до числа вказаного в addNumb
add[i] = address_not_entered;
}
return temp;
}
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:
return add[addNumb];
break;
case check_emb:
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){//якщо попадемо на нуль повертаємося
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 bsd selection(1);//вибір так або ні
static bsd 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[] = {600, 300, 100};//test
static byte frequency;//frequency ЧАСТОТА
static byte flashFlag;
static Timer tmr(false, 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.fr();
break;
case accelerate_frame_blinking://при накопиченні кліків збільшується частота блимання рамки навколо вибору
frequency++;
break;
case reset://якщо накопичили необхідну кількість ліків відбудеться скидання до початкового стану
selection = frequency = 0;
tmr.fr();
break;
case print_not_need://якщо настройки не мінялись виводимо повідомлення що не потрібне скидання
if (tmr.fm()) {
tmr.ft(500);
}
else if (tmr.fmg()) {
display.setCursor(azixX+4,azixY+3);
display.print("not need");
display.drawRect(azixX, azixY, 56, 15, WHITE);// велика рамка навколо так ні
}
else{
tmr.fr();
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.fm()) {
tmr.ft(frameBlink[frequency]);
}
if (tmr.fmg()) {//рамка світилась одразу після запуску або змінні вибору
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.fa()) {
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.fa()) {
tmr.ft(60000);
for (int i = 0; i <= 9 ; i++) {
if (loadVoltage_V >= batLevels[i]) {
if (chargeLevel != map(i, 0, 9, 0, 11)) {
//displayRefresh(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 output
byte displayOutput(byte AcceptedCommand) {
static byte Regime = info_display;
display.clearDisplay();//test
//displayRefresh(clear_display);
if (AcceptedCommand) {
Regime = AcceptedCommand;
}
switch (Regime) {
case info_display:
if (em.but() == click) {
displayOutput(menu);
}
/*
else if (em.but() == hold_b_e) {// по плану має виключати дисплей при утриманні ?
display.clearDisplay();
display.display();
}
*/
else{
infoDisplay();
}
break;
case menu:
Md_MainDisplay();
break;
}
display.display();
//displayRefresh(print_display);
return 0;
}
void displayRefresh(byte acceptedCommand){//функція оновлює дисплей тоді коли є дані з енкодера або екземпляр класу Timer є з параметром ref_dis
static byte stageExecution;
static byte permit = 1;
if(stageExecution && permit) {//очищення і вивід зображення якщо є дозвіл
if(acceptedCommand == clear_display && stageExecution == clear_display) {
stageExecution = print_display;
display.clearDisplay();
}
else if(acceptedCommand == print_display && stageExecution == print_display) {
stageExecution = no_data;
display.display();
}
}
if(acceptedCommand){
if(acceptedCommand == display_refresh && permit){//даємо дозвіл на оновлення дисплею всередині функцій! які можуть змінювати картинку
stageExecution = clear_display;
}
else if(acceptedCommand == permit_change_status){
stageExecution = no_data;
permit = !permit;
}
}
}
// Do
// 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)) {
Md_autoPowerOff(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 <= 15; i++) {//Що ти всі кнопки разом з дублікатами
butGamepad[i].gamepadBatton(read_button_state); //виконується натискання кнопки за допомогою необхідного методу вказаному при створенні через конструктор
}
}
byte Gf_gamepadButtonsDuplicators(byte acceptedCommand){
if (profil[mps(gapn)].rrsps){
butGamepad[16].gamepadBatton(read_button_state);
}else{
butGamepad[16].gamepadBatton(r_b_double_press);
}
butGamepad[17].gamepadBatton(read_button_state);
if (acceptedCommand){
if (acceptedCommand == dbrs_change_mode){
profil[mps(gapn)].rrsps = !profil[mps(gapn)].rrsps;//режим роботи зберігається в профілі
}
else if (acceptedCommand == dbrs_get_mode){
return profil[mps(gapn)].rrsps;
}
else if (acceptedCommand == dbrs_reset){
profil[mps(gapn)].rrsps = 1;
}
}
return 0;
}
// Gf
void setup() {
Serial.begin(9600);// test В ФІНАЛІ НЕЗАБУТИ ЗАКОМЕНТУВАТИ!!!
Serial.println(" Serial.println("");");
//запускаємо час відключення
Md_displayTimeout(reset_f);
//
//запускаємо ефекти якщо є дозвіл
Md_triggerLight(trigger_color_start);
Md_shotgunBolt(bolt_pin_activate);
Md_vibration(vt_pin_activate);
//
//виконуємо настройки геймпада
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);// ініціалізація з енергонезалежної пам'яті профілів
// Налаштування типу енкодера та таймауту на швидкі повороту
enc.setType(TYPE2); // 2шаговий енкодер (TYPE2)
enc.setFastTimeout(20); // Таймаут для швидкого повороту (в мс)
//
//обробка даних енкодера за допомогою приривання
attachInterrupt(digitalPinToInterrupt(p_clk), af_isr, CHANGE); //attachInterrupt(пін, функція для виклику, зміна з 0-1 або 1-0);
attachInterrupt(digitalPinToInterrupt(p_dt), af_isr, CHANGE);
//
// ----------Display--------------
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextSize(1);
display.setTextColor(WHITE);
display.clearDisplay();
display.setCursor(46, 28); // Числові координати по центру
display.print("FS-G36");
delay(1000);
display.display();
// запуск гороскопа
//mpu.initialize();
//
// af_startAnimation();// анімація при включенні. до робити в кінці!
}
void loop() {
//Md_triggerReadEffects();
//Md_displayTimeout();
// Md_autoPowerOffa();
// af_buttonOnOffa();
// af_batteryCheck();
//
display.clearDisplay();
//Md_disableButton();
//displayOutput();
//Md_duplicateButtons();
Md_giroCalibration();
//Md_p_autoPowerOffa();
//Md_p_gyroscopeSettings();
display.display();
if (bleGamepad.isConnected()) {
//Gf_GyroAnalogR();
Gf_gamepadButtonsDuplicators();
Gf_gamepadButtons();
bleGamepad.sendReport();
}
}// void loop()