#include <Wire.h>
#include <EEPROM.h>
#include <BleGamepad.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "esp32-hal-timer.h"
#include <RotaryEncoder.h>
//#include "INA219_WE.h"
//INA219_WE ina219(0x40);
Adafruit_SSD1306 display(128, 64, &Wire, -1);
BleGamepad bleGamepad("FS G36", "UA", 100);
RotaryEncoder encoder(12, 14, RotaryEncoder::LatchMode::TWO03);
//Profiles 1
//Profiles
#define t_sp 1//не міняти число!
#define t_mp 2//не міняти число!
#define r_off 0 // = off
#define r_on 12 // = on
#define get_name 20
#define get_name_a_f 21
#define change_letter 22
#define delete_letter 23
//*prof()
#define direct_p_sp 20
#define direct_p_mp 21
#define act_dir 22
#define activate_p_sp 23
#define activate_p_mp 24
#define eeprom_initialization 25
#define rerecording 26// test
uint32_t tmrProf = 0;
byte defButton[11] = {0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};// test
class Profiles {
public:
Profiles(int deZoH, byte senHor, int deZoV, byte senVer, char* nameProf, byte tpProf, byte nabProf)
: deadZoneHor(deZoH),
sensitivityHor(senHor),
deadZoneVer(deZoV),
sensitivityVer(senVer),
tape(tpProf),
namber(nabProf),
status(r_off),
buttonArray{0, 18, 19, 15, 5, 25, 16, 17, 26, 33, 13, 23, 27} // test
{ //при глюках замінити на memcpy(buttonArray, defaultButtonArray, sizeof(buttonArray));
strncpy(nameArray, nameProf, 20); //даємо назву профілю через конструктор
nameArray[20] = '\0';
letterIndicator = [this]() -> byte {//переміна зберігає собі кількість букв в назві потрібно для зміни імен
for (int i = 19; i >= 0; i--) {
if (this->nameArray[i] != ' ') {
return i + 1;
}
}
return 20;
}();
}
int setDeZoHor(int deZoH) {//set
if (deadZoneHor != deZoH) {sensitivityHor = 0;}
deadZoneHor = deZoH;
return 0;
}
byte setSenHor(byte senHor) {
sensitivityHor = senHor;
return 0;
}
int setDeZoVer(int deZoV) {
if (deadZoneVer != deZoV) {sensitivityVer = 0;}
deadZoneVer = deZoV;
return 0;
}
byte setSenVer(byte senVer) {
sensitivityVer = senVer;
return 0;
}
byte setStatus(byte st) {
status = st;
return 0;
}
const char* nameProfiles(byte acceptedCommand, char Letter = ' '){
char baldness[] = "";//baldness беззмістовність
if (status == r_on && tmrProf < millis()){
tmrProf = millis()+600;
Flag = !Flag;
}
else if (status == r_off && !Flag){
Flag = 1;
}
switch (acceptedCommand) {
case get_name:
return nameArray;
break;
case get_name_a_f:
if (Flag) {
return nameArray;
}
else {
return baldness;
}
break;
case change_letter:
if (letterIndicator != 20){
nameArray[letterIndicator] = Letter;
letterIndicator++;
}
break;
case delete_letter:
if (letterIndicator){
letterIndicator--;
nameArray[letterIndicator] = ' ';
}
break;
return baldness;
}
return baldness;
}
byte centroName() {
for (int i = 19; i >= 0; i--) {
if (nameArray[i] != ' '){
return 61-i*3;
break;
}
}
return 0;
}
byte getNamberTape(){
return tape * 10 +namber;//логіка роботи тип може бути 1 або 2 множимо на 10 щоб не збігалася числа
}
void resetButtons(){
for (int i = 0; i < sizeof(defButton); i++) {
buttonArray[i] = defButton[i];
}
}
void checkName() {//якщо немає імені то вставляє власне ім'я залежно від номеру профіля
bool allSpaces = true; // Припускаємо, що всі символи - пробіли
for(int i = 0; i < 20; i++) { // Останній символ не перевіряємо
if(nameArray[i] != ' ') {
allSpaces = false;
break;
}
}
if(allSpaces) {
snprintf(nameArray, 21, "My profile %d", namber); // Форматуємо нову назву
}
}
void copyFrom(const Profiles& other) {
deadZoneHor = other.deadZoneHor;
sensitivityHor = other.sensitivityHor;
deadZoneVer = other.deadZoneVer;
sensitivityVer = other.sensitivityVer;
tape = other.tape;
namber = other.namber;
status = other.status;
memcpy(buttonArray, other.buttonArray, sizeof(buttonArray));
memcpy(nameArray, other.nameArray, sizeof(nameArray));
}
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(buttonArray, other.buttonArray, sizeof(buttonArray)) != 0) ||
(memcmp(nameArray, other.nameArray, sizeof(nameArray)) != 0);
}
// prof(act_dir)->sensitivityHor;
char nameArray[21];
byte buttonArray[13];
byte Flag;
int deadZoneHor;//(primary початкова) початок руху
byte sensitivityHor;
int deadZoneVer;
byte sensitivityVer;
byte tape;
byte namber;
byte status;
byte letterIndicator;
private:
};
// Ініціалізуємо статичну змінну класу Profiles
Profiles profilSeve(0, 0, 0, 0, "0", 0, 0);
Profiles profilSpOne(2000, 100, 2556, 92, "CodMW", t_sp, 1);
Profiles profilSpTwo(166, 120, 1965, 112, "Batl3", t_sp, 2);
Profiles profilSpThree(2000, 100, 2556, 92, "SP 33", t_sp, 3);
Profiles profilSpFour(166, 120, 1965, 112, "SP 44", t_sp, 4);
Profiles profilSpFive(2000, 100, 2556, 92, "SP 55", t_sp, 5);
Profiles profilSpSix(166, 120, 1965, 112, "SP 66", t_sp, 6);
Profiles profilSpSeven(2000, 100, 2556, 92, "SP 77", t_sp, 7);
Profiles profilMpOne(2000, 100, 2556, 92, "05461 ", t_mp, 1);
Profiles profilMpTwo(166, 120, 1965, 112, "Xmen 74 ", t_mp, 2);
Profiles profilMpThree(2000, 100, 2556, 92, "Mp 3 ", t_mp, 3);
Profiles profilMpFour(166, 120, 1965, 112, "Mp 4 ", t_mp, 4);
Profiles profilMpFive(2000, 100, 2556, 92, "Mp 5 ", t_mp, 5);
Profiles profilMpSix(166, 120, 1965, 112, "Mp 6 ", t_mp, 6);
Profiles profilMpSeven(2000, 100, 2556, 92, "Mp 7 ", t_mp, 7);
Profiles *prof(byte acceptedCommand, byte number = 0){
Profiles *profArray[] = {&profilSeve, &profilSpOne, &profilSpTwo, &profilSpThree, &profilSpFour, &profilSpFive, &profilSpSix, &profilSpSeven,
&profilMpOne, &profilMpTwo, &profilMpThree, &profilMpFour, &profilMpFive, &profilMpSix, &profilMpSeven};
static byte acting = []() -> byte {//одноразова інт пер лямбда функція оскільки метод не повертає значення потрібно зробити так
byte temp;
int tempProfSize = 7 * sizeof(Profiles);
int tempProfSizebutArr = 7 * sizeof(profArray[1]->buttonArray);
EEPROM.get(7 * sizeof(Profiles) + 1, temp); // Читаємо значення з EEPROM
return temp; // Повертаємо прочитане значення
}();
if (acceptedCommand == act_dir) {//скерування до активного профілю
return profArray[acting];
}
else if (acceptedCommand == direct_p_sp) {//скеровує до стандартного профіля Для певних настройок
return profArray[number];
}
else if (acceptedCommand == direct_p_mp) {//скеровує до власного профіля Для певних настройок
return profArray[number+7];
}
else if (acceptedCommand == activate_p_sp || acceptedCommand == activate_p_mp) {//вибраний профіль активується
for (int i = 1; i < 15; i++){
profArray[i]->setStatus(r_off);
}
if (acceptedCommand == activate_p_sp ) {
profArray[number] -> setStatus(r_on);
acting = number;
}
else {
profArray[number+7] -> setStatus(r_on);
acting = number+7;
}
EEPROM.put(7 * sizeof(Profiles) + 1, acting);
EEPROM.commit();
//Serial.println(sizeof(Profiles));
//Serial.println(EEPROM.get(0, acting));
}
else if (acceptedCommand == eeprom_initialization) {
int tempProfSize = 7 * sizeof(Profiles);
int tempProfSizebutArr = 7 * sizeof(profArray[1]->buttonArray);
int sizebutArr = sizeof(profArray[1]->buttonArray);
//Serial.println("sizeof(Profiles)");
//Serial.println(sizeof(Profiles));
if (acting == 255 || Ae_BatR(13)) {// test ( Ae_BatR(13) ) Якщо правда то це самий перший запуск потрібно зберегти в профілі і перемінну енергонезалежну пам'ять подальші збереження будуть виконуватися при виході з настройок якщо вибрати так
acting = 8;
EEPROM.put(0 * sizeof(Profiles), profilMpOne);
EEPROM.put(1 * sizeof(Profiles), profilMpTwo);
EEPROM.put(2 * sizeof(Profiles), profilMpThree);
EEPROM.put(3 * sizeof(Profiles), profilMpFour);
EEPROM.put(4 * sizeof(Profiles), profilMpFive);
EEPROM.put(5 * sizeof(Profiles), profilMpSix);
EEPROM.put(6 * sizeof(Profiles), profilMpSeven);
// Зберігаємо buttonArray кожного профілю окремо
EEPROM.put(tempProfSize + 0 * sizebutArr, profArray[1]->buttonArray);
EEPROM.put(tempProfSize + 1 * sizebutArr, profArray[2]->buttonArray);
EEPROM.put(tempProfSize + 2 * sizebutArr, profArray[3]->buttonArray);
EEPROM.put(tempProfSize + 3 * sizebutArr, profArray[4]->buttonArray);
EEPROM.put(tempProfSize + 4 * sizebutArr, profArray[5]->buttonArray);
EEPROM.put(tempProfSize + 5 * sizebutArr, profArray[6]->buttonArray);
EEPROM.put(tempProfSize + 6 * sizebutArr, profArray[7]->buttonArray);
// Зберігаємо значення acting після всіх Profiles та їх buttonArray
EEPROM.put(tempProfSize + tempProfSizebutArr, acting);
EEPROM.commit();
/*
for (int i = 0; i <= 6; i++){ //варіант з циклом for можливо буде працювати не в симуляторі
EEPROM.put(i * sizeof(Profiles), profArray[i+8]);
EEPROM.put(tempProfSize + i * sizebutArr, profArray[i+1]->buttonArray);
Serial.println(i);
}
EEPROM.put(tempProfSize + tempProfSizebutArr, acting);
EEPROM.commit();
*/
}
else{//якщо Дані були вже збережені раніше то повертаємо їх з енергонезалежної пам'яті
/*
delay(500);
for (int i = 0; i <= 6; i++){
EEPROM.get(i * sizeof(Profiles), profArray[i+8]);
EEPROM.get(tempProfSize + i * sizebutArr, profArray[i+1]->buttonArray);
}
*/
EEPROM.get(0 * sizeof(Profiles), profilMpOne);
EEPROM.get(1 * sizeof(Profiles), profilMpTwo);
EEPROM.get(2 * sizeof(Profiles), profilMpThree);
EEPROM.get(3 * sizeof(Profiles), profilMpFour);
EEPROM.get(4 * sizeof(Profiles), profilMpFive);
EEPROM.get(5 * sizeof(Profiles), profilMpSix);
EEPROM.get(6 * sizeof(Profiles), profilMpSeven);
EEPROM.get(tempProfSize + 0 * sizebutArr, profArray[1]->buttonArray);
EEPROM.get(tempProfSize + 1 * sizebutArr, profArray[2]->buttonArray);
EEPROM.get(tempProfSize + 2 * sizebutArr, profArray[3]->buttonArray);
EEPROM.get(tempProfSize + 3 * sizebutArr, profArray[4]->buttonArray);
EEPROM.get(tempProfSize + 4 * sizebutArr, profArray[5]->buttonArray);
EEPROM.get(tempProfSize + 5 * sizebutArr, profArray[6]->buttonArray);
EEPROM.get(tempProfSize + 6 * sizebutArr, profArray[7]->buttonArray);
}
/*
for (int i = 0; i <= 6; i++){
EEPROM.get(i * sizeof(Profiles), *profArray[i+8]); //*profArray[i+8]???
Serial.println(i);
}
*/
profArray[acting] -> setStatus(r_on);
}
return 0;
}
//P
//Additional_elements
bool Vibrgyro;
// СПІЛЬНІ КОНСТАНТИ ПІД ХОД ДЛЯ БАГ ФУНК
#define no_action 0 // НЕМА ДІЇ
#define no_data 0 // немає даних
#define no_indication 0 // немає вказівки
#define off 0
#define get_status 1 // получити настройки
#define yes 1
#define save_settings 4 // зберехти настройки
#define reset 11
#define on 12
#define click 13
#define execute_code 14// execution without comman - виконання без команди
#define gyro 15
#define profil 16
#define centering 17
#define melody_start 1
#define melody_off_power 2
#define x_axis_hor 1
#define y_axis_ver 2
#define z_axis_ver 3
#define get_y 2
#define get_z 3
#define get_vibr 4
//Ae_BatR();
//Піни карта
//#define b_mein 25
#define b_emul_dpad 1
#define b_handle 4
#define b_triger_r 23
#define b_analog_l 33
#define b_on_off 14
#define b_emul_dpad 26
#define b_sw 32 // кнопка енко
#define p_dt 12
#define p_clk 14
#define thandle_touch_button 27
//Mf_EncoderButton();
//#define click 13
#define press_b_e 20
#define hold_b_e 21
#define reset_status 23
#define take_but_pos 24
#define button_pressed 25
#define button_released 26
#define releas_b_e 27
#define completed_work 28
#define status_allowed 29
//Md_Display(byte Receiving) {
#define info_display 20
#define menu 21
#define confirm_selection 22
#define lang_eng 25
#define lang_ukr 26
#define click_in_dis_on 27
//Md_MainDisplayENG();
#define menu_off 21
#define menu_start 22
//byte Md_addres();
//#define click 13
#define back 20
#define address_entered 21
#define get_add 22
#define int_add 23
#define add_status 24
#define int_add_last 25
#define address_deleted 26
#define click_reset_off 27
//int Md_cursor();
//#define reset 11
#define selection_inversion 21
#define freeze_position 22
#define get_pos 23
#define return_pos 24
#define cursor_type_one 25
#define cursor_type_two 26
byte noData[1];
byte posOne[] = {1};
byte posTwo[] = {2};
byte posThree[] = {3};
byte posFour[] = {4};
byte posFive[] = {5};
byte posSix[] = {6};
byte posEight[] = {7};
//Mf_encoderRotationSpeed()
//Ae_encoderInterrupt()
#define rotated_r1 1
#define rotated_l1 -1
#define rotated_r2 2
#define rotated_l2 -2
#define check_rotation 20
#define get_rotation1 21
#define get_rotation2 22
volatile uint32_t tmr;
volatile int8_t seveEncPos;
//MdAf_confirmYesNo()
//MdAf_consenDisplay()
#define choice_yes 20
#define choice_no 21
//MdAf_profileStorage()
#define copy_profile 20
#define return_save 21
#define check_changes 22
#define recorded_changes 23//зафіксовані зміни
//Mf_Buttons()
//#define reset 11
#define next_animation 20
//Ae_displayRefresh()
//#define reset 11
#define clear_display 20
#define print_display 21
// прототипи
//int8_t Md_addres(int8_t addNumb = 0, int8_t acceptedCommand = 0, int8_t acceptedData = 0);
byte Md_p_Buttons(byte receivedData = 0, byte selectedButton = 0);
int Md_movingButtons(int acceptedCommand = 0);
byte Mf_EncoderButton(byte acceptedCommand = 0);
byte Md_Display(byte Receiving = 0);
byte Ae_autoShutdown(byte acceptedCommand = 0);
byte Ae_displayRefresh(byte acceptedCommand = 0);
byte Ae_BatR(byte Taking) {
return !digitalRead(Taking);
}
byte Ae_autoShutdown(byte acceptedCommand){
static uint32_t tmr = 300000;
if (acceptedCommand == reset) {
tmr = 300000 + millis();// 5 hv
}
else if (tmr < millis()){
delay(400);
esp_deep_sleep_start();
}
return 0;
}
void Ae_buttonOnOff(){
if (!digitalRead(13)) {
delay(400);
esp_deep_sleep_start();
}
delay(100);
}
byte Ae_displayRefresh(byte acceptedCommand){
/*
static byte clearRefresh = 1;
if(acceptedCommand == reset && !clearRefresh) {
Serial.println("reset");
clearRefresh = 1;
}
else if(clearRefresh) {
if(acceptedCommand == clear_display && clearRefresh % 2 != 0) {
display.clearDisplay();
}
else if(acceptedCommand == print_display && clearRefresh % 2 == 0) {
display.display();
}
clearRefresh++;
if(clearRefresh == 101) {
clearRefresh = 0;
}
}
*/
if(acceptedCommand == clear_display) {
display.clearDisplay();
}
else if(acceptedCommand == print_display) {
display.display();
}
return 0;
}
void gyroStart(){
/*
mpu.initialize();
delay(10);
mpu.dmpInitialize();
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
static byte flag = []() -> byte {
byte temp;
EEPROM.get(7 * sizeof(Profiles) + 1, temp);
return !temp;
}();
*/
static byte flag = 1;//TECT
long offsets[6];
if(Ae_BatR(b_triger_r) && Ae_BatR(b_analog_l)){
byte temp = 10;
while(temp > 0){
display.clearDisplay();
display.setCursor(0, 16);
display.println(" Calibration starts");
display.println();
display.print (" ");
display.println(temp);
display.display();
Ae_buttonOnOff();
temp--;
delay(1000);
}
display.clearDisplay();
display.setCursor(2, 16);
display.println(" Wait for completion");
display.display();
delay(2000);//TE
/*
mpu.setXAccelOffset(0);
mpu.setYAccelOffset(0);
mpu.setZAccelOffset(0);
mpu.setXGyroOffset(0);
mpu.setYGyroOffset(0);
mpu.setZGyroOffset(0);
delay(10);
mpu.CalibrateAccel(60);//не забудьте додати рестарт при помилках калібровки
mpu.CalibrateGyro(120);
delay(10);
offsets[0] = mpu.getXAccelOffset();
offsets[1] = mpu.getYAccelOffset();
offsets[2] = mpu.getZAccelOffset();
offsets[3] = mpu.getXGyroOffset();
offsets[4] = mpu.getYGyroOffset();
offsets[5] = mpu.getZGyroOffset();
*/
flag = 1;
/*
EEPROM.put(7 * sizeof(Profiles) + 1, flag);
EEPROM.put(7 * sizeof(Profiles) + 2, offsets);
EEPROM.commit();
*/
display.clearDisplay();
display.setCursor(4, 16);
display.println("Calibration complete");
display.display();
}
else if(!flag){
display.clearDisplay();
display.setCursor(1, 16);
display.println("Gyroscope calibration");
display.println(" not done");
display.display();
while(1){
Ae_buttonOnOff();
}
}
else if(flag){
/*
EEPROM.get(7 * sizeof(Profiles) + 2, offsets);
mpu.setXAccelOffset(offsets[0]);
mpu.setYAccelOffset(offsets[1]);
mpu.setZAccelOffset(offsets[2]);
mpu.setXGyroOffset(offsets[3]);
mpu.setYGyroOffset(offsets[4]);
mpu.setZGyroOffset(offsets[5]);
*/
}
}
void startAnimation(){
display.clearDisplay();
display.invertDisplay(0);
display.setCursor(-2, 28);
display.println(" FS G36 ");
display.display();
delay(500);
display.clearDisplay();
display.display();
}
void Ae_encoderInterrupt() {
encoder.tick(); // Оновлення стану енкодер
}
//Ae
//Main_display
// add1-----------------------------------------------------------------------------------------------------------
void Md_MainDisplay() {// головна фунція виклик решта функцій дисплея
switch (Md_addres(1, int_add)) {
case no_action:
MdId_BluetoothPrint();
MdAf_typeNumberPrint();
MdId_battery();
Md_ItemOne();
break;//1
case 1:
Md_Profile();
break;//a2
case 2:
//qr-код ссылка на інструкцію
break;//a2
case address_entered:
if (Md_addres(1, get_add) == 3) {
Md_Display(info_display);
Md_cursor(posOne, return_pos);
Md_addres(1, back);
}
else{
Md_cursor(posOne, return_pos);
}
break;
}
}
void Md_ItemOne(){
byte Lines[] = {22, 32, 43};
Md_cursor(Lines, sizeof(Lines));
display.setCursor(0, 22);
display.println(" Profile");
display.setCursor(0, 32);
display.println(" Instruction (!)");
display.setCursor(0, 43);
display.println(" Exit menu ");
}
//
// add2-----------------------------------------------------------------------------------------------------------
void Md_Profile() {
if (prof(act_dir)->tape == t_sp) {//в залежності від типу профілю вид меню міняється
switch (Md_addres(2, int_add)) {
case no_action:
Md_p_Profile();
break;
case 1:
Md_ProfileSettings();
break;//a3
case 2:// QR code
break;
case 3:
Md_SelectProfile();
break;//a3
case address_entered:
if (Md_addres(2, get_add) == 4) {
Md_addres(1, back);
}
else if (Md_addres(2, get_add) == 1) {
MdAf_profileStorage(copy_profile);////копіюємо профіль при вдоході в настройки для подальшої перевірки на зміни при виході
}
Md_cursor(posOne, return_pos);
break;
}
}
else {
switch (Md_addres(2, int_add)) {
case no_action:
Md_p_Profile();
break;
case 1://a3
Md_ProfileSettings();
break;
case 2: //a3
Md_SelectProfile();
break;
case address_entered:
if (Md_addres(2, get_add) == 3) {
Md_addres(1, back);
}
else if (Md_addres(2, get_add) == 1) {
MdAf_profileStorage(copy_profile);////копіюємо профіль при вдоході в настройки для подальшої перевірки на зміни при виході
}
Md_cursor(posOne, return_pos);
break;
}
}
}
void Md_p_Profile() {
byte LinesSp[] = {31, 39, 49, 57};
byte LinesMp[] = {35, 49, 57};
if (prof(act_dir)->tape == t_sp) {
Md_cursor(LinesSp, sizeof( LinesSp));
}
else {
Md_cursor(LinesMp, sizeof(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);
if (prof(act_dir)->tape == t_sp) {
display.print("Sp");
display.println(prof(act_dir)->namber);
}
else {
display.print("Mp");
display.println(prof(act_dir)->namber);
}
display.setCursor(prof(act_dir)->centroName(), 15);//функція centroName() в залежності від кількості ім'я вранці
display.println(prof(act_dir)->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);
if (prof(act_dir)->tape == t_sp) {
display.setCursor(0, 31);
display.println(" Settings " );
display.println(" QR code");
}
else {
display.setCursor(0, 35);
display.println(" Settings " );
}
display.setCursor(0, 49);
display.println(" Select profile ");
display.println(" Back" );
}
/*
void Md_p_Settings() {
display.setCursor(0, 0);
display.println(" Hibernation ");
display.println(" Sounds ");
display.println(" Gamepad mode ");
display.println(" Back" );
}
*/
//
// add3-----------------------------------------------------------------------------------------------------------
void Md_SelectProfile() {
switch (Md_addres(3, int_add)) {
case no_action:
Md_p_SelectProfile();
break;
case 1:
Md_StandardProfiles();
break;
case 2:
Md_MyProfiles();
break;
case address_entered:
if (Md_addres(3, get_add) == 3) {
if (prof(act_dir)->tape == t_sp) {
Md_cursor(posThree, return_pos);
}
else{
Md_cursor(posTwo, return_pos);
}
Md_addres(2, back);
}
else{
Md_cursor(posOne, return_pos);
}
break;
}
}
void Md_p_SelectProfile() {
byte Lines[] = {16, 24, 32};
Md_cursor(Lines, sizeof(Lines));
display.setCursor(0, 16);
display.println(" Standard profiles" );
display.println(" My profiles");
display.println(" Back" );
}
void Md_ProfileSettings() {
if (prof(act_dir)->tape == t_sp) { //вид пунктiv меню якщо профіль стандартний
switch (Md_addres(3, int_add)) {
case no_action:
Md_p_ProfileSettings();
break;
case 1:
Md_Buttons();
break;//Buttons
case 2:// якщо були зміни то повернення відбудеться через функцію MdAf_profileStorage
if (MdAf_profileStorage(check_changes) == recorded_changes) {//перевірка на наявність змін
switch (MdAf_consenDisplay()) {//якщо є зміни запуститься дисплей підтвердження при виході з настройок
case choice_yes:
MdAf_profileStorage(reset);//якщо виберемо так профіль повторно скопірується комадою ресеті функція MdAf_profileStorage перестане видавати recorded_changes
Md_addres(2, back);
break;
case choice_no:
MdAf_profileStorage(return_save);//якщо виберемо Ні функція dAf_profileStorage перестане видавати recorded_changes і умова перестане виконуватися
Md_addres(2, back);
break;
}
}else{
Md_addres(2, back);
}
break;
case address_entered:
Md_cursor(posOne, return_pos);
break;
}
}
else { // вид пунктів якщо вибрано власний профіль
switch (Md_addres(3, int_add)) {
case no_action: //a2
Md_p_ProfileSettings();//відображення пунктів настройки
break;
case 1://Gyroscope //a4
Md_gyroscopeSettings();
break;
case 2://Buttons
Md_Buttons();
break;
case 3://Name
Md_changeNameProfile();
break;
case 4:// якщо були зміни то повернення в попередній пункт меню відбудеться через функцію MdAf_profileStorage
if (MdAf_profileStorage(check_changes) == recorded_changes) {//перевірка на наявність змін
switch (MdAf_consenDisplay()) {//якщо є зміни запуститься дисплей підтвердження при виході з настройок
case choice_yes:
MdAf_profileStorage(reset);//якщо виберемо так перестане видавати recorded_changes
Md_addres(2, back);
break;
case choice_no:
MdAf_profileStorage(return_save);//якщо виберемо Ні функція dAf_profileStorage перестане видавати recorded_changes і умова перестане виконуватися
Md_addres(2, back);
break;
}
}
else{//якщо змін не було то просто повертаємося
Md_addres(2, back);
}
break;
case address_entered:
Md_cursor(posOne, return_pos);
break;
}
}
}
void Md_p_ProfileSettings() {
byte LinesSp[] = {16, 24};
byte LinesMp[] = {16, 24, 32, 50};
if (prof(act_dir)->tape == t_sp) {
Md_cursor(LinesSp, sizeof(LinesSp));
display.setCursor(0, 16);
display.println(" Buttons" );//не добавляти пункт reset не потрібно
display.println(" Back" );
} else {
Md_cursor(LinesMp, sizeof(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" );
}
}
//
// add4-----------------------------------------------------------------------------------------------------------
void Md_StandardProfiles(){
switch (Md_addres(4, int_add)) {
case no_action:
Md_p_StandardProfiles();//відображення списку профілів вибір
break;
case address_entered:
if (Md_addres(4, get_add) == 8) {
Md_cursor(posOne, return_pos);
Md_addres(3, back);
}
else{
if (prof(act_dir)->getNamberTape() != prof(direct_p_sp, Md_addres(4, get_add))->getNamberTape()) {
prof(activate_p_sp, Md_addres(4, get_add));
Md_cursor(posOne, return_pos);
Md_addres(2, back);
}
else{Md_addres(4, back); }
}
break;
}
}
void Md_p_StandardProfiles(){
byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
Md_cursor(Lines, sizeof(Lines));
for (int i = 0; i < 7; i++) {//виводим імена профілів діючий профіль мигає
display.setCursor(6, i*8);
display.print(prof(direct_p_sp, i+1)->nameProfiles(get_name_a_f));
}
display.setCursor(0, 56);
display.println(" Back" );
}
void Md_MyProfiles(){
switch (Md_addres(4, int_add)){//третій параметр забороняє вибирати діючий профіль
case no_action:
Md_p_MyProfiles();
break;
case address_entered:
if (Md_addres(4, get_add) == 8) {//повертаємося в передній пункт
Md_cursor(posOne, return_pos);
Md_addres(3, back);
}
else{//якщо вибраний профіль не є діючий виконуємо заміну якщо так просто повертаємося
if (prof(act_dir)->getNamberTape() != prof(direct_p_mp, Md_addres(4, get_add))->getNamberTape()) {
prof(activate_p_mp, Md_addres(4, get_add));
Md_cursor(posOne, return_pos);
Md_addres(2, back);//не міняти двоєчку будуть глюки
}
else{Md_addres(4, back);}
}
break;
}
}
void Md_p_MyProfiles() {
byte Lines[] = {0, 8, 16, 24, 32, 40, 48, 56};
Md_cursor(Lines, sizeof(Lines));
for (int i = 0; i < 7; i++) {
display.setCursor(6, i*8);
display.print(prof(direct_p_mp, i+1)->nameProfiles(get_name_a_f));
}
display.setCursor(6, 56);
display.println("Back" );
}
void Md_gyroscopeSettings(){//a3
int seveData;//використовую щоб було більш зрозуміліше
if (Md_addres(4, add_status) == address_deleted){//відновляємо у виконанні функції Gf_GyroAnalogR після виходу з настройок мертвої зони (якщо адрес видалився)
/*
timerAlarmEnable(timer);
*/
}
if (Md_addres(4, get_add) != 5){//зроблено для того щоб курсор не міняв свій вигляд при виході
Md_p_gyroscopeSettings();
}
switch (Md_addres(4, int_add_last)) {//4
case no_action:
break;
case 1:
seveData = Mf_Magnifier(prof(act_dir)->deadZoneHor, 0, 9999, 100);
prof(act_dir)->setDeZoHor(seveData);
break;
case 2:
seveData = Mf_Magnifier(prof(act_dir)->sensitivityHor, 0, 100, 10);
prof(act_dir)->setSenHor(seveData);
break;
case 3:
seveData = Mf_Magnifier(prof(act_dir)->deadZoneVer, 0, 9999, 100);
prof(act_dir)->setDeZoVer(seveData);
break;
case 4:
seveData = Mf_Magnifier(prof(act_dir)->sensitivityVer, 0, 100, 10);
prof(act_dir)->setSenVer(seveData);
break;
case 5:
Md_cursor(posOne, return_pos);
Md_addres(3, back);
break;
case address_entered:
if (Md_addres(4, get_add) == 1 || Md_addres(4, get_add) == 3) {//зупиняємо виконання функції Gf_GyroAnalogR для того щоб настроїти мертву зону
/*
timerAlarmDisable(timer);
*/
}
break;
}
}
void Md_p_gyroscopeSettings(){
static uint32_t tmr;
static byte dashFlag;//прапорець м'ягання
if (tmr < millis()) {
tmr = millis() + 500;
dashFlag = ! dashFlag;
}
byte Lines[] = {5, 15, 34, 44, 55};
Md_cursor(Lines, sizeof(Lines));
display.setCursor(8, 5);
display.println("Dead zone ");
display.setCursor(8, 15);
display.println("Sensitivity");
display.setCursor(8, 34);
display.println("Dead zone ");
display.setCursor(8, 44);
display.println("Sensitivity");
display.setCursor(8, 55);
display.println("Back");
display.setCursor(84, 10);
display.println("H");
display.setCursor(84, 39);
display.println("V");
display.setCursor(102, 4);
display.println(prof(act_dir)->deadZoneHor);
display.setCursor(104, 17);
display.println(prof(act_dir)->sensitivityHor);
display.setCursor(102, 33);
display.println(prof(act_dir)->deadZoneVer);
display.setCursor(104, 46);
display.println(prof(act_dir)->sensitivityVer);
display.drawRoundRect(6, 2, 69, 23, 3, WHITE); //РАМКА НАВКОЛО Dead zone Sensitivity H
display.drawRoundRect(6, 31, 69, 23, 0, WHITE);//РАМКА НАВКОЛО Dead zone Sensitivity V
//H
if((Md_cursor(noData, get_pos) == 1 || Md_cursor(noData, get_pos) == 2) && !Md_addres(4, get_add)){// РАМКА НВКОЛО H
if(dashFlag){display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
}
else{display.drawRoundRect(79, 6, 15, 15, 0, WHITE);}
//V
if((Md_cursor(noData, get_pos) == 3 || Md_cursor(noData, get_pos) == 4) && !Md_addres(4, get_add)){// РАМКА НВКОЛО V
if(dashFlag){display.drawCircle(86, 42, 7, WHITE);}
}
else{display.drawCircle(86, 42, 7, WHITE);}
if(Md_addres(4, get_add) == 1){ // число рамка DZ H
if(dashFlag){display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
}
else{display.drawRoundRect(99, 2, 29, 11, 4, WHITE);}
if(Md_addres(4, get_add) == 2){ // число рамка S H
if(dashFlag){display.drawRect(101, 15, 23, 11, WHITE);}
}
else{display.drawRect(101, 15, 23, 11, WHITE);}
if(Md_addres(4, get_add) == 3){ // число рамка DZ V
if(dashFlag){display.drawRect(99, 31, 29, 11, WHITE);}
}
else{display.drawRect(99, 31, 29, 11, WHITE);}
if(Md_addres(4, get_add) == 4){ // число рамка S V
if(dashFlag){display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
}
else{display.drawRoundRect(101, 44, 23, 11, 4, WHITE);}
}
void Md_Buttons(){//замінити if на Switch в кінці при використанні свіч глюки
Md_p_Buttons();
if(Md_addres(no_data, add_status) == address_deleted){//якщо адрес був видалений скидає функцію
Md_p_Buttons(reset);
Md_movingButtons(reset);
}
switch (Md_addres(4, int_add_last)) {
case no_action:
break;
case 1:
Md_movingButtons();
break;
case 2:
switch (MdAf_confirmYesNo(37, 46)) {
case no_action:
break;
case choice_yes:
Md_addres(4, back);
prof(act_dir)->resetButtons();
break;
case choice_no:
Md_addres(4, back);
break;
}
break;
case 3:
Md_cursor(posTwo, return_pos);
Md_addres(3, back);
break;
case address_entered:
Serial.print("address_entered");
if(Md_addres(4, get_add) == 1){
Md_p_Buttons(next_animation);
}
else if(Md_addres(4, get_add) == 2){
Md_addres(no_data, click_reset_off);//потрібно для того щоб Клік працював тепер в функції MdAf_confirmYesNo(37, 46)
}
break;
}
}
int Md_movingButtons(int acceptedCommand){//не закінчено тест кнопки опитуються не повністю
static unsigned long tmr;//таймер для захисту від тремтіння контактів кнопки
static int8_t buttonFlag;//затвор кнопки захист від тремтіння
static int8_t sequenceNumber;//при першому натиску зберігаємо порядковий номер піна-кнопки яка спрацювала
if (acceptedCommand == reset) {
buttonFlag = sequenceNumber = 0;
return 0;
}
else if (!buttonFlag && tmr < millis()) { //кнопка одноразово спрацьовує з захистом
for (int i = 3; i <= 4; i++) {
if (Ae_BatR(prof(act_dir)->buttonArray[i])) {
tmr = millis()+500;
buttonFlag = 1;
Md_p_Buttons(next_animation, i);
if(!sequenceNumber){//якщо була натиснута перша кнопка зберігаємо порядковий номер
sequenceNumber = i;
}
else{//якщо натиснули другу кнопку міняємо місцями елементи масиву-кнопки
int temp = prof(act_dir)->buttonArray[sequenceNumber];// переміщення даних між елементами масиву Напряги мозги і поймеш
prof(act_dir)->buttonArray[sequenceNumber] = prof(act_dir)->buttonArray[i];
prof(act_dir)->buttonArray[i] = temp;
buttonFlag = sequenceNumber = 0;
Md_addres(4, back);
}
break;
}
}
}
else if (buttonFlag && tmr < millis() && digitalRead(prof(act_dir)->buttonArray[sequenceNumber])) {//захист від брязки до першої кнопки захист натиску другої кнопки не потрібен програма завершує роботу
buttonFlag = 0;
}
return 0;
}
void Md_resetButtons(){///масив буде потрібно змінити в кінці
const byte defaultButton[] = {0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
for (int i = 0; i < sizeof(defaultButton); i++) {
prof(act_dir)->buttonArray[i] = defaultButton[i];
}
}
byte Md_p_Buttons(byte receivedData, byte selectedButton){
//бегуща стрілка. анімація. кнопка один збереження. крапка 2 збереження. анімація кнопка ліва. анімація кнопка права
static byte runningArrow, animation, buttonOne, buttonTwo, accumulationL, accumulationR;
static unsigned long tmr; //таймер для виконання анімації
const char *buttonNames[] = {//масив імена кнопок виводим при натиску
" ",
" ",
" ",
"A",
"B",
"X",
"Y",
"LB",
"RB",
"LT",
"RT",
"LA",
"RA",
};
display.drawRect(6, 5, 31, 31, WHITE); // квадрат L
display.drawRect(90, 5, 31, 31, WHITE);// квадрат R
display.drawCircle(21, 16, 8, WHITE);//кружки над кнопками
display.drawCircle(105, 16, 8, WHITE);
display.drawRect(12, 26+accumulationL, 19, 5, WHITE);//ліва кнопка accumulationL збільшення зменшення анімація кнопки
display.fillRect(9, 30, 25, 4, WHITE);
display.drawRect(96, 26+accumulationR, 19, 5, WHITE);//ПРАВА кнопка accumulationR збільшення зменшення анімація кнопки
display.fillRect(93, 30, 25, 4, WHITE);
byte Lines[] = {40, 48, 56};//передаємо координати переміщення функції
Md_cursor(Lines, sizeof(Lines));
display.setCursor(0, 40);//понятно
display.println(" Move buttons");
display.println(" Reset");
display.println(" Back");
if (!receivedData && !animation) {//якщо фаус то закінчуємо роботу функції економія ресурсів
return 0;
}
if (receivedData == reset) {
animation = accumulationL = accumulationR = runningArrow = 0;
}
else if (receivedData == next_animation) {//в коді викликаємо функцію переключаємо анімацію
accumulationL = accumulationR = runningArrow = 0;
if (animation != 3) {
animation++;
}
if (animation == 2) {
buttonOne = selectedButton;
}else if (animation == 3) {
buttonTwo = selectedButton;
tmr = millis()+1000;
}
}
if (animation == 1 && tmr < millis()) { //анімація кнопок за допомогою перемінної
accumulationL++;
if (accumulationL == 4) {
accumulationL = 0;
}
}
else if (animation == 2 && tmr < millis()) {
accumulationR++;
runningArrow++;
if (accumulationR == 4 ) {
accumulationR = 0;
}
if (runningArrow == 8 ) {
runningArrow = 0;
}
}
else if (animation == 3 && tmr < millis()) {//відображення другої кнопки завершення роботи з запізнення
animation = 0;
}
if (animation == 1 ) {
if (accumulationL){//знак запитання мигає якщо кнопка не натиснута
display.setCursor(19, 13);
display.println('?');
}
}
else if (animation == 2 ) {
display.setCursor(22-strlen(buttonNames[buttonOne])*3, 13);//strlen автоцентровка в колі може бути одна або дві букви
display.println(buttonNames[buttonOne]);
if(accumulationR){//знак запитання мигає якщо кнопка не натиснута
display.setCursor(103, 13);
display.println('?');
}
for (int i = 0; i < 8; ++i) {//відображаємо кутові складки
display.setCursor(40+i*6, 13);
display.write(62);
}
display.setCursor(40+runningArrow*6, 13);//трикутник переміщається по углових скобках анімація
display.write(16);
}
else if (animation == 3 ) {//завершення програми відображення натиснутих кнопок
display.setCursor(22-strlen(buttonNames[buttonOne])*3, 13);
display.println(buttonNames[buttonOne]);
display.setCursor(106-strlen(buttonNames[buttonTwo])*3, 13);
display.println(buttonNames[buttonTwo]);
}
if (tmr < millis()) {//всі дії виконуються по таймеру
tmr = millis()+400;
}
return 0;
}
void Md_changeNameProfile(){
static int8_t AxisX, AxisY, delFlag;//XYнаведення на вибрану букву delFlag видалення за допомогою отримання
static uint32_t tmr;//використовується для видалення утриманняm
static int8_t magnitude = 1;//костилі використовується для вибору пробілу і виходу
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', '-'},
{25 , 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '.', 27 },
{' ', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>'}
};
char letterSmall[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', '-'},
{24 , 'z', 'x', 'c', 'v', 'b', 'n', 'm', '.', 27 },
{' ', ' ', ' ', ' ', ' ', ' ', ' ', '>', '>', '>'}
};
static char (*ptrLetter)[10] = letterSmall;//вказівник вказує в залежності від настройки на великі або малі букви
if (Mf_EncoderButton() == click ) {//після кліку виконується видалення додавання букви
if (ptrLetter[AxisY][AxisX] == 25) {
ptrLetter = letterSmall;
}
else if (ptrLetter[AxisY][AxisX] == 24) {
ptrLetter = letterBig;
}
else if (ptrLetter[AxisY][AxisX] == 27) {
prof(act_dir)->nameProfiles(delete_letter);
if (!prof(act_dir)->letterIndicator) {
ptrLetter = letterBig;
}
}
else if (ptrLetter[AxisY][AxisX] == '>') {
AxisX = AxisY = delFlag = 0;
ptrLetter = letterSmall;
Md_cursor(posThree, return_pos);
Md_addres(3, back);
}
else{
if (!prof(act_dir)->letterIndicator) {//якщо вводиться перша буква всі букви стають малими одноразовo
ptrLetter = letterSmall;
}
prof(act_dir)->nameProfiles(change_letter, ptrLetter[AxisY][AxisX]);
}
}
if (Mf_EncoderButton(take_but_pos) == button_pressed){//якщо утримувати кнопку можна зробити дві дії видалення букви утриманням або переміщення по осі Y
if (Mf_encoderRotationSpeed(check_rotation)) {//переміщення курсу вверх-вниз якщо кнопка отримується
delFlag = 1;//забороняємо видалення утриманням коли почались оберти енкод
Mf_EncoderButton(reset);//Клік не повернеться скидаємо функцію
switch (Mf_encoderRotationSpeed(get_rotation1)) {
case rotated_r1:
AxisY--;
if (AxisY < 0) {
AxisY = 4;
}
break;
case rotated_l1:
AxisY++;
if (AxisY > 4) {
AxisY = 0;
}
break;
}
}
else if(ptrLetter[AxisY][AxisX] == 27 && !delFlag) {//якщо не було обертів під час отримання кнопки І ми навелились на знак видалення і минув час починається видалення букв
if (tmr < millis()) {
Mf_EncoderButton(reset);
tmr = millis()+300;
prof(act_dir)->nameProfiles(delete_letter);
if (!prof(act_dir)->letterIndicator) {
ptrLetter = letterBig;
}
}
}
}
else if (Mf_encoderRotationSpeed(check_rotation)){//звичайний режим роботи курсор рухається вправо і вниз і повторно починає свої дії якщо дійти до кінця і наоборот
switch (Mf_encoderRotationSpeed(get_rotation1)) {
case 0:
break;
case rotated_r1:
AxisX -= magnitude;
break;
case rotated_l1:
AxisX += magnitude;
break;
}
if (AxisX < 0) {//тут відбувається обмеження мінімального і максимального значення перемінних X Y також змінюється амплітуда якщо y = 4
magnitude = 1;
AxisX = 9;
AxisY--;
}
else if (AxisX > 9) {
magnitude = 1;
AxisX = 0;
AxisY++;
}
if (AxisY < 0) {
AxisY = 4;
}
else if (AxisY > 4 ) {
AxisY = 0;
}
if (AxisY == 4) {//якщо y вказує на останній рядок магнітуда збільшується до 9 так щоб після двох обертів ми покидали нижню частину
if (AxisX != 0 && AxisX != 9) {//користуємо якщо X не є дев'яткою або нулем після того як переміщалися вверх-вниз курсором
if (AxisX >= 7) {
AxisX = 9;
}
else{
AxisX = 0;
}
}
magnitude = 9;//якщо перетворити на 9 після нуля буде дев'ятка а після дев'ятки буде перебільшення і скидання курсова наверх після оберту енкодером
}
}
else{//захист від випадкового видалення коли хочемо почати переміщатися вверх-вниз коли знаходимося на пункті видалення
tmr = millis()+400;
delFlag = 0;
}
Md_p_changeNameProfile(ptrLetter, AxisX, AxisY);//віалізуємо програму
}
byte Md_p_changeNameProfile(char letter[][10], byte AxisX, byte AxisY){
char str[] = {24, 25, 27, '>' , ' '};//маси використовується для мигання необхідної рамки
static byte flagBlink;
static uint32_t tmr;
display.setCursor(114, 54);//back
display.print('s');
display.drawPixel(120, 60, WHITE); //крапка
for (int i = 0; i <= 4; i++) {//виводимо рамку навколо букви якщо курсор рамку не вказує на пункти
if(letter[AxisY][AxisX] == str[i]){
break;
}
if(i == 4){//якщо елемент не дорівнює забороненим знакам то виводимо рамку
display.drawRect(4+AxisX*12, 12+AxisY*10, 9, 11, WHITE);//рамка курса 10 вверх вниз, вліво вправо 12
}
}
if((letter[AxisY][AxisX] != 24 && letter[AxisY][AxisX] != 25) || flagBlink){//кружок стрілка вниз вверх
display.drawCircle(8, 47, 5, WHITE);
}
//принцип роботи якщо навелись на додатковий знак то починаємо ним мигати за допомогою флажка
if(letter[AxisY][AxisX] != 27 || flagBlink){// знак видалення
display.drawCircle(116, 47, 4, WHITE);
}
if(letter[AxisY][AxisX] != '>' || flagBlink){//рамка s.
display.drawRoundRect(112, 54, 9, 9, 0, WHITE);
}
display.fillRect(36, 54, 54, 6, WHITE);//пробіл
if(letter[AxisY][AxisX] == ' '){//додаткова рамка якщо навели на пробіл
display.drawRect(34, 52, 58, 10, WHITE);
}
if(tmr < millis()){//мигння рамкою
tmr = millis()+400;
flagBlink = !flagBlink;
}
if(!flagBlink && prof(act_dir)->letterIndicator != 20){//перестає мигати якщо 20 символів також мигаємо за допомогою flagBlink
display.drawRect(2+6*prof(act_dir)->letterIndicator, 0, 1, 9, WHITE);
}
display.setCursor(2, 1);//виводив ім'я профіля
display.print(prof(act_dir)->nameProfiles(get_name));
for (int i = 0; i < 4; i++) {//виводим масив букв на дисплей
for (int j = 0; j < 10; j++) {
display.setCursor(6+j*12, 14+i*10);
display.print(letter[i][j]);
}
}
return 0;
}
//
// Md_Additional functions-------------------------------------------------------------------------------------------
byte MdAf_consenDisplay(){ //викликається для підтвердження вибору
static int8_t flagChoice;
switch (Mf_encoderRotationSpeed(get_rotation1)) {//керування рамкою вліво вправо
case no_action:
break;
case rotated_l1:
if (flagChoice > -2) {
flagChoice--;
}
break;
case rotated_r1:
if (flagChoice < 2) {
flagChoice++;
}
break;
}
if ((flagChoice == -2 || flagChoice == 2) && Mf_EncoderButton() == click) {// V1 якщо відбувся Клік виконується дії в залежності від вибору
if (flagChoice == -2) {//якщо вибрали ні виконуються дії//вихід виконується в любому випадку з збереженими настройками або ні
flagChoice = 0;
return choice_no;
}
else if (flagChoice == 2) {
flagChoice = 0;
return choice_yes;
}
}
MdAf_p_consenDisplay(flagChoice);//візуальна частина коду
return 0;
}
int8_t MdAf_p_consenDisplay(int8_t acceptedFlag){
static uint32_t tmr;
static int8_t FlagFrame;
display.drawRoundRect(20, 8, 87, 22, 0, WHITE); //рамка навколо слова зверху
display.setCursor(25, 15);
display.println("Save settings");
if (tmr < millis()) {// виконує механія рамкою навколо слова так ні
tmr = millis() + 400;
FlagFrame = !FlagFrame;
}
if (FlagFrame) {//виводим рамку навколо слова і мигаємо нею
display.drawRect(52+acceptedFlag*18, 39, 21, 13, WHITE);//переміна приймає число від -2 до +2 І в залежності від числа міняє координати рамки
}
for (int i=39; i <= 75; i+=18){//відображаємо стрілки вліво вправо
display.setCursor(i,42);
display.write(27);display.write(26);
}
display.setCursor(21, 42);
display.println("NO");
display.setCursor(18, 42);
display.println(" YES");
return 0;
}
byte MdAf_profileStorage(byte acceptedCommand){//функція викликається якщо Md_addres(3, get_add) == 1
switch (acceptedCommand) {//false якщо повертаємо збережені настройки
case return_save:
prof(act_dir)->copyFrom(profilSeve);//це означає що ми вибрали ні і повертаємо настройки
break;
case copy_profile:
profilSeve.copyFrom(*prof(act_dir));//копіювання діючий профіль
break;
case check_changes:
if (profilSeve != *prof(act_dir)) {//провіряємо зміни
return recorded_changes;
}
break;
case reset://прост спосіб скинути функцію перестане відправляти recorded_changes виконані зміни збережуться
prof(act_dir)->checkName();//якщо видалені всі букви То встановлюється стандартне ім'я профілю
profilSeve.copyFrom(*prof(act_dir));
if (prof(act_dir)->tape == t_mp) {//якщо ми настроювали власний профіль то перезаписуємо настройки в енергонезалежну пам'ять
Serial.println("t_mp s");
EEPROM.put((prof(act_dir)->namber - 1) * sizeof(Profiles), *(prof(act_dir)));
EEPROM.commit();
}
else if (prof(act_dir)->tape == t_sp) {
int tempProfSize = 7 * sizeof(Profiles);
Serial.println("t_mp s");
EEPROM.put(tempProfSize + (prof(act_dir)->namber - 1) * sizeof(prof(act_dir)->buttonArray), prof(act_dir)->buttonArray);
EEPROM.commit();
}
break;
}
return 0;
}
void MdAf_typeNumberPrint(){
display.setCursor(49, 0);
if (prof(act_dir)->tape == t_sp) {
display.print("(Sp");
display.print(prof(act_dir)->namber);
}
else {
display.print("(Mp");
display.print(prof(act_dir)->namber);
}
display.print(")");
}
byte MdAf_confirmYesNo(byte azixX, byte azixY){
//функцію можна відобразити в любому місці біля слова рецепт після завершення потрібно скинути вручну
static byte flagChoice;
if (Mf_encoderRotationSpeed(get_rotation1)) {
flagChoice = !flagChoice;
}
if (Mf_EncoderButton() == click) {
byte temp = flagChoice;
flagChoice = 0;
if (!temp) {
Serial.print("yes");
return choice_no;
}else{
return choice_yes;
}
}
display.setCursor(azixX+4, azixY+4);
display.println("NO YES");
if(!flagChoice) {
display.drawRect(azixX+2, azixY+2, 15, 11, WHITE);//NO
}else{
display.drawRect(azixX+32, azixY+2, 21, 11, WHITE);//YES
}
display.drawRect(37, 46, 55, 15, WHITE);//рамка навколо так ні
//MdAf_p_confirmYesNo(flagChoice, 37, 46);
return 0;
}
// Md_Af
// Menu_functions 5
byte Md_cursor(byte* arr, byte dataCommand) {
static int8_t permissionArrow, Point = 1, workPermit = 1;
switch (dataCommand) {
case return_pos:
Point = arr[0];
permissionArrow = 0;
break;
case cursor_type_two:
permissionArrow = 1;
break;
case cursor_type_one:
permissionArrow = 0;
break;
case get_pos:
return Point;
break;
default:
if(!permissionArrow){
switch (Mf_encoderRotationSpeed(get_rotation1)) {
case rotated_l1:
Point--;
if (Point == 0) {
Point = dataCommand;
}
break;
case rotated_r1:
Point++;
if (dataCommand < Point) {
Point = 1;
}
break;
}
}
if (!permissionArrow) {
display.setCursor(0, arr[Point-1]); //21,8 максимум символів
display.write(26);
}
else if (permissionArrow) {
display.setCursor(0, arr[Point-1]); //21,8 максимум символів
display.write(16);
}
}
return 0;
}
int8_t Md_addres(int8_t addNumb, int8_t acceptedCommand) {
static byte adr[19], modeSwitch = 1, namb = 1, workPermit = 1, del;
switch (acceptedCommand) {
case back:
Md_cursor(noData, cursor_type_one);
workPermit = modeSwitch = 1;
for (int i=18; i >= 1; i--){//команда back виконує видалення всіх адресів до числа вказаного в addNumb
adr[i] = 0;
if (i == addNumb) {
namb = addNumb;
break;
}
}
break;
case get_add:
return adr[addNumb];
break;
case add_status://якщо адрес видалявся потрібно іноді обнуляти функції
if (del){
del = 0;
return address_deleted;
}
break;
case click_reset_off://заборон кнопці нкодера скидати адрес кнопку перенаправ. для викорис в іншій фун
workPermit = 0;
break;
case int_add://при тій команді відбувається набір адреса з подальшим набором при натиску кнопки енкодера
if (!adr[addNumb] && Mf_EncoderButton() == click){//набір адреса
adr[namb] = Md_cursor(noData, get_pos);//позиція курс означає набраний адрес
namb++;
return address_entered;
}
break;
case int_add_last://набір адреса з подальшим скиданням при натиску
if (workPermit && Mf_EncoderButton() == click){//набір адреса
if (modeSwitch){
adr[namb] = Md_cursor(noData, get_pos);//позиція курс означає набраний адрес
Md_cursor(noData, cursor_type_two);//міняємо вид курсора
modeSwitch = 0;//міняємо флажок наступний кліп буде означати скидання адресу
return address_entered;
}
else if (!modeSwitch){
adr[namb] = 0;
del = modeSwitch = 1;
Md_cursor(noData, cursor_type_one);//міняємо вид курсора
}
}
break;
}
return adr[addNumb];//функція постійно повертає адрес вказаний в параметрах
}
int8_t Mf_encoderRotationSpeed(int8_t acceptedCommand) {
static uint32_t tmr;
static int8_t seveEncPos;
if (!tmr && encoder.getPosition()) {
tmr = millis() + 100;
}
else if (tmr && tmr < millis()) {
tmr = 0;
encoder.setPosition(0);
}
if (acceptedCommand == check_rotation) {//по проходженню часу перевіряємо наявність обертів їх кількість
return constrain(abs(encoder.getPosition()), 1, 2);
}
else if (acceptedCommand == get_rotation2 && abs(encoder.getPosition()) > 1) {//перевіряємо збільшого до меншого при подальші викликики не призведуть до повернення даних перемінна encoder.getPosition() обновляється
acceptedCommand = constrain(encoder.getPosition(), -2, 2);
encoder.setPosition(0);
return acceptedCommand;
}
else if (acceptedCommand == get_rotation1) {
acceptedCommand = constrain(encoder.getPosition(), -1, 1);
encoder.setPosition(0);
return acceptedCommand;
}
return 0;
}
byte Mf_EncoderButton(byte acceptedCommand) {
static byte saveAction = releas_b_e;
static byte functionState = status_allowed;
static uint32_t tmr;
if (acceptedCommand){//в Kodi можемо зробити reset щоб не поверталося утримання hold_b_e або click
if (acceptedCommand == reset){//в Kodi можемо зробити reset щоб не поверталося утримання hold_b_e або click
functionState = reset_status;
}
else if (acceptedCommand == take_but_pos){ //викликаємо стан кнопки якщо кнопка нажата то можемо наприклад скинути функцію і вона не поверне hold_b_e або click
if (Ae_BatR(b_sw)){
return button_pressed;
}
return button_released;
}
}
if (functionState == status_allowed){
if (Ae_BatR(b_sw)){
if (saveAction == releas_b_e) {
Ae_autoShutdown(reset);
saveAction = press_b_e;
tmr = millis() + 500;
return press_b_e;
}
else if (saveAction == press_b_e && tmr < millis()) {
saveAction = completed_work;
return hold_b_e;
}
}
else if (saveAction && !Ae_BatR(b_sw)) {
if (saveAction == press_b_e){
Ae_displayRefresh(reset);
saveAction = releas_b_e;
return click;
}
saveAction = releas_b_e;
}
}
else if (functionState == reset_status && !Ae_BatR(b_sw)) {
saveAction = releas_b_e;
functionState = status_allowed;
}
return 0;
}
int Mf_Magnifier(int Numeric, int Min, int Max, int Speed) {
if (Mf_encoderRotationSpeed(check_rotation)) {//encoder.getPosition() перевіряємо наявність даних якщо дані є виконуємо перевірку кіль обер
switch (Mf_encoderRotationSpeed(get_rotation2)) {//якщо функція поверне два оберта то 1 не повертається
case rotated_r2:
Numeric += Speed;
break;
case rotated_l2:
Numeric -= Speed;
break;
}
switch (Mf_encoderRotationSpeed(get_rotation1)) {
case rotated_r1:
Numeric++;
break;
case rotated_l1:
Numeric--;
break;
}
}
if (Numeric > Max) {
Numeric = Min;
}
else if (Numeric < Min) {
Numeric = Max;
}
return Numeric;
}
// Mf
//Md
//MdId
void MdId_BluetoothPrint() {
const byte bluArr[9] = {
0b00100,
0b00110,
0b10101,
0b01110,
0b00100,
0b01110,
0b10101,
0b00110,
0b00100
};
static uint32_t tmr;
static byte Flag;
display.drawCircle(6, 6, 6, WHITE);//кружок
//display.setCursor(4, 3);
if (bleGamepad.isConnected()) {
display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
}
else{
if (tmr < millis()) {
Flag = !Flag;
tmr = millis() + 1000;
}
if (Flag) {
display.drawBitmap(1, 2, bluArr, 8, 9, WHITE);
}
}
}
void MdId_battery() {
const float batLevels[] = {4.15, 4.0, 3.82, 3.76, 3.65, 3.6, 3.5, 3.4, 3.3, 3.0};
/*
float shuntVoltage_mV = ina219.getShuntVoltage_mV();
float busVoltage_V = ina219.getBusVoltage_V();
float loadVoltage_V = busVoltage_V + (shuntVoltage_mV / 1000);
*/
static float loadVoltage_V = 3.0;// test
static byte chargeLevel;
static uint32_t tmr;
if (tmr < millis()) {
tmr = millis() + 60000;
for (int i = 0; i <= 9 ; i++) {
if (loadVoltage_V >= batLevels[i]) {
if (chargeLevel != map(i, 0, 9, 0, 11)) {
Ae_displayRefresh(reset);
}
chargeLevel = map(i, 0, 9, 0, 11);
break;
}
}
}
MdId_p_battery(chargeLevel);
}
byte MdId_p_battery(byte receivedData) {
display.drawRect(111, 2, 1, 4, WHITE);//картинка батареї
display.drawRect(112, 0, 16, 8, WHITE);
display.fillRect(114+receivedData, 2, 12-receivedData, 4, WHITE);//шкала заряду батареї
return 0;
}
//MI
//infoDisplay
void infoDisplay() {
MdId_BluetoothPrint();
MdAf_typeNumberPrint();
MdId_battery();
display.drawRect(0, 14, 127, 19, WHITE);// РАМКА НАВКОЛО НАЗВИ
display.setCursor(prof(act_dir)->centroName(), 20);// функція centroName() в залежності від кількості ім'я вранці
display.print(prof(act_dir)->nameProfiles(get_name));
display.setCursor(44,44);
display.write(26);
display.drawRect(50, 42, 27, 11, WHITE);
display.setCursor(52,44);
display.print("Menu");
}
//Id
//Display
byte Md_Display(byte Receiving) {
static byte Regime = info_display;
//static byte Regime = menu;
Ae_displayRefresh(clear_display);
if (Receiving) {
Regime = Receiving;
}
switch (Regime) {
case info_display:
if (Mf_EncoderButton() == click) {
Md_Display(menu);
}
else if (Mf_EncoderButton() == hold_b_e) {//по плану має виключати дисплей при утриманні ?
display.clearDisplay();
display.display();
}
else{
infoDisplay();
}
break;
case menu:
Md_MainDisplay();
break;
}
Ae_displayRefresh(print_display);
return 0;
}
//D
//Gamepad_function
void IRAM_ATTR Gf_GyroAnalogR() {
//static uint32_t tmr;
volatile static int GiroDedZone;
volatile static int rightStickX;
volatile static int rightStickY;
static int x; //x
static int z; //z
/*
*/
//Serial.println(" 11111");
//if (Ae_BatR(b_handle) && tmr < millis()) {
//if (Ae_BatR(b_handle) && bleGamepad.isConnected()) {
if (Ae_BatR(b_handle)) {
Ae_autoShutdown(reset);
/*
Quaternion q;
VectorFloat gravity;
VectorInt16 gyro;
volatile static float ypr[3];
*/
/*
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGyro(&gyro, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
//tmr = millis() + 11;
}
*/
if (abs(x) > 1) {
GiroDedZone = map(z, -150, 150, -30, 30);
GiroDedZone = abs(GiroDedZone);
if (x < -GiroDedZone) {
x = map(x, -1, -prof(act_dir)->sensitivityHor, -prof(act_dir)->deadZoneHor, -32767);
x = constrain(x, -32767, -1);
bleGamepad.setZ(x);
}
else if (x > GiroDedZone) {
x = map(x, 1, prof(act_dir)->sensitivityHor, prof(act_dir)->deadZoneHor, 32767);
x = constrain(x, 1, 32767);
bleGamepad.setZ(x);
}
}
else{
bleGamepad.setZ(0);
}
if (abs(z) > 1) {
GiroDedZone = map(x, -150, 150, -30, 30);
GiroDedZone = abs(GiroDedZone);
if (z < -GiroDedZone) {
z = map(z, -1, -prof(act_dir)->sensitivityHor, prof(act_dir)->deadZoneHor, 32767);
z = constrain(z, -32767, -1);
bleGamepad.setRZ(z);
}
else if (z > GiroDedZone) {
z = map(z, 1, prof(act_dir)->sensitivityHor, -prof(act_dir)->deadZoneHor, -32767);
z = constrain(z, 1, 32767);
bleGamepad.setRZ(z);
}
}
else{
bleGamepad.setRZ(0);
}
bleGamepad.sendReport();
}
else if (rightStickX || rightStickY){
rightStickX = rightStickY = 0;
bleGamepad.setRightThumb(0, 0);
bleGamepad.sendReport();
}
}
void Gf_AnLeftDpad(){
static bool flag;
int AxisX = constrain(analogRead(39), 45, 4050);
int AxisY = constrain(analogRead(36), 45, 4050);
if ((AxisX < 1800 || AxisX > 2200) || (AxisY < 1800 || AxisY > 2200)) {
if (!Ae_BatR(b_emul_dpad)) {
if (flag) {
flag = 0;
bleGamepad.setHat1(HAT_CENTERED);
}
AxisX = map(AxisX, 45, 4050, -32767, 32767);
AxisY = map(AxisY, 45, 4050, -32767, 32767);
bleGamepad.setLeftThumb(AxisX, AxisY);
}
else{
if (!flag) {
flag = 1;
bleGamepad.setLeftThumb(0, 0);
}
if (AxisX < 1800 || AxisX > 2200) {
AxisX = constrain(AxisX, 1800, 2200);
AxisX = map(AxisX, 1800, 2200, 2, 4);
}
else{
AxisX = 0;
}
if (AxisY < 1800 || AxisY > 2200) {
AxisY = constrain(AxisY, 1800, 2200);
AxisY = map(AxisY, 1800, 2200, 1, 6);
}
else{
AxisY = 0;
}
const byte dpadArr[] = {0, 1, 3, 2, 7, 8, 5, 0, 4, 0, 6};
bleGamepad.setHat1(dpadArr[AxisX+AxisY]);
}
}
else{
bleGamepad.setLeftThumb(0, 0);
bleGamepad.setHat1(0);
}
}
void Gf_gamepadButtons(){
if (Ae_BatR(prof(act_dir)->buttonArray[1])) {
bleGamepad.pressSelect();
}
else{
bleGamepad.releaseSelect();
}
if (Ae_BatR(prof(act_dir)->buttonArray[2])) {
bleGamepad.pressStart();
}
else{
bleGamepad.releaseStart();
}
if (Ae_BatR(prof(act_dir)->buttonArray[11])) {
bleGamepad.setLeftTrigger(32767);
}
else{
bleGamepad.setLeftTrigger(0);
}
if (Ae_BatR(prof(act_dir)->buttonArray[12])) {
bleGamepad.setRightTrigger(32767);
}
else{
bleGamepad.setRightTrigger(0);
}
for (int i = 3; i <= 12; i++) {
if (Ae_BatR(prof(act_dir)->buttonArray[i])) {
bleGamepad.press(i);
}
else{
bleGamepad.release(i);
}
}
}
//Gf
void setup() {
Serial.begin(9600);// В ФІНАЛІ НЕЗАБУТИ ЗАКОМЕНТУВАТИ!!!
bleGamepad.begin();
EEPROM.begin(512);
prof(eeprom_initialization);//ініціалізація з енергонезалежної пам'яті профілів
pinMode(3, OUTPUT);//подаємо високий сигнал на транзистор щоб заживити модулі
digitalWrite(3, HIGH);
pinMode(32, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(13, INPUT_PULLUP);
/*
byte pinsWithPullup[] = {33, 13, 27, 26, 25, 16, 17, 5, 23, 2, 18, 19, 32, 15, 4, 12, 14};//добавити 1
for (int i = 0; i < sizeof(pinsWithPullup); i++) {
pinMode(pinsWithPullup[i], INPUT_PULLUP);
}
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0);
hw_timer_t * timer = NULL;
timer = timerBegin(0, 80, true); // Таймер 0, прескалер 80 (для ESP32), true для повторення таймера
timerAttachInterrupt(timer, &Gf_GyroAnalogR, true); // Встановлюємо функцію, яка буде викликана таймером
timerAlarmWrite(timer, 11000, true); // Встановлюємо час спрацьовування таймера (11 мілісекунд)
timerAlarmEnable(timer); // Вмикаємо таймер
ina219.setADCMode(BIT_MODE_12); // Устанавливаем разрешение АЦП 12 бит
ina219.setPGain(PG_40); // Ток в пределах 400 мА
ina219.setBusRange(BRNG_16); // Напряжение до 16 В
*/
attachInterrupt(digitalPinToInterrupt(p_dt), Ae_encoderInterrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(p_clk), Ae_encoderInterrupt, CHANGE);
//----------Display--------------
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextSize(1);
display.setTextColor(WHITE);
//gyroStart();//при потребі проводимо калібровку якщо калібровка виконана виконуємо старт функції гіроскопа
//startAnimation();//анімація при включенні до робити в кінці
}
void loop() {
//Ae_autoShutdown();
//Ae_buttonOnOff();
//Ae_batteryCheck();
Md_Display();
/*
if (bleGamepad.isConnected()) {
//Gf_GyroAnalogR();
Gf_AnLeftDpad();
Gf_gamepadButtons();
bleGamepad.sendReport();
}
*/
}//void loop()