#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "GyverEncoder.h"
#include <EEPROM.h>
#include "I2Cdev.h"//не забути про розгон!!!
#include "MPU6050_6Axis_MotionApps20.h"
#include <BleGamepad.h> // при загрузці нa реально Arduino даний фрагмент розкоментувати a пуcті класи закоментувати
#include <FastLED.h> //FastLED Daniel Garcia
// 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 profil 15
#define data_available 16// дані доступні
#define delete_data 17
#define start 18
#define reset_settings 19
#define stage_0 0 // етапи виконання
#define stage_1 1
#define stage_2 2
#define stage_3 3
#define stage_4 4
#define stage_5 5
//
// Піни карта
#define p_bolt_rifle_12 12
#define p_vibration_motor_18 18
#define p_h_b_14 14 //test
#define p_triger_r_35 35
#define p_triger_l_34 34
#define p_s_t_x_39 39//test
#define p_s_t_y_36 36
#define p_sw_25 25 // кнопка енко // RotaryEncoder encoder(27, 14, RotaryEncoder::LatchMode::TWO03);
#define p_dt_32 32
#define p_clk_33 33
#define p_on_off 26
#define p_led_pin_13 13
#define p_test_25 25 // test
//
//Md_moveButtonsAnimation();
#define print_batton 20
#define reset_animation 21
//
//af_displayPrintLain()
#define L(x) (x) // line
#define N(x) (x) // number
//
//Gf_combinedGyroRightJoystick()Gf_da()
#define g_x 0
#define g_y 1
#define s_x 2
#define s_y 3
#define joystick_full_priority 1
#define joystick_priority_axial 2
#define gyro_full_priority 3
#define gyro_priority_axial 4
#define gyro_joystick_combination 5
#define only_gyro 6
#define g_o_m 7
//
//Gf_combinedGyroRightJoystick()
#define g_x_h 1//
#define g_y_v 2
#define j_x_h 0
#define j_y_v 1
#define c_x 0
#define c_y 1
//
// Gf_handleButton()
#define r_s 0 // read sensor
#define g_p 1 // get press
#define s_g_p 2 // single get press
#define s_g_r 3 // single get release
//
//Gf_g()
#define g_x 0
#define g_y 1 //g_z = axis y horizon
#define g_z 2 //g_z = axis z vertical
#define g_g_d 20 //g_g_d = get gyro data
#define r_g_d 21 //g_g_d = get gyro data
//
//Gf_leftJoystick() Gf_rj() Gf_rjp()
#define x_l 0 // x_l = X left
#define x_r 1 // x_r = X right
#define y_u 2 // y_u = Y up
#define y_d 3 // y_d = Y down
#define x_a 0 // x_a = X axis h
#define y_a 1 // y_a = Y axis v
#define r_a_d 20
#define g_a_d 21 // g_a_d = get axis data
#define g_d_z 22 // g_d_z = get dead zones
#define g_m_m 23 // gmm = get minimum maximum
#define s_d_z 24 // s_d_z = set dead zones
#define s_m_m 25 // s_m_m = set minimum maximum
//
//Gf_rj(int cmd){
#define r_j_a 20//read joystick axis
#define g_j_a 21// Get joystick axis
//
//Md_disableButton()
#define check_for_pressed_buttons 20
#define deactivated_buttons_get 21
#define deactivation_buttons_start 22
#define reset_deactivation_buttons 23
//
//Md_saveSelectedProfile( )
#define ssp_get_setting 20
#define ssp_change_setting 21
//
//af_triggerLight()
#define color_light_setting 20
#define color_switching 21
#define clssc 22 //clssc = color light standard settings check
#define color_light_reset 23
#define color_one 24
#define color_two 25
#define get_color1 26
#define get_color2 27
#define get_light1 28
#define get_light2 29
#define set_color1 30
#define set_color2 31
#define set_light1 32
#define set_light2 33
#define color_light_default 34
//mdf_batteryCheck()
#define g_b_s 20 //get battery status
#define b_c_p 21 // battery charge plus
#define g_c_m 22 // battery charge minus
#define b_c_m 23 //// battery charge minus
//
//Md_gyroCalibrationExecution()
#define calibration_start 20
//
//af_saveToEEPROM()
#define get_eeprom 101
#define set_eeprom 102
#define eeprom_initialization 103
#define eeprom_seve 104
#define get_eeprom_last_read 105
#define address_indicator_reset 106
//
//Effects
//af_vibration();
//#define p_vibration_motor_18 18
#define vibration_start 21
#define vibration_stop 22
#define vt_get_permission 23
#define vt_pin_activate 24
#define vt_change 25
//
// af_shotgunBolt()
#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
//
// af_shotgunBolt()
//#define p_bolt_rifle_12 12
#define bolt_start 21
#define bolt_stop 22
#define bolt_get_permission 23
#define bolt_pin_activate 24
#define bolt_change 25
//
//
//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 d_u_d 23 //disable_updates_display
#define e_u_d 24 //enablet_updates_display
//
// 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
//
// mdf_confirm() // mdf_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
//
// Md_autoPowerOffa()
// #define reset_f 11
#define apo_change_time 20
#define apo_get_time 21
#define apo_restart_time 22
//
// Md_movingButtons()
#define choice_bat_waiting 20
#define button_selection_complete 21
#define return_default 22
#define select_button 23
#define select_button_completed 34 //34 тому що щоб не було співпадінь з кнопками
//
//Md_displayTimeout()
//#define take_status 1
#define dt_get_tim_minutes 20
#define dt_get_tim_seconds 21
#define dt_change_time 22
#define timeout_restart 23
#define timeout_reset 24
//
// 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
//calibrationData()
#define d_z 0 // Dead zone
#define m_v 1 // maximum value
#define g_c_d 2 // get calibration data
#define s_c_d 3 // set calibration data
// 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 gamepad_button_pressed 22
#define gamepad_button_release 23
#define button_get_name 24
#define r_b_double_press 25
#define read_analog_state 26
#define i_p_p_g_b 27 //input pullup pin gamepad button
//byte bgc_buttonGamepadPressRelease();
#define press_button 20
#define release_button 21
//
// 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_gyro 34
#define reset_all_mp 35
//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
#define get_button 32
//
#define send_report_execute 0
#define send_report 1
#define send_report_stop 2
#define send_report_start 3
#define waiting_command 4
//Gf_functionsGamepad( ){
#define functions_gamepad_stop 20
#define functions_gamepad_start 21
//
//
// c-v-a
// Function prototypes
bool Gf_handleButton(int cmd = 0);
int16_t* Gf_g(int cmd = 0);
int* Gf_ar(int cmd = 0, int* newData = nullptr);
int* Gf_rj(int cmd = 0);
int Gf_da(int cmd = 0, int data = 0);
byte Gf_combinedGyroRightJoystick(int cmd = 0);
int* Gf_leftJoystick(int cmd = 0, int* newData = nullptr);
template<typename T>
bool mdf_magnifier(T &numeric, int minNam = 0, int maxNam = 0, int Speed1 = 0, int Speed2 = 0);
int af_triggerLight(byte acceptedCommand = 0, int acceptedData = 0);
void Gf_functionsGamepad(byte acceptedCommand = 0);
void Gf_sendReport(byte acceptedCommand = 0);
byte Md_moveButtonsAnimation(byte cmd = 0, byte buttonOne = 0, byte buttonTwo = 0);
void Md_gyroCalibrationExecution(byte acceptedCommand = 0);
byte af_shotgunBolt(byte acceptedCommand = 0);
byte af_vibration(byte acceptedCommand = 0);
int af_saveToEEPROM(int acceptedCommand = 0, void* address = nullptr, size_t dataSize = 0);
byte Gf_gamepadButtonsDuplicators(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 mdf_confirm(byte acceptedCommand = 0, byte x = 0, byte y = 0);
byte mdf_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);
void mdf_displayOutput();
void displayRefresh(byte acceptedCommand = 0);
byte Md_autoPowerOff(byte acceptedCommand = 0);
byte bgc_buttonGamepadPressRelease(byte command = 0, byte buttonName = 0);
byte mdf_cursor(byte acceptedCommand = 0, byte data = 0, const byte* coordinate = nullptr);
void af_triggerShootingEffects(byte startEffects = 0);
// Fp
// Ae
Adafruit_SSD1306 display(128, 64, &Wire, -1);
BleGamepadConfiguration bleGamepadConfig;//потрібно для настройок геймпада
BleGamepad bleGamepad("FS G36", "UA", 100);
Encoder enc(p_clk_33, p_dt_32, p_sw_25);
MPU6050 mpu;
CRGB leds[1];
// Byte saving data (bsd) використовується як унікальна зміна яка після досягнення потрібного нам значення повертає його і скидається на нуль
class bsd {
private:
const byte maxValue; // Максимальне значення
byte val = 0; // Накопичене значення
public:
// Конструктор
bsd(byte mV = 0, byte minV = 0) : maxValue(mV){
val = minV;
}
// Метод 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; // Повертаємо збережене значення
}
byte tnar() { //tnar = take number automatic reset функція доплюсовує повертає і скидає самостійно
byte temp = val; // Зберігаємо значення
val = (val == maxValue) ? 0 : val + 1;
return temp;
}
byte enar(byte addValue) { //enar = enter number and return вписує нове значення і повертає його
val = addValue;
return val;
}
};
// Bsd
// Timer class (конструкція працює приблизно 49 днів після чого потрібен перезапуск мікроконтролера)
class Timer {
private:
bool sendFlag = 1;
uint32_t tmr = 1;
bool timerFlag = 0;
uint32_t restartTimer = 0;//час перезапуску
public:
//конструктор вказуємо час і з чого починати правда брехня
Timer(uint32_t aRT = 0, bool firstCall = 1) { //екземпляри таймерів можуть створюватися одразу з вказаним часом або вказувати його пізніше
restartTimer = aRT;//якщо не вказати час тоді буде постійно повертатись значення яке є в timerFlag
tmr = millis() + restartTimer;
if (firstCall) {//Якщо другий параметр правда Тоді почне одразу з правда
timerFlag = 1;
}else{
timerFlag = 0;
}
}
//
//таймер одноразово поверне правда з вказаною частотою
bool gp() { // gt = get pulsation з вказаною частотою повертає правда
switch (timerFlag) {//таймер стартує в залежності від настр.
case false:
if (restartTimer && tmr <= millis()) {//якщо не вказати час то повернення в даному випадку брехня постійно аж допоки не в кажди нема час
tmr = millis() + restartTimer;
return true;//після того як час вийде одноразово повернемо правда
}
return false;
break;
case true:
if (restartTimer) {
timerFlag = false;
}
return true;
break;
}
}
void sp(uint32_t aRT = 0, bool firstCall = 1){ // sp = set pulsation
restartTimer = aRT;
tmr = millis() + restartTimer;
if (firstCall) {//Якщо другий параметр правда(стандартно) Тоді timer почне одразу з правда
timerFlag = true;
}else{
timerFlag = false;
}
}
//
//таймер одноразовий сигнал (брехня один раз правда по завершенню таймера брехня до перезапуску)
bool ts() { // ts = timer single одноразова повернуть правда і буде чекати перезапуску
switch (timerFlag) {//таймер стартує в залежності від настр.
case false:
if (restartTimer && tmr <= millis()) {//
restartTimer = false;
return true;//
}
return false;
break;
case true:
if (restartTimer) {
timerFlag = false;
}
return true;
break;
}
}
void tst(uint32_t aRT = 0, bool firstCall = 1){ // /tat = timer single time
restartTimer = aRT;
tmr = millis() + restartTimer;
if (firstCall) {//Якщо другий параметр правда(стандартно) Тоді timer почне одразу з правда
timerFlag = true;
}else{
timerFlag = false;
}
}
//
//таймер (секунда правда секунда брехня)
bool ta() {//ta = timer auto постійно повертає правда брехня використовується там де не потрібна точність
if (restartTimer && tmr < millis()){//по завершенню часу змінюємо стан флажка стан флажка повертається постійно
tmr = millis() + restartTimer;
timerFlag = !timerFlag;
}
return timerFlag;//весь час повертаємо стан флажка
}
void tat(uint32_t rT = 0, bool firstCall = 1){ //tat = timer auto time вказуємо новий час спрацювання для ta
restartTimer = rT;
tmr = millis() + restartTimer;
if (firstCall) {//Якщо другий параметр правда Тоді timer почне одразу з правда
timerFlag = true;
}else{
timerFlag = false;
}
}
void tar(bool firstCall = 1){//tar = timer auto reset привиклику залежності від настройки ta() почне з правда або брехня
tmr = false;
if (firstCall) {//Якщо параметр правда Тоді timer почне одразу з правда
timerFlag = false;//тут інша логіка Тому потрібно вказати брехня
}else{
timerFlag = true;
}
}
//
//повертає постійно по завершенню часу правда або брехня залежно від настройки
bool tm() { // tm = timer manual
if (restartTimer && tmr <= millis()) {//
tmr = millis() + restartTimer;
timerFlag = !timerFlag;//по завершенню таймера постійно буде повертати значення
}
return timerFlag;//постійно повертається по завершенню таймера зміни свій стан
}
void tmt(uint32_t rT = 0, bool firstCall = 1){ //tmt = timer manual time вказуємо новий час спрацювання для tm
restartTimer = rT;
tmr = millis() + restartTimer;
if (firstCall) {//Якщо другий параметр правда Тоді timer почне одразу з правда
timerFlag = true;
}else{
timerFlag = false;
}
}
void tmr(bool firstCall = 1){//скидання, привиклику ta() поверне правда
tmr = false;
if (firstCall) {//Якщо другий параметр правда Тоді timer почне одразу з правда
timerFlag = false;//тут потрібна інверсія специфіка роботи tm()
}else{
timerFlag = true;
}
}
//
};
// Tc
// Profiles class + supporting functions
class Profiles {
public:
Profiles(int deZoH, byte senHor, int deZoV, byte senVer, const 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);//(listButton) даємо стандартні імена-номера кнопкам + дублікатори натискають кнопку нуль
}
Profiles() {}// структор без параметрів потрібен для сейф профайл
//
//гіроскоп настройка
void gyroReset(){
sensitivityHor = sensitivityVer = 150;//це означає що чутливість мінімальна а максимальна 50
deadZoneHor = deadZoneVer = 0;
}
bool gssc(){//gssc = gyroStandardSettingsCheck якщо в нас стандартні настройки повернуться правда щоб не виконувати дії в подальшому використовується
return ((!deadZoneHor && !deadZoneVer) && (sensitivityVer == 150 && sensitivityHor == 150));
}
void setDeZoHor(int newDeZoH) {// set
if (deadZoneHor != newDeZoH) {//якщо встановлюємо нову мертву зону чутливість потрібно скинути чутливість настроюється після мертвої зони
sensitivityHor = 150;//ставим мінімальну чутливість в даному випадку це 150
}
deadZoneHor = newDeZoH;
}
void setSenHor(byte senHor) {
sensitivityHor = senHor;
}
void setDeZoVer(int newDeZoV) {
if (deadZoneVer != newDeZoV) {
sensitivityVer = 150;
}
deadZoneVer = newDeZoV;
}
void setSenVer(byte senVer) {
sensitivityVer = senVer;
}
//
// робота з іменами профілів String nps;
String nameProfiles(byte acceptedCommand, char Letter = ' '){// функція працює з іменем профілю
static Timer tmr(1000);
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://iм'я активного профілю в списку буде мигати
if (status == r_on) {//test можливі баги чомусь не мигало
return tmr.ta() ? 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 "";
}
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) {
resetName();
}
}
void trimTrailingSpaces() { // trim Trailing Spaces видаляю пробіли в кінці речення
int i = nps.length() - 1;
while (i >= 0 && nps.charAt(i) == ' ') {
i--; // Знаходимо позицію останнього непустого символу
}
nps.remove(i + 1); // Видаляємо всі символи після останнього значущого
}
byte lettersTakeNumber(){//повертає кількість букв в реченні
return nps.length();
}
void resetName(){
nps = " My profile " + String(namber) + String(" "); // Присвоюємо стандартне ім'я
}
//
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 не чіпати Все правильно)
}
listButton[16] = listButton[17] = 0;//iціалізуємо дублікатори нулями тому що там сміттєві значення
}
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;
}
void profileResetAll(){//функція скидає всі настройки профілю
gyroReset();
resetName();
malb(enter_default_location);//даємо стандартні імена-номера кнопкам
}
void eepromProfile(byte acceptedCommand){
if (acceptedCommand == get_eeprom) {
char buffer[21];
af_saveToEEPROM(get_eeprom, (void*)buffer, 21);
nps = String(buffer);
af_saveToEEPROM(get_eeprom, (void*)listButton, sizeof(listButton));
af_saveToEEPROM(get_eeprom, &deadZoneHor, sizeof(deadZoneHor));
af_saveToEEPROM(get_eeprom, &sensitivityHor, sizeof(sensitivityHor));
af_saveToEEPROM(get_eeprom, &deadZoneVer, sizeof(deadZoneVer));
af_saveToEEPROM(get_eeprom, &sensitivityVer, sizeof(sensitivityVer));
af_saveToEEPROM(get_eeprom, &tape, sizeof(tape));
af_saveToEEPROM(get_eeprom, &namber, sizeof(namber));
af_saveToEEPROM(get_eeprom, &status, sizeof(status));
af_saveToEEPROM(get_eeprom, &lblm, sizeof(lblm));
af_saveToEEPROM(get_eeprom, &tape, sizeof(tape));
af_saveToEEPROM(get_eeprom, &rrsps, sizeof(rrsps));
}
else if (acceptedCommand == set_eeprom) {
af_saveToEEPROM(set_eeprom, (void*)nps.c_str(), 21);
af_saveToEEPROM(set_eeprom, (void*)listButton, sizeof(listButton));
af_saveToEEPROM(set_eeprom, &deadZoneHor, sizeof(deadZoneHor));
af_saveToEEPROM(set_eeprom, &sensitivityHor, sizeof(sensitivityHor));
af_saveToEEPROM(set_eeprom, &deadZoneVer, sizeof(deadZoneVer));
af_saveToEEPROM(set_eeprom, &sensitivityVer, sizeof(sensitivityVer));
af_saveToEEPROM(set_eeprom, &tape, sizeof(tape));
af_saveToEEPROM(set_eeprom, &namber, sizeof(namber));
af_saveToEEPROM(set_eeprom, &status, sizeof(status));
af_saveToEEPROM(set_eeprom, &lblm, sizeof(lblm));
af_saveToEEPROM(set_eeprom, &tape, sizeof(tape));
af_saveToEEPROM(set_eeprom, &rrsps, sizeof(rrsps));
}
}
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);
}
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 lblm;//list button located monitoring
byte rrsps = 1; //rrsps = regime rifle stock profile save
private:
};
// Profile designers конструктори класу Profiles
Profiles profil[] = {// test
Profiles (0, 150, 0, 150, "PROFILE NOT SELECTED", 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, "05461545454546441264", 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 Profiles saveProfile;//використовується для збереження настройок щоб можна Їх було повернути
static byte active = 0;
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
saveProfile = profil[active];
}
else if (acceptedCommand == active_copy_compare) {
return (saveProfile == profil[active]) ? active_copy_same : 0;
}
else if (acceptedCommand == not_save) {
profil[active] = saveProfile;
}
//скидання настроек гіроскопа
else if (acceptedCommand == reset_gyro) {
if (profil[mps(gapn)].gssc()) {//gssc = gyroStandardSettingsCheck якщо є стандартні настройки і хочемо скинути то відображаємо повідомлення що не потрібно
if (mdf_confirm(print_not_need) == function_completed) {
return function_completed;
}
}
else if (mdf_confirm(offer_a_choice) == data_ready) {//якщо настройки нестандартні перевіряємо чи є готові дані-вибір
if (mdf_confirm(get_data) == choice_yes) {profil[mps(gapn)].gyroReset();}//якщо настройки нестандартні перевіряємо чи є готові дані вибір
return function_completed;//в любому випадку так або ні повертається команда function_completed
}
}
//
else if (acceptedCommand == reset_settings) {
active = 8;
}
else if (acceptedCommand == get_eeprom) {
af_saveToEEPROM(get_eeprom, &active, sizeof(active));
profil[active].setStatus(r_on);
}
else if (acceptedCommand == set_eeprom) {
if (Md_saveSelectedProfile(ssp_get_setting)) {//якщо є дозвіл тоді зберігаємо якщо ні то при включенні потрібно буде вибрати профіль
af_saveToEEPROM(set_eeprom, &active, sizeof(active));//!!!
}
}
return 0;
}
// Pc
// Button debounce class
class DebButton {// нагадування Не міняй сука нічого тут все добре
private:
const byte pin;
Timer tmr; //test
byte debounceTime;
byte buttonState = 0;
public:
// Конструктор класу, ініціалізація піна, номера кнопки геймпада та часу захисту від випадкових спрацьовувань
DebButton(int p, int dT = 10) : pin(p) {
debounceTime = dT;
pinMode(pin, INPUT_PULLUP);
tmr.sp(debounceTime);
}
// Метод для обробки натискання кнопки
bool press() {
if (!digitalRead(pin) && !buttonState && tmr.gp()) {// після натську кнопки буде повернення true
buttonState = true;
}
else if (buttonState && digitalRead(pin) && tmr.gp()) {// пісня відпущення кнопки буде повернення брехня
buttonState = false;// тепер переміна буде брехня
}
return buttonState;// постійно повертаємо стан кнопки
}
};
// Bdc
// Button gamepad class + supporting functions
class ButGamepad {
private:
byte gamepadButtonName;
byte pinReadType;
byte numbPin;
int flagPress = 0; //можемо зчитати через метод Get стан кнопки
uint32_t tmr = 0;
int cD[2] = {2048+300, 4000};//calibration data
//вказівник вказує на одну з вибраних функцій які ми вибираємо конструкторi
typedef byte (ButGamepad::*GamepadPressMethod)(byte);
GamepadPressMethod gamepadPressPtr;
public:
//функції використовуються всередині класу обробка натиску кнопки
byte tactilePinButtonRead(byte acceptedCommand = 0) {
if (acceptedCommand == read_button_state) {//після натиску відбувається одноразове повернення правда або брехня
bool dr = digitalRead(numbPin);
if (!dr && !flagPress && (tmr < millis())) {//
tmr = millis() + 50;
flagPress = true;
return press_button;
}
else if (flagPress && dr && (tmr < millis())) {//
tmr = millis() + 50;
flagPress = false;// тепер переміна буде брехня
return release_button;
}
}
else if (acceptedCommand == i_p_p_g_b && pinReadType == tactile_read){ //i_p_p_g_b=input pullup pin gamepad button)
pinMode(numbPin, INPUT_PULLUP);
}
return 0;// постійно повертаємо стан кнопки
}
byte touchPinButtonRead(byte acceptedCommand = 0) {
bool tr = touch_dead_zone < touchRead(numbPin);
if (tr && !flagPress && (tmr < millis())) {// після натську кнопки буде повернення true
tmr = millis() + 50;
flagPress = true;
return press_button;
}
else if (!tr && flagPress && (tmr < millis())) {// пісня відпущення кнопки буде повернення брехня
tmr = millis() + 50;
flagPress = false;
return release_button;
}
return 0;// постійно повертаємо стан кнопки
}
int analogPinButtonRead(byte acceptedCommand = 0) {
if(acceptedCommand == read_button_state){
bool ar = cD[d_z] < analogRead(numbPin);
if (ar && !flagPress) {
flagPress = true;
return press_button;
}
else if (!ar && flagPress) {// пісня відпущення кнопки буде повернення брехня
flagPress = false;
return release_button;
}
}
else if (acceptedCommand == read_analog_state){
int ar = analogRead(numbPin);
if (cD[d_z] < ar) {// після натську кнопки буде повернення true
flagPress = true;
ar = constrain(ar, 0, cD[m_v]);
return map(ar, 0, cD[m_v], -32767, 32767);
}
}
return flagPress = false;// якщо нема виходу за мертву зону получаємо Нолик //скидаємо стан флажка на 0 він використовується для получення даних про стан кнопки
}
//
ButGamepad(byte gBN, byte pRT, byte nP) { //конструктор проводимо настройку при першому запуску
gamepadButtonName = gBN;
pinReadType = pRT;
numbPin = nP;
if (gamepadButtonName == trigger_r || gamepadButtonName == trigger_l) {// вибір яким способом буде натиснути курок(R L)геймпада. курок(R L) == tactile touch analog
if (gamepadButtonName == trigger_r) {//14 означає правий курок
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;
}
}
}
// функції які викликаються через вказівник зроблено для того щоб в циклі фор викликати через gamepadBatton різні функції по черзі
// Bluetooth gamepad push button
byte tactilePress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (tactilePinButtonRead(read_button_state)) {
case press_button:
bgc_buttonGamepadPressRelease(press_button, gamepadButtonName);
return press_button;
case release_button: //після того як кнопка відпущена повертається правда
bgc_buttonGamepadPressRelease(release_button, gamepadButtonName);
return release_button;
}
}
else if (acceptedCommand == r_b_double_press) {//використовується для кнопки приціл коли не можна змінити режим включення прицілу
switch (tactilePinButtonRead(r_b_double_press)) { ///test
case true:
bgc_buttonGamepadPressRelease(press_button, gamepadButtonName);
break;
case false:
bgc_buttonGamepadPressRelease(release_button, gamepadButtonName);
break;
}
}
return 0;
}
byte touchPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (touchPinButtonRead()) {
case press_button:
bgc_buttonGamepadPressRelease(press_button, gamepadButtonName);
return press_button;
case release_button:
bgc_buttonGamepadPressRelease(release_button, gamepadButtonName);
return release_button;
}
}
return 0;
}
byte analogReadPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (analogPinButtonRead(read_button_state)) {
case press_button:
bgc_buttonGamepadPressRelease(press_button, gamepadButtonName);
return press_button;
case release_button:
bgc_buttonGamepadPressRelease(release_button, gamepadButtonName);
return release_button;
}
}
return 0;
}
//
// Bluetooth gamepad right trigger pressure
byte analogReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {//
static int pd; //
int ar = analogPinButtonRead(read_analog_state);//
if (ar != pd) {//звіряємо нові дані з старими
pd = ar;//перезаписуємо нові дані
bleGamepad.setRightTrigger(pd);//відправляємо зчитані перетворені дані
if (pd) {return press_button;}//якщо дані весь час міняються в плюс або мінус повертаємо повідомлення що кнопка нажата
else if (!pd) {return release_button;}//якщо останній раз повернеться Нолик курок буде мертвій зоні виведем повідомлення що кнопка відпущена
}
}
return 0;
}
byte touchReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (touchPinButtonRead()) {
case true:
bleGamepad.setRightTrigger(32767);
return press_button;
case false:
bleGamepad.setRightTrigger(0);
return release_button;
}
}
return 0;
}
byte tactileReadTriggerRightPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (touchPinButtonRead()) {
case true:
bleGamepad.setRightTrigger(32767);
return press_button;
case false:
bleGamepad.setRightTrigger(0);
return release_button;
}
}
return 0;
}
//
// Bluetooth gamepad left trigger pressure
byte analogReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
static int pd; //
int ar = analogPinButtonRead(read_analog_state);
if (ar != pd) {
pd = ar;
bleGamepad.setLeftTrigger(pd);
if (pd) {return press_button;}//якщо дані весь час міняються в плюс або мінус повертаємо повідомлення що кнопка нажата
else if (!pd) {return release_button;}
}
}
return 0;
}
byte touchReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (touchPinButtonRead()) {
case true:
bleGamepad.setLeftTrigger(32767);
return press_button;
case false:
bleGamepad.setLeftTrigger(0);
return release_button;
}
}
return 0;
}
byte tactileReadTriggerLeftPress(byte acceptedCommand) {
if (acceptedCommand == read_button_state) {
switch (touchPinButtonRead()) {
case true:
bleGamepad.setLeftTrigger(32767);
return press_button;
case false:
bleGamepad.setLeftTrigger(0);
return release_button;
}
}
return 0;
}
//
//використовується для переінціалізації вказівника на функцію заміна кнопок місцями
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) {
return (this->*gamepadPressPtr)(acceptedCommand);
}
int* calibrationData(int cmd, int newData[]){//калібровочні дані для курків
if (cmd == g_c_d) {
return cD;
}
else if (cmd == s_c_d) {
memcpy(cD, newData, sizeof(cD));
}
return nullptr;
}
bool getStatus(){//получимо стан флажка-кнопки
return flagPress;//стан флажка міняється в допоміжних функціях які обробляють сигнал дивитись вверху
}
};// Button gamepad
//створюємо екземпляри класу настроюємо вказівник на необхідну функцію
//ButGamepad(1(reinstall!), 2, 3) 1-вибираємо курок чи кнопка 2-вибираємо чим будемо натискати сенсорна-звичайна кнопка або аналог 3-номер піна
ButGamepad butGamepad[] = {// test конструктор класів вказуємо номера пінів і способи зчитування які відповідають за кнопки
ButGamepad(profil[mps(gapn)].listButton[0], tactile_read, 5), //стандартно listButton[0] = gamepad button n1
ButGamepad(profil[mps(gapn)].listButton[1], tactile_read, 23),
ButGamepad(profil[mps(gapn)].listButton[2], tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[3], tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[4], tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[5], tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[6], tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[7], tactile_read, 16),
ButGamepad(profil[mps(gapn)].listButton[8], tactile_read, 16), // button_select
ButGamepad(profil[mps(gapn)].listButton[9], tactile_read, 16), // button_start
ButGamepad(profil[mps(gapn)].listButton[10], tactile_read, 16), // dpad_up
ButGamepad(profil[mps(gapn)].listButton[11], tactile_read, 17), // dpad_down
ButGamepad(profil[mps(gapn)].listButton[12], tactile_read, 16), // dpad_left
ButGamepad(profil[mps(gapn)].listButton[13], tactile_read, 17), // dpad_right
ButGamepad(profil[mps(gapn)].listButton[14], analog_read, p_triger_r_35), // trigger_r
ButGamepad(profil[mps(gapn)].listButton[15], analog_read, p_triger_l_34), // trigger_l
ButGamepad(profil[mps(gapn)].listButton[16], tactile_read, 16), //два останніх для дублювання кнопок
ButGamepad(profil[mps(gapn)].listButton[17], tactile_read, 16)
};
byte bgc_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);//перетворюємо натиснуті кнопки в координати по осі x для використання в масиві
}
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 bgc_buttonGamepadReinstallAll() {
for (int i = 0; i <= 15; i++) {
butGamepad[i].buttonReinstall(profil[mps(gapn)].listButton[i]); //аналог перезапуску всіх(16ти) конструкторів екземплярів класу
}
}
void bgc_buttonConnectionResistor(){
for (int i = 0; i <= 15; i++) {//
butGamepad[i].tactilePinButtonRead(i_p_p_g_b);
}
delay(10);
}
void bgc_shutdownButtonsSticking(){
Gf_gamepadButtons();//кнопки які залив натисниться
for (int i = 1; i <= 8; i++) {//І дальше в обхід відбудеться примусове відключення
bleGamepad.release(i);
}
// Відпустити кнопки Start та Select
bleGamepad.releaseStart();
bleGamepad.releaseSelect();
// Якщо використовуються L2/R2 як осі Z і RZ (осьові тригери)
bleGamepad.setZ(0); // L2
bleGamepad.setRZ(0); // R2
// Якщо ти використовуєш AutoReport = 0
bleGamepad.sendReport();
}
// Bgc
// Additional functions Function svmbols
//Additional functions
//void tick(){}
constexpr byte af_centerY(byte index, byte totalItems, byte spacing = 0) {
constexpr byte displayHeight = 64;
byte effectiveLineHeight = (spacing == 0 && totalItems > 1) ? displayHeight / totalItems : (8 + spacing);
return (displayHeight - totalItems * effectiveLineHeight) / 2 + index * effectiveLineHeight;
}
int clampMinMax(int value, int minVal, int maxVal) {
if (value < minVal) return minVal;
if (value > maxVal) return maxVal;
return value;
}
//Encoder module
bool af_checkClick() {
if (enc.isClick()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkTurn() {
if (enc.isTurn()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkRightHolded() {
if (enc.isRightH()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkLeftHolded() {
if (enc.isLeftH()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkFastRight() {
if (enc.isFastR()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkFastLeft() {
if (enc.isFastL()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkRight() {
if (enc.isRight()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
bool af_checkLeft() {
if (enc.isLeft()) {
Md_autoPowerOff(apo_restart_time);
Md_displayTimeout(timeout_restart);
return true;
}
return false;
}
//
//Shooting effects
void af_triggerShootingEffects(byte acceptedCommand){//функція запускає ефекти викликається в analogReadTriggerRightPress
static byte flagPress;
if (!flagPress && butGamepad[14].getStatus()) { //працює лише коли підключений Bluetooth
flagPress = true;
af_triggerLight(color_two);
af_shotgunBolt(bolt_start);
af_vibration(vibration_start);
}
else if (flagPress && !butGamepad[14].getStatus()) {// пісня відпущення кнопки буде повернення брехня
flagPress = false;
af_triggerLight(color_one);
af_shotgunBolt(bolt_stop);
af_vibration(vibration_stop);
}
af_triggerLight();
af_shotgunBolt();
af_vibration();
}
byte af_shotgunBolt(byte acceptedCommand){
static byte boltPermission = 1;
if (acceptedCommand){
if (acceptedCommand == bolt_start && boltPermission){
digitalWrite(p_bolt_rifle_12, HIGH);
}
else if (acceptedCommand == bolt_stop && boltPermission){
digitalWrite(p_bolt_rifle_12, LOW);
}
else if (acceptedCommand == bolt_change){
if (mdf_magnifier(boltPermission, 0, 1, 1, 1)){
if (!boltPermission){digitalWrite(p_bolt_rifle_12, LOW);}
}
}
else if (acceptedCommand == bolt_get_permission){ //використовується для відображення включена чи виключено
return boltPermission;
}
else if (acceptedCommand == bolt_pin_activate){
pinMode(p_bolt_rifle_12, OUTPUT);
digitalWrite(p_bolt_rifle_12, LOW);
}
else if (acceptedCommand == acceptedCommand == reset_settings) {
boltPermission = 1;
}
else if (acceptedCommand == get_eeprom) {
af_saveToEEPROM(get_eeprom, &boltPermission, sizeof(boltPermission));
}
else if (acceptedCommand == set_eeprom) {
af_saveToEEPROM(set_eeprom, &boltPermission, sizeof(boltPermission));
}
}
return 0;
}
byte af_vibration(byte acceptedCommand){
static byte vibrationPermission = 1;
if (acceptedCommand){
if (acceptedCommand == vibration_start && vibrationPermission){
digitalWrite(p_vibration_motor_18, HIGH);
}
else if (acceptedCommand == vibration_stop && vibrationPermission){
digitalWrite(p_vibration_motor_18, LOW);
}
else if (acceptedCommand == vt_change){
if (mdf_magnifier(vibrationPermission, 0, 1, 1, 1)){
if (vibrationPermission){
digitalWrite(p_vibration_motor_18, HIGH); //test можливо заребриє на секунду потрібно тестувати
digitalWrite(p_vibration_motor_18, LOW);
}
else {digitalWrite(p_vibration_motor_18, LOW);}
}
}
else if (acceptedCommand == vt_get_permission){
return vibrationPermission;
}
else if (acceptedCommand == vt_pin_activate){
pinMode(p_vibration_motor_18, OUTPUT);
digitalWrite(p_vibration_motor_18, LOW);
}
else if (acceptedCommand == reset_settings) {
vibrationPermission = 1;
}
else if (acceptedCommand == get_eeprom) {
af_saveToEEPROM(get_eeprom, &vibrationPermission, sizeof(vibrationPermission));
}
else if (acceptedCommand == set_eeprom) {
af_saveToEEPROM(set_eeprom, &vibrationPermission, sizeof(vibrationPermission));
}
}
return 0;
}
int af_triggerLight(byte acceptedCommand, int acceptedData){
static byte light1 = 100;
static byte light2 = 100;
static int color1 = 260;
static int color2 = 0;
static byte lom = color_switching; // lom = light operating mode
if (acceptedCommand){
if (acceptedCommand == color_one){
if (lom == color_switching){
af_setColorByValue(color1, light1);
}
}
else if (acceptedCommand == color_two){
if (lom == color_switching){
af_setColorByValue(color2, light2);
}
}
else if (acceptedCommand == get_color1){
return color1;
}
else if (acceptedCommand == get_color2){
return color2;
}
else if (acceptedCommand == get_light1){
return light1;
}
else if (acceptedCommand == get_light2){
return light2;
}
else if (acceptedCommand == set_color1){
color1 = acceptedData;
af_setColorByValue(color1, light1);
}
else if (acceptedCommand == set_color2){
color2 = acceptedData;
af_setColorByValue(color2, light2);
}
else if (acceptedCommand == set_light1){
light1 = acceptedData;
af_setColorByValue(color1, light1);
}
else if (acceptedCommand == set_light2){
light2 = acceptedData;
af_setColorByValue(color2, light2);
}
else if (acceptedCommand == clssc){ //clssc = color light standard settings check
if (light1 == 100 && light2 == 100 && color1 == 260 && color2 == 0){
return color_light_default;
}
}
else if (acceptedCommand == color_light_reset){
light1 = 100;
light2 = 100;
color1 = 260;
color2 = 0;
af_setColorByValue(color1, light1);
}
else if (acceptedCommand == color_switching){//один з двох режимів переключення кольорів або настройка кольору
lom = color_switching;
}
else if (acceptedCommand == color_light_setting){
lom = color_light_setting;
}
}
return 0;
}
void af_setColorByValue(uint16_t colorIndex, uint8_t brightnessPercent) {
// Обмеження: не нижче 1, не вище 1000
colorIndex = constrain(colorIndex, 1, 1000);
brightnessPercent = constrain(brightnessPercent, 0, 100);
uint8_t hue;
uint8_t sat;
if (colorIndex <= 700) {
// Відтінки кольору (насиченість максимальна)
hue = map(colorIndex, 1, 700, 0, 255);
sat = 255;
} else {
// Перехід до білого — зменшуємо насиченість
hue = map(700, 1, 700, 0, 255); // Зберігаємо останній hue
sat = map(1000 - colorIndex, 0, 300, 0, 255);
}
// Яскравість
uint8_t val = map(brightnessPercent, 0, 100, 0, 255);
leds[0] = CHSV(hue, sat, val);
FastLED.show();
}
//
void af_animatePointer(int x, int y) {//демонструє що є перехід на наступний пункт
static byte pointerPos = 0;
static unsigned long pointerTimer = 0;
display.setCursor(x, y);
display.print(">>>");
display.setCursor(x + pointerPos * 6, y);
display.write(16); // це стрілочка ►
if (pointerTimer < millis()) {
pointerTimer = millis()+400;
pointerPos++;
if (pointerPos > 2) {pointerPos = 0;}
}
}
#define START_TIMER() \
static unsigned long prevMicros = 0; \
unsigned long startMicros = micros();
#define END_TIMER() \
{ \
unsigned long endMicros = micros(); \
unsigned long elapsed = endMicros - startMicros; \
if (elapsed > prevMicros) { \
Serial.print("Час виконання: "); \
Serial.print(elapsed); \
Serial.print(" мкс | "); \
Serial.print(elapsed / 1000.0, 3); \
Serial.println(" мс"); \
prevMicros = elapsed; \
} \
}
void af_powerOffExecution(){
//af_saveToEEPROM(eeprom_seve);
display.clearDisplay();
display.display();
leds[0] = CHSV(0, 0, 0);
FastLED.show();
btStop(); // Вимкнути Bluetooth, якщо був увімкнений
esp_deep_sleep_start();
}
// !!! нагадування можна просто викликати всі функції які мають мати дані з енергонезалежної пам'яті по черзі і проінтеризувати зміни всередині тих функцій через лямбда функцію
int af_saveToEEPROM(int acceptedCommand, void* address, size_t dataSize) {//TEST
static int addInd = 1;///перший байт використовується для флажка
uint8_t* bytePointer = (uint8_t*)address;
if (acceptedCommand == set_eeprom) {
bool dataChanged = false; // Флаг для перевірки змін
for (size_t i = 0; i < dataSize; i++) {
if (*(bytePointer + i) != EEPROM.read(addInd + i)) {//економія ресурсів перед записом Перевіряємо чи дані відрізняються якщо відрізняються відбудеться перезапис
dataChanged = true; // Дані змінилися
break; // Якщо знайшли різницю, не потрібно перевіряти далі
}
}
if (dataChanged) {//якщо стан важка змінився значить дані потрібно перезаписати
for (size_t i = 0; i < dataSize; i++) {
EEPROM.write(addInd + i, *(bytePointer + i)); // Побайтово записуємо в EEPROM
}
EEPROM.commit(); // Фіксуємо зміни
}
addInd += dataSize;
}
else if (acceptedCommand == get_eeprom || acceptedCommand == get_eeprom_last_read) {//по байтах зчитуємо збережені дані
for (size_t i = 0; i < dataSize; i++) {
*(bytePointer + i) = EEPROM.read(addInd+i);
}
if (acceptedCommand == get_eeprom) {
addInd += dataSize;// Переходимо до наступного індексу
} else{addInd = 1;}//якщо в нас get_eeprom_last_read то потрібно скинути щоб записувати знову з першого байта
}
else if (acceptedCommand == address_indicator_reset) {
addInd = 1;
}
else if (acceptedCommand == eeprom_initialization && !EEPROM.read(0)) {
for (int i = 8; i < 14; i++) {
profil[i].eepromProfile(get_eeprom);
}
mps(get_eeprom);
af_vibration(get_eeprom);
af_shotgunBolt(get_eeprom);
af_triggerLight(get_eeprom);
Md_displayTimeout(get_eeprom);
Md_autoPowerOff(get_eeprom);
Md_gyroCalibrationExecution(get_eeprom);
af_saveToEEPROM(address_indicator_reset);
}
else if (acceptedCommand == eeprom_seve) {
if (EEPROM.read(0)) {EEPROM.put(0, 0);EEPROM.commit();}
for (int i = 8; i < 14; i++) {
profil[i].eepromProfile(set_eeprom);
}
mps(set_eeprom);
af_vibration(set_eeprom);
af_shotgunBolt(set_eeprom);
af_triggerLight(set_eeprom);
Md_displayTimeout(set_eeprom);
Md_autoPowerOff(set_eeprom);
Md_gyroCalibrationExecution(set_eeprom);
}
return 0;
}
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 (enc.isHolded()) {
currentStage = 1; // Переходимо до наступного етапу
}
break;
case 1: // Етап 2: Очікування кліку
if (af_checkClick()) {
currentStage = 2; // Переходимо до наступного етапу
return password_entered;
}
break;
}
return password_not_entered;
}
else if (acceptedCommand == password_reset){
currentStage = 0;
}
return 0;
}
void af_centerText(int x, int y, auto input, bool minusNo = false) {
// Перетворюємо вхідне значення у рядок
static String text;
static int adjustedLength;
static int textWidth;
// Якщо треба ігнорувати мінус, коригуємо довжину тексту
text = String(input);
adjustedLength = text.length();
if (minusNo && text[0] == '-') {
adjustedLength--; // Ігноруємо мінус
}
// Обчислюємо ширину тексту
textWidth = adjustedLength * 6; // 6 пікселів на символ
// Встановлюємо курсор
display.setCursor(x - (textWidth / 2), y);
// Виводимо текст
display.print(text);
}
void af_displayPrintLain(String text, byte x, byte y, byte linePosition, byte cropText) {// (ДИВИТИСЬ ПОЯСНЕННЯ)
// Статичні змінні для збереження стану між викликами
static Timer tmr(10); // Таймер для контролю автоматичної прокрутки також включаємо оновлення дисплея при кожному зміщенні тексту
static byte lps; // Змінна для збереження line Position seve
static int stx; // Позиція прокрутки по горизонталі
byte stxt; // Тимчасова змінна для прокрутки stxt =crolling text x temporary
static int tlip; // Довжина тексту в пікселях tlip = text 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 (tmr.ta()) {// test здається має бути інший таймер
if (tlip > stx) {
stx += 1; // Збільшення позиції прокрутки
} else {
stx = 0; // Скидання прокрутки до початку тексту
}
}
stxt = stx; // Присвоєння тимчасової змінної значення прокрутки
}
// Встановлення курсора з урахуванням прокрутки та виведення тексту
//if (linePosition) {display.drawRect((x + cropText * 6)-12, y, 5, 8, 1);}
display.setCursor(x - stxt, y);
display.print(text);
//чорні прямокутники для закриття потрібні лише тоді коли даний параметр приймає любе значення окрім нуля
//якщо даний параметр брехня То це означає що текст поміщається виділену область
if (linePosition) {
display.fillRect(0, y, x, 8, 0); // Ліва область
display.fillRect(x + cropText * 6, y, 128, 8, 0); // Права область відповідно до обрізаного тексту
display.fillRect(0, y + 7, 128, 64, 0); // Очищення області під текстом
display.setCursor((x + cropText * 6)-3, y);
display.write(16); // це стрілочка ►
}
}
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_startAnimation(){
display.clearDisplay();
display.invertDisplay(0);
display.setCursor(-2, 28);
display.println(" FS G36 ");
display.display();
delay(500);
display.clearDisplay();
display.display();
}
//
//Function svYmbols (функції відображають власні створені символи)
void fs_bluetoothSymbol(){
const byte bluArr[9] = {
0b00100,
0b00110,
0b10101,
0b01110,
0b00100,
0b01110,
0b10101,
0b00110,
0b00100
};
display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
}
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); // Малюємо білий піксель
}
}
}
}
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); // Малюємо білий піксель
}
}
}
}
void fs_letterDeletionsvYmbol(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); // Малюємо білий піксель
}
}
}
}
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); // Малюємо білий піксель
}
}
}
}
void fs_square2x2(int xOffset, int yOffset) {
display.drawPixel(xOffset, yOffset, SSD1306_WHITE);
display.drawPixel(xOffset + 1, yOffset, SSD1306_WHITE);
display.drawPixel(xOffset, yOffset + 1, SSD1306_WHITE);
display.drawPixel(xOffset + 1, yOffset + 1, SSD1306_WHITE);
}
void fs_horizontal6(int xOffset, int yOffset) {
for (int x = 0; x < 6; x++) {
display.drawPixel(x + xOffset, yOffset, SSD1306_WHITE);
}
}
void fs_vertical6(int xOffset, int yOffset) {
for (int y = 0; y < 6; y++) {
display.drawPixel(xOffset, y + yOffset, SSD1306_WHITE);
}
}
void fs_miniTesterSmiley(int xOffset, int yOffset) {
}
//
// Af Fs
// Main display
// point 0 information display
void Md_infoDisplay() {// головна фунція виклик решта функцій дисплея
switch (mdf_addres(check_emb, 0)) {
case address_not_entered:
Md_p_infoDisplay();//виводимо перший пункт меню який буде викликати решту пунктів
break;
case 1:
Md_mainDisplay();
break;
}
}
void Md_p_infoDisplay() {
static Timer tmr(1000);
const byte Lines[] = {255};//щоб стрілка курсора не відображалася ставимо невідому координату
mdf_cursor(cursor_read_encoder, 1, Lines);
mdf_bluetoothPrint();
mdf_typeNumberPrint();
//mdf_batteryPrint();
display.drawRect(0, 14, 127, 19, WHITE);// РАМКА НАВКОЛО НАЗВИ
if (!mps(gapn)){ //якщо профіль не вибрано виводимо SELECT PROFILE
if (60000 < millis() || tmr.ta()){
af_centerText(X(64), Y(20), profil[mps(gapn)].nameProfiles(get_name));
}
}
else{
af_centerText(X(64), Y(20), 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");
}
//
// point 0-1 selecting point menu
void Md_mainDisplay() {// головна фунція виклик решта функцій дисплея
switch (mdf_addres(check_emb, 1)) {
case address_not_entered:
Md_p_mainDisplay();
mdf_bluetoothPrint();
mdf_typeNumberPrint();
//mdf_batteryPrint();
break;//
case 1:
if(!mps(gapn)){//якщо профіль не вибрано переходимо в пункт selectProfile
Md_selectProfile();
}else{
profil[mps(gapn)].tape == t_sp ? Md_ProfileSp() : Md_ProfileMp();//відображаємо меню залежності від типу профіля
}
break;
case 2:
Md_GeneralSettings();
break;
case 3:
// qr-код ссылка на інструкцію
break;
case 4: //back
af_saveToEEPROM(eeprom_seve);
mdf_cursor(set_pos, 1);
mdf_addres(back_add, 0);
break;
case address_entered:
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_mainDisplay(){
const byte Lines[] = {22, 32, 42, 52};
mdf_cursor(cursor_read_encoder, 4, Lines);
if(!mps(gapn)){//якщо профіль не вибрано то попадаємо в пункт для вибору
display.setCursor(0, 22);
display.println(" Select profile");
}else{
display.setCursor(0, 22);
display.println(" Profile");
}
display.setCursor(0, 32);
display.println(" Settings");
display.setCursor(0, 42);
display.println(" Instruction (!)");
display.setCursor(0, 52);
display.println(" Exit ");
}
//
// point 0-1-1 profile setting
// add2-----------------------------------------------------------------------------------------------------------
// Profile Sp
void Md_ProfileSp() {
switch (mdf_addres(check_emb, 2)) {
case address_not_entered:
Md_p_ProfileSp();
break;
case 1:
Md_profileSettingsSp();
break;
case 2: // QR code
// Додайте код для обробки QR-коду тут, якщо потрібно
break;
case 3:
Md_selectProfile();
break;
case 4: //back
mdf_cursor(set_pos, mdf_addres(get_add, 1));
mdf_addres(back_add, 1);
break;
case address_entered:
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_ProfileSp() {
const byte LinesSp[] = {31, 39, 49, 57};
mdf_cursor(cursor_read_encoder, 4, 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);
af_centerText(X(64), Y(15), 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, 2)) {
case address_not_entered:
Md_p_ProfileMp();
break;
case 1:
Md_profileSettingsMp();
break;
case 2:
Md_selectProfile();
break;
case 3: //back
mdf_cursor(set_pos, mdf_addres(get_add, 1));
mdf_addres(back_add, 1);
break;
case address_entered:
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_ProfileMp() {
const byte LinesMp[] = {35, 49, 57};
mdf_cursor(cursor_read_encoder, 3, 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);
af_centerText(X(64), Y(15), 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" );
}
//
//
// add3-----------------------------------------------------------------------------------------------------------
// Profile settings Sp
void Md_profileSettingsSp() {
switch (mdf_addres(check_emb, 3)) {
case address_not_entered:
Md_p_profileSettingsSp();
break;
case 1:
Md_Buttons();
break;// Buttons
case 2:// back //якщо були зміни то повернення відбудеться через функцію prof mdf_profileStorage
mdf_cursor(set_pos, mdf_addres(get_add, 2)); //повернення
mdf_addres(back_add, 2);
break;
case address_entered:
if (mdf_addres(get_add, 3) == 1){mdf_cursor(set_pos, 1);}
break;
}
}
void Md_p_profileSettingsSp() {
const 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, 3)) {
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
mdf_cursor(set_pos, mdf_addres(get_add, 2));
mdf_addres(back_add, 2);
break;
case address_entered:
if (mdf_addres(get_add, 3) != 4 ){ mdf_cursor(set_pos, 1);}
break;
}
}
void Md_p_profileSettingsMp() {
const 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, 3)) {
case address_not_entered:
Md_p_selectProfile();
break;
case 1:
Md_standardProfiles();
break;
case 2:
Md_myProfiles();
break;
case 3: //back
if(!mps(gapn)){//якщо профіль все одно не вибрана то повертаємося в головний пункт
mdf_cursor(set_pos, mdf_addres(back_add, 1));
}else{//якщо хотіли змінити профіль і передумали повертаємося в попередній пункт і ставимо курсор в залежності від типу профіля
mdf_addres(back_add, 2);
profil[mps(gapn)].tape == t_sp ? mdf_cursor(set_pos, 3) : mdf_cursor(set_pos, 2);//вказуємо вручну попередній пункт може мінятися Залежно від типу
}
break;
case address_entered:
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_selectProfile() {
const byte Lines[] = {16, 24, 32};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.setCursor(0, 16);
display.println(" Standard profiles" );
display.println(" My profiles");
display.println(" Back" );
}
// Sp
//
//
// add4-----------------------------------------------------------------------------------------------------------
// Standard profiles
void Md_standardProfiles(){
switch (mdf_addres(check_emb, 4)) {
case address_not_entered:
Md_p_standardProfiles();// відображення списку профілів вибір
break;
case 8: //back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
if(mdf_addres(get_add, 4) != 8){
if(!mps(gapn)){// після того як перший раз вибрали профіль повертаємося в основне меню
mps(activate_p, mdf_addres(get_add, 4));
bgc_buttonGamepadReinstallAll();// test аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, mdf_addres(back_add, 0));
}else{//
mps(activate_p, mdf_addres(get_add, 4));
bgc_buttonGamepadReinstallAll();// test аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, 3);//в даному випадку вказуємо позицію три тому що є три пункти
mdf_addres(back_add, 2);//повертаємося через один пункт
}
}
break;
}
}
void Md_p_standardProfiles(){
const 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, 4)){// третій параметр забороняє вибирати діючий профіль
case address_not_entered:
Md_p_MyProfiles();
break;
case 8: //back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
if (mdf_addres(get_add, 4) != 8) {
if(!mps(gapn)){//
mps(activate_p, mdf_addres(get_add, 4)+7);
bgc_buttonGamepadReinstallAll();// аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, mdf_addres(back_add, 0));
}else{//
mps(activate_p, mdf_addres(get_add, 4)+7);
bgc_buttonGamepadReinstallAll();// аналог конструкторів класу перезапуск, listButton потрібно взяти з активного профіля і перевстановити
mdf_cursor(set_pos, 2);
mdf_addres(back_add, 2);// не міняти двоєчку будуть глюки
}
}
break;
}
}
void Md_p_MyProfiles() {
const 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(){
Md_p_gyroscopeSettings();//відображаємо пункти меню постійно
switch (mdf_addres(check_emb, 4)) {
case address_not_entered:
break;
case 1:
mdf_magnifier(profil[mps(gapn)].deadZoneHor, 0, 9999, 1, 100); //міняємо чутливість в профілю
break;
case 2:
mdf_magnifier(profil[mps(gapn)].sensitivityHor, 50, 150, 1, 5);//якщо функція поверне -1 значить передане число Не збільшувалось і не зменшувалось і тому не потрібно виконувати перезапис
break;
case 3:
mdf_magnifier(profil[mps(gapn)].deadZoneVer, 0, 9999, 1, 100);
break;
case 4:
mdf_magnifier(profil[mps(gapn)].sensitivityVer, 50, 150, 1 , 5);
break;
case 5:
if (mps(reset_gyro) == function_completed) {mdf_addres(back_add, 4);}//скидаємо настройки гіроскопа після вибору так
break;
case 6: //back
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered:
if (mdf_addres(get_add, 4) <= 4) {
mdf_addres(mode_click_back);
}
else if (mdf_addres(get_add, 4) == 5 ) {//якщо вибрали даний пункт значить робимо скидання до стандартних настройок
mdf_addres(mode_function_returns);//
mdf_confirm(output_by_coordinates, X(39), Y(46));//
}
/*
if (mdf_addres(check_emb, 4) == 1 || mdf_addres(check_emb, 4) == 3) {// test зупиняємо виконання функції Gf_gyro для того щоб настроїти мертву зону
timerAlarmDisable(timer);
}
*/
break;
}
}
void Md_p_gyroscopeSettings(){
static Timer tmr(500);
const 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_centerText(X(113), Y(2), profil[mps(gapn)].deadZoneHor);//виводимо настройки гіроскопа центруємо значення в рамці
int temp = map(profil[mps(gapn)].sensitivityHor, 50, 150, 100, 0);//відображаємо в процентному співвідношенні а не в одиницях
af_centerText(X(113), Y(11), temp);
af_centerText(X(113), Y(26), profil[mps(gapn)].deadZoneVer);
temp = map(profil[mps(gapn)].sensitivityVer, 50, 150, 100, 0);
af_centerText(X(113), Y(35), temp);
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.ta()){// РАМКА H
display.drawRoundRect(79, 2, 15, 15, 0, WHITE);
}
if((3 != mdf_cursor(get_pos) && 4 != mdf_cursor(get_pos)) || tmr.ta()){// РАМКА V
display.drawRoundRect(79, 2+24, 15, 15, 0, WHITE);
}
}
// Gs
// Buttons
void Md_Buttons(){
switch (mdf_addres(check_emb, 4)) {
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(get_add, 3));
mdf_addres(back_add, 3);
break;
case address_entered: // address_not_entered == 21
mdf_cursor(set_pos, 1);
break;
}
}
void Md_p_Buttons(){
static Timer tmr(500);
const 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));
display.drawRect(77, 15, 45, 11, WHITE); //рамка навколо слів
if (profil[mps(gapn)].malb(check_location) == non_standard_location) {//якщо кнопки мінялися місцями виводимо повідомлення про це
if (tmr.ta()) {af_displayPrintLain("Changed", X(79), Y(17));}
}
else{
af_displayPrintLain("Default", X(79), Y(17));
}
}
// B
// changeNameProfile
//change name profile additional elements
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;// // Вказівник на поточний масив символів (великі або малі букви)
int8_t getActionSelection() {//якщо навелись то функція поверне координату яка відповідає символу
if (cnpaY == 3 && cnpaX == 0) return cl;
if (cnpaY == 3 && cnpaX == 9) return dl;
if (cnpaY == 4 && cnpaX == 9) return tb;
if ((cnpaY == 4) && (cnpaX == 4 || cnpaX == 5)) return sb;
return 0; // Значення за замовчуванням
}
//
void Md_changeNameProfile(){
static int8_t magnitude = 1;// костилі використовується для вибору пробілу і виходу
static int8_t fl;// Перемикач між великими та малими літерами костилі вказівник не працює належним чином
if (af_checkRightHolded()){cnpaY--;cnpaY = af_clamp(cnpaY, 0, 4);}
else if (af_checkLeftHolded()){ cnpaY++;cnpaY = af_clamp(cnpaY, 0, 4);}
else{
if(mdf_magnifier(cnpaX, -1, 10, 1, 1)) {//якщо X вийшло за межі допустимих чисел виконуємо пробірку і збільшення або зменшення Y
if (cnpaX == 10) {cnpaY += 1;}//зменшуємо або збільшуємо y для того щоб перемістити курсор вниз після досягнення максимуму по X
else if (cnpaX == -1) {cnpaY -= 1;}
cnpaY = af_clamp(cnpaY, 0, 4);//додатково перевіряємо y щоб не Вийти за межі
}
cnpaX = af_clamp(cnpaX, 0, 9);//скидання на мінімальне і наоборот якщо перевищили значення
}
if (af_checkClick()) {//Клік виконує зміну бук видалення пробіл додавання букви вихід
if (getActionSelection() == cl) {//міняємо величину бук
ptrLetter = (ptrLetter == letterSmall) ? letterBig : letterSmall;
}
else if (getActionSelection() == dl) {//видаляємо букву
profil[mps(gapn)].nameProfiles(delete_letter);
if (!profil[mps(gapn)].lettersTakeNumber()) {ptrLetter = letterBig;}//якщо видаляємо останню букву робимо всі букви великими одноразово
}
else if (getActionSelection() == tb) {//вихід плюс скидання і перевірка набраного тексту
profil[mps(gapn)].nameProfiles(g_s_n_i_n_l_a_r_s); //(give standard name if no letters and remove spaces ) TEST якщо Видалити всі букви то заміниться ім'я на стандартне
cnpaX = cnpaY = 0;
ptrLetter = letterSmall;
mdf_cursor(set_pos, mdf_addres(get_add, 3));
mdf_addres(back_add, 3);
}
else{ //доплюсовуємо до речення вибрано букву
profil[mps(gapn)].nameProfiles(add_letter, ptrLetter[cnpaY][cnpaX]);
if (profil[mps(gapn)].lettersTakeNumber() == 1) {ptrLetter = letterSmall;}//речення починається з великої букви і автоматично скидається на Малі
}
}
Md_p_changeNameProfile();// віалізуємо програму
}
byte Md_p_changeNameProfile(){
static Timer tmr(500);
display.setCursor(2, 1);// виводи ім'я профіля
display.print(profil[mps(gapn)].nameProfiles(get_name));
if(tmr.ta()){// вертикальна риска біля букви
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.ta()){
ptrLetter == letterSmall ? fs_arrowLowercaseLetters(4, 42) : fs_arrowCapitalLetters(4, 42);
}
if(getActionSelection() != dl){
fs_letterDeletionsvYmbol(110, 44);
}
else if(getActionSelection() == dl && !tmr.ta()){
fs_letterDeletionsvYmbol(110, 44);
}
if(getActionSelection() != sb){
display.fillRect(50, 55, 25, 6, WHITE);
}
else if(getActionSelection() == sb && !tmr.ta()){
display.fillRect(50, 55, 25, 6, WHITE);
}
if(getActionSelection() != tb){
fs_arrowReturn(110, 54);
}
else if(getActionSelection() == tb && !tmr.ta()){
fs_arrowReturn(110, 54);
}
return 0;
}
// cNP
//
// add5-----------------------------------------------------------------------------------------------------------
// Moving buttons
void Md_moveButtons(){
Md_p_moveButtons();
switch (mdf_addres(check_emb, 5)) {
case address_not_entered:
break;
case 1:
if (Md_moveButtonsExecute(select_button) == function_completed){//якщо функція завершена повертаємось назад дозволяємо дії геймпада
mdf_addres(back_add, 5);
Gf_functionsGamepad(functions_gamepad_start);
}
break;
case 2:
Md_moveButtonsExecute(return_default) == function_completed ? mdf_addres(back_add, 5) : [](){return 0;}();
break;
case 3: //back
mdf_cursor(set_pos, mdf_addres(get_add, 4));
mdf_addres(back_add, 4);
break;
case address_entered:
mdf_addres(mode_function_returns);
if (mdf_addres(get_add, 5) == 1){Gf_functionsGamepad(functions_gamepad_stop);}//якщо міняємо кнопки місцями забороняємо дії геймпада
mdf_addres(get_add, 5) == 2 ? mdf_confirm(output_by_coordinates, X(37), Y(46)) : [](){return 0;}();
break;
}
}
void Md_p_moveButtons(){
const 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");
}
int Md_moveButtonsExecute(int acceptedCommand){// test
static bsd sbn1; // sbno = save button name 1
static bsd sbn2; // sbno = save button name 2
switch (acceptedCommand) {
case select_button:
if (af_checkClick()){//функція може завершитися по кліку один з варіантів
sbn1.r();
sbn2.r();
Md_moveButtonsAnimation(reset_animation);
return function_completed;
}
else if (!sbn2.g()) {//якщо sbn2.g() стан правда значить вибрана друга кнопка виконується переміщення
for (int i = 0; i <= 15; i++) {
if (!sbn1.g()) {
if (butGamepad[i].gamepadBatton(read_button_state) == release_button) {//потрібно очікувати відпущення тому що курок постійно повертає кнопка натиснута якщо його повільно відпускати
sbn1.s(i+1); //записуємо порядковий номер екземпляра який спрацював перший Тобто було натиснуто кнопка
break;
}
}
else if (!sbn2.g()) {
if (butGamepad[i].gamepadBatton(read_button_state) == release_button) {
sbn2.s(i+1); //записуємо порядковий номер екземпляра який спрацював перший Тобто було натиснуто кнопка
break;
}
}
}
if(sbn1.g() && sbn2.g()){//потрібно щоб порядковий номер який получимо другим був іншим захист від натиснутого курка дивитись принцип роботи
int savedButton1 = profil[mps(gapn)].listButton[sbn1.g()-1];//зберігаємо елементи масиву в тимчасовій переміні
int savedButton2 = profil[mps(gapn)].listButton[sbn2.g()-1];
profil[mps(gapn)].listButton[sbn2.g()-1] = savedButton1; //міняємо елементи масиву (номер-назви) місцями
profil[mps(gapn)].listButton[sbn1.g()-1] = savedButton2;
bgc_buttonGamepadReinstallAll();//виконуємо перенастройку всіх екземплярів класу buttonGamepad тому що listButton получив зміни
}
}
if (sbn1.g() && sbn2.g()) {//нижче виконується віалізація вибраних кнопок
int t1 = profil[mps(gapn)].listButton[sbn1.g()-1];//зберігаємо елементи масиву в тимчасовій переміні
int t2 = profil[mps(gapn)].listButton[sbn2.g()-1];
if (Md_moveButtonsAnimation(print_batton, t1, t2) == function_completed) {//3//коли натиснути друга кнопка відбувається двосекундне відображення і завершення функції
sbn1.r();
sbn2.r();
return function_completed;
}
}
else if (sbn1.g()) {Md_moveButtonsAnimation(print_batton, profil[mps(gapn)].listButton[sbn1.g()-1]);}//2
else if (!sbn1.g()) {Md_moveButtonsAnimation(print_batton);}//1
break;
case return_default:////виконується reset або якщо нема потреби відображення надпису not_need
if (profil[mps(gapn)].malb(check_location) == standard_location) {
if (mdf_confirm(print_not_need) == function_completed) {
return function_completed;
}
}
else{
if (mdf_confirm(offer_a_choice) == data_ready) {//пропонуємо вибір якщо вибір зроблено получимо data_ready для подальшої перевірки
if (mdf_confirm(get_data) == choice_yes) {//Перевіряємо чи вибір так Якщо так тоді скидаємо
profil[mps(gapn)].lblm = by_default;
profil[mps(gapn)].malb(enter_default_location); //повертаємо стандартну номер-назву
bgc_buttonGamepadReinstallAll();//перенастройка всіх екземплярів класу тому що значення в listButton[] помінялись
}
return function_completed;
}
}
break;
}
return 0;
}
byte Md_moveButtonsAnimation(byte cmd, byte buttonOne, byte buttonTwo) {
static Timer tmr(500); // таймер для виконання анімації //і накопичення ТІК
const char** buttonNames = mdf_getButtonNames();
static bsd t(5); // tick накопичує спрацювання таймера
static bsd ra(7);// ra = Running Arrow
display.drawRect(6, 5, 31, 31, WHITE); // квадрат L
display.drawRect(90, 5, 31, 31, WHITE);// квадрат R
display.drawCircle(21, 20, 8, WHITE);// кружки над кнопками
display.drawCircle(105, 20, 8, WHITE);
if (cmd == print_batton){
if (buttonOne && buttonTwo){//3// відображає вибрані кнопки приблизно дві секунди
af_centerText(X(22), Y(17), buttonNames[buttonOne]);
af_centerText(X(106), Y(17), buttonNames[buttonTwo]);
if (tmr.ta()){t.p(1);} //кожні пів секунди + 1
else if (t.m()){//test накопичуємо 5 і завершуємо програму буде приблизно дві секунди
t.r();
ra.r();
tmr.tat(500,true);
return function_completed;
}
}
else if (buttonOne){//2//очікуємо натиску кнопки 2
af_centerText(X(22), Y(17), buttonNames[buttonOne]);
if (tmr.ta()){
ra.p(1);
af_centerText(X(106), Y(17), '?');
}
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+ra.g()*6, 13);// трикутник переміщається по углових скобках анімація
display.write(16);
display.setCursor(83-ra.g()*6, 23);// трикутник переміщається по углових скобках анімація
display.write(17);
}
else if (!buttonOne && tmr.ta()){//1//Очікуємо на натиск кнопки 1
af_centerText(X(22), Y(17), '?');
}
}
else if (cmd == reset_animation){
tmr.tar();
ra.r();
t.r();
}
return 0;
}
//
// Duplicate buttons
void Md_duplicateButtons(){//вибираємо Яку кнопку будемо дублювати або яку кнопку потрібно відключити від дублюючої кнопки
byte regime[] = {default_mode, select_button_r_s, r_s_button_reset, select_button_r_m, r_m_button_reset};
Md_p_duplicateButtons(regime[mdf_addres(get_add, 5)]);//змінюємо анімацію в залежності від набраного адресу
switch (mdf_addres(check_emb, 5)) {
case 1:
if (Md_duplicateButtonsExecute(select_button_r_s) == function_completed){
Gf_functionsGamepad(functions_gamepad_start);
mdf_addres(back_add, 5);
}
break;
case 2:
if (Md_duplicateButtonsExecute(r_s_button_reset) == function_completed){mdf_addres(back_add, 5);}
break;
case 3:
if (Md_duplicateButtonsExecute(select_button_r_m) == function_completed){
mdf_addres(back_add, 5);
Gf_functionsGamepad(functions_gamepad_start);
}
break;
case 4:
if (Md_duplicateButtonsExecute(r_m_button_reset) == function_completed){mdf_addres(back_add, 5);}
break;
case 5: //back
mdf_cursor(set_pos, mdf_addres(back_add, 4));
break;
case address_entered:
Md_p_duplicateButtons();
if (mdf_addres(get_add, 5) == 1 || mdf_addres(get_add, 5) == 3){
Gf_functionsGamepad(functions_gamepad_stop);
mdf_addres(mode_click_back);
}
else if (mdf_addres(get_add, 5) == 2 || mdf_addres(get_add, 4) == 5){
mdf_addres(mode_function_returns);
}
break;
}
}
void Md_p_duplicateButtons(byte acceptedCommand){
static Timer tmr(300);
const char** buttonNames = mdf_getButtonNames();
af_displayPrintLain("Rifle stock", X(10), Y(4));
af_displayPrintLain("Reset", X(6), Y(14));
af_displayPrintLain("Rifle magazine", X(10), Y(35), L(3), N(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("=");
}
const byte LinesSp[] = {4, 14, 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.ta()) { //виконуємо мигання за вказаною вище періодичністю
display.setCursor(108, 4);
display.println(buttonNames[0]);//відображаємо знак запитання з таблиці символів
}
}
else if (acceptedCommand == select_button_r_m) {//майже та сама схема що і вище
if (tmr.ta()) {
display.setCursor(108, 35);
display.println(buttonNames[0]);
}
}
if (acceptedCommand == select_button_r_m ){
af_centerText(X(111), Y(4), buttonNames[profil[mps(gapn)].listButton[16]]);
}
else if (acceptedCommand == select_button_r_s ){
af_centerText(X(111), Y(35), buttonNames[profil[mps(gapn)].listButton[17]]);
}
}
else {
af_centerText(X(111), Y(4), buttonNames[profil[mps(gapn)].listButton[16]]);
af_centerText(X(111), Y(35), buttonNames[profil[mps(gapn)].listButton[17]]);
}
}
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(read_button_state) == press_button) {//очікуємо натиску кнопки яку будемо дублювати
if (acceptedCommand == select_button_r_s) { // в залежності від вибраної кнопки для дублювання виконуємо копіювання
profil[mps(gapn)].listButton[16] = profil[mps(gapn)].listButton[i];
}
else if (acceptedCommand == select_button_r_m) {
profil[mps(gapn)].listButton[17] = profil[mps(gapn)].listButton[i];
}
bgc_buttonGamepadReinstallAll();//test потрібно повторно підібрати метод натискання тому що кнопки переставились місцями
return function_completed;
}
}
}
else if (acceptedCommand == r_s_button_reset){//в залежності від вибору вибираємо кнопку яку будемо скидати
profil[mps(gapn)].listButton[16] = 0;
return function_completed;
}
else if (acceptedCommand == r_m_button_reset) {//логіка Та ж сама що і описано зверху
profil[mps(gapn)].listButton[17] = 0;
return function_completed;
}
return 0;
}
//
//
//
// point 0-1-2 General settings
// add2 -----------------------------------------------------------------------------------------------------------
void Md_GeneralSettings(){
switch (mdf_addres(check_emb, 2)) {
case 0:
Md_p_GeneralSettings();
break;
case 1:
Md_Various();
break;
case 2:
Md_Effects();
break;
case address_entered:
if (mdf_addres(get_add, 2) == 4){
mdf_cursor(set_pos, mdf_addres(back_add, 1));
}
else if (mdf_addres(get_add, 2) <= 2){
mdf_cursor(set_pos, 1);
}
break;
}
if (mdf_addres(get_add, 2) == 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, 2);
}
break;
case password_entered:
mdf_cursor(set_pos, 1);
break;
default:
Md_expertSettings();
break;
}
}
}
void Md_p_GeneralSettings(){
const 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" );
}
//
// add3 -----------------------------------------------------------------------------------------------------------
// Various
void Md_Various(){
switch (mdf_addres(check_emb, 3)) {
case 1:
Md_aimMode();//тут перехід решта кінцеві
break;
case 2:
//Volume
break;
case 3:
Md_saveSelectedProfile(ssp_change_setting);
break;
case 4:
Md_autoPowerOff(apo_change_time);
break;
case 5:
Md_displayTimeout(dt_change_time);
break;
case address_entered:
if (mdf_addres(get_add, 3) == 6){//back
mdf_cursor(set_pos, mdf_addres(back_add, 2));
}
else if (mdf_addres(get_add, 3) != 1){
mdf_addres(mode_click_back);
}
break;
}
if (mdf_addres(get_add, 3) != 1){//
Md_p_Various();
}
}
void Md_p_Various(){
const byte p = 2, l = 6;
const byte y[6] = {
af_centerY(0, l, p),//af_centerY(номер рядка , кількість рядків, відстань між рядками ),
af_centerY(1, l, p),
af_centerY(2, l, p),
af_centerY(3, l, p),
af_centerY(4, l, p),
af_centerY(5, l, p)
};
const int x = strlen(" Aim mode ") * 6;//test
af_animatePointer(x, y[0]);
af_displayPrintLain("Aim mode", X(6), y[0]);
Md_p_volume(y[1]);
Md_p_saveSelectedProfile(y[2], L(3));
Md_p_autoPowerOff(y[3]);
Md_p_displayTimeout(y[4]);
af_displayPrintLain("Back", X(6), y[5]);
mdf_cursor(cursor_read_encoder, sizeof(y), y);
}
//
// Effects
void Md_Effects(){
switch (mdf_addres(check_emb, 3)) {
case 1:
af_triggerLightSettings();//тут перехід решта кінцеві
break;
case 2:
af_shotgunBolt(bolt_change);
break;
case 3:
af_vibration(vt_change);
break;
case address_entered:
if (mdf_addres(get_add, 3) == 2 || mdf_addres(get_add, 3) == 3){
mdf_addres(mode_click_back);
}
else if (mdf_addres(get_add, 3) == 4){//back
mdf_cursor(set_pos, mdf_addres(back_add, 2));
}
break;
}
if (mdf_addres(get_add, 3) != 1){//
Md_p_Effects();
}
}
void Md_p_Effects(){
const byte Lines[] = {16, 24, 32, 40};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
//const int width = strlen(" Trigger light ") * 6;//test
//af_animatePointer(width, 16);
af_displayPrintLain(" Trigger light ", 0, 16);
Md_p_shotgunBoltSettings();
Md_p_vibration();
af_displayPrintLain(" Back ", 0, 40);
}
//
// Expert settings
void Md_expertSettings(){
switch (mdf_addres(check_emb, 3)) {
case 0:
Md_p_expertSettings();
break;
case 1:
Md_gyroCalibration();
break;
case 2:
Md_disableButton();
break;
case 3:
Md_resetAll();
break;
case address_entered:
if (mdf_addres(get_add, 3) == 4) { //back
af_passwordEntry(password_reset);
mdf_cursor(set_pos, mdf_addres(back_add, 2));
}else{
mdf_cursor(set_pos, 1);
}
break;
}
}
void Md_p_expertSettings(){
af_displayPrintLain("Gyro calibration", X(6), Y(16));
af_displayPrintLain("Stuck buttons", X(6), Y(25));
af_displayPrintLain("Reset all", X(6), Y(34));
af_displayPrintLain("Back", X(6), Y(43));
const byte Lines[] = {16, 25, 34, 43};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
//
//
// add4 -----------------------------------------------------------------------------------------------------------
// Various
//Aim mode
void Md_aimMode(){
if (mdf_addres(check_emb, 4)){
int temp = mdf_addres(get_add, 4);
if (temp == 8){//back
mdf_cursor(set_pos, mdf_addres(back_add, 3));
}
else if (temp == 7){//reset
Gf_combinedGyroRightJoystick(joystick_full_priority);
mdf_cursor(set_pos, mdf_addres(back_add, 4));
}
else {
Gf_combinedGyroRightJoystick(temp);//temp 1-6? перетворення всередині функції 0-5
mdf_cursor(set_pos, mdf_addres(back_add, 4));
}
}
Md_p_aimMode();
}
void Md_p_aimMode(){
const byte p = 0, l = 8;
const byte y[8] = {
af_centerY(0, l, p),//af_centerY(номер рядка , кількість рядків, відстань між рядками ),
af_centerY(1, l, p),
af_centerY(2, l, p),
af_centerY(3, l, p),
af_centerY(4, l, p),
af_centerY(5, l, p),
af_centerY(6, l, p),
af_centerY(7, l, p)
};
af_displayPrintLain("Joy full priority", X(6), y[0]);
af_displayPrintLain("Joy priority axial", X(6), y[1]);
af_displayPrintLain("Gyro full priority", X(6), y[2]);
af_displayPrintLain("Gyro priority axial", X(6), y[3]);
af_displayPrintLain("Joy Gyro mix", X(6), y[4]);
af_displayPrintLain("Gyro (Joy off)", X(6), y[5]);
af_displayPrintLain("Reset", X(6), y[6]);
af_displayPrintLain("Back", X(6), y[7]);
mdf_cursor(cursor_read_encoder, sizeof(y), y);
Md_p_aimModeActiveBlinking(y);
}
void Md_p_aimModeActiveBlinking(const byte* coordinate){
static Timer tmr(1000);
static byte y;
if (tmr.ta()) {
y = coordinate[Gf_combinedGyroRightJoystick(g_o_m)-1];//функція повертає мінімум 1 а потрібно 0
display.fillRect(X(6), y, 128-6, 8, BLACK);
}
}
//
// Volume?
void Md_p_volume(byte y){
af_displayPrintLain("Volume", X(6), y);
}
//
// Save selected profile
byte Md_saveSelectedProfile(byte acceptedCommand){
static byte ssps;//ssps = save Selected Profile switch
if (acceptedCommand == ssp_change_setting){
mdf_magnifier(ssps, 0, 1, 1, 1);
}
else if (acceptedCommand == ssp_get_setting){
return ssps;
}
else if (acceptedCommand == reset_settings) {
ssps = 0;
}
else if (acceptedCommand == get_eeprom) {
af_saveToEEPROM(get_eeprom, &ssps, sizeof(ssps));
}
else if (acceptedCommand == set_eeprom) { //не забути добавити
af_saveToEEPROM(set_eeprom, &ssps, sizeof(ssps));
}
return 0;
}
void Md_p_saveSelectedProfile(byte y, byte l){
af_displayPrintLain("Save selected profile", X(6), y, l, N(13));
if (Md_saveSelectedProfile(ssp_get_setting)){
af_displayPrintLain("on", X(128-12), y);
} else{
af_displayPrintLain("off", X(128-18), y);
}
}
//
// Auto power off
byte Md_autoPowerOff(byte acceptedCommand){
static byte tmrRestartMinutes = 10;
static uint32_t tmrRestartMillis = 3*60000;// = 3 хвилини
static Timer tmr(tmrRestartMillis, false);//вказуємо тут час і правда повернеться не одразу а по завершенню часу tmr. gp()
if (acceptedCommand) {
if (acceptedCommand == apo_restart_time) {
tmr.sp(tmrRestartMillis);
}
else if (acceptedCommand == apo_change_time) {
if (mdf_magnifier(tmrRestartMinutes, 1, 10, 1, 2)) {
tmrRestartMillis = tmrRestartMinutes*60000;
}
}
else if (acceptedCommand == apo_get_time) {
return tmrRestartMinutes;
}
else if (acceptedCommand == get_eeprom) { //
af_saveToEEPROM(get_eeprom, &tmrRestartMinutes, sizeof(tmrRestartMinutes));
tmr.sp(tmrRestartMillis);//якщо беремо дані з енергонезалежної пам'яті потрібно перезапуск
}
else if (acceptedCommand == set_eeprom) {
af_saveToEEPROM(set_eeprom, &tmrRestartMinutes, sizeof(tmrRestartMinutes));
}
else if (acceptedCommand == reset_settings) {
tmrRestartMillis = 3*60000;
tmrRestartMinutes = 3;
}
}
else if (tmr.gp()){
af_powerOffExecution();
}
return 0;
}
void Md_p_autoPowerOff(byte y){
af_displayPrintLain("Auto power off m.", X(6), y);
af_centerText(X(107), y, Md_autoPowerOff(apo_get_time));
}
//
// Display timeout
byte Md_displayTimeout(byte acceptedCommand){
static byte status = function_not_completed;
const unsigned int shutdownTime[] = {0, 5000, 15000, 30000, 60000, 120000, 300000, 600000};
static int8_t oti = 7; //oti = off time indicator вказівник на час відключення
static Timer tmr(shutdownTime[oti], false);
static byte bss; //bss = Bluetooth status saving
if (acceptedCommand) {
if (oti && acceptedCommand == timeout_restart) { //в основному якщо модуль encorder працює то виключення дисплею перезапускається
tmr.tst(shutdownTime[oti],false);
}
else if (acceptedCommand == dt_change_time) {
mdf_magnifier(oti, 0, 7, 1, 1);
if (!oti) {tmr.tst(false, false);} //захист від миттєвого згасання якщо перейдемо з 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 (acceptedCommand == reset_settings) {
oti = 1;
}
else if (acceptedCommand == get_eeprom) {
af_saveToEEPROM(get_eeprom, &oti, sizeof(oti));
Md_displayTimeout(timeout_restart);//потрібно перезапустити з новим часом взятим з енергонезалежною пам'яті
}
else if (acceptedCommand == set_eeprom) {
af_saveToEEPROM(set_eeprom, &oti, sizeof(oti));
}
/*
else if (acceptedCommand == timeout_reset) {//використовується коли скидаємо всі настройки
tmr.sp(shutdownTime[oti]);
}
*/
}
else if (oti && tmr.ts()){
return true;
}
return 0;
}
void Md_p_displayTimeout(byte y){
if (Md_displayTimeout(dt_get_tim_minutes)){
af_centerText(X(107), y, Md_displayTimeout(dt_get_tim_minutes));
af_displayPrintLain("Display timeout m.", X(6), y);
}
else if (Md_displayTimeout(dt_get_tim_seconds)){
af_centerText(X(107), y, Md_displayTimeout(dt_get_tim_seconds));
af_displayPrintLain("Display timeout s.", X(6), y);
}
else {
af_displayPrintLain("Display timeout off", X(6), y);
}
}
//
//
// Effects
// Trigger light
void af_triggerLightSettings(){
int seveDataTemp;
switch (mdf_addres(check_emb, 4)) {
case 1:
seveDataTemp = af_triggerLight(get_light1);
if (mdf_magnifier(seveDataTemp, 0, 100, 1, 10)) {af_triggerLight(set_light1, seveDataTemp);}
break;
case 2:
seveDataTemp = af_triggerLight(get_light2);
if (mdf_magnifier(seveDataTemp, 0, 100, 1, 10)) {af_triggerLight(set_light2, seveDataTemp);}
break;
case 3:
seveDataTemp = af_triggerLight(get_color1);
if (mdf_magnifier(seveDataTemp, 0, 1000, 1, 50)) {
af_triggerLight(set_color1, seveDataTemp);
if (!af_triggerLight(get_light1)) {af_triggerLight(set_light1, 100);}
}
break;
case 4:
seveDataTemp = af_triggerLight(get_color2);
if (mdf_magnifier(seveDataTemp, 0, 1000, 1, 50)) {
af_triggerLight(set_color2, seveDataTemp);
if (!af_triggerLight(get_light2)) {af_triggerLight(set_light2, 100);}
}
break;
case 5:
if (af_triggerLight(clssc) == color_light_default) { //clssc = color light standard settings check
if (mdf_confirm(print_not_need) == function_completed) {
mdf_addres(back_add, 4);
}
}
else if (mdf_confirm(offer_a_choice) == data_ready) {
if (mdf_confirm(get_data) == choice_yes) {
af_triggerLight(color_light_reset);
}
mdf_addres(back_add, 4);
}
break;
case address_entered:
if (mdf_addres(get_add, 4) <= 4){
if (mdf_addres(get_add, 4) == 1 || mdf_addres(get_add, 4) == 3){
af_triggerLight(color_one);
}
else if (mdf_addres(get_add, 4) == 2 || mdf_addres(get_add, 4) == 4){
af_triggerLight(color_two);
}
af_triggerLight(color_light_setting);//забороняємо переключати колір перед тим включили колір який потрібно настроїти
mdf_addres(mode_click_back);
}
else if (mdf_addres(get_add, 4) == 5){
mdf_addres(mode_function_returns);
mdf_confirm(output_by_coordinates, X(47), Y(47));
}
else {
mdf_cursor(set_pos, mdf_addres(back_add, 3));
}
break;
case address_deleted: //якщо повертаємося назад включаємо стандартний режим роботи
af_triggerLight(color_switching);
af_triggerLight(color_one); //включаємо на всяк випадок перший колір невідомо в якому стані гачок
break;
}
Md_p_triggerLightSettings();
}
void Md_p_triggerLightSettings(){
byte tc = 0;
int temp;
af_displayPrintLain(" Light 1", 0, 0+tc);
af_displayPrintLain(" Light 2", 0, 8+tc);
af_displayPrintLain(" Color 1", 0, 24+tc);
af_displayPrintLain(" Color 2", 0, 32+tc);
af_displayPrintLain(" Reset ", 0, 48+tc);
af_displayPrintLain(" Back ", 0, 56+tc);
if (af_triggerLight(get_light1)){//перемо яскравість і відображаємо її якщо нуль показуємо off
temp = af_triggerLight(get_light1);
af_centerText(X(65), Y(0), temp);
af_displayPrintLain(" %", 0, 0);
}else{
af_displayPrintLain("off", 55, 0+tc);
}
if (af_triggerLight(get_light2)){
temp = af_triggerLight(get_light2);
af_centerText(X(65), Y(8), temp);
af_displayPrintLain(" %", 0, 8);
}else{
af_displayPrintLain("off", 55, 8+tc);
}
temp = af_triggerLight(get_color1); //беремо колір і відображаємо його відтінок
af_centerText(X(65), Y(24), temp);
temp = af_triggerLight(get_color2);
af_centerText(X(65), Y(32), temp);
const byte Lines[] = {0, 8, 24, 32, 48, 56};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
//
void Md_p_shotgunBoltSettings(){
if (af_shotgunBolt(bolt_get_permission)){
af_displayPrintLain(" Shotgun bolt on", 0, 24);
}else{
af_displayPrintLain(" Shotgun bolt off", 0, 24);
}
}
void Md_p_vibration(){
if (af_vibration(vt_get_permission)){
af_displayPrintLain(" Vibration on", 0, 32);
}else{
af_displayPrintLain(" Vibration off", 0, 32);
}
}
//
// Expert settings
// Gyroscope calibration
void Md_gyroCalibration(){
Md_p_gyroCalibration();
switch (mdf_addres(check_emb, 4)) {
case 0:
Md_gyroDataPrint();
break;
case 1:
Md_gyroCalibrationExecution();
break;
case 2:
mdf_cursor(set_pos, mdf_addres(back_add, 3));
break;
case address_entered:
if (mdf_addres(get_add, 4) == 1) {
mdf_addres(mode_click_back);
}
break;
case address_deleted:
Md_gyroCalibrationExecution(reset_f);
break;
}
}
void Md_p_gyroCalibration(){
const byte Lines[] = {36, 46};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
display.drawRect(0, 0, 128, 33, WHITE);//великий прямокутник вверху
af_displayPrintLain("Calibrate", X(6), Y(36));
af_displayPrintLain("Back", X(6), Y(46));
}
void Md_gyroDataPrint(){
int temp; //test
if (1 < temp) {
display.setCursor(20, 13);
display.write(27); // Код символу "←"
}else if (temp < -1){
display.setCursor(56, 13);
display.write(26); // Код символу "→"
}
if (1 < abs(temp)) {
af_centerText(X(38), Y(13), abs(temp)-1);
}else{
af_centerText(X(38), Y(13), 0);
}
if (1 < temp) {
display.setCursor(91, 3);
display.write(24); // Код символу "↑"
}else if (temp < -1){
display.setCursor(91, 22);
display.write(25); // Код символу "↓"
}
if (1 < abs(temp)) {
af_centerText(X(93), Y(13), abs(temp)-1);
}else{
af_centerText(X(95), Y(13), 0);
}
}
// Gyro calibration execution
void Md_gyroCalibrationExecution(byte acceptedCommand) {
static int countdown = 10;
static Timer tmr(500);
if (acceptedCommand) {
if (acceptedCommand == reset_f) {
countdown = 10;
tmr.sp(500);//перед виходом перезапускаємо таймер правда при першому виклику
mdf_addres(back_add, 4);
}
else if (acceptedCommand == set_eeprom) {
int avgX, avgY, avgZ;
avgX = mpu.getXGyroOffset();
avgY = mpu.getYGyroOffset();
avgZ = mpu.getZGyroOffset();
af_saveToEEPROM(set_eeprom, &avgX, sizeof(avgX));
af_saveToEEPROM(set_eeprom, &avgY, sizeof(avgY));
af_saveToEEPROM(set_eeprom, &avgZ, sizeof(avgZ));
}
else if (acceptedCommand == get_eeprom) {
int avgX, avgY, avgZ;
af_saveToEEPROM(get_eeprom, &avgX, sizeof(avgX));
af_saveToEEPROM(get_eeprom, &avgY, sizeof(avgY));
af_saveToEEPROM(get_eeprom, &avgZ, sizeof(avgZ));
// Встановлення корекції для осі X
mpu.setXGyroOffset(avgY); // Встановлення корекції для осі Y
mpu.setYGyroOffset(avgZ); // Встановлення корекції для осі Z
mpu.setZGyroOffset(avgZ);
}
}
else if (tmr.gp()) {
countdown--;
if (countdown == -1) {
mpu.CalibrateGyro(5);//TEST
}
else if (countdown <= -4) {
countdown = 10;
tmr.sp(500);
mdf_addres(back_add, 4);
}
}
Md_p_gyroCalibrationExecution(countdown);
}
void Md_p_gyroCalibrationExecution(int acceptedData) {
if (0 < acceptedData) {
af_centerText(X(64), Y((33/2)-8), "Don't move");
af_centerText(X(64), Y(33/2), "Beginning in-"+String(acceptedData));
}
else if (acceptedData == 0){
af_centerText(X(64), Y((33/2)-8), "Don't move");
af_centerText(X(64), Y(33/2), "Wait for completion");
}
else if (acceptedData <= -1){
af_centerText(X(64), Y((33/2)-4), "Calibration completed");
}
}
//
//
// Disabling buttons
void Md_disableButton(){
Md_p_disableButton();
switch (mdf_addres(check_emb, 4)) {
case 1:
if (mdf_confirm(offer_a_choice) == data_ready) {// після вибору робимо дію відключення кнопок або нічого
if (mdf_confirm(get_data) == choice_yes) {
Md_tactileButtonsDeactivation(deactivation_buttons_start);
}
mdf_addres(back_add, 4);
}
break;
case 2:
Md_tactileButtonsDeactivation(reset_deactivation_buttons);//скидаємо всі відключені кнопки
mdf_addres(back_add, 4);
break;
case address_entered:
if (mdf_addres(get_add, 4) == 1) {//перед виключенням Перевіряємо чи є зажаті кнопки
mdf_addres(mode_function_returns);
if (Md_tactileButtonsDeactivation(check_for_pressed_buttons)) {
mdf_confirm(output_by_coordinates, X(60), Y(30));
}else {
mdf_addres(back_add, 4);
}
}
else if (mdf_addres(get_add, 4) == 2) {mdf_addres(mode_function_returns);}//не чіпати інакше не працює
else {mdf_cursor(set_pos, mdf_addres(back_add, 3));}//back
break;
}
}
void Md_p_disableButton(){
display.drawRect(0, 0, 127, 19, WHITE);
af_centerText(X(127/2), Y(19/2-4), "Turned off("+ String(Md_tactileButtonsDeactivation(deactivated_buttons_get))+")");
af_displayPrintLain("Deactivation start", X(6), Y(22));
af_displayPrintLain("Reset", X(6), Y(32));
af_displayPrintLain("Back", X(6), Y(42));
const byte Lines[] = {22, 32, 42};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
byte Md_tactileButtonsDeactivation(byte acceptedCommand){
static byte maxFive;
static byte pinDeactivat[14];
const uint8_t pinList[14] = {//TEST
23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 5
};
if (acceptedCommand == deactivated_buttons_get) {
return maxFive;
}
else if (acceptedCommand == deactivation_buttons_start) {
for (int i = 0; i <= 13; i++) {
if (maxFive != 5 && digitalRead(pinList[i]) == LOW) {
// Якщо пін у низькому стані — міняємо його на вихід і ставимо HIGH
pinDeactivat[i] = true;
maxFive++;
pinMode(pinList[i], OUTPUT);
digitalWrite(pinList[i], HIGH);
}
}
}
else if (acceptedCommand == check_for_pressed_buttons) {
for (int i = 0; i <= 13; i++) {
if (digitalRead(pinList[i]) == LOW) {
return true;
}
}
}
else if (acceptedCommand == reset_deactivation_buttons) {
maxFive = 0;
}
return 0;
}
//
// Reset all
void Md_resetAll(){
switch (mdf_addres(check_emb, 4)) {
case 1:
if (mdf_confirm(offer_a_choice) == data_ready) {// після вибору робимо дію відключення кнопок або нічого
mdf_addres(back_add, 4);
if ( mdf_confirm(get_data) == choice_yes) {
Md_saveSelectedProfile(reset_settings);
af_vibration(reset_settings);
af_shotgunBolt(reset_settings);
af_triggerLight(reset_settings);
Md_displayTimeout(reset_settings);
Md_autoPowerOff(reset_settings);
mps(reset_settings);
for (int i = 8; i < 14; i++) {
profil[i].profileResetAll();
}
}
}
break;
case 2:
mdf_cursor(set_pos, mdf_addres(back_add, 3));
break;
case address_entered:
if (mdf_addres(get_add, 4) == 1) {
mdf_confirm(output_by_coordinates, X(50), Y(40));
mdf_addres(mode_function_returns);
}
break;
}
Md_p_resetAll();
}
void Md_p_resetAll(){
display.drawRect(0, 0, 128, 37, WHITE);
af_displayPrintLain("Reset all my profiles", X(2), Y(5));
af_displayPrintLain("and", X(56), Y(14));
af_displayPrintLain("general settings", X(17), Y(23));
af_displayPrintLain("Reset", X(6), Y(42));
af_displayPrintLain("Back", X(6), Y(52));
const byte Lines[] = {42, 52};
mdf_cursor(cursor_read_encoder, sizeof(Lines), Lines);
}
//
//
//
// add5-----------------------------------------------------------------------------------------------------------
//
// Main display functions
void mdf_displayOutput() {
/*
void applyContrast(byte val) {//потрібно щоб змінювати яскравість
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(val);//0-255
}
*/
static byte switchOnOff = e_u_d; //переключатель включено виключено
if (switchOnOff == e_u_d){
display.clearDisplay();
Md_infoDisplay(); //перший пункт меню який викликає решту пунктів
display.display();
if (Md_displayTimeout()){//відключаємо дисплей якщо не було ні дій n-кодера
switchOnOff = d_u_d;
display.clearDisplay();
display.display();
}
} //Md_displayTimeout(timeout_restart);
else if (af_checkFastRight() || af_checkFastLeft() || af_checkRight() || af_checkLeft() || af_checkClick()) {//включаємо дисплей якщо зафіксували роботу енкодера
switchOnOff = e_u_d;
}
}
// Cursor
byte mdf_cursor(byte acceptedCommand, byte data, const byte* coordinate) {
static int8_t y = 1;
static int8_t cursorVisualTypes = cursor_type_one;
switch (acceptedCommand) {
case cursor_read_encoder:
// переміщаємо курсор якщо тип стрілки є cursor_type_one
// і якщо кнопка нкодра не натиснута при натиску і обертання використовується для прокрутки тексту Тому потрібно заборона на переміщення
if(cursorVisualTypes == cursor_type_one){
mdf_magnifier(y, 1, data, 1, 1);
}
mdf_p_cursor(cursorVisualTypes, coordinate[y-1]);
break;
case cursor_type_one:
cursorVisualTypes = cursor_type_one;
break;
case cursor_type_two:
cursorVisualTypes = cursor_type_two;
break;
case get_pos:
return y;
break;
case set_pos: //вказуємо позицію курс при переходах вперед або назад по адресу
y = data;
cursorVisualTypes = cursor_type_one;//зазвичай Після цього потрібно вказати візуальний тип один щоб лишній раз не викликати робимо це автоматично
break;
}
/*
if (arr != nullptr) {
mdf_p_cursor(cursor, arr[point-1]);
}
*/
return 0;
}
byte mdf_p_cursor(int visualTypes, int coordinateY) {
const byte cursor[] = {26, 16}; //cursor_type_one == 0 cursor_type_two == 1
display.setCursor(0, coordinateY);
display.write(cursor[visualTypes]);//вибираємо один з двох видів курсора з таблиці символів
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:
{
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;//також повертаємо адрес після видалення щоб можна було скористатися mdf_cursor(set_pos, mdf_addres(back_add, 1));
}
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){
//printArrayEverySecond(add, sizeof( add));
if (add[addNumb] == address_not_entered && af_checkClick()){// набір адреса
add[addNumb] = mdf_cursor(get_pos);
return address_entered;
}
}
else if (inputMode == mode_click_back){
if (add[addNumb+1] == address_not_entered && af_checkClick()){//якщо попадемо на нуль повертаємося
mdf_addres(back_add, addNumb);
return address_deleted;
}
}
return add[addNumb];
break;
}
return 0;
}
// magnifier
template <typename T>//функція працює безпосередньо з змінною напряму
bool mdf_magnifier(T &numeric, int minNam, int maxNam, int Speed1, int Speed2) {
if (af_checkFastRight()) {
numeric += Speed2;
Serial.println(2);
} else if (af_checkFastLeft()) {
numeric -= Speed2;
Serial.println(-2);
} else if (af_checkRight()) {
numeric += Speed1;
Serial.println(1);
} else if (af_checkLeft()) {
numeric -= Speed1;
Serial.println(-1);
} else {
return false;
}
numeric = af_clamp(numeric, minNam, maxNam);
return true;
}
//
// Saving Settings Display
byte mdf_savingSettingsDisplay(){ //коротко копіювання mps(copy profile) порівняння mps(active_copy_compa re) якщо == active_copy_same просто виходимо якщо != active_copy_sameрізні викликаються Дана функція
mdf_confirm(output_by_coordinates, X(36), Y(40));
mdf_p_savingSettingsDisplay();// візуальна частина коду
if (mdf_confirm(offer_a_choice) == data_ready) {
if (mdf_confirm(get_data) == choice_no) {//якщо вибір ні повертаємо настройки
mps(not_save);
}
return function_completed;//незалежно від вибору функція поверне function_completed
}
return 0;
}
void mdf_p_savingSettingsDisplay(){
display.drawRoundRect(20, 8, 87, 22, 0, WHITE); // рамка навколо слова зверху
display.setCursor(25, 15);
display.println("Save settings");
}
//
void mdf_typeNumberPrint(){//функція відображає номер і тип профілю в інфодисплей і першому пункті меню
if (mps(gapn)) {//номер тип відображається лише коли вибрано профіль
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 mdf_confirm(byte acceptedCommand, byte x, byte y){//функція надає варіант для вибору та каву ні або виводить повідомлення що не потрібно якщо нема потреби
static bsd selection(1);//вибір так або ні
static bsd clicksCount(3);//зміна накопичує вказані ліки при досягненні виконається дія
byte variants[] = {choice_no, choice_yes};
if (acceptedCommand == output_by_coordinates) {// кожен раз перед основним викликом одноразово функція приймає координати
mdf_p_confirm(output_by_coordinates, x, y);// переправляє координати в mdf_p_confirm для збереження
}
else if (acceptedCommand == print_not_need) {//після перевірки можна подати команду вивести повідомлення якщо підтвердження не потрібне
if (mdf_p_confirm(print_not_need) == function_completed || af_checkClick()) {
mdf_p_confirm(reset);
return function_completed;
}
}
else if (acceptedCommand == offer_a_choice) {//якщо підтвердження потрібне запропоновуємо вибір виводимо так або ні
if (af_checkTurn()) {//при обертах енкодера в залежності від накопичених значень відбудеться різні дії
selection.p(1);//якщо вже є одиничка і ми доплюсуємо один змінна скинеться на нуль
mdf_p_confirm(change_selection); // міняємо позицію відображення рамки так або ні
clicksCount.r();//якщо було накопичення ліків відбудеться скидання із-за того що позиція рамки змінилася
}
if (af_checkClick()) {//робимо кліки і накопичуємо їх
clicksCount.p(1);
if (clicksCount.m()) { // якщо максимум повідомляємо що дані готові
mdf_p_confirm(reset);
return data_ready;
}
else if (variants[selection.g()] == choice_no) { //
mdf_p_confirm(reset);
return data_ready;
}
mdf_p_confirm(accelerate_frame_blinking);
}
mdf_p_confirm(offer_a_choice);
}
else if (acceptedCommand == get_data) {//якщо получили data_ready беремо дані-вибір
return variants[selection.r()]; //метод .r() одноразово повертає накопичне число і скидає його
}
return 0;
}
byte mdf_p_confirm(byte acceptedCommand, byte x, byte y){
const byte frameSizeWidth[] = {15, 21};// розмір рамки міняємо в залежності yes no
const byte framePosition[] = {2, 33};//координати для позиції рамки так ні
const int frameBlink[] = {1000, 500, 100};//test частота мигання рамки якщо натискаємо кнопку
static byte frequency;//frequency ЧАСТОТА вказує на частоту мигання рамки
static byte flashFlag;
static Timer tmr(frameBlink[frequency]);
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.tat(frameBlink[frequency], true);
break;
case accelerate_frame_blinking://при накопиченні кліків збільшується частота блимання рамки навколо вибору
frequency++;
tmr.tat(frameBlink[frequency], true);
break;
case reset://якщо накопичили необхідну кількість bліків відбудеться скидання до початкового стану
selection = frequency = 0;
tmr.tat(frameBlink[frequency]);//після того як скинули frequency вказуємо знову чистоту
tmr.tar();//скид таймер при наступному виклику tmr.ta() почне з правди
break;
case print_not_need://якщо настройки не мінялись виводимо повідомлення що не потрібне скидання
//test Потрібно провести перевірку логіка така що при скиданні встановлювалась секунда а тут секунда буде правда потім стане брехня
if (!tmr.ta()) {tmr.tar();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.ta()) {//рамка світилась одразу після запуску або змінні вибору
display.drawRect(azixX + framePosition[selection], azixY + 2, frameSizeWidth[selection], 11, WHITE);//рамка навколо так або ні
}
display.drawRect(azixX, azixY, 56, 15, WHITE);// велика рамка навколо так ні
break;
}
return 0;
}
//
void mdf_bluetoothPrint() {
static Timer tmr(600);
display.drawCircle(6, 6, 6, WHITE);// кружок
if (bleGamepad.isConnected() || tmr.ta()) {
fs_bluetoothSymbol();
}
}
const char** mdf_getButtonNames() {//функція яка повертає масив з іменами кнопок
static const char* buttonNames[] = {
"?",//для дублікатів кнопки
"A",
"B",
"X",
"Y",
"A1",
"A2",
"RB",
"LB",
"Se",
"St",
"dU",
"dD",
"dL",
"dR",
"RT",
"LT"
};
return buttonNames;
}
/*
byte mdf_batteryCheck(byte acceptedCommand) {
static float batLevels[] = {3.05, 3.10, 3.20, 3.29, 3.39, 3.48, 3.58, 3.67, 3.77, 3.86, 3.96, 4.06, 4.14, 4.18}; //1-14 0-13
static Timer tmr;
static byte li = []()->byte {
if (ina219.getCurrent_mA() > 50){
batLevels[12] = 4.18;
return 1;
}
return 12;
}(); //li = level Indicator;
static byte csf = []()->byte {return ina219.getCurrent_mA() > 50 ? b_c_p : b_c_m;}();//на старті Перевіряємо чи зарядка підключена
if (tmr.gp()) {
float voltageNewData = ina219.getBusVoltage_V();
float currentMa = ina219.getCurrent_mA();
if (currentMa <= 0){
if (csf == b_c_m && voltageNewData < batLevels[li]){
li--;
}
else if (li <= 4) {
Md_displayTimeout(timeout_reset);
if (li == 1) {af_powerOffExecution();}
}
else if (csf == b_c_p) {
Md_displayTimeout(timeout_reset);
tmr.ft(2000);
csf = b_c_m;
}
}
else if (50 < currentMa){
if (csf == b_c_p && batLevels[li+1] < voltageNewData && li !=12){
li++;
if (batLevels[12] < voltageNewData){batLevels[12] = 4.14;}
}
else if (csf == b_c_m){
batLevels[12] = 4.18;
Md_displayTimeout(timeout_reset);
tmr.ft(2000);
csf = b_c_p;
li++;
}
tmr.sp(2000);
}
if (acceptedCommand == g_b_s) {
return csf == b_c_p ? b_c_p : li;
}
return 0;
}
void mdf_batteryPrint() {
static byte sevedChargeScale; //sevedChargeScale = збереження шкали заряду
static Timer tmr(300);
static bsd ca(12, mdf_batteryCheck(g_b_l)); //ca = charging animation
display.drawRect(111, 2, 1, 4, WHITE);// картинка батареї
display.drawRect(112, 0, 16, 8, WHITE);
if (mdf_batteryCheck(b_c_p)) {
display.fillRect(126-ca.tnar(), 2, ca.tnar(), 4, WHITE);
}
else if ((3 <= ca.g()) || tmr.ta()) {
ca.s(mdf_batteryCheck(g_b_l));
display.fillRect(126-ca.g(), 2, ca.g(), 4, WHITE);// шкала заряду батареї
}
}
*/
// Mdf
// Md
// Gamepad function
// Right joystick + Gyroscope (Z(V) RZ(H))
// Gyroscope
bool Gf_handleButton(int cmd){ // handle Button
static int TDZ = []() { int s=0; for(int i=0;i<10;i++){s+=touchRead(p_h_b_14); delay(20);} Serial.print("touchRead(p_h_b_14) ");
Serial.println(touchRead(p_h_b_14));return s/10 - 30; }();//test
static Timer tmr(3*60000, false);
static byte dhb;// data handle button
static byte fp; // flag press
static byte fr; // flag release
switch (cmd) {
case r_s:
if (dhb = stage_0 && touchRead(p_h_b_14) < TDZ) {//test //натиснуто
dhb = stage_1;
}
else if (stage_1 && touchRead(p_h_b_14) > TDZ) {//відпущено
dhb = stage_0;
tmr.tmt(3*60000, false);
}
break;
case g_p:// Зчитуємо дані натиск (можна постійно получати про стан сенсора правда або брехня)
return dhb;
break;
case s_g_p: //single Одноразово зчитуємо натиск
if (dhb) {//test
if (!fp) {
return fp = true;
}
}
else {//test
fp = false;
}
break;
case s_g_r: // Одноразово зчитуємо відпущення
if (!dhb) {//test
if (!fr) {
return fr = true;
}
}
else {//test
fr = false;
}
break;
}
return 0;
}
int16_t* Gf_g(int cmd){//перетворюємо дані з гіроскопа і керуємо правим аналогом джойстика
static byte fs;
static int16_t pd[3]; // previous data
static int16_t ud[3]; // unconverted data неперетворені
static int16_t gd[3]; // Get Data
static uint8_t fifoBuffer[42];
static Timer tmr(10);//таймер щоб опитувати кожні 10 мілісекунд
static int dzhZ;
static byte shZ;
static int dzvY;
static byte svY;
dzhZ = profil[mps(gapn)].deadZoneHor;// Мертва зона
shZ = profil[mps(gapn)].sensitivityHor;// чутливість
dzvY = profil[mps(gapn)].deadZoneVer;
svY = profil[mps(gapn)].sensitivityVer;
if (cmd == r_g_d) {
if (tmr.gp() && mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {// коли берем дані одразу зчитуємо нові якщо є
mpu.dmpGetGyro(ud, fifoBuffer);
if (!Gf_handleButton(g_p)) {//якщо не натиснута кнопкa рукоятки
if (gd[x_a] || gd[y_a]) {//провіряємо наявність попередніх даних
gd[x_a] = gd[y_a] = 0;//якщо там щось було скидаємо на нуль
return gd;//повертаємо правда щоб встановити одноразову цей нуль
}
}
else{
if (map(abs(ud[g_z]), 0, 150, 1, 30) < abs(ud[g_y])) {// получ знач мають бути більше одинички // h=x // Перевіряємо чи є вихід динамічної мертвої зони
fs = true;
ud[g_y] = constrain(ud[g_y], -svY, svY);
if (0 < ud[g_y]) {
gd[x_a] = map(ud[g_y], 1, svY, -dzvY, -32767);
}
else if(ud[g_y] < 0){
gd[x_a] = map(ud[g_y], -1, -svY, dzvY, 32767);
}
}
else if(gd[x_a]){
fs = true;
gd[x_a] = 0;
}
if (map(abs(ud[g_y]), 0, 150, 1, 30) < abs(ud[g_z])) {//v=y V якщо є вихід за динамічну мертву зону тоді можна виконати перетворення даних
fs = true;
ud[g_z] = constrain(ud[g_z], -shZ, shZ);
if (0 < ud[g_z]) {
gd[y_a] = map(ud[g_z], 1, shZ, -dzhZ, -32767);
}
else if(ud[g_z] < 0){
gd[y_a] = map(ud[g_z], -1, -shZ, dzhZ, 32767);
}
}
else if(gd[y_a]){
fs = true;
gd[y_a] = 0;
}
if (fs) {//останній раз ще поверн правда і можна буде становити нуль
fs = false;
return gd;
}
}
}
}
else if (cmd == g_g_d) {return gd;}//звертаємося до цього масиву pd за даними щоб користуватися звичайними константами x_a y_a
return nullptr;
}
//
// Right joystick
int* Gf_ar(int cmd, int newData[]){
static int dz[4] = {2048-300, 2048+300, 2048-300, 2048+300}; // dzxy = dead zones x y//test
static int mm[4] = {95, 4000, 95, 4000}; // minimum maximum x y
static int da[2]; // analog data converted дані аналог перетворені
if (cmd == r_a_d) {//один раз викликається в коді
da[x_a] = analogRead(p_s_t_x_39);//test
da[y_a] = analogRead(p_s_t_y_36);
if ((da[x_a] < dz[x_l]) || (dz[x_r] < da[x_a])) {//Перевіряємо чи є вихід за мертву зону
Serial.print ("-");
da[x_a] = constrain(da[x_a], mm[x_l], mm[x_r]);
if (!da[x_a]){da[x_a] = 1;}// test Можливі конфлікт Нолик не перетворюється ні в що, А тут можливий 0 крайнє положення джойстика
}
else{
da[x_a] = 0;
}
if ((da[y_a] < dz[y_u]) || (dz[y_d] < da[y_a])) {//Перевіряємо чи є вихід за мертву зону
Serial.print ("-");
da[y_a] = constrain(da[y_a], mm[y_u], mm[y_d]);
if (!da[y_a]){da[y_a] = 1;}
}
else{
da[y_a] = 0;
}
return da;
}
else if (cmd == g_a_d) {return da;}//беремо з читані збережені дані
else if (cmd == g_d_z) {return dz;}
else if (cmd == g_m_m) {return mm;}
else if (cmd == s_m_m) {memcpy(mm, newData, sizeof(mm));} // Вказуємо мінімальне !! максимальне значення мінімальне значення не може бути нулем буде конфлікт
else if (cmd == s_d_z) {memcpy(dz, newData, sizeof(dz));} // Вказуємо мертві зони !!! нагадування Мертва зона може бути в певних межах зробити перевірку
return nullptr;
}
bool Gf_rjp(){ // rjp = right joystick protection
static Timer tmr;
static bool status;
if (Gf_handleButton(s_g_p)){//після натиску рукоятки одноразово получаємо правда
//test неправильна логіка має стартувати таймер
if (Gf_ar(g_a_d)[x_a] || Gf_ar(g_a_d)[y_a]){//якщо правда то означає що джойстик не вмерти в зоні коли заторкнулись до сенсора рукоятки
Serial.println("Gf_hb(true) 2");
status = true;
tmr.tst(2000, false);
}
}
else if(status){
if (tmr.ts() || Gf_handleButton(s_g_r)){//щоб джойстик запрацював потрібно дві секунди (tmr.ts()) бути в мертвій зоні або відпустити сенсор рукоятки
Serial.println("tmr.gp()");
status = false;
}
else if (Gf_ar(g_a_d)[x_a] || Gf_ar(g_a_d)[y_a]){//якщо джойстик не відкалібрований або не відпустити його то він не буде працювати
Serial.println("A T ");
tmr.tst(2000, false);
}
}
return status;
}
int* Gf_rj(int cmd){
static int da[2]; //= data axis дані осей
static byte fs; //= flag switch
if (cmd == r_j_a){
if (Gf_ar(r_a_d) && !Gf_rjp()) {
if (Gf_ar(g_a_d)[x_a]) {// якщо є правда Тоді виконуємо перетворення необхідні значення
fs = true;
da[x_a] = map(Gf_ar(g_a_d)[x_a], Gf_ar(g_m_m)[x_l], Gf_ar(g_m_m)[x_r], -32767, 32767);
}
else if (da[x_a]){//якщо нема даних то ще останній раз відправляємо Нулик
fs = true;
da[x_a] = 0;
}
if (Gf_ar(g_a_d)[y_a]) {
fs = true;
da[y_a] = map(Gf_ar(g_a_d)[y_a], Gf_ar(g_m_m)[y_u], Gf_ar(g_m_m)[y_d], -32767, 32767);
}
else if (da[y_a]){
fs = true;
da[y_a] = 0;
}
if (fs) {//останній раз ще може повернути правда що встановити положення джойстика в центр
fs = false;
return da;
}
}
else if (da[x_a] || da[y_a]){ //якщо захист зпрацює Gf_rjp() то одноразово повернем нуль якщо перед тим були дані
da[x_a] = 0;
da[y_a] = 0;
return da;
}
}
else if (cmd == g_j_a) {return da;}//беремо зчитані збережені дані
return nullptr;
}
//
// Combined gyro right joystick
int Gf_da(int cmd, int data){// data axis функція зберігає і повертає попередні дані по осям
static int pd[2];//
switch (cmd) {
case g_x:
return pd[x_a];
break;
case g_y:
return pd[y_a];
break;
case s_x:
pd[x_a] = data;
bleGamepad.setRZ(data);
Gf_sendReport(send_report);
break;
case s_y:
pd[y_a] = data;
bleGamepad.setZ(data);
Gf_sendReport(send_report);
break;
}
return 0;
}
byte Gf_combinedGyroRightJoystick(int cmd){
static byte om = joystick_full_priority; //operating mode;
bool gyroActive = Gf_g(r_g_d); //виконується зчитування даних і повертається правда що дані міняються або в кінці получаємо нуль
bool joyActive = Gf_rj(r_a_d);
if (!cmd) {
switch (om) {
case joystick_full_priority: // = 0
if (joyActive) {
if (Gf_da(g_x) != Gf_rj(g_j_a)[x_a]) {
Gf_da(s_x, Gf_rj(g_j_a)[x_a]);
}
if (Gf_da(g_y) != Gf_rj(g_j_a)[y_a]) {
Gf_da(s_y, Gf_rj(g_j_a)[y_a]);
}
}
else if (gyroActive) {
if (Gf_da(g_x) != Gf_g(g_g_d)[x_a]) {
Gf_da(s_x, Gf_g(g_g_d)[x_a]);
}
if (Gf_da(g_y) != Gf_g(g_g_d)[y_a]) {
Gf_da(s_y, Gf_g(g_g_d)[y_a]);
}
}
break;
case joystick_priority_axial:
if (joyActive || gyroActive) {
if (Gf_rj(g_j_a)[x_a]) {
if (Gf_da(g_x) != Gf_rj(g_j_a)[x_a]) {
Gf_da(s_x, Gf_rj(g_j_a)[x_a]);
}
}
else if (Gf_g(g_g_d)[x_a]) {
if (Gf_da(g_x) != Gf_g(g_g_d)[x_a]) {
Gf_da(s_x, Gf_g(g_g_d)[x_a]);
}
}
else if (Gf_da(g_x)) {
Gf_da(s_x, 0);
}
if (Gf_rj(g_j_a)[y_a]) {
if (Gf_da(g_y) != Gf_rj(g_j_a)[y_a]) {
Gf_da(s_y, Gf_rj(g_j_a)[y_a]);
}
}
else if (Gf_g(g_g_d)[y_a]) {
if (Gf_da(g_y) != Gf_g(g_g_d)[y_a]) {
Gf_da(s_y, Gf_g(g_g_d)[y_a]);
}
}
else if (Gf_da(g_y)) {
Gf_da(s_y, 0);
}
}
break;
case gyro_full_priority:
if (gyroActive) {
if (Gf_da(g_x) != Gf_g(g_g_d)[x_a]) {
Gf_da(s_x, Gf_g(g_g_d)[x_a]);
}
if (Gf_da(g_y) != Gf_g(g_g_d)[y_a]) {
Gf_da(s_y, Gf_g(g_g_d)[y_a]);
}
}
else if (joyActive) {
if (Gf_da(g_x) != Gf_rj(g_j_a)[x_a]) {
Gf_da(s_x, Gf_rj(g_j_a)[x_a]);
}
if (Gf_da(g_y) != Gf_rj(g_j_a)[y_a]) {
Gf_da(s_y, Gf_rj(g_j_a)[y_a]);
}
}
break;
case gyro_priority_axial:
if (gyroActive || joyActive) {
if (Gf_g(g_g_d)[x_a]) {
if (Gf_da(g_x) != Gf_g(g_g_d)[x_a]) {
Gf_da(s_x, Gf_g(g_g_d)[x_a]);
}
}
else if (Gf_rj(g_j_a)[x_a]) {
if (Gf_da(g_x) != Gf_rj(g_j_a)[x_a]) {
Gf_da(s_x, Gf_rj(g_j_a)[x_a]);
}
}
else if (Gf_da(g_x)) {
Gf_da(s_x, 0);
}
if (Gf_g(g_g_d)[y_a]) {
if (Gf_da(g_y) != Gf_g(g_g_d)[y_a]) {
Gf_da(s_y, Gf_g(g_g_d)[y_a]);
}
}
else if (Gf_rj(g_j_a)[y_a]) {
if (Gf_da(g_y) != Gf_rj(g_j_a)[y_a]) {
Gf_da(s_y, Gf_rj(g_j_a)[y_a]);
}
}
else if (Gf_da(g_y)) {
Gf_da(s_y, 0);
}
}
break;
case gyro_joystick_combination:
if (gyroActive || joyActive) {
int temp = constrain((long)Gf_g(g_g_d)[x_a] + (long)Gf_rj(g_j_a)[x_a], -32768, 32767);
if (Gf_da(g_x) != temp) {
Gf_da(s_x, temp);
}
temp = constrain((long)Gf_g(g_g_d)[y_a] + (long)Gf_rj(g_j_a)[y_a], -32768, 32767);
if (Gf_da(g_y) != temp) {
Gf_da(s_y, temp);
}
}
break;
case only_gyro://при переході на даний режим не можна нахиляти джойстик
if (gyroActive) {
if (Gf_da(g_x) != Gf_g(g_g_d)[x_a]) {
Gf_da(s_x, Gf_g(g_g_d)[x_a]);
}
if (Gf_da(g_y) != Gf_g(g_g_d)[y_a]) {
Gf_da(s_y, Gf_g(g_g_d)[y_a]);
}
}
break;
}
}else{
switch (cmd) {//тому що получаємо значення від 1
case 0: // = 0 пустота для того щоб був Jump table
break;
case joystick_full_priority: // = 1
om = joystick_full_priority;
break;
case joystick_priority_axial:
om = joystick_priority_axial;
break;
case gyro_full_priority:
om = gyro_full_priority;
break;
case gyro_priority_axial:
om = gyro_priority_axial;
break;
case gyro_joystick_combination:
om = gyro_joystick_combination;
break;
case only_gyro: // при переході на даний режим не можна нахиляти джойстик
om = only_gyro;
break;
case g_o_m: //get operating mode;
return om;
break;
}
}
return 0;
}
//
//
int* Gf_leftJoystick(int cmd, int newData[]){//test
static int ds[2]; // ds = data storage x y
static int dz[4] = {2048-100, 2048+100, 2048-100, 2048+100}; // test dzxy = dead zones x y
static int mm[4] = {95, 4000, 95, 4000}; // minimum maximum x y
static int ar; // ar = analog Read
if (!cmd) {//коли нема команди виконується основний код
ar = analogRead(p_s_t_x_39);//test
if ((ar < dz[x_l]) || (dz[x_r] < ar)) {//Перевіряємо чи є вихід з мертвої зони ідеальний центр -2048+
ar = constrain(ar, mm[x_l], mm[x_r]);
ar = map(ar, mm[x_l], mm[x_r], -32767, 32767);//якщо був вихід з дз перетворюємо в необхідні значення
if (ds[x_a] != ar) {//Перевіряємо чи відрізняються нові значення (дальше Все зрозуміло)
bleGamepad.setX(ar);// обережно при копіюванні !!!
Gf_sendReport(send_report);
ds[x_a] = ar;
}
}
else if (ds[x_a]){
ds[x_a] = 0;
bleGamepad.setX(0);// обережно при копіюванні !!!
Gf_sendReport(send_report);
}
ar = analogRead(p_s_t_y_36);//test
if ((ar < dz[y_u]) || (dz[y_d] < ar)) {
ar = constrain(ar, mm[y_u], mm[y_d]);
ar = map(ar, mm[y_u], mm[y_d], -32767, 32767);
if (ds[y_a] != ar) {
bleGamepad.setY(ar);
Gf_sendReport(send_report);
ds[y_a] = ar;
}
}
else if (ds[y_a]){
ds[y_a] = 0;
bleGamepad.setY(0);// обережно при копіюванні !!!
Gf_sendReport(send_report);
}
} else{
if (cmd == g_a_d) {return ds;}
else if (cmd == g_d_z) {return dz;}
else if (cmd == g_m_m) {return mm;}
else if (cmd == s_d_z) {memcpy(dz, newData, sizeof(dz));}
else if (cmd == s_m_m) {memcpy(mm, newData, sizeof(mm));}
}
return nullptr;
}
void Gf_gamepadButtons(){
byte flagSendReport;
for (byte i = 0; i <= 17; i++) {//Перевіряємо чи натиснуті кнопки
if (butGamepad[i].gamepadBatton(read_button_state)){//в любому випадку якщо відпустити або натиснути получим правда
flagSendReport = send_report;
}
}
Gf_sendReport(flagSendReport);//
}
/*
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;
}
*/
void Gf_functionsGamepad(byte acceptedCommand){
//START_TIMER();
static byte fep = true; // fep = flag execution permission
if (acceptedCommand) {// TEST Для зупинки потрібно використати Gf_sendReport(stop); ому що гіроскоп може перестати працювати
if (acceptedCommand == functions_gamepad_stop) {Gf_controlElementsStope(); fep = false;}
else if (acceptedCommand == functions_gamepad_start) {fep = true;}
}
else if (bleGamepad.isConnected() && fep) {
Gf_leftJoystick();//!!!
Gf_combinedGyroRightJoystick();
Gf_gamepadButtons();
Gf_sendReport(send_report_execute);
}
//END_TIMER();
}
void Gf_sendReport(byte acceptedCommand){
static byte gamepadSendReport = waiting_command;
switch (acceptedCommand) {
case send_report_execute:
if (gamepadSendReport == send_report) {
Md_autoPowerOff(apo_restart_time);
gamepadSendReport = waiting_command;
bleGamepad.sendReport();
}
break;
case send_report:
gamepadSendReport = send_report;
break;
}
}
void Gf_controlElementsStope(){
for (int i = 1; i <= 8; i++) {// перед тим як заборонити відправку потрібно відмінити все що відбувалось перед тим
bleGamepad.release(i);
}
// Відпустити кнопки Start та Select
bleGamepad.releaseStart();
bleGamepad.releaseSelect();
//курки
bleGamepad.setRightTrigger(0);
bleGamepad.setLeftTrigger(0);
//правий джойстик
bleGamepad.setRZ(0);
bleGamepad.setZ(0);
//лівий джойстик
bleGamepad.setX(0);
bleGamepad.setY(0);
// Якщо ти використовуєш AutoReport = 0
bleGamepad.sendReport();
}
// Gf
void setup() {
Wire.begin();
Serial.begin(115200);
delay(1000);
Serial.println("Serial.println("");");
//виконуємо настройки геймпада
bleGamepadConfig.setAutoReport(0);
bleGamepadConfig.setButtonCount(8);
bleGamepadConfig.setIncludeStart(true);
bleGamepadConfig.setIncludeSelect(true);
bleGamepadConfig.setHatSwitchCount(1);
bleGamepadConfig.setAxesMin(-32767);
bleGamepadConfig.setAxesMax(32767);
bleGamepadConfig.setWhichAxes(1, 1, 1, 1, 1, 1, 0, 0);
bleGamepad.begin(&bleGamepadConfig);
//
// Encoder setting
enc.setFastTimeout(2000); // Таймаут для швидкого повороту енкодера (в мс)
enc.setType(TYPE2);
//
// ----------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();
if (!mpu.dmpInitialize()) {
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);//встановлюємо максимальну чутливість
mpu.CalibrateGyro(5); // калібрування гіроскопа test
Serial.println("Enabling DMP...");
mpu.setDMPEnabled(true); // вмикаємо DMP
}
//
// af_startAnimation();// анімація при включенні. до робити в кінці!
//робота з енергонезалежною пам'яттю
EEPROM.begin(512);
delay(50);
EEPROM.put(0, 1);EEPROM.commit();//test
//af_saveToEEPROM(eeprom_initialization); //беремо збереження настройки з енергонезалежної пам'яті
//
//настройка rgb стрічки test
FastLED.addLeds<WS2812, p_led_pin_13, GRB>(leds,1);
FastLED.setBrightness(255); // глобальна яскравість (0–255)
//
// додаткові настройки геймпада
bgc_buttonConnectionResistor();//виконує підключення до резисторів якщо потрібно
bgc_shutdownButtonsSticking(); //захист від залипання кнопок
//
//настрою необхідні параметри для роботи ефектів
af_triggerLight(color_one); // встановлюємо початковий колір
af_shotgunBolt(bolt_pin_activate); // настроюємо піни
af_vibration(vt_pin_activate);
//
}
void loop() {
//START_TIMER();
// tick
enc.tick();
Md_autoPowerOff();//якщо є дозвіл автоматичне відключення по завершенню часу викликається перезапуск в Gf_sendReportExecute encoderModule
Gf_handleButton();
af_triggerShootingEffects();//постійно моніторимо функцію функція запускає ефекти після того як дається команда (дивитись вище)
Gf_functionsGamepad();
mdf_displayOutput();
// display.clearDisplay();
// Md_disableButton();
//display.display();
//END_TIMER();
}// void loop()