#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "GyverEncoder.h"
//#include <BleGamepad.h> // при загрузці нa реально Arduino даний фрагмент розкоментувати a class BleGamepad закоментувати
//#include "esp32-hal-timer.h"///потрібно для зчитування даних гіроскопа по таймеру
//#include <EEPROM.h>
// #include "INA219_WE.h"
// INA219_WE ina219(0x40);
// Additional elements
// constants-variable-arrays
bool Vibrgyro;
// СПІЛЬНІ КОНСТАНТИ ПІД ХОД ДЛЯ БАГ ФУНК
#define X(x) (x)
#define Y(y) (y)
#define zero_void -1
#define reset 0
#define no_action 0 // НЕМА ДІЇ
#define no_data 0 // немає даних
#define no_indication 0 // немає вказівки
#define off 0
#define no 0
#define default_mode 0
#define function_interrupt_finish 0
#define by_default 0
#define data_ready 1
#define 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 click 13
#define gyro 14
//#define profil 15
#define data_available 16// дані доступні
#define delete_data 17
#define start 18
//
//?
#define melody_start 1
#define melody_off_power 2
#define x_axis_hor 1
#define y_axis_ver 2
#define z_axis_ver 3
#define get_y 2
#define get_z 3
#define get_vibr 4
//
// Піни карта
// #define b_mein 25
#define b_emul_dpad 1
#define b_handle 4
#define b_triger_r 23
#define b_analog_l 33
#define b_on_off 14
#define b_emul_dpad 26
#define b_sw 25 // кнопка енко // RotaryEncoder encoder(27, 14, RotaryEncoder::LatchMode::TWO03);
#define p_dt 32
#define p_clk 33
//
// mdf_encoderButton();
// #define click 13
#define press_b_e 20
#define hold_b_e 21
#define reset_status 23
#define take_but_pos 24
#define button_pressed 25
//
// Md_Display() {
#define info_display 20
#define menu 21
#define confirm_selection 22
#define lang_eng 25
#define lang_ukr 26
#define click_in_dis_on 27
//
// Md_MainDisplayENG();
#define menu_off 21
#define menu_start 22
//
// byte mdf_addres()
//#define click 13 ???
#define back 20
#define get_add 23
#define check_button_enc 24
#define mode_click_input 25
#define mode_click_back 26
#define back_add 27
#define mode_function_returns 28
#define address_not_entered 0
#define address_entered -1
#define address_deleted -2
//
// int mdf_cursor() // mdf_p_cursor()
#define get_pos 20
#define set_pos 21
#define cursor_read_encoder 24
#define cursor_type_one 0
#define cursor_type_two 1
//
// mdf_encoder() b
#define check_rotation 20
#define get_rotation 21
#define rotated_r 22
#define rotated_l 23
#define rotated_r_fast 24
#define rotated_l_fast 25
#define encoder_rotates 26 // дані доступні
#define reset_rotation 27
volatile int8_t g_encFast;
//
// mdaf_confirm() // mdaf_p_confirm() // mdf_p_outputNotNeed()
//#define X(x) (x)
//#define Y(y) (y)
//#define reset 0
//#define no_data 0
//#define get_data 1
//#define function_completed 1
#define choice_no 20
#define choice_yes 21
#define accelerate_frame_blinking 22
#define print_not_need 23
#define offer_a_choice 24
#define output_by_coordinates 25
#define change_selection 26
//
// Mf_Buttons()
// #define reset_f 11
#define next_animation 20
//
// af_outputDisplay()
//#define no_data 0 // немає даних
#define clear_display 20
#define print_display 21
#define refresh 22
//
// af_autoShutdown()
// #define reset_f 11
#define change_time 20
#define get_time 21
//
// Md_movingButtons()
#define choice_bat_waiting 20
#define button_selection_complete 21
#define return_default 22
#define select_button 23
//
// Md_duplicateButtons()
// #define default_mode 0
#define select_button_r_s 20
#define select_button_r_m 21
#define duplicate_button_reset 22
#define show_duplicate_buttons 23
#define r_s_button_reset 24
#define r_m_button_reset 25
//
// Button gamepad class
#define touch_dead_zone 50
#define analog_dead_zone 2600
// ButGamepad(byte gBT, byte gBN, byte pRT, byte n)
#define button_gamepad 20
#define trigger_gamepad 21
#define tactile_read 22
#define touch_read 23
#define analog_read 24
// byte (*gamepadPressPtr)();
#define read_button_state 20
#define press_check 21
#define gamepad_button_pressed 22
#define gamepad_button_release 23
#define button_get_name 24
//byte buttonGamepadPressRelease();
#define press_button 20
#define release_button 21
#define dpad_check 22
#define dpad_center 23
//
// Profiles class
#define t_sp 1// не міняти число!
#define t_mp 2// не міняти число!
#define r_off 0 // = off
#define r_on 12 // = on
//nameProfiles(byte acceptedCommand, char Letter = ' ')
#define get_name 20
#define get_name_a_f 21
#define change_letter 22
#define delete_letter 23
// *mps()
#define gapn 22 //get active profile number-name
#define activate_p_sp 23
#define activate_p_mp 24
#define eeprom_initialization 25
#define activate_p 26
#define copy_profile 27
#define set_push_permit 28
#define get_push_permit 29
#define not_save 30
#define save_changes 31
#define active_copy_compare 32
#define active_copy_same 33 // зафіксовані зміни
//listButton[18]//даний масив має в собі номер-назву кнопки потрібно для порядкового опитування останні два елемента використовуються для дублювання кнопок
#define button_1 1
#define button_2 2
#define button_3 3
#define button_4 4
#define button_5 5
#define button_6 6
#define button_7 7
#define button_8 8
#define button_select 9
#define button_start 10
#define dpad_up 11
#define dpad_down 12
#define dpad_left 13
#define dpad_right 14
#define trigger_r 15
#define trigger_l 16
uint32_t tmrProf = 0;
//
// c-v-a
// Function prototypes
void af_displayPrintLain(String text = "", byte x = 0, byte y = 0, byte cursorPos = 0, byte textSize = 20);
byte mdaf_confirm(byte acceptedCommand = 0, byte x = 0, byte y = 0);
byte mdaf_p_confirm(byte acceptedCommand = 0, byte x = 0, byte y = 0);
void Md_p_duplicateButtons(byte acceptedCommand = 0);
int mdf_addres(byte acceptedCommand = 0, byte addNumb = 0, byte additionalCommand = 0);
byte Md_p_moveButtons(byte receivedData = 0, byte selectedButton = 0);
byte mdf_encoderButton(byte acceptedCommand = 0);
byte Md_Display(byte Receiving = 0);
byte af_outputDisplay(byte acceptedCommand = 0);
byte af_autoShutdown(byte acceptedCommand = 0, byte acceptedData = 0);
byte buttonGamepadPressRelease(byte command = 0, byte buttonName = 0);
byte mdf_cursor(byte acceptedCommand = 0, byte data = 0, byte* arr = nullptr);
// Fp
// Ae
// (пуcті класи) Пустишки для того щоб не нагружати час компіляції
#define HAT_CENTERED 0
#define HAT_UP 1
#define HAT_UP_RIGHT 2
#define HAT_RIGHT 3
#define HAT_DOWN_RIGHT 4
#define HAT_DOWN 5
#define HAT_DOWN_LEFT 6
#define HAT_LEFT 7
#define HAT_UP_LEFT 8
class BleGamepadConfiguration {
public:
void setAutoReport(int value) {
// Нічого не робить
}
void setButtonCount(int count) {
// Нічого не робить
}
void setIncludeStart(bool include) {
// Нічого не робить
}
void setIncludeSelect(bool include) {
// Нічого не робить
}
void setHatSwitchCount(int count) {
// Нічого не робить
}
void setAxesmin(int minNam) {
// Нічого не робить
}
void setAxesmax(int maxNam) {
// Нічого не робить
}
void setWhichAxes(bool x, bool y, bool z, bool rx, bool ry, bool rz, bool slider1, bool slider2) {
// Нічого не робить
}
};
class BleGamepad {
public:
BleGamepad(const char* deviceName, const char* deviceManufacturer, uint8_t batteryLevel) {
// Нічого не робить
}
void press(uint8_t button) {
// Нічого не робить
}
void release(uint8_t button) {
// Нічого не робить
}
void setLeftTrigger(uint16_t value) {
// Нічого не робить
}
void setRightTrigger(uint16_t value) {
// Нічого не робить
}
bool isConnected() {
// Повертає false для симуляції відсутності з'єднання
return false;
}
void setZ(uint16_t value) {
// Нічого не робить
}
void setRZ(uint16_t value) {
// Нічого не робить
}
void sendReport() {
// Нічого не робить
}
void setHat1(uint8_t value) {
// Нічого не робить
}
void setLeftThumb(int16_t x, int16_t y) {
// Нічого не робить
}
void setRightThumb(int16_t x, int16_t y) {
// Нічого не робить
}
void pressSelect() {
// Нічого не робить
}
void releaseSelect() {
// Нічого не робить
}
void pressStart() {
// Нічого не робить
}
void releaseStart() {
// Нічого не робить
}
void begin(BleGamepadConfiguration* config) {
// Нічого не робить
}
void end() {
// Нічого не робить
}
// Додаткові методи-пустишки
void setButtonCount(uint8_t count) {
// Нічого не робить
}
void setHatSwitchCount(uint8_t count) {
// Нічого не робить
}
void setAxes(bool leftX, bool leftY, bool rightX, bool rightY, bool leftTrigger, bool rightTrigger, bool zAxis, bool rzAxis) {
// Нічого не робить
}
void setLeftThumbDeadzone(uint16_t deadzone) {
// Нічого не робить
}
void setRightThumbDeadzone(uint16_t deadzone) {
// Нічого не робить
}
void setRumble(uint16_t leftMotor, uint16_t rightMotor) {
// Нічого не робить
}
};
//
Adafruit_SSD1306 display(128, 64, &Wire, -1);
BleGamepadConfiguration bleGamepadConfig;//потрібно для настройок геймпада
BleGamepad bleGamepad("FS G36", "UA", 100);
Encoder enc(p_clk, p_dt);
// Byte size control(bsc)(використовується як унікальна зміна яка після досягнення потрібного нам значення повертає його і скидається на нуль)
class bsc {
private:
const byte maxValue; // Максимальне значення
byte val; // Накопичене значення
public:
// Конструктор
bsc(byte mv = 0) {
maxValue = mv;
}
// Метод p() для додавання значення
void p(byte addValue) {
val += addValue;
val = af_clamp(val, 0, maxValue);// Скидання на нуль, якщо перевищено
}
// Метод m() для перевірки досягнення максимального значення
bool m() {
if (val >= maxValue) {
val = 0;
return true; // Повертаємо true, якщо досягнуто максимального значення
}
return false; // Максимальне значення не досягнуто
}
// Метод g() для повернення накопиченого значення
byte g() {
return val;
}
byte s(byte recordDatae) {
return val = recordDatae;
}
byte r() {//перед скиданням одноразового відправляємо получене число
byte temp = val; // Зберігаємо значення
val = 0; // Скидання на нуль
return temp; // Повертаємо збережене значення
}
};
// bsc
// Timer class (конструкція працює приблизно 49 днів після чого потрібен перезапуск мікроконтролера)
class Timer {
private:
uint32_t tmr;
int autoRestartTimer;
bool flag;
byte displayRefreh;
public:
Timer(int aRT = 0, byte dR = 0){//конструктор класу
autoRestartTimer = aRT; //використовується для f() щоб був автоперезапуску якщо t != 0
displayRefreh = dR;//якщо є команда ref_dis то буде виконуватись оновлення при кожному виклику методів в яких є af_ outputDisplay(displayRefreh);
}
bool e() {//таймер End по закінченню часу видасть тру
if (tmr < millis()) {
f();//вручну міняємо стан флажка щоб в коді получити правда або брехня після проходження часу
return true;
}
return false;
}
void b(int acceptedTime) {//почати відлік часу таймер Begin
af_outputDisplay(displayRefreh);
tmr = millis() + acceptedTime;
}
bool f() {// метод флажок періодично повертає правду брехню
if (tmr < millis()) {
af_outputDisplay(displayRefreh);
flag = !flag;
tmr = millis() + autoRestartTimer;
}
return flag;
}
void r() {// reset скидаємо таймер на нуль щоб можна було получити тру v методі e() при першому виклику після скидання
flag = tmr = 0;
}
};
// Tc
// Profiles class
class Profiles {
public:
Profiles(int deZoH, byte senHor, int deZoV, byte senVer, char* nameProf, byte tpProf, byte nabProf)
:deadZoneHor(deZoH),
sensitivityHor(senHor),
deadZoneVer(deZoV),
sensitivityVer(senVer),
tape(tpProf),
namber(nabProf),
status(r_off)
{
listButtonsStandard();//даємо стандартні імена-номера кнопкам
strncpy(nameArray, nameProf, 20); // даємо назву профілю через конструктор
nameArray[20] = '\0';
letterIndicator = [this]() -> byte {// переміна зберігає собі кількість букв в назві потрібно для зміни імен
for (int i = 19; i >= 0; i--) {
if (this->nameArray[i] != ' ') {
return i + 1;
}
}
return 20;
}();
}
//гіроскоп настройка
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;
}
//
//робота з іменами профілів
const char* nameProfiles(byte acceptedCommand, char Letter = ' '){// функція працює з іменем профілю та буквами також виконує мигання якщо стан профілю активний
static Timer tmr;
char baldness[] = "";// baldness беззмістовність return baldness; аналог return 0;
if (status == r_on && tmr.e()){// якщо стан профілю активний буде виконуватися мигання
tmr.b(600);
Flag = !Flag;
}
else if (status == r_off && !Flag){// захист можливий розрив функції переміна флаг може бути фолс
Flag = 1;
}
switch (acceptedCommand) {
case get_name: // беремо ім'я
return nameArray;
break;
case get_name_a_f:// при даній команді ім'я активного профілю буде мигати
if (Flag) {
return nameArray;
}
else {
return baldness;
}
break;
case change_letter:// міняємо букву
if (letterIndicator != 20){
nameArray[letterIndicator] = Letter;
letterIndicator++;
}
break;
case delete_letter:// видаляємо букву
if (letterIndicator){
letterIndicator--;
nameArray[letterIndicator] = ' ';
}
break;
return baldness;
}
return baldness;
}
byte centroName() {// центрує набране ім'я в рамці інфо дисплей
for (int i = 19; i >= 0; i--) {
if (nameArray[i] != ' '){
return 61-i*3;
break;
}
}
return 0;
}
void checkName() {// якщо немає імені то вставляє е ім'я залежно від номеру профіля
bool allSpaces = true; // Припускаємо, що всі символи - пробіли
for(int i = 0; i < 20; i++) { // Останній символ не перевіряємо
if(nameArray[i] != ' ') {
allSpaces = false;
break;
}
}
if(allSpaces) {
snprintf(nameArray, 21, "My profile %d", namber); // Форматуємо нову назву
letterIndicator = 12;//test вгадав положення індикатора костилі
}
}
//
void listButtonsStandard() {
for (byte i = 0; i < 16; i++) {//стандартний порядок кнопок два останніх елемента використовуються для дублювання
listButton[i] = i + 1; //вказуємо кнопкам номер-ім'я
}
}
byte setStatus(byte st) {
status = st;
return 0;
}
bool operator==(const Profiles& other) const {//використовується для порівняння профілів
return (deadZoneHor == other.deadZoneHor) &&
(sensitivityHor == other.sensitivityHor) &&
(deadZoneVer == other.deadZoneVer) &&
(sensitivityVer == other.sensitivityVer) &&
(tape == other.tape) &&
(namber == other.namber) &&
(status == other.status) &&
(memcmp(listButton, other.listButton, sizeof(listButton)) == 0) &&
(memcmp(nameArray, other.nameArray, sizeof(nameArray)) == 0);
}
// profil[mps(gapn)].
char nameArray[21];
byte listButton[18];
byte Flag;
int deadZoneHor;// (primary початкова) початок руху
byte sensitivityHor;
int deadZoneVer;
byte sensitivityVer;
byte tape;
byte namber;
byte status;
byte letterIndicator;
byte lblm;//list button located monitoring
private:
};
// Profile designers конструктори класу Profiles
Profiles profil[] = {// test
Profiles (0, 0, 0, 0, "0", 0, 0),// використовується для перевірки чи були зміни
Profiles (2000, 100, 2556, 92, "CodMW", t_sp, 1),
Profiles (166, 120, 1965, 112, "Batl3", t_sp, 2),
Profiles (2000, 100, 2556, 92, "SP 33", t_sp, 3),
Profiles (166, 120, 1965, 112, "SP 44", t_sp, 4),
Profiles (2000, 100, 2556, 92, "SP 55", t_sp, 5),
Profiles (166, 120, 1965, 112, "SP 66", t_sp, 6),
Profiles (2000, 100, 2556, 92, "SP 77", t_sp, 7),
Profiles (2000, 100, 2556, 92, "05461 ", t_mp, 1),
Profiles (166, 120, 1965, 112, "Xmen 74 ", t_mp, 2),
Profiles (2000, 100, 2556, 92, "Mp 3 ", t_mp, 3),
Profiles (166, 120, 1965, 112, "Mp 4 ", t_mp, 4),
Profiles (2000, 100, 2556, 92, "Mp 5 ", t_mp, 5),
Profiles (166, 120, 1965, 112, "Mp 6 ", t_mp, 6),
Profiles (2000, 100, 2556, 92, "Mp 7 ", t_mp, 7)
};
// Pd
byte mps(byte acceptedCommand, byte number = 0 , byte acceptedValue = 0) {// mps= management profile setting
static byte active = 1;
static byte returnResult;
static bool pushPermit[18]; //загальний масив дозволів на натиск кнопки для всіх профілів
if (acceptedCommand == gapn) { // Скеровування до активного профілю
return active;
}
else if (acceptedCommand == activate_p) {// вибраний профіль активується
for (int i = 1; i < 15; i++) {
profil[i].setStatus(r_off);
}
active = number;
profil[active].setStatus(r_on);
}
else if (acceptedCommand == copy_profile) { //виконується одноразово при наборі адресу при заході в настройки див. адрес 2
profil[0] = profil[active];
}
else if (acceptedCommand == active_copy_compare) {
return (profil[0] == profil[active]) ? active_copy_same : 0;
}
else if (acceptedCommand == not_save) {
profil[active] = profil[0];
}
else if (acceptedCommand == get_push_permit) {//значення всередині елементу несе в собі дозвіл на натиск кнопки
// Повертаємо значення елемента масиву pushPermit дізнаємось по значенню чи є дозвіл на натиск кнопки
return !pushPermit[number];
}
else if (acceptedCommand == set_push_permit) {
pushPermit[number] = acceptedValue; // Можете змінити значення
}
return 0;
}
// P
// Button debounce class
class DebButton {
private:
byte pin;
Timer tmr;
bool buttonState;
public:
// Конструктор класу, ініціалізація піна, номера кнопки геймпада та часу захисту від випадкових спрацьовувань
DebButton(int p, int dt = 300) {
pin = p;
tmr = dt;
pinMode(pin, INPUT_PULLUP);
}
// Метод для обробки натискання кнопки
bool press() {
if (!digitalRead(pin) && !buttonState && tmr.e()) {// після натську кнопки буде повернення true
buttonState = true; // тепер переміна буде правда
tmr.b(300);// даємо час кнопці для заспокоєння від брязкaiту при натиску
}
else if (buttonState && digitalRead(pin) && tmr.e()) {// пісня відпущення кнопки буде повернення брехня
buttonState = false;// тепер переміна буде брехня
tmr.b(300);// даємо час кнопці для заспокоєння від брязкaiту при відпуску
}
return buttonState;// постійно повертаємо стан кнопки
}
};
DebButton DebButtonEncoder(b_sw);// b_sw25
/*
DebButton debButton[] = {
DebButton(19),
DebButton(18),
DebButton(5)
};
*/
//
// Button gamepad class
class ButGamepad {
private:
byte gamepadButtonName;
byte pinReadType;
byte pushPermit;
byte numb;
byte flagPress;
byte buttonCheckFlag;
DebButton* debButton = nullptr;
//вказівник вказує на одну з вибраних функцій які ми вибираємо конструкторi
typedef byte (ButGamepad::*GamepadPressMethod)(byte);
GamepadPressMethod gamepadPressPtr;
//функції використовуються всередині класу обробка натиску кнопки
bool tactilePinButtonRead() {
return debButton && debButton->press(); // Перевірка та виклик методу
}
bool touchPinButtonRead() {
return touchRead(numb) < touch_dead_zone;
}
bool analogPinButtonRead() {
return analogRead(numb) > 2600;
}
//
public:
ButGamepad(byte gBN, byte pP, byte pRT, byte n){ //конструктор проводимо настройку при першому запуску
gamepadButtonName = gBN;
pinReadType = pRT;
pushPermit = pP;
numb = n;
//створюється новий debounce екземпляр якщо будемо натиснути звичайну кнопку
if (pinReadType == tactile_read) {
debButton = new DebButton(numb);
}
if (gamepadButtonName == trigger_r || gamepadButtonName == trigger_l) {// вибір яким способом буде натиснути курок(R L)геймпада. курок(R L) == tactile touch analog
if (gamepadButtonName == trigger_r) {
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerRightPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerRightPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerRightPress;
}
}
else if (gamepadButtonName == trigger_l) {
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerLeftPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerLeftPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerLeftPress;
}
}
}
else {// вибір яким способом методом буде натиснути кнопки геймпада 1-8. depad. Select Start. варіанти tactile touch analog
if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactilePress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchPress;
}
else if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadPress;
}
}
}
~ButGamepad() {//GPT пише що помилки не буде що це потрібно
if (debButton) {
delete debButton;
debButton = nullptr;
}
}
// функції які викликаються через вказівник зроблено для того щоб в циклі фор викликати через gamepadBatton різні функції по черзі
// Bluetooth gamepad push button
byte tactilePress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (tactilePinButtonRead()) {
if (!flagPress) {
buttonGamepadPressRelease(press_button, gamepadButtonName);
flagPress = 1;
}
}
else if (flagPress){
flagPress = 0;
buttonGamepadPressRelease(release_button, gamepadButtonName);
}
}
else if (acceptedCommand == press_check) {
if (tactilePinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !tactilePinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte touchPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
buttonGamepadPressRelease(press_button, gamepadButtonName);
}
}
else if (flagPress){
flagPress = 0;
buttonGamepadPressRelease(release_button, gamepadButtonName);
}
}
else if (acceptedCommand == press_check) {
if (touchPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !touchPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte analogReadPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (analogPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
buttonGamepadPressRelease(press_button, gamepadButtonName);
}
}
else if (flagPress){
flagPress = 0;
buttonGamepadPressRelease(release_button, gamepadButtonName);
}
}
else if (acceptedCommand == press_check) {
if (analogPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !analogPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
//
// Bluetooth gamepad right trigger pressure
byte analogReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
int temp = analogRead(numb);
Serial.print("---"); Serial.print(temp);
Serial.print("---"); Serial.println(numb);
if (temp > analog_dead_zone) { // test
temp = map(temp, analog_dead_zone, 4095, 0, 32767);
temp = constrain(temp, 0, 32767);
bleGamepad.setRightTrigger(temp);
flagPress = 1;
}
else if (flagPress) {
flagPress = 0;
bleGamepad.setRightTrigger(0);// test
}
}
else if (acceptedCommand == press_check) {
if (analogPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !analogPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte touchReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
bleGamepad.setRightTrigger(32767);
}
}
else if (flagPress){
flagPress = 0;
bleGamepad.setRightTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (touchPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !touchPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte tactileReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (tactilePinButtonRead()) {
bleGamepad.setRightTrigger(32767);
flagPress = 1;
}
else if (flagPress){
flagPress = 0;
bleGamepad.setRightTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (tactilePinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !tactilePinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
//
// Bluetooth gamepad left trigger pressure
byte analogReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
int temp = analogRead(numb);
if (temp > analog_dead_zone) { // test
temp = map(temp, analog_dead_zone, 4095, 0, 32767);
temp = constrain(temp, 0, 32767);
bleGamepad.setLeftTrigger(temp);
flagPress = 1;
}
else if (flagPress) {
flagPress = 0;
bleGamepad.setLeftTrigger(0);// test
}
}
else if (acceptedCommand == press_check) {
if (analogPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !analogPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte touchReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (touchPinButtonRead()) {
if (!flagPress) {
flagPress = 1;
bleGamepad.setLeftTrigger(32767);
}
}
else if (flagPress){
flagPress = 0;
bleGamepad.setLeftTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (touchPinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !touchPinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
byte tactileReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
if (tactilePinButtonRead()) {
bleGamepad.setLeftTrigger(32767);
flagPress = 1;
}
else if (flagPress){
flagPress = 0;
bleGamepad.setLeftTrigger(0);
}
}
else if (acceptedCommand == press_check) {
if (tactilePinButtonRead() && !buttonCheckFlag) {
buttonCheckFlag = 1;
return gamepad_button_pressed;
}
else if (buttonCheckFlag && !tactilePinButtonRead()){
buttonCheckFlag = 0;
return gamepad_button_release;
}
}
else if (acceptedCommand == button_get_name) {
return gamepadButtonName;
}
return 0;
}
//
//використовується для переінціалізації вказівника на функцію заміна кнопок місцями
byte buttonReinstall(byte newButton){
/*
Тарас з майбутнього Ну і в нас погана пам'ять то капець мусимо так писати.
В даній ситуації піни вже є настроєними в конструкторі на види зчитування аналогове цифрове і дотик розглянемо приклад Дана зміна gamepadButtonName = newButton несе в собі номер-ім'я кнопки геймпада (віртуальне ім'я) Наприклад якщо вона несе в собі номер-ім'я 16 то це номер-ім'я відповідає правому курку геймпада, якщо від 1 до 8 то це є звичайні кнопки геймпада щось краще зрозуміти дивитись //listButton[18]//.Якщо наприклад в нас Дана нова кнопка gamepadButtonName
яку ми хочемо перемістити на це місце яке вже є настроєне в конструкторі на один з трьох способів зчитування піну
відповідає назві правого курка if (gamepadButtonName == trigger_r) в залежності від способу зчитування даних з піна pinReadType відбудеться підбір метода класу який буде натискати кнопку одним з трьох методів класу, якщо це наприклад звичайна кнопка else if (pinReadType == tactile_read) {gamepadPressPtr = &ButGamepad::tactileReadTriggerRightPress; } хоче натискати правий курок то після натиску фізичної кнопки відбудеться миттєве натискання правого курка без вимірювання степені нахилу решту можна зрозуміти по даній підкасті.
*/
gamepadButtonName = newButton;
if (gamepadButtonName == trigger_r || gamepadButtonName == trigger_l) {// вибір яким способом буде натиснути курок(R L)геймпада курок(R L) = tactile touch analog
if (gamepadButtonName == trigger_r) {
//підбір метода яким будемо натискати правий курок опираючись на настроєний в конструкторі тип зчитування піна
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerRightPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerRightPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerRightPress;
}
}
else if (gamepadButtonName == trigger_l) {
if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadTriggerLeftPress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchReadTriggerLeftPress;
}
else if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactileReadTriggerLeftPress;
}
}
}
else {// вибір яким способом буде натиснути кнопка геймпада кнопка(1...10) = tactile touch analog
if (pinReadType == tactile_read) {
gamepadPressPtr = &ButGamepad::tactilePress;
}
else if (pinReadType == touch_read) {
gamepadPressPtr = &ButGamepad::touchPress;
}
else if (pinReadType == analog_read) {
gamepadPressPtr = &ButGamepad::analogReadPress;
}
}
return 0;
}
//функція повертає вказівник який вказує на функцію яка була вибрана через конструктор
byte gamepadBatton(byte acceptedCommand) {
if (pushPermit) {
return (this->*gamepadPressPtr)(acceptedCommand);
}
return 0;
}
};// Button gamepad
//створюємо екземпляри класу настроюємо вказівник на необхідну функцію
//ButGamepad(1(reinstall!), 2, 3) 1-вибираємо курок чи кнопка 2-вибираємо чим будемо натискати сенсорна-звичайна кнопка або аналог 3-номер піна
ButGamepad butGamepad[] = {// test конструктор класів вказуємо номера пінів і способи зчитування які відповідають за кнопки
ButGamepad(profil[mps(gapn)].listButton[0], mps(get_push_permit, 0), tactile_read, 13), //
ButGamepad(profil[mps(gapn)].listButton[1], mps(get_push_permit, 1), tactile_read, 14),
ButGamepad(profil[mps(gapn)].listButton[2], mps(get_push_permit, 2), tactile_read, 18),
ButGamepad(profil[mps(gapn)].listButton[3], mps(get_push_permit, 3), tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[4], mps(get_push_permit, 4), tactile_read, 17),
ButGamepad(profil[mps(gapn)].listButton[5], mps(get_push_permit, 5), tactile_read, 5),
ButGamepad(profil[mps(gapn)].listButton[6], mps(get_push_permit, 6), tactile_read, 19),
ButGamepad(profil[mps(gapn)].listButton[7], mps(get_push_permit, 7), tactile_read, 13),
ButGamepad(profil[mps(gapn)].listButton[8], mps(get_push_permit, 8), tactile_read, 23), // button_select
ButGamepad(profil[mps(gapn)].listButton[9], mps(get_push_permit, 9), tactile_read, 26), // button_start
ButGamepad(profil[mps(gapn)].listButton[10], mps(get_push_permit, 10), tactile_read, 13 + false), // dpad_up
ButGamepad(profil[mps(gapn)].listButton[11], mps(get_push_permit, 11), tactile_read, 12), // dpad_down
ButGamepad(profil[mps(gapn)].listButton[12], mps(get_push_permit, 12), tactile_read, 13 + false), // dpad_left
ButGamepad(profil[mps(gapn)].listButton[13], mps(get_push_permit, 13), tactile_read, 27), // dpad_right
ButGamepad(profil[mps(gapn)].listButton[14], mps(get_push_permit, 14), analog_read, 15), // trigger_r
ButGamepad(profil[mps(gapn)].listButton[15], mps(get_push_permit, 15), analog_read, 4), // trigger_l
ButGamepad(profil[mps(gapn)].listButton[16], mps(get_push_permit, 16), analog_read, 13 + false), //дублікатори
ButGamepad(profil[mps(gapn)].listButton[17], mps(get_push_permit, 17), analog_read, 13 + false)
};
//butGamepad[i].mps(set_push_permit, 0, true);
byte buttonGamepadPressRelease(byte command, byte buttonName) {//функція натискає кнопки 1-8 Select Start і d-пад викликається в кожному екземплярі класу через метод gamepadBatton
static byte x = 1;
static byte y = 1;
byte dPad[3][3] = {
{HAT_UP_LEFT, HAT_UP, HAT_UP_RIGHT},
{HAT_LEFT, HAT_CENTERED, HAT_RIGHT},
{HAT_DOWN_LEFT, HAT_DOWN, HAT_DOWN_RIGHT}
};
if (command == press_button) {
if (buttonName <= button_8 ) {//звичайні кнопки
bleGamepad.press(buttonName);//номер кнопки є одночасно і назвою кнопки (buttonName = число-назва)
}
else if (buttonName == button_select || buttonName == button_start) {//кнопки Старт Select
if (buttonName == button_select) {
bleGamepad.pressSelect();
}
else if (buttonName == button_start) {
bleGamepad.pressStart();
}
}
else{//кнопки Dpad
if (dpad_up == buttonName || dpad_down == buttonName){//якщо жодна умова не виконується X Y буде 1 == центер Dpad, інакше виконається скерування по координатах
x = map(buttonName, 13, 14, 0, 2);
}
else if (dpad_left == buttonName || dpad_right == buttonName) {
y = map(buttonName, 15, 16, 0, 2);
}
bleGamepad.setHat1(dPad[x][y]);
}
}
else if (command == release_button) {
if (buttonName <= button_8 ) {
bleGamepad.release(buttonName);
}
else if (buttonName == button_select || buttonName == button_start) {
if (buttonName == button_select) {
bleGamepad.releaseSelect();
}
else if (buttonName == button_start) {
bleGamepad.releaseStart();
}
}
else{//кнопки Dpad//в залежності від попередніх натисків відбудеться центровка або перезапис старих даних
if (dpad_up == buttonName || dpad_down == buttonName) {
x = 1;
}
else if (dpad_left == buttonName || dpad_right == buttonName) {
y = 1;
}
bleGamepad.setHat1(dPad[x][y]);//старі дані перезаписуються новими
}
}
return 0;
}
void buttonGamepadReinstallAllButtons() {
for (int i = 0; i < 16; i++) {
butGamepad[i].buttonReinstall(profil[mps(gapn)].listButton[i]); //аналог перезапуску всіх(16ти) конструкторів екземплярів класу
}
}
// Bdc
// Additional functions
void af_curvedArrow(int x, int y) {//крива стрілка Вверх
display.setCursor(x, y);
display.println("_");
display.setCursor(x + 3, y); // Зсув на 3 пікселі для другого символу
display.write(24);
}
int af_clamp(int value, int minVal, int maxVal) {//якщо вийти за рамки то поверне в початок або кінець
if (value < minVal) {
return maxVal;
}
else if (value > maxVal) {
return minVal;
}
return value;
}
byte af_BatR(byte Taking) {// васна функція зчитує дані кнопки нагадування потрібно зробити власний клас кнопка додати debouns
if (!digitalRead(Taking)) {
af_autoShutdown(reset_f);
}
return !digitalRead(Taking);
}
byte af_autoShutdown(byte acceptedCommand, byte acceptedData){
static uint32_t timerMagnifier = 3 * 60000;
static Timer tmr;
if (acceptedCommand) {
if (acceptedCommand == reset_f) {
tmr.b(timerMagnifier);
}
else if (acceptedCommand == change_time) {
timerMagnifier = acceptedData * 60000;
}
else if (acceptedCommand == get_time) {
return timerMagnifier / 60000;
}
}
else if (tmr.e()){
delay(400);
esp_deep_sleep_start();
}
return 0;
}
void af_buttonOnOff(){
if (!digitalRead(13)) {
delay(400);
esp_deep_sleep_start();
}
delay(100);
}
byte af_outputDisplay(byte acceptedCommand){
static byte savedCommand = clear_display;
if(acceptedCommand == refresh ) {
savedCommand = clear_display;
}
else if(savedCommand) {
if(acceptedCommand == clear_display && savedCommand == clear_display) {
savedCommand = print_display;
display.clearDisplay();
}
else if(acceptedCommand == print_display && savedCommand == print_display) {
savedCommand = no_data;
display.display();
}
}
return 0;
}
void gyroStart(){
/*
mpu.initialize();
delay(10);
mpu.dmpInitialize();
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
static byte flag = []() -> byte {
byte temp;
EEPROM.get(7 * sizeof(Profiles) + 1, temp);
return !temp;
}();
*/
static byte flag = 1;// TECT
long offsets[6];
if(af_BatR(b_triger_r) && af_BatR(b_analog_l)){
byte temp = 10;
while(temp > 0){
display.clearDisplay();
display.setCursor(0, 16);
display.println(" Calibration starts");
display.println();
display.print (" ");
display.println(temp);
display.display();
af_buttonOnOff();
temp--;
delay(1000);
}
display.clearDisplay();
display.setCursor(2, 16);
display.println(" Wait for completion");
display.display();
delay(2000);// TE
/*
mpu.setXAccelOffset(0);
mpu.setYAccelOffset(0);
mpu.setZAccelOffset(0);
mpu.setXGyroOffset(0);
mpu.setYGyroOffset(0);
mpu.setZGyroOffset(0);
delay(10);
mpu.CalibrateAccel(60);// не забудьте додати рестарт при помилках калібровки
mpu.CalibrateGyro(120);
delay(10);
offsets[0] = mpu.getXAccelOffset();
offsets[1] = mpu.getYAccelOffset();
offsets[2] = mpu.getZAccelOffset();
offsets[3] = mpu.getXGyroOffset();
offsets[4] = mpu.getYGyroOffset();
offsets[5] = mpu.getZGyroOffset();
*/
flag = 1;
/*
EEPROM.put(7 * sizeof(Profiles) + 1, flag);
EEPROM.put(7 * sizeof(Profiles) + 2, offsets);
EEPROM.commit();
*/
display.clearDisplay();
display.setCursor(4, 16);
display.println("Calibration complete");
display.display();
}
else if(!flag){
display.clearDisplay();
display.setCursor(1, 16);
display.println("Gyroscope calibration");
display.println(" not done");
display.display();
while(1){
af_buttonOnOff();
}
}
else if(flag){
/*
EEPROM.get(7 * sizeof(Profiles) + 2, offsets);
mpu.setXAccelOffset(offsets[0]);
mpu.setYAccelOffset(offsets[1]);
mpu.setZAccelOffset(offsets[2]);
mpu.setXGyroOffset(offsets[3]);
mpu.setYGyroOffset(offsets[4]);
mpu.setZGyroOffset(offsets[5]);
*/
}
}
void startAnimation(){
display.clearDisplay();
display.invertDisplay(0);
display.setCursor(-2, 28);
display.println(" FS G36 ");
display.display();
delay(500);
display.clearDisplay();
display.display();
}
void isr() {
enc.tick(); // Оновлюємо стан енкодера в перериванні
}
// Af
// Main display
//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() {//функція яка повертає масив з іменами кнопок
/*
(нагадування приклад роботи відображення назв )наприклад при заміні місцями елементів масиву listButton[0](1) і
listButton[1](2) це означатиме що елемент масиву listButton[1] тепер = 1 і тепер вказує на порядковий номер в масиві імен buttonNames[listButton[1]]
Таким чином їх номер-ім'я вказує на порядковий номер в масиві. listButton[?] несе в собі число що являється і номером
і ім'ям і порядковим номером для відображення імені з масива buttonNames[] Таким чином як ми не міняли елементи масиву listButton місцями вони
завжди будуть вказувати на своє ім'я із списку buttonNames
*/
static const char* buttonNames[] = {
"?",//для дублікатів кнопки
"A",
"B",
"X",
"Y",
"A1",
"A2",
"RB",
"LB",
"Se",
"St",
"dU",
"dD",
"dL",
"dR",
"RT",
"LT"
};
return buttonNames;
}
//
// Main display functions
// Cursor
byte mdf_cursor(byte acceptedCommand, byte data, byte* arr) {
static int8_t point = 1;
static int8_t cursor = cursor_type_one;
switch (acceptedCommand) {
case cursor_read_encoder:
//переміщаємо encoder якщо є дозвіл (cursor_type_one) також можна використовувати дані з кодера якщо кнопка не натиснута
//якщо кнопка натиснута оберти енкодера будуть використовуватися для прокрутки тексту бегущеї строки якщо швидкість пркрутки
//не влаштовує Можна вручну для того щоб зрозуміти треба дивитись всередині af_displayPrintLain();
if(cursor == cursor_type_one && mdf_encoderButton(take_but_pos) == !button_pressed){
switch (mdf_encoder(get_rotation)) {
case rotated_l:
Serial.println("--");
point--;
af_outputDisplay(refresh);
break;
case rotated_r:
Serial.println("++");
point++;
af_outputDisplay(refresh);
break;
}
point = af_clamp(point, 1, data);
}
break;
case cursor_type_one:
mdf_encoder(reset_rotation);//енкодер опрацьовується в прериванні і тому можуть появлятися непотрібні дані стрілка може скакнути
cursor = cursor_type_one;
break;
case cursor_type_two:
cursor = cursor_type_two;
break;
case get_pos:
return point;
break;
case set_pos: //вказуємо позицію курс при переходах вперед або назад по адресу
point = data;
cursor = cursor_type_one;
break;
}
mdf_p_cursor(cursor, arr[point-1]);
return 0;
}
byte mdf_p_cursor(byte cursorPrint, byte cursorPosition) {
const byte cursorTypes[] = {26, 16}; //cursor_type_one == 0 cursor_type_two == 1
display.setCursor(0, cursorPosition);
display.write(cursorTypes[cursorPrint]);//вибираємо один з двох видів курсора з таблиці символів
return 0;
}
//
int mdf_addres(byte acceptedCommand, byte addNumb, byte additionalCommand) {//N
static byte add[10];//для коректної роботи можна використати тільки вісім адресів 1-8
static byte inputMode = mode_click_input;
switch (acceptedCommand) {
case back_add:
af_outputDisplay(refresh);
mdf_cursor(cursor_type_one);
inputMode = mode_click_input;
for (int i=9; i >= addNumb; i--){// команда back виконує видалення всіх адресів до числа вказаного в addNumb
add[i] = address_not_entered;
}
return address_deleted;//????
break;
case mode_click_back:
mdf_cursor(cursor_type_two);
af_outputDisplay(refresh);
inputMode = mode_click_back;
break;
case mode_function_returns:
mdf_cursor(cursor_type_two);
af_outputDisplay(refresh);
inputMode = mode_function_returns;
break;
case get_add:
if (additionalCommand == check_button_enc){
if (inputMode == mode_click_input){
if (add[addNumb] == address_not_entered && mdf_encoderButton() == click){// набір адреса
add[addNumb] = mdf_cursor(get_pos);
return address_entered;
}
}
else if (inputMode == mode_click_back){
if (add[addNumb+1] == address_not_entered && mdf_encoderButton() == click){//якщо попадемо на нуль повертаємося
Serial.println("if (mode_click_back)");
mdf_addres(back_add, addNumb);
return address_deleted;
}
}
}
return add[addNumb];
break;
}
return 0;
}
int8_t mdf_encoder(int8_t acceptedCommand) {// функція повертає бистре або повільне обертання працює разом з функцією af_encoderInterrupt() для обробки даних
if (acceptedCommand == get_rotation && enc.isTurn()) {
af_outputDisplay(refresh);
if (enc.isFastR()) {// якщо енкодер обертається з достатньою швидкістю
Serial.println("rotated_r_fast");
return rotated_r_fast;
}
else if (enc.isFastL()) {
Serial.println("rotated_l_fast");
return rotated_l_fast;
}
else if (enc.isRight()) {
return rotated_r;
}
else if (enc.isLeft()) {
return rotated_l;
}
}
else if (acceptedCommand == reset_rotation) {//скидання накопичених даних дані можуть мінятися Навіть якщо ми не викликаємо mdf_-encoder(get_rotation); тому що обробка відбувається в перериванні
mdf_encoder(get_rotation);
}
return 0;
}
byte mdf_encoderButton(byte acceptedCommand) {
static DebButton butEnc(b_sw);// b_sw pin = 25
static bsc pes; //pes = program execution stage(етап виконання програми)
static Timer tmr;
static byte executionResult;
if (acceptedCommand){
if (acceptedCommand == take_but_pos && butEnc.press()){
return button_pressed;
}
else if (acceptedCommand == reset_f){
}
}
if (butEnc.press()){
if (pes.g() != press_end_turn && tmr.e()) {
if (!pes.g()) {
tmr.b(500);
return pes.s(press_b_e);
}
else if (pes.g() == press_b_e) {//якщо досягнули максимуму тоді метод м поверне правда і скине число на нуль
return pes.s(hold_b_e);
}
}
if (enc.isTurn()) {
return pes.s(press_end_turn);
}
}
else if (pes.g()) {
af_outputDisplay(ref_dis); //оновлення дисплею при кліку
tmr.r();//таймер завжди потрібно скидати щоб можна було при потребі клікати кнопкою Напряги мозги Якщо хочеш зрозуміти
if (pes.r() == press_b_e) {
return click;
}
}
return no_data;
}
int mdf_magnifier(int numeric, int minNam, int maxNam, int Speed) {
switch (mdf_encoder(get_rotation)) {
case rotated_r:
numeric++;
break;
case rotated_l:
numeric--;
break;
case rotated_r_fast:
numeric+=Speed;
break;
case rotated_l_fast:
numeric-=Speed;
break;
}
numeric = af_clamp(numeric, minNam, maxNam);
return numeric;
}
// mdf
// Menu display additional functions -------------------------------------------------------------------------------------------
// Saving Settings Display
byte mdaf_savingSettingsDisplay(){ //коротко копіювання mps(copy profile) порівняння mps(active_copy_compare) якщо == active_copy_same просто виходимо якщо != active_copy_sameрізні викликаються Дана функція
mdaf_confirm(output_by_coordinates, X(36), Y(40));
mdaf_p_savingSettingsDisplay();// візуальна частина коду
if (mdaf_confirm(offer_a_choice) == data_ready) {
if (mdaf_confirm(get_data) == choice_no) {//якщо вибір ні повертаємо настройки
mps(not_save);
}
return function_completed;//незалежно від вибору функція поверне function_completed
}
return 0;
}
void mdaf_p_savingSettingsDisplay(){
display.drawRoundRect(20, 8, 87, 22, 0, WHITE); // рамка навколо слова зверху
display.setCursor(25, 15);
display.println("Save settings");
}
//
void mdaf_typeNumberPrint(){
char name[3][3] = {" ", "Sp", "Mp"};
display.setCursor(49, 0);
display.print("( )");
display.setCursor(55, 0);
display.print(name[profil[mps(gapn)].tape]);//tape може бути t_sp = 1 t_mp = 2
display.print(profil[mps(gapn)].namber);
}
// Confirm
byte mdaf_confirm(byte acceptedCommand, byte x, byte y){//функція надає варіант для вибору та каву ні або виводить повідомлення що не потрібно якщо нема потреби
static bsc selection(1);
static bsc clicksCount(3);
if (acceptedCommand == output_by_coordinates) {// приймає зберігає координати по яких вона буде відображатися
mdaf_p_confirm(output_by_coordinates, x, y);
}
else if (acceptedCommand == print_not_need) {//після перевірки можна подати команду вивести повідомлення якщо підтвердження не потрібне
if ( mdaf_p_confirm(print_not_need) == function_completed || mdf_encoderButton() == click) {
mdaf_p_confirm(reset);
return function_completed;
}
}
else if (acceptedCommand == offer_a_choice) {//якщо підтвердження потрібне запропоновуємо вибір виводимо так або ні
if (mdf_encoder(get_rotation)) {
selection.p(1);
mdaf_p_confirm(change_selection);
clicksCount.r();
}
if (mdf_encoderButton() == 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) {
byte variants[] = {choice_no, choice_yes};
return variants[selection.g()];
}
return 0;
}
byte mdaf_p_confirm(byte acceptedCommand, byte x, byte y){
const byte frameSizeWidth[] = {15, 21};
const byte framePosition[] = {2, 33};
const int frameBlink[] = {1000, 500, 200};//test
static byte frequency;//frequency ЧАСТОТА
static byte flashFlag;
static Timer tmr;
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 = flashFlag = 0;
tmr.r();
break;
case accelerate_frame_blinking:
frequency++;
break;
case reset:
selection = frequency = flashFlag = 0;
tmr.r();
break;
case print_not_need:
if (!tmr.s()){
tmr.b(1000);
}
else if (tmr.e()) {
tmr.r();
return function_completed;
}
display.setCursor(azixX+4,azixY+3);
display.print("not need");
display.drawRect(azixX, azixY, 56, 15, WHITE);// велика рамка навколо так ні
break;
case offer_a_choice:
display.setCursor(azixX+4, azixY+4);
display.println("NO");
display.setCursor(azixX+35, azixY+4);
display.println("YES");
if (tmr.e()) {//рамка світилась одразу після запуску або змінні вибору
flashFlag = !flashFlag;
tmr.b(frameBlink[frequency]);
}
if (flashFlag) {
display.drawRect(azixX + framePosition[selection], azixY + 2, frameSizeWidth[selection], 11, WHITE);//рамка навколо так або ні
}
display.drawRect(azixX, azixY, 56, 15, WHITE);// велика рамка навколо так ні
af_outputDisplay(refresh);
break;
}
return 0;
}
//
//
// Md
void setup() {
Serial.begin(9600);// test В ФІНАЛІ НЕЗАБУТИ ЗАКОМЕНТУВАТИ!!!
/*//виконуємо настройки геймпада
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), isr, CHANGE);
attachInterrupt(digitalPinToInterrupt(p_dt), isr, CHANGE);
//
// ----------Display--------------
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextSize(1);
display.setTextColor(WHITE);
// gyroStart();// при потребі проводимо калібровку якщо калібровка виконана виконуємо старт функції гіроскопа
// startAnimation();// анімація при включенні до робити в кінці
}
void loop() {
display.clearDisplay();
af_displayPrintLain("123456789", 10, 10, 1, 5);
af_displayPrintLain("123456789123456---------fgfgfgfgghjghjfkhkl", 20, 20, 2, 9);
af_displayPrintLain("sentence 5645159789678979hlkl", 10, 30);
byte LinesSp[] = {10, 20, 30};
mdf_cursor(cursor_read_encoder, sizeof(LinesSp), LinesSp);
display.display();
}// void loop()
// Функція для відображення тексту з можливістю прокручування на дисплеї
void af_displayPrintLain(String text, byte x, byte y, byte linePosition, byte cropText) {
// Статичні змінні для збереження стану між викликами
static Timer tmr(0); // Таймер для контролю автоматичної прокрутки також включаємо оновлення дисплея при кожному зміщенні тексту
static byte lps; // Змінна для збереження line Position seve
static int stx; // Позиція прокрутки по горизонталі
byte stxt; // Тимчасова змінна для прокрутки stxt =crolling text x temporary
static int tlip; // Довжина тексту в пікселях text tlip = Length In Pixels
if (mdf_cursor(get_pos) != lps) {
tlip = text.length() * 6; // Довжина тексту у пікселях (6 пікселів на символ)
stx = 0; // Початкова позиція прокрутки
lps = linePosition; // Оновлення y
}
if (mdf_cursor(get_pos) == linePosition) {
// Якщо натиснута кнопка енкодера
if(mdf_encoderButton(take_but_pos) == button_pressed){
// Перевірка повороту енкодера для прокрутки
if(enc.isTurn()){
// Прокрутка вправо
if (enc.isFastR() || enc.isRight()) {
stx += 6; // Збільшення позиції прокрутки
}
// Прокрутка вліво
else if (enc.isFastL() || enc.isLeft()) {
stx -= 6; // Зменшення позиції прокрутки
}
}
// Обмеження позиції прокрутки в межах розміру тексту
stx = constrain(stx, 0, tlip);
}
// Автоматична прокрутка за таймером, якщо кнопка не натиснута
else if (tmr.f()) {
if (tlip > stx) {
stx += 2; // Збільшення позиції прокрутки
} else {
stx = 0; // Скидання прокрутки до початку тексту
}
}
stxt = stx; // Присвоєння тимчасової змінної значення прокрутки
}
// Встановлення курсора з урахуванням прокрутки та виведення тексту
display.setCursor(x - stxt, y);
display.print(text);
// Очищення областей навколо тексту для запобігання накладання символів
display.fillRect(0, y, x, 7, 0); // Ліва область
display.fillRect(x + cropText * 6, y, 128, 7, 0); // Права область відповідно до обрізаного тексту
display.fillRect(0, y + 7, 128, 64, 0); // Очищення області під текстом
}
Loading
ssd1306
ssd1306