Цифровые автоматы в классах по qwone
- Войдите на сайт для отправки комментариев
Сб, 11/08/2018 - 19:49
Похоже надо открыть новую тему, что бы не смешавать скетчи со старой.
http://arduino.ru/forum/programmirovanie/klassy-arduino-po-qwone-dlya-ch...
И да как это не страно, но похоже мои скетчи с использованием цифровых автоматов и без использования будут отличаться друг от друга. Примитивные не совсем, а дальше по мере усложнения полезут и принципиальные отличия.
Ну начальный шаблон похож
/**/ unsigned long mill; // переменная под millis() //-------------------------------- class Cl_Led { protected: byte pin; public: Cl_Led(byte p):pin(p) {} void init() {} void run() {} }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13); //-------------------------------- void setup() { Led.init(); } void loop() { mill = millis(); Led.run(); } /**/Мигание светодиода с равными полупериодами тоже
/**/ unsigned long mill; // переменная под millis() //-------------------------------- enum {ssOFF = 0, ssON}; /*состояние ВЫКЛ/ВКЛ*/ class Cl_Led { protected: byte pin; unsigned long time; unsigned long past; byte state; void stand(byte s) { state = s; past = mill; switch (state) { case ssOFF: digitalWrite(pin, LOW); break; case ssON: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p, unsigned long t): pin(p), time(t) {} void init() { pinMode(pin, OUTPUT); stand(ssOFF); } void run() { if (state == ssOFF && mill - past >= time) stand(ssON); if (state == ssON && mill - past >= time) stand(ssOFF); } }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13,/*время перекл*/500); //-------------------------------- void setup() { Led.init(); } void loop() { mill = millis(); Led.run(); } /**/ПС: Вот так тоже но запись получше
/**/ unsigned long mill; // переменная под millis() //-------------------------------- enum {ssOFF = 0, ssON}; /*состояние ВЫКЛ/ВКЛ*/ class Cl_Led { protected: byte pin; unsigned long time; unsigned long past; enum onLed{ssOFF = 0, ssON}state; /*состояние ВЫКЛ/ВКЛ*/ void stand(onLed s) { state = s; past = mill; switch (state) { case ssOFF: digitalWrite(pin, LOW); break; case ssON: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p, unsigned long t): pin(p), time(t) {} void init() { pinMode(pin, OUTPUT); stand(ssOFF); } void run() { if (state == ssOFF && mill - past >= time) stand(ssON); if (state == ssON && mill - past >= time) stand(ssOFF); } }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13,/*время перекл*/500); //-------------------------------- void setup() { Led.init(); } void loop() { mill = millis(); Led.run(); } /**/Мигание с различными полупериодами так же
/**/ unsigned long mill; // переменная под millis() //-------------------------------- enum {ssOFF=0, ssON}; /*состояние ВЫКЛ/ВКЛ*/ class Cl_Led { protected: byte pin; unsigned long timeON, timeOFF; unsigned long past; byte state; void stand(byte s) { state = s; past = mill; switch (state) { case ssOFF: digitalWrite(pin, LOW); break; case ssON: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p, unsigned long t1, unsigned long t2) : pin(p), timeON(t1), timeOFF(t2) {} void init() { pinMode(pin, OUTPUT); stand(ssOFF); } void run() { if (state == ssOFF && mill - past >= timeOFF) stand(ssON); if (state == ssON && mill - past >= timeON) stand(ssOFF); } }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13,/*время ВКЛ*/500,/*время ВЫКЛ*/100); //-------------------------------- void setup() { Led.init(); } void loop() { mill = millis(); Led.run(); } /**/Поправочка
/**/ unsigned long mill; // переменная под millis() //-------------------------------- class Cl_Led { protected: byte pin; unsigned long timeON, timeOFF; unsigned long past; enum onLed{ssOFF = 0, ssON} state; /*состояние ВЫКЛ/ВКЛ*/ void stand(onLed s) { state = s; past = mill; switch (state) { case ssOFF: digitalWrite(pin, LOW); break; case ssON: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p, unsigned long t1, unsigned long t2) : pin(p), timeON(t1), timeOFF(t2) {} void init() { pinMode(pin, OUTPUT); stand(ssOFF); } void run() { if (state == ssOFF && mill - past >= timeOFF) stand(ssON); if (state == ssON && mill - past >= timeON) stand(ssOFF); } }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13,/*время ВКЛ*/500,/*время ВЫКЛ*/100); //-------------------------------- void setup() { Led.init(); } void loop() { mill = millis(); Led.run(); } /**/А вот с появлением кнопок полезли принципиальные отличия. Вот управление с просто нажатием
/**/ unsigned long mill; // переменная под millis() //------Cl_Btn---------------------- enum {sbNONE = 0, sbClick}; /*состояние не изменилось/клик*/ class Cl_Btn { protected: const byte pin; byte state; bool bounce = 0; bool btn = 1, oldBtn; unsigned long past; public: Cl_Btn(byte p): pin(p) {} /*инициализация-вставить в setup()*/ void init() { pinMode(pin, INPUT_PULLUP); } /*работа-вставить в loop()*/ void run() { state = sbNONE; bool newBtn = digitalRead(pin); if (!bounce && newBtn != btn) { bounce = 1; past = mill; } if (bounce && mill - past >= 10) { bounce = 0 ; oldBtn = btn; btn = newBtn; if (!btn && oldBtn) state = sbClick; } } byte read() { return state; } }; //-------------------------------- enum {ssSTOP = 0, ssOFF, ssON}; /*состояние ВЫКЛ/ГОРИТ/НЕГОРИТ*/ class Cl_Led { protected: byte pin; unsigned long timeON, timeOFF; unsigned long past; byte state; void stand(byte s) { state = s; past = mill; switch (state) { case ssSTOP: digitalWrite(pin, LOW); case ssOFF: digitalWrite(pin, LOW); break; case ssON: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p, unsigned long t1, unsigned long t2) : pin(p), timeON(t1), timeOFF(t2) {} void init() { pinMode(pin, OUTPUT); stand(ssSTOP); } void run() { if (state == ssOFF && mill - past >= timeOFF) stand(ssON); if (state == ssON && mill - past >= timeON) stand(ssOFF); } void ON() { stand(ssON); } }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13,/*время ВКЛ*/500,/*время ВЫКЛ*/100); Cl_Btn Btn1(/*пин*/2); //-------------------------------- void setup() { Btn1.init(); Led.init(); } void loop() { mill = millis(); Led.run(); Btn1.run(); if (Btn1.read() == sbClick)Led.ON(); } /**/Управление с коротким и длиным нажатием.
/**/ unsigned long mill; // переменная под millis() //------Cl_Btn---------------------- enum {sbNONE = 0, sbClick, sbLong}; /*состояние не изменилось/клик/долгое наж*/ class Cl_Btn { protected: const byte pin; byte state; bool bounce = 0; bool btn = 1, oldBtn; unsigned long past; const uint32_t time = 500 ; bool flag = 0; uint32_t past_flag = 0 ; public: Cl_Btn(byte p): pin(p) {} /*инициализация-вставить в setup()*/ void init() { pinMode(pin, INPUT_PULLUP); } /*работа-вставить в loop()*/ void run() { state = sbNONE; bool newBtn = digitalRead(pin); if (!bounce && newBtn != btn) { bounce = 1; past = mill; } if (bounce && mill - past >= 10) { bounce = 0 ; oldBtn = btn; btn = newBtn; if (!btn && oldBtn) { flag = 1; past_flag = mill; } if (!oldBtn && btn && flag && mill - past_flag < time ) { flag = 0; state = sbClick; } } if (flag && mill - past_flag >= time ) { flag = 0; state = sbLong; } } byte read() { return state; } }; //-------------------------------- enum {ssSTOP = 0, ssOFF, ssON}; /*состояние ВЫКЛ/ГОРИТ/НЕГОРИТ*/ class Cl_Led { protected: byte pin; unsigned long timeON, timeOFF; unsigned long past; byte state; void stand(byte s) { state = s; past = mill; switch (state) { case ssSTOP: digitalWrite(pin, LOW); case ssOFF: digitalWrite(pin, LOW); break; case ssON: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p, unsigned long t1, unsigned long t2) : pin(p), timeON(t1), timeOFF(t2) {} void init() { pinMode(pin, OUTPUT); stand(ssSTOP); } void run() { if (state == ssOFF && mill - past >= timeOFF) stand(ssON); if (state == ssON && mill - past >= timeON) stand(ssOFF); } void OFF() { stand(ssSTOP); } void ON() { stand(ssON); } }; //---Компоновка----------------------------- Cl_Led Led(/*пин*/13,/*время ВКЛ*/500,/*время ВЫКЛ*/100); Cl_Btn Btn1(/*пин*/2); //-------------------------------- void setup() { Btn1.init(); Led.init(); } void loop() { mill = millis(); Led.run(); Btn1.run(); if (Btn1.read() == sbClick)Led.ON(); if (Btn1.read() == sbLong)Led.OFF(); } /**/Ну пока все. Немного похоже на кнопки Клапы, но все же не они.
Квон, прошлая тем была свалкой неработающих кодов (я тебе в #270 прямо привёл пример на котором валится, но ты и не подумал поправить).
Решил ещё одну помойку для неработающего говнокода завести?
Хотя, я забыл, ты же там в посте #35 прямо сказал, что принципиально не исправляешь ошибок. Извини. Продолжай гадить.
Может раздел сразу завести на форуме: "Ардуино по qwone"... И ссылать туда всех борзых новичков. Навек.
/*Скетч Аналоговая клавиатура */ unsigned long mill; //----------Cl_Key--------- // класс аналоговая клавиатура enum {skNone = 0, skLeft, skRight, skUp, skDown, skSelect}; class Cl_Key { byte pin;// нога byte state; int val_old, val; // состояние на клавиатуре unsigned long past; int read_() { // считать состояние клавиатуры int value = analogRead(pin); if (value < 130) return skLeft; else if (value < 310) return skUp; else if (value < 500) return skDown; else if (value < 730) return skRight; else if (value < 900) return skSelect; return skNone; } public: Cl_Key(byte p): pin(p) {} void init() { val_old = read_(); } void run() { state = skNone; if (mill - past >= 50) { past = mill; val_old = val; val = read_(); if (val_old == skNone) { state = val; } } } byte read() { return state; } }; //--------Компоновка----------- Cl_Key Key(/*нога*/A0); //----------main()--------- void setup() { Serial.begin(9600); Key.init(); } void loop() { mill = millis(); Key.run(); if (Key.read() == skLeft) Serial.println("Left"); if (Key.read() == skRight) Serial.println("Right"); if (Key.read() == skUp) Serial.println("Up"); if (Key.read() == skDown) Serial.println("Down"); if (Key.read() == skSelect) Serial.println("Select"); } /*Скетч использует 1850 байт (6%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 226 байт (11%) динамической памяти, оставляя 1822 байт для локальных переменных. Максимум: 2048 байт. */Предлагаю использовать здесь новый термин : "квонокод"
/*LCD Key Shield*/ unsigned long mill; //--------------------Cl_aKeyboard-------------------------------- #include <LiquidCrystal.h> /*состояние-нет нажатий/правая/левая/вверх/вниз/влево/выбор*/ enum {skNone = 0, skRight, skUp, skDown, skLeft, skSelect}; class Cl_aKeyboard { const byte pin; int data_next, data, data_old; bool bounce = 0; // антидребезговый флаг unsigned long past; byte state;/**/ public: Cl_aKeyboard(byte p) : pin(p) {} void init() { data = analogRead (pin); } void run() { state = skNone; data_next = analogRead (pin); if (! bounce && data != data_next) { // если прошел фронт изм на выводн bounce = 1; // выставить флаг past = mill; // сделать временую засветку } else if ( bounce && mill - past >= 5 ) { // если прошло антидребезговое время bounce = 0; // то снять флаг data_old = data ; data = data_next ; if (data_old > 1000) { if (data < 60) state = skRight; else if (data < 200) state = skUp; else if (data < 400) state = skDown; else if (data < 600) state = skLeft; else if (data < 800) state = skSelect; } } } byte read() { return state; } }; //---------------Компоновка---------------------------------------------- LiquidCrystal lcd(/*RS*/8,/*Enable*/9,/*DB4*/4,/*DB5*/5,/*DB6*/6,/*DB7*/7); void lcd_setup() { lcd.begin(16, 2); lcd.setCursor(0, 0); lcd.print("LCD Key Shield"); lcd.setCursor(0, 1); lcd.print("Press Key:None "); } void Do_Right() { lcd.setCursor(10, 1); lcd.print ("Right "); } void Do_Up() { lcd.setCursor(10, 1); lcd.print ("Up "); } void Do_Down() { lcd.setCursor(10, 1); lcd.print ("Down "); } void Do_Left() { lcd.setCursor(10, 1); lcd.print ("Left "); } void Do_Select() { lcd.setCursor(10, 1); lcd.print ("Select"); } Cl_aKeyboard Keyboard(/*пин*/A0); //----------------- ----------------------- void setup() { lcd_setup(); Keyboard.init(); } void loop() { mill = millis(); Keyboard.run(); if (Keyboard.read() == skRight ) { Do_Right(); } if (Keyboard.read() == skUp ) { Do_Up(); } if (Keyboard.read() == skDown ) { Do_Down(); } if (Keyboard.read() == skLeft ) { Do_Left(); } if (Keyboard.read() == skSelect ) { Do_Select(); } } /**/Предлагаю использовать здесь новый термин : "квонокод"
Надо дать определение. Предлагаю такое:
"Квонокод - разновидность говнокода, написанного коллегой qwone. Отличительная особенность - широкое использование ключевого слова "class" при полном отсутствии объектно-ориентированного подхода".
Предлагаю использовать здесь новый термин : "квонокод"
Надо дать определение. Предлагаю такое:
"Квонокод - разновидность говнокода, написанного коллегой qwone. Отличительная особенность - широкое использование ключевого слова "class" при полном отсутствии объектно-ориентированного подхода".
Это оперделение требует корректировки:
"Квонокод - разновидность программ, написанных коллегой qwone. Отличительная особенность - широкое использование ключевого слова "class" при полном отсутствии объектно-ориентированного говнокода".
"
Теперь попробуем заменить аналоговую клавиатару на клавиатуру на Serial
/**/ unsigned long mill; //--------------------Cl_sKeyboard-------------------------------- /*состояние-нет нажатий/правая/левая/вверх/вниз/влево/выбор*/ enum {skNone = 0, skRight, skUp, skDown, skLeft, skSelect}; struct com_t { byte com; byte state; }; const byte numCom = 5; com_t MAP[numCom] { {'r', skRight}, {'u', skUp}, {'d', skDown}, {'l', skLeft}, {'s', skSelect} }; class Cl_sKeyboard { protected: byte state = skNone; public: Cl_sKeyboard() {} void init() { Serial.begin(9600); } void run() { state = skNone; if (Serial.available() > 0) { byte a = Serial.read(); for (int i = 0; i < numCom; i++) { if (a == MAP[i].com) state = MAP[i].state; } } } byte read() { return state; } }; //---------------Компоновка---------------------------------------------- #include <LiquidCrystal.h> LiquidCrystal lcd(/*RS*/8,/*Enable*/9,/*DB4*/4,/*DB5*/5,/*DB6*/6,/*DB7*/7); void lcd_setup() { lcd.begin(16, 2); lcd.setCursor(0, 0); lcd.print("LCD Key Shield"); lcd.setCursor(0, 1); lcd.print("Press Key:None "); } void Do_Right() { lcd.setCursor(10, 1); lcd.print ("Right "); } void Do_Up() { lcd.setCursor(10, 1); lcd.print ("Up "); } void Do_Down() { lcd.setCursor(10, 1); lcd.print ("Down "); } void Do_Left() { lcd.setCursor(10, 1); lcd.print ("Left "); } void Do_Select() { lcd.setCursor(10, 1); lcd.print ("Select"); } Cl_sKeyboard Keyboard; //----------------- ----------------------- void setup() { lcd_setup(); Keyboard.init(); } void loop() { mill = millis(); Keyboard.run(); if (Keyboard.read() == skRight ) { Do_Right(); } if (Keyboard.read() == skUp ) { Do_Up(); } if (Keyboard.read() == skDown ) { Do_Down(); } if (Keyboard.read() == skLeft ) { Do_Left(); } if (Keyboard.read() == skSelect ) { Do_Select(); } } /*Скетч использует 2820 байт (8%) памяти устройства. Всего доступно 32256 байт. Глобальные переменные используют 287 байт (14%) динамической памяти, оставляя 1761 байт для локальных переменных. Максимум: 2048 байт. */ПС: Попозже я ее оптимизирую и переведу на PROGMEM
Ну где-то так
/**/ unsigned long mill; //--------------------Cl_sKeyboard-------------------------------- /*состояние-нет нажатий/правая/левая/вверх/вниз/влево/выбор*/ enum {skNone = 0, skRight, skUp, skDown, skLeft, skSelect}; struct com_t { byte com; byte state; }; const byte numCom = 5; const com_t MAP[numCom] PROGMEM = { {'r', skRight}, {'u', skUp}, {'d', skDown}, {'l', skLeft}, {'s', skSelect} }; class Cl_sKeyboard { protected: byte state = skNone; public: Cl_sKeyboard() {} void init() { Serial.begin(9600); } void run() { state = skNone; if (Serial.available() > 0) { byte a = Serial.read(); for (int i = 0; i < numCom; i++) { if (a == pgm_read_byte(&(MAP[i].com))) { state = pgm_read_byte(&(MAP[i].state)); break; } } } } byte read() { return state; } }; //---------------Компоновка---------------------------------------------- Cl_sKeyboard Keyboard; //----------------- ----------------------- void setup() { Keyboard.init(); } void loop() { mill = millis(); Keyboard.run(); if (Keyboard.read() == skRight ) { Serial.println("Right"); } if (Keyboard.read() == skUp ) { Serial.println("Up"); } if (Keyboard.read() == skDown ) { Serial.println("Down"); } if (Keyboard.read() == skLeft ) { Serial.println("Left"); } if (Keyboard.read() == skSelect ) { Serial.println("Select"); } } /*Скетч использует 1690 байт (5%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 217 байт (10%) динамической памяти, оставляя 1831 байт для локальных переменных. Максимум: 2048 байт. */Искуство иллюзии в исполнении квона - навязчивая публичная демонстрация владения ООП и цифрового автомата при полном их отсутствии в коде.
Переделал меню отсюда #251 . Работает. Но надо ее еще обдумывать.
/**/ unsigned long mill; typedef void (*pDo)(); //--------------------Cl_sKeyboard-------------------------------- /*состояние-нет нажатий/+/-/exe*/ enum {skNone = 0, skPlus, skMimus, skExe}; struct com_t { byte com; byte state; }; const byte numCom = 3; const com_t MAP[numCom] PROGMEM = { {'+', skPlus}, {'-', skMimus}, {'e', skExe} }; class Cl_sKeyboard { protected: byte state = skNone; public: Cl_sKeyboard() {} void init() { Serial.begin(9600); } void run() { state = skNone; if (Serial.available() > 0) { byte a = Serial.read(); for (int i = 0; i < numCom; i++) { if (a == pgm_read_byte(&(MAP[i].com))) { state = pgm_read_byte(&(MAP[i].state)); break; } } } } byte read() { return state; } }; //------Cl_Display---------------------- // класс регулярный вывод на дисплей class Cl_Display { protected: pDo Do;//обработчик bool refreshON = 1; //сигнал обновить public: /*конструктор*/ Cl_Display(pDo D): Do(D) {} /*инициализация-вставить в setup()*/ void init() { } /*работа-вставить в loop()*/ void run() { if (refreshON) { refreshON = 0; Do(); } } void refresh() { refreshON = 1; } /*записать новый обработчик*/ void write( pDo D) { Do = D; } }; //-----Компоненты Меню------------------------- void ExitMenu() {}; int Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8, Value9; byte screen ; byte line ; bool fEdit; void initMenu( byte screen);/*предварительное объявление*/ //-----sItemMenu---------------- enum { ROOT, HOLDER, CONFIGINT }; struct sItemMenu { byte type; byte num; }; const sItemMenu dbItemMenu[] PROGMEM = { /*type,num*/ {ROOT, 0}, /*#0 ROOT[0] 1-3*/ {HOLDER, 0}, /*#1 HOLDER[0] 4-6*/ {HOLDER, 1}, /*#2 HOLDER[1] 7-9 */ {CONFIGINT, 0},/*#3 var 1 CONFGINT[0]*/ {HOLDER, 2}, /* #4 HOLDER[2] 10-12 */ {CONFIGINT, 1}, /* #5 var 2 CONFGINT[1] */ {CONFIGINT, 2}, /* #6 var 3 CONFGINT[2] */ {CONFIGINT, 3}, /* #7 var 4 CONFGINT[3] */ {CONFIGINT, 4}, /* #8 var 5 CONFGINT[4] */ {CONFIGINT, 5}, /* #9 var 6 CONFGINT[5] */ {CONFIGINT, 6}, /* #10 var 7 CONFGINT[6]*/ {CONFIGINT, 7}, /* #11 var 8 CONFGINT[7]*/ {CONFIGINT, 8} /* #12 var 9 CONFGINT[8]*/ }; void pgm_viev(char *name) {/*вывод из PROGMEM*/ char buffer[15]; strcpy_P(buffer, name); Serial.print(buffer); } //------sItemRoot------------------- struct sItemRoot { const byte top; const byte bottom; const pDo Do; }; const sItemRoot dbItemRoot[] PROGMEM = { {1, 3, ExitMenu} /*ROOT[0]*/ }; byte readTopItemRoot(byte the) { /* <- top[the] */ return pgm_read_byte(&dbItemRoot[the].top); } byte readBottomItemRoot(byte the) { /* <- top[the] */ return pgm_read_byte(&dbItemRoot[the].bottom); } void DoItemRoot(byte the) { /* Do[the] */ pDo Do = pgm_read_word(&dbItemRoot[the].Do); Do(); } //------sItemHolder------------------- struct sItemHolder { const byte father; const byte top; const byte bottom; const char *name; }; const char txtItemHolder0[] PROGMEM = "R0 HOLDER 1"; const char txtItemHolder1[] PROGMEM = "R0 HOLDER 2"; const char txtItemHolder2[] PROGMEM = "H0 HOLDER 3"; const sItemHolder dbItemHolder[] PROGMEM = { /*father,top,bottom,name*/ {0, 4, 6, txtItemHolder0}, /*#0 HOLDER[0]*/ {0, 7, 9, txtItemHolder1}, /*#1 HOLDER[1]*/ {1, 10, 12, txtItemHolder2} /*#2 HOLDER[2]*/ }; byte readFatherItemHolder(byte the) { /* <- father[the] */ return pgm_read_byte(&dbItemHolder[the].father); } byte readTopItemHolder(byte the) { /* <- top[the] */ return pgm_read_byte(&dbItemHolder[the].top); } byte readBottomItemHolder(byte the) { /* <- top[the] */ return pgm_read_byte(&dbItemHolder[the].bottom); } char * readItemHolderName(byte the) { /* <- name[the] */ return (char *)pgm_read_byte(&dbItemHolder[the].name); } //-------sItemConfigInt---------------- struct sItemConfigInt { int * pointer; byte addr; int maxValue; int minValue; int setValue; const char *name; }; const byte numItemConfigInt = 9; /*кол-во элементов ConfigInt */ const char txtItemConfigInt0[] PROGMEM = "R0 var 1="; const char txtItemConfigInt1[] PROGMEM = "H0 var 2="; const char txtItemConfigInt2[] PROGMEM = "H0 var 3="; const char txtItemConfigInt3[] PROGMEM = "H1 var 4="; const char txtItemConfigInt4[] PROGMEM = "H1 var 5="; const char txtItemConfigInt5[] PROGMEM = "H1 var 6="; const char txtItemConfigInt6[] PROGMEM = "H2 var 7="; const char txtItemConfigInt7[] PROGMEM = "H2 var 8="; const char txtItemConfigInt8[] PROGMEM = "H2 var 9="; const sItemConfigInt dbItemConfigInt[] PROGMEM = { /*pointer,addr,max,min,set,name*/ { &Value1, 0, 100, 10, 10, txtItemConfigInt0}, /*CONFGINT[0]*/ { &Value2, 2, 100, 10, 20, txtItemConfigInt1}, /*CONFGINT[1]*/ { &Value3, 4, 100, 10, 30, txtItemConfigInt2}, /*CONFGINT[2]*/ { &Value4, 6, 100, 10, 40, txtItemConfigInt3}, /*CONFGINT[3]*/ { &Value5, 8, 100, 10, 50, txtItemConfigInt4}, /*CONFGINT[4]*/ { &Value6, 10, 100, 10, 60, txtItemConfigInt5}, /*CONFGINT[5]*/ { &Value7, 12, 100, 10, 70, txtItemConfigInt6}, /*CONFGINT[6]*/ { &Value8, 14, 100, 10, 80, txtItemConfigInt7}, /*CONFGINT[7]*/ { &Value9, 16, 100, 10, 90, txtItemConfigInt8} /*CONFGINT[8]*/ }; int readItemConfigInt(byte the) { /*<-VAL[the]* ++ */ int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer); return * pointer; } void readItemConfigIntfromPROGMEM(byte the) { /*VAL[the]<-PROGMEM[the] ++ */ int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer); *pointer = pgm_read_word(&dbItemConfigInt[the].setValue); } void plusItemConfigInt(byte the) {/*if (VAL[the]<max[the]) ++VAL[the]*/ int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer); byte maxValue = pgm_read_byte(&dbItemConfigInt[the].maxValue); if (* pointer < maxValue) *pointer += 1; } void minusItemConfigInt(byte the) {/*if (VAL[the]>min[the]) --VAL[the]*/ int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer); byte minValue = pgm_read_byte(&dbItemConfigInt[the].minValue); if (* pointer > minValue ) *pointer -= 1; } char * readItemConfigIntName(byte the) { /* <- name[the] */ return (char *)pgm_read_byte(&dbItemConfigInt[the].name); } #include <EEPROM.h> void readItemConfigIntfromEEPROM(byte the) { /*VAL[the]<-EEPROM[the]*/ int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer); byte addr = pgm_read_byte(&dbItemConfigInt[the].addr); EEPROM.get(addr, *pointer); } void writeItemConfigIntToEEPROM(byte the) { /*EEPROM[the]<-VAL[the]*/ int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer); byte addr = pgm_read_byte(&dbItemConfigInt[the].addr); EEPROM.put(addr, *pointer); } void readAllItemConfigIntfromEEPROM() { /*VAL[All]<-EEPROM[All]*/ for (int i = 0; i < numItemConfigInt; ++i) { readItemConfigIntfromEEPROM(i); } } void AllItemConfigIntPROGMEMtoEEPROM() { /*EEPROM[All]<-PROGMEM[the][All]*/ for (int i = 0; i < numItemConfigInt; ++i) { int ConfigInt = pgm_read_word(&dbItemConfigInt[i].setValue); byte addr = pgm_read_byte(&dbItemConfigInt[i].addr); EEPROM.put(addr, ConfigInt); } } //------------------- void initMenu( byte scr) { screen = scr; byte type = pgm_read_byte(&dbItemMenu[screen].type); byte num = pgm_read_byte(&dbItemMenu[screen].num); if (type == ROOT) line = pgm_read_byte(&dbItemRoot[num].top); if (type == HOLDER) line = pgm_read_byte(&dbItemHolder[num].top); fEdit = 0; } //------------------- void VievMenu(byte screen, byte line, byte fEdit) { byte type = pgm_read_byte(&dbItemMenu[screen].type); byte num = pgm_read_byte(&dbItemMenu[screen].num); if (type == ROOT) { byte top = pgm_read_byte(&dbItemRoot[num].top); byte bottom = pgm_read_byte(&dbItemRoot[num].bottom); Serial.println(); for (int i = top; i <= bottom; ++i) { Serial.println(); if (i == line) { if (fEdit)Serial.print("*"); else Serial.print(">"); } else Serial.print(" "); byte t = pgm_read_byte(&dbItemMenu[i].type); byte n = pgm_read_byte(&dbItemMenu[i].num); if (t == CONFIGINT) { char * name = readItemConfigIntName(n); pgm_viev(name); int ItemValue = readItemConfigInt(n); Serial.print(ItemValue); } if (t == HOLDER) { char * name = readItemHolderName(n); pgm_viev(name); } } } if (type == HOLDER) { byte top = pgm_read_byte(&dbItemHolder[num].top); byte bottom = pgm_read_byte(&dbItemHolder[num].bottom); Serial.println(); for (int i = top; i <= bottom; ++i) { Serial.println(); if (i == line) { if (fEdit)Serial.print("*"); else Serial.print(">"); } else Serial.print(" "); byte t = pgm_read_byte(&dbItemMenu[i].type); byte n = pgm_read_byte(&dbItemMenu[i].num); if (t == HOLDER) { char * name = readItemHolderName(n); pgm_viev(name); } if (t == CONFIGINT) { char * name = readItemConfigIntName(n); pgm_viev(name); int ItemValue = readItemConfigInt(n); Serial.print(ItemValue); } } } } //-----Компоновка------------------------------ void DoViev() { VievMenu(screen, line, fEdit); } Cl_Display Display(/*обработчик*/DoViev); void DoMinus() {/*-*/ Display.refresh(); if (fEdit) { /*реж редактирования*/ byte t = pgm_read_byte(&dbItemMenu[line].type); byte n = pgm_read_byte(&dbItemMenu[line].num); if (t == CONFIGINT) { minusItemConfigInt(n); } } else {/*реж передвиж*/ byte type = pgm_read_byte(&dbItemMenu[screen].type); byte num = pgm_read_byte(&dbItemMenu[screen].num); if (type == ROOT) { byte top = pgm_read_byte(&dbItemRoot[num].top); if (line > top ) --line; } if (type == HOLDER) { byte top = pgm_read_byte(&dbItemHolder[num].top); if (line > top ) --line; else if (line == top ) { screen = pgm_read_byte(&dbItemHolder[num].father); byte t = pgm_read_byte(&dbItemMenu[screen].type); byte n = pgm_read_byte(&dbItemMenu[screen].num); if (t == ROOT) line = pgm_read_byte(&dbItemRoot[n].top); else line = pgm_read_byte(&dbItemHolder[n].top); } } } } void DoPlus() {/*+*/ Display.refresh(); if (fEdit) { /*реж редактирования*/ byte t = pgm_read_byte(&dbItemMenu[line].type); byte n = pgm_read_byte(&dbItemMenu[line].num); if (t == CONFIGINT) { plusItemConfigInt(n); } } else {/*реж передвиж*/ byte type = pgm_read_byte(&dbItemMenu[screen].type); byte num = pgm_read_byte(&dbItemMenu[screen].num); if (type == ROOT) { byte bottom = pgm_read_byte(&dbItemRoot[num].bottom); if (line < bottom ) ++line; } if (type == HOLDER) { byte bottom = pgm_read_byte(&dbItemHolder[num].bottom); if (line < bottom ) ++line; } } } void DoExe() {/*exe*/ Display.refresh(); byte t = pgm_read_byte(&dbItemMenu[line].type); byte n = pgm_read_byte(&dbItemMenu[line].num); if (fEdit) { /*реж редактирования*/ if (t == CONFIGINT) { writeItemConfigIntToEEPROM(n); fEdit = 0; } return; } else { /*реж передвиж*/ if (t == HOLDER ) initMenu(line); if (t == CONFIGINT) fEdit = 1; } } Cl_sKeyboard Keyboard; //--main()------------------- void setup() { Serial.begin(9600); //AllItemConfigIntPROGMEMtoEEPROM();/*первичная настройка EEPROM из PROGMEM*/ readAllItemConfigIntfromEEPROM(); /*прочитать из памяти*/ initMenu(0);/*установить меню в начало*/ Display.init(); Keyboard.init(); } void loop() { mill = millis(); Display.run(); Keyboard.run(); if (Keyboard.read() == skMimus ) DoMinus(); if (Keyboard.read() == skPlus ) DoPlus(); if (Keyboard.read() == skExe ) DoExe(); } /*Скетч использует 3554 байт (11%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 221 байт (10%) динамической памяти, оставляя 1827 байт для локальных переменных. Максимум: 2048 байт. */Теперь попробуем заменить аналоговую клавиатару на клавиатуру на Serial
Ну где-то так
Ни чё не понял .. а где там "конченный автомат"? Вы же не считаете, что раз обозвали метод run() и создали поле state то он сразу превратил класс в КА?
Я бы для начала, переформулировал задачу под решение методом конечных автоматов, а уже потом делал реализацию. Тут задача, вообще - какая?
Судя по тексту: есть 5 условных кнопок/команд: "влево", "вправо", "вверх", "вниз" и "выбор", принимаемых из Serial. По "нажатию кнопки" (приходу команды) выполнить свое действие. Где тут задача для КА? :)
Есть "общепринятое" понятие об КА как системе с конечным набором состояний. В этой задаче состояние одно: принять сигнал - сделать действие. Но если у Вас своя "концепция", то умолкаю..
Ну да, а кнопки или что-то еще подобное,по вашему, это система с бесконечным набором состояний. Кнопку , а точнее программную часть тоже можно называть автоматом. А то что она переключается под действием механических сигналов, а не электрических, то это пусть Вас не смущает. Вот переключаю светодиод через Serial кнопки. Похоже по-ходу сумел организовать систему собитий. Так что Logik попозже будет попу рвать, говоря что я организовал не событийные события.
/**/ unsigned long mill; //--------------------Cl_sKeyboard-------------------------------- /*события-нет /вкл/выкл/мигать*/ enum class event_t {None = 0, OFF, ON, Blink}; struct com_t { byte com; event_t event; }; const byte numCom = 3; const com_t MAP[numCom] PROGMEM = { {'l', event_t::OFF}, {'L', event_t::ON}, {'B', event_t::Blink} }; class Cl_sKeyboard { event_t event; public: bool is(event_t e) { return e == event; } Cl_sKeyboard() {} void init() { Serial.begin(9600); } void run() { event = event_t::None; if (Serial.available() > 0) { byte a = Serial.read(); for (int i = 0; i < numCom; i++) { if (a == pgm_read_byte(&(MAP[i].com))) { event = (event_t)pgm_read_byte(&(MAP[i].event)); break; } } } } }; //----------------------------------------------------------------------- class Cl_Led { public: enum event_t {onNone = 0, onOFF, onON, onBlink};/*событие нет/Выкл/Вкл/Блинк*/ event_t event; protected: byte pin; unsigned long past; unsigned long time = 500; enum state_t {sNone = 0, sOFF, sON, sBlink, sON2, sOFF2} state;/*состояние нет/Выкл/Вкл/Блинк/БлинкВКЛ/БлинкВыкл*/ bool is(state_t s) { return s == state; } void stand(state_t s) { state = s; past = mill; switch (state) { case sNone: digitalWrite(pin, LOW); break; case sOFF: event = onOFF; digitalWrite(pin, LOW); break; case sON: event = onON; digitalWrite(pin, HIGH); break; case sBlink: event = onBlink; state = sON2; case sON2: digitalWrite(pin, HIGH); break; case sOFF2: digitalWrite(pin, LOW); break; } } public: bool is(event_t e) { return e == event; } Cl_Led(byte p): pin(p) {} void init() { pinMode(pin, OUTPUT); stand(sNone); } void run() { event = onNone; if (is(sON2) && mill - past >= time )stand(sOFF2); if (is(sOFF2) && mill - past >= time)stand(sON2); } void ON() { stand(sON); } void OFF() { stand(sOFF); } void blink() { stand(sBlink); } }; //---------------Компоновка---------------------------------------------- Cl_sKeyboard Keyboard; Cl_Led Led(/*пин*/13); //----------------- ----------------------- void setup() { Keyboard.init(); Led.init(); } void loop() { mill = millis(); Led.run(); Keyboard.run(); if (Keyboard.is(event_t::OFF)) { Led.OFF(); } if (Keyboard.is(event_t::ON)) { Led.ON(); } if (Keyboard.is(event_t::Blink)) { Led.blink(); } if (Led.is(Cl_Led::onOFF)) { Serial.println("Led OFF"); } if (Led.is(Cl_Led::onON)) { Serial.println("Led ON"); } if (Led.is(Cl_Led::onBlink)) { Serial.println("Led Blink"); } } /*Скетч использует 2372 байт (7%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 229 байт (11%) динамической памяти, оставляя 1819 байт для локальных переменных. Максимум: 2048 байт. */qwone, у конченного автомата есть не только набор заданных состояний, но ещё и правила (граф) переходов промеж них, который в большинстве случаев может быть описан матрицей переходов, зачастую сильно вырожденной. А ещё есть "слушатель входных новостей", который и поставляет конечному автомату условия (события) по переходам этого графа. Триединство в одном флаконе.
Кнопку можно натянуть на конечный автомат, ибо как миниум 2 состояния у неё есть. Только стоит ли, кроме как "примера для"? :)
P.S. И мне кажется, что разумней не столько демонстрировать огромный код на классах (что сразу показывает проигрыш решения - стока писать лично мне - лениво), сколько попытаться показать КАК можно переформулировать "обыкновенное ТЗ" в постановку под КА.
Как пример: "светофор имеет 3 лампочки, которые загораются по порядку и горят 30сек - красный, 2сек - желтый и 30сек - зеленый". Можно решать с delay(), но можно переформулировать задачу:
"Светофор может находится в трех состояниях: 30сек. включен красный, 2сек включен желтый и 30сек включен зеленый. Изменением состояний управляет системное время millis(). Переходы между состояниями: выключить зеленый + включить красный, выключить красный + включить желтый, .."
Тут как видно, решение на delay() уже не возникает из самой постановки ТЗ. А "блин без делай" - так и сам просится как слушатель потока новостей и программа вырождается в тривиальную даже для начинающего школьника. :)
P.P.S. правильная постановка задачи - 50% решения.
Архат, не святотацтвуй (с его точки зрения), ты же виишь, Пуха перекрестили, теперь он послушник Секты Свидетелей Всемогущества Святого Конечного Автомата. Вот пусь пока живет в счастливом неведении, главное, чтоб квартиру на них не переписал.
Так сам вроде как в этой секте .. думаю в ней "раскол" не нужен.. :)
Преподавал по лету КА детишкам, оказалось что переформулировка задачи (как выше) приводит к существенно лучшему восприятию и построению алгоритмов и внезапно, оказывается: -"Вы им (12лет) преподаете конечные автоматы? Это же очень сложно!" .. совсем не так, а очень даже просто и им понятней чем как преодолеть delay() в коде.
У qwone, КМК, есть небольшое недопонимание КА: кроме состояний, есть граф переходов и именно он реализует "методы переходов", а слушатель новостей всего лишь .. поставляет новость о переходе или .. не поставляет (ничего не происходит - состояние неизменно).
Отсюда: класс КА должен содержать не только переменную state, но и иметь МЕТОДЫ переходов (возможно и матрицу переходов) а также СЛУШАТЕЛЯ новостей. Далее сюда в С++ настырно просится "базовый" класс КА с виртуальными наследуемыми методами .. а это есть безусловное зло для микроконтроллера с гарвардской архитектурой.
Отсюда: реализация на "С" базовых методов КА есть достаточное действо и скрипач (С++) тут не нужен. :)
Итак пусть нам надо такой Автомат
И скетч к нему.
/**/ unsigned long mill; //------------------------------------- class Cl_AAA { protected: /*сщстояние нейтраль/ A/ B/ C/ D/ E*/ enum state_t : byte {sNone = 0, sA, sB, sC, sD, sE} state = sNone; unsigned long time = 500; unsigned long past; void stand(state_t s) { state = s; past = mill; switch ((byte)state) { case sA: Serial.println("state=sA"); break; case sB: Serial.println("state=sB"); break; case sC: Serial.println("state=sC"); break; case sD: Serial.println("state=sD"); break; case sE: Serial.println("state=sE"); break; } } bool is(state_t s) { return state == s && mill - past >= time; } public: Cl_AAA() {} void init() { Serial.begin(9600); stand(sA); } void run() { if (is(sA)) stand(sB); if (is(sB)) stand(sC); if (is(sC)) stand(sD); if (is(sD)) stand(sE); if (is(sE)) stand(sA); } }; //----------------------------------- Cl_AAA AAA; //---------------------------------- void setup() { AAA.init(); } void loop() { mill = millis(); AAA.run(); } /**/Но полезная переменная внутри автомата - состояние совершенно бесполезна для устройств снаружи. Ну зачем нам знать в каком состоянии находится автомат. Нам важнее знать когда он перешел и сделать все обработчики этого события снаружи. Так что приходится вводить такую переменную событие.
/**/ unsigned long mill; //------------------------------------- class Cl_AAA { public: /*событие нейтраль/ A/ B/ C/ D/ E */ enum event_t : byte {eNone = 0, eA, eB, eC, eD, eE}; bool is(event_t e) { //<-проверка на это событие return event == e; } protected: /*состояние нейтраль/ A/ B/ C/ D/ E */ enum state_t : byte {sNone = 0, sA, sB, sC, sD, sE} state = sNone; event_t event = eNone; unsigned long time = 500; unsigned long past; void stand(state_t s) { state = s; past = mill; switch ((byte)state) { case sA: event = eA;//<-устанавливаем событие в A break; case sB: event = eB; break; case sC: event = eC; break; case sD: event = eD; break; case sE: event = eE; break; } } bool is(state_t s) { return state == s && mill - past >= time; } public: Cl_AAA() {} void init() { stand(sA); } void run() { event = eNone;//<--сбрасываем событие if (is(sA)) stand(sB); if (is(sB)) stand(sC); if (is(sC)) stand(sD); if (is(sD)) stand(sE); if (is(sE)) stand(sA); } }; //----------------------------------- Cl_AAA AAA; //---------------------------------- void setup() { Serial.begin(9600); AAA.init(); } void loop() { mill = millis(); AAA.run(); if (AAA.is(Cl_AAA::eA))Serial.println("state=sA"); if (AAA.is(Cl_AAA::eB))Serial.println("state=sB"); if (AAA.is(Cl_AAA::eC))Serial.println("state=sC"); if (AAA.is(Cl_AAA::eD))Serial.println("state=sD"); if (AAA.is(Cl_AAA::eE))Serial.println("state=sE"); } /**/Теперь появилась возможность писать куски кода отлаживать и формировать библиотеки уже готовых компонентов
Итак пусть нам надо такой Автомат
Допустим, ниже обобщение без КА, и с возможностью юзать любые интервалы между командами - например, подгружая следующую команды из файла макроописания, например, чтобы сделать алгоритм работы чего-либо - сменяемым, без перезакачки прошивки:
class Command { uint16_t mDelay; public: Command() {} uint16_t getDelay() { return mDelay; } void run() { Serial.print("run after "); Serial.println(mDelay); } Command* next() { // загружаем следующую команду mDelay = random(200,2000); static uint8_t stop = 0; // тупо останавливаемся на каком-либо шаге if(++stop > 99) return NULL; return this; } }; class Execution { unsigned long past; Command* mCmd; public: Execution(Command* c) { past = 0; mCmd = c->next(); } void run() { if(!mCmd) return; if(millis() - past > mCmd->getDelay()) { mCmd->run(); mCmd = mCmd->next(); past = millis(); } } }; Command runner; Execution exec(&runner); void setup() { } void loop() { exec.run(); }Виртуальные методы и наследование - спецом не вводил, но при желании и необходимости - оно можно.
Пух, я вот одного не пойму - к чему твои вырожденные примеры, к тому же такие, где мухи с котлетами в одном фарше? У тебя, например, переменная mill - глобальная, накохер? При том, что в одном классе аж две четырёхбайтовых переменных - накохер? В приведённом тобой выше примере КА - накохер сам КА, если его там - просто не надо, это тупой перебор интервалов, при этом весь интерес, что по истечению интервала просто выполняется другой кусок кода - накохер там КА? Кмк, ты сам уже запутался в КА и в посылах - зачем они нужны. Знаешь, это как купил молоток - и пока всё им не переколотишь - не остановишься, и похер - гвоздь торчит или шуруп - всё заколотим к ебенематери.
Чуть переписал скетч что выше. Теперь можно задавать время перескока в создании объекта.Косметическая доработка.
/**/ unsigned long mill; //------------------------------------- class Cl_AAA { public: /*событие нейтраль/ A/ B/ C/ D/ E */ enum event_t : byte {eNone = 0, eA, eB, eC, eD, eE}; bool is(event_t e) { //<-проверка на это событие return event == e; } protected: event_t event = eNone; /*состояние нейтраль/ A/ B/ C/ D/ E */ enum state_t : byte {sNone = 0, sA, sB, sC, sD, sE} state = sNone; unsigned long time = 500; unsigned long past; void stand(state_t s) { state = s; past = mill; switch ((byte)state) { case sA: event = eA;//<-устанавливаем событие в A break; case sB: event = eB; break; case sC: event = eC; break; case sD: event = eD; break; case sE: event = eE; break; } } bool is(state_t s) { return state == s && mill - past >= time; } bool is(state_t s, unsigned long t) { return state == s && mill - past >= t; } public: Cl_AAA(unsigned long t = 500): time(t) { } void init() { stand(sA); } void run() { event = eNone;//<--сбрасываем событие if (is(sA, time)) stand(sB); if (is(sB, time)) stand(sC); if (is(sC, time)) stand(sD); if (is(sD, time)) stand(sE); if (is(sE, time)) stand(sA); } }; //----------------------------------- Cl_AAA AAA(/*время перескока*/200); //---------------------------------- void setup() { Serial.begin(9600); AAA.init(); } void loop() { mill = millis(); AAA.run(); if (AAA.is(Cl_AAA::eA))Serial.println("state=sA"); if (AAA.is(Cl_AAA::eB))Serial.println("state=sB"); if (AAA.is(Cl_AAA::eC))Serial.println("state=sC"); if (AAA.is(Cl_AAA::eD))Serial.println("state=sD"); if (AAA.is(Cl_AAA::eE))Serial.println("state=sE"); } /**/DIYMan, Вас зовут видно Потапов из Ералаша.https://www.youtube.com/watch?v=nZAvX2BvpPc
Разумеется я здесь на простых примерах раскладываю опять же простые концепции. Но я не мешаю Вам открыть свою тему и вести уже разговоры на сложные темы. Вот сколько вы открыли тем и что. Почему Вы лезите сюда, а не в Ваши темы. А знаю - ВЫ ЗДЕСЬ САМЫЙ УМНЫЙ.
О как, млять. Ещё один ЧСВ-шник у нас завёлся, робяты. Шо деется-то :) Да др@чи тут один, пжалста - кроме тебя твои никчёмные поделия никому не интересны, как видишь. А я так, забежал, грешным делом подумав - может, прислушается и уже наконец-то начнёт чему-то учиться, вместо плясок на месте? Ошибался, жестоко. Прощенья просим, барин.
Видел тут на сайте много разных классов, появился вопрос. Может я чего то непойму, но это ведь классы C++? Просто я ковырял GNU C reference manual, и под термином class я видел только классы памяти. Это ведь разные вещи?
semaawp, classы в Си это мощный инструмент. И да в этой теме используются классы причем на 2% . Я не вижу смысла выкладывать все про классы здесь . Это очень много, есть соответствующая литература, а и я не смогу достаточно все это приподнести. А если я начну объяснять здесь, то от форумчан будет ну очень много критики.
Основное отличие языка программирования С++ от С состоит в том, что в С нет классов, а следовательно язык С не поддерживает ООП, в отличие от С++.
Я вот это хотел узнать, но все равно спасибо
Скопипастить - это я еще могу, даже кое что поправить, а вот с нуля писать под классы, уже учиться видимо поздновато. Решил слелать автоматику для горелки на отработке самому. Понравились классы qwone , хоть гуру его склоняют тут как хотят, но главное они работают и занимают мало места.
Что хочется от ТС - чтобы помог переписать мой скетч под свои классы, может и восьмую атмегу влезет. Заранее спасибо.
//**/ #define pinds1 14 //пины для подключения Далласов #define pinds2 15 #define timeDallas 1000 //тут определяем время через которое меряем температуру далласа 1 секунда #define timePurge 5000 //тут определяем время продувки 5 секунд #define timeIgnition 3000 //тут определяем время розжига 3 секунд #define set_temp_water 550 //тут температура котла #define hysteresis 100 // гистерезис на нагрев котла unsigned long mill;// переменная для millis() unsigned long millDallas; unsigned long millPurge; unsigned long millIgnition; byte valIgnition=0; bool FlagDallas = 0; bool rejim = 0;// реЖим работы 0-ожидание, 1-Auto typedef void (*pDo)() ;// тип -функция обработчик //---------------------------------------- #include <EEPROM.h> //#include <Wire.h> #include <LiquidCrystal_I2C.h> #include <OneWire.h> int temp[2];//текущая температура в термометрах int tempCorrect[2]={0,0};//температура корректировки текущего термометра //Если на 1-шине сидит один датчик, то его ID знать не требуется! OneWire ds1(pinds1); OneWire ds2(pinds2); OneWire *pinds[]={&ds1,&ds2}; #define START_CONVERT 0 #define READ_TEMP 1 enum event_State : byte {eNone = 0, eExpectation=1, eHeating=2, ePurge=3, eIgnition=4, eCombustion=5, eAutoExpectation=6, eError=7};// режимы работы КА event_State appState=eNone; LiquidCrystal_I2C lcd(0x3F, 16, 2); // инициализация библиотеки дисплея // чтение int EEPROM_int_read(int addr) { byte raw[2]; for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i); int &num = (int&)raw; return num; } // запись void EEPROM_int_write(int addr, int num) { byte raw[2]; (int&)raw = num; for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]); } // обновить void EEPROM_int_update(int addr, int num) { if (EEPROM_int_read(addr) != num) EEPROM_int_write(addr, num); } //------Cl_Display---------------------- // класс регулярный вывод на дисплей class Cl_Display { protected: pDo Do;//обработчик bool refreshON = 1; //сигнал обновить public: /*конструктор*/ Cl_Display(pDo D): Do(D) {} /*инициализация-вставить в setup()*/ void init() { /* my */ // lcd.begin(); // Старт дисплея lcd.init(); // Старт дисплея lcd.backlight(); lcd.print("helloy"); delay(2000); /* my */ } /*работа-вставить в loop()*/ void run() { if (refreshON) { refreshON = 0; Do(); } } void refresh() { refreshON = 1; } /*записать новый обработчик*/ void write( pDo D) { Do = D; } }; //------Cl_Btn---------------------- // класс кнопка enum {sbNONE = 0, sbClick, sbLong}; /*состояние не изменилось/клик/долгое наж*/ class Cl_Btn { protected: const byte pin; pDo Do;//обработчик bool bounce = 0; bool btn = 1, oldBtn; unsigned long past; const uint32_t time = 4000 ; //500 ; bool flag = 0; uint32_t past_flag = 0 ; public: byte state; /*конструктор*/ Cl_Btn(byte p, pDo D): pin(p), Do(D) {} /*инициализация-вставить в setup()*/ void init() { pinMode(pin, INPUT_PULLUP); } /*работа-вставить в loop()*/ void run() { state = sbNONE; bool newBtn = digitalRead(pin); if (!bounce && newBtn != btn) { bounce = 1; past = mill; } if (bounce && mill - past >= 10) { bounce = 0 ; oldBtn = btn; btn = newBtn; past = mill; if (!btn && oldBtn) { flag = 1; past_flag = mill; } if (!oldBtn && btn && flag && mill - past_flag < time ) { flag = 0; state = sbClick; Do(); } if (flag && mill - past_flag >= time ) { flag = 0; state = sbLong; rejim = !rejim; // Do(); } } } /*записать новый обработчик*/ void write( pDo D) { Do = D; } byte read() { return state; } }; //------Cl_BtnR---------------------- class Cl_BtnR : public Cl_Btn { /* класс кнопка с повтором при удерж кнопки*/ protected: public: /*конструктор*/ Cl_BtnR(byte p, pDo D): Cl_Btn(p, D) {} ////проверить /*работа-вставить в loop()*/ void run() { bool newBtn = digitalRead(pin); if (!bounce && newBtn != btn) { bounce = 1; past = mill; } if (bounce && mill - past >= 10) { bounce = 0 ; oldBtn = btn; btn = newBtn; past = mill; if (!btn && oldBtn) Do(); } if (!newBtn && !btn && mill - past >= 300) { past = mill; Do(); } } }; class Cl_Check : public Cl_Btn { /* класс датсик - это тоже кнопка */ protected: public: /*конструктор*/ Cl_Check(byte p, pDo D): Cl_Btn(p, D) {} ////проверить /*работа-вставить в loop()*/ void run() { state = sbNONE; bool newBtn = digitalRead(pin); if (!bounce && newBtn != btn) { bounce = 1; past = mill; } if (bounce && mill - past >= 10) { bounce = 0 ; oldBtn = btn; btn = newBtn; past = mill; state = sbLong; if (!btn && oldBtn) { //тут датчик погас ? state = sbNONE; Do(); } } } }; //-----Компоновка---------------------- // база данных настроечных переменных const int maxNumber = 20; int number = 0; int var0, var1, var2, var3, var4, var5, var6, var7, var8, var9; /*настроечные переменные !!!!!*/ int var10, var11, var12, var13, var14, var15, var16, var17, var18, var19; int * const pVar[maxNumber] PROGMEM = { &var0 , &var1, &var2, &var3, &var4, &var5, &var6, &var7, &var8, &var9, &var10, &var11, &var12, &var13, &var14, &var15, &var16, &var17, &var18, &var19 }; /*указатели на переменные*/ const char name0[] PROGMEM = "var0"; /*имя*/ const char name1[] PROGMEM = "var1"; const char name2[] PROGMEM = "var2"; const char name3[] PROGMEM = "var3"; const char name4[] PROGMEM = "var4"; const char name5[] PROGMEM = "var5"; const char name6[] PROGMEM = "var6"; const char name7[] PROGMEM = "var7"; const char name8[] PROGMEM = "var8"; const char name9[] PROGMEM = "var9"; const char name10[] PROGMEM = "shim3"; const char name11[] PROGMEM = "var11"; const char name12[] PROGMEM = "var12"; const char name13[] PROGMEM = "var13"; const char name14[] PROGMEM = "var14"; const char name15[] PROGMEM = "var15"; const char name16[] PROGMEM = "var16"; const char name17[] PROGMEM = "var17"; const char name18[] PROGMEM = "var18"; const char name19[] PROGMEM = "var19"; const char* const name[maxNumber] PROGMEM = { name0 , name1 , name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19 }; const int addr[maxNumber] PROGMEM = { /*адресс*/ 0 , 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18, 20 , 22 , 24 , 26 , 28 , 30 , 32 , 34 , 36 , 38 }; const int minVar[maxNumber] PROGMEM = { /*минимум*/ 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10, 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 }; const int maxVar[maxNumber] PROGMEM = { /*максимум*/ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }; // char buffer[10]; int var_, minVar_, maxVar_; char *name_; /*вывевети на экран*/ void vievVar() { // Serial.print("temp[0]="); //// Serial.print(": "); // Serial.print(var_); // Serial.println(); lcd.setCursor(0, 0); // устанавливаем курсор в 2-ом столбце, 3 строка (начинается с 0) lcd.print("T1="); lcd.print(float (temp[0])/10,1);// печать температуры на дисплей temp // lcd.setCursor(0, 1); // lcd.print(Upit,1);// печать температуры на дисплей temp lcd.print(" T2="); lcd.print(float(temp[1])/10);// печать температуры на дисплей temp if (rejim){ lcd.setCursor(15, 1); lcd.print("A"); }else { lcd.setCursor(15, 1); lcd.print(" "); } lcd.setCursor(0, 1); // lcd.setCursor(0, 1); // // lcd.print("H="); lcd.print(buffer); // печать влажности на дисплей lcd.print("="); // lcd.print(rejim); lcd.print(var_); lcd.print(" "); } /*прочитать var*/ void loadVar() { var_ = *(int*)pgm_read_word(&pVar[number]); strcpy_P(buffer, (char*)pgm_read_word(&(name[number]))); minVar_ = pgm_read_byte_near(minVar + number); maxVar_ = pgm_read_byte_near(maxVar + number); } /*прочитать все из EEPROM*/ void loadAll() { for (int i = 0; i < maxNumber; ++i) { *(int*)pgm_read_word(&pVar[i]) = EEPROM_int_read(pgm_read_byte_near(addr + i)); } loadVar(); }; /*записать var*/ void saveVar() { *(int*)pgm_read_word(&pVar[number]) = var_; EEPROM_int_update(pgm_read_byte_near(addr + number), var_); } // дисплей Cl_Display Display(/*обработчик*/vievVar); /*увеличить var1*/ void incVar() { Display.refresh(); ++var_; if (var_ > maxVar_) var_ = maxVar_; } /*уменьшить var1*/ void decVar() { Display.refresh(); --var_; if (var_ < minVar_) var_ = minVar_; } // кнопки Cl_BtnR Btn1(/*пин*/3,/*обработчик*/incVar);/*инкрем*/ Cl_BtnR Btn2(/*пин*/4,/*обработчик*/decVar);/*декримент*/ void DoBtn3() { Display.refresh(); saveVar(); ++number; if (number >= maxNumber) number = 0; loadVar(); } Cl_Btn Btn3(/*пин*/5,/*обработчик*/DoBtn3);/*Next*/ //--------------------------------------------------------------- // класс исполнительный механизм #define ON_ HIGH class Cl_Isp_Meh { protected: const byte pin; pDo Do;//обработчик public: /*конструктор*/ Cl_Isp_Meh(byte p): pin(p) {} /*инициализация- вставить в setup*/ void init() { pinMode(pin, OUTPUT); digitalWrite(pin, ON_); } /*работа- вставить в loop*/ void run() {} /*включить*/ void ON() { digitalWrite(pin, !ON_); } /*выключить*/ void OFF() { digitalWrite(pin, ON_); } }; void DoCheck() { // if (Check.read()==sbNONE) SetState(eError); // Serial.println("Transporter Swap"); } Cl_Check Check(/*пин счетчика*/10,/*обработчик*/&DoCheck); Cl_Isp_Meh ClapanOil(/*пин*/6);//пин маслянного клапана Cl_Isp_Meh ClapanAir(/*пин*/7);//пин воздушного клапана Cl_Isp_Meh MotorSecondaryAir(/*пин*/11);//пин включения мотора вентилятора вторичного воздуха Cl_Isp_Meh HeatingOil(/*пин*/8);//пин нагревателя Cl_Isp_Meh pinIgnition(/*пин*/9);//пин реле розжига Cl_Isp_Meh pinAuto(/*пин*/13);//пин индикации автоматического режима Cl_Isp_Meh pinError(/*пин*/17);//пин индикации аварии //-------------------------------------------- //-----main---------------------- void setup() { Serial.begin(9600); TCCR2B = TCCR2B & 0b11111000 | 0x07; //настроим второй таймер на шим loadAll(); Display.init(); Btn1.init(); Btn2.init(); Btn3.init(); Check.init(); ClapanOil.init(); ClapanAir.init(); MotorSecondaryAir.init(); HeatingOil.init(); pinIgnition.init(); pinAuto.init(); pinError.init(); for (int i=0; i<2; i++){ tempProcess(START_CONVERT,*pinds[i]);//конвентируем Dallas } millDallas=millis(); millPurge=millis(); millIgnition=millis(); } void SetState(event_State newState){ if (appState == newState) return; // если мы уже в этом состоянии - выход appState = newState; // запомним новое состояние switch (appState) { case eNone: // если мы ПЕРЕШЛИ в это состояние, это ошибка SetState(eError); break; case eExpectation: // в режиме ожидания ClapanOil.OFF(); ClapanAir.OFF(); MotorSecondaryAir.OFF(); HeatingOil.OFF(); pinIgnition.OFF(); pinAuto.OFF(); pinError.OFF(); break; case eHeating: // в режиме продувка ClapanOil.OFF(); ClapanAir.OFF(); MotorSecondaryAir.OFF(); HeatingOil.ON(); pinIgnition.OFF(); pinAuto.ON(); pinError.OFF(); break; case ePurge: // в режиме продувка ClapanOil.OFF(); ClapanAir.OFF(); MotorSecondaryAir.ON(); HeatingOil.ON(); pinIgnition.OFF(); pinAuto.ON(); pinError.OFF(); break; case eIgnition: // в режиме розжиг ClapanOil.ON(); ClapanAir.ON(); MotorSecondaryAir.ON(); HeatingOil.ON(); pinIgnition.ON(); pinAuto.ON(); pinError.OFF(); break; case eCombustion: // в режиме горение ClapanOil.ON(); ClapanAir.ON(); MotorSecondaryAir.ON(); HeatingOil.ON(); pinIgnition.OFF(); pinAuto.ON(); pinError.OFF(); break; case eAutoExpectation: // в режиме Ожидание в автомате ClapanOil.OFF(); ClapanAir.OFF(); MotorSecondaryAir.OFF(); HeatingOil.OFF(); pinIgnition.OFF(); pinAuto.ON(); pinError.OFF(); break; case eError: // если ошибка ClapanOil.OFF(); ClapanAir.OFF(); MotorSecondaryAir.OFF(); HeatingOil.OFF(); pinIgnition.OFF(); pinAuto.OFF(); pinError.ON(); // cli(); // входим в клинч // abort(); // выход только по Reset break; default: break; } } void loop() { mill = millis(); Display.run(); Btn1.run(); Btn2.run(); Btn3.run(); Check.run(); if (Btn3.read() == sbLong) Display.refresh(); if (FlagDallas) { thermostat (); Display.refresh(); millDallas = mill; FlagDallas = 0; } if (mill - millDallas >= timeDallas) { FlagDallas = 1; } //пока время возьмем как умеем, позже перепишу взяв параметры из меню if (rejim){ if (appState == eExpectation) SetState(eHeating); // если только включили автоматический режим, включаем нагрев if (appState == eHeating){ //если нагрелось больше 65 градусов и не горит включаем продувку if (temp[0]>=650 && Check.read()==sbNONE) SetState(ePurge); millPurge=mill; //засекаем вреня на продувку } if (appState == ePurge && mill-millPurge>=timePurge){ SetState(eIgnition); //продули пытаемся зажечь millIgnition=mill; //засекаем вреня на розжиг valIgnition++; //увеличиваем счетчик циклов розжига if (valIgnition>3) SetState(eError); //Больше трех раз не зажгли ошибка } if (appState == eIgnition && Check.read()==sbLong){ SetState(eCombustion); }// подожгли if (appState == eIgnition && Check.read()==sbNONE && mill-millIgnition>=timeIgnition) SetState(ePurge); //если не загорелось, возвращаемся в продувку if (appState == eCombustion && Check.read()==sbLong){//если горит if (temp[1] >= set_temp_water + hysteresis) {//и температура воды меньше установленной + гистерезис SetState(eAutoExpectation); // включаем ожидание } } if (appState == eAutoExpectation && temp[1] <= set_temp_water - hysteresis) SetState(eHeating); //если уала ниже чем установленная температура - гистерезис //возвращаемся к нагреву }else{ SetState(eExpectation); } } //enum event_State : byte {eNone = 0, eExpectation=1, eHeating=2, ePurge=3, eIgnition=4, eCombustion=5, eAutoExpectation=6, eError=7};// режимы работы КА void thermostat () { for (int i=0; i<2; i++){ temp[i]= tempProcess(READ_TEMP,*pinds[i])+tempCorrect[i];//читаем Температуру и корректировку к ней tempProcess(START_CONVERT,*pinds[i]); } } //============================== int tempProcess(boolean ch,OneWire &ds){ int t=0; if(!ch){ ds.reset(); ds.write(0xCC); ds.write(0x44); } else{ ds.reset(); ds.write(0xCC); ds.write(0xBE); t= ds.read(); t = t | (ds.read()<<8); //return t>>4;//целые *C, десятые отброшены //return (t+8)>>4;//целые *С с округлением return (t*10)>>4;//целое в десятых *C (214=>21,4*C) } } void beeper(int duration) { // tone(beeper_pin, 2000, duration); } //Скетч использует 10 676 байт (67%) памяти устройства. Всего доступно 15 872 байт. //Глобальные переменные используют 680 байт динамической памяти.Еще вентиляторы и насосы будут работать от шим, сам пытался под них класс написать, но почему - то не работает
class Cl_Isp_Meh_Shim { // класс исполнительный механизм с Шим на 3 или 11 выводе protected: const byte pin; pDo Do;//обработчик int var_shim; int nomer_shim; public: /*конструктор*/ Cl_Isp_Meh_Shim(byte p,int nomer, pDo D): pin(p),nomer_shim(nomer), Do(D) {} /*работа-вставить в loop()*/ void run() { var_shim=*(int*)pgm_read_word(&pVar[nomer_shim]); analogWrite(pin, map(var_shim,0,100,0,255)); } };IvanP, ваш код это конечно хорошо. Но лучше сначало познакомится с алгоритмом работы. Просто проводить обратный инженериг по коду не очень интересно и долго.
/**/ template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } typedef void (*pDo)(); pDo run; bool state; unsigned long past; void stand(byte s) { state = s; past = millis(); switch (state) { case false: run = []{ if (millis() - past >= 500) { stand(true); Serial << "ON\n"; } }; break; case true: run = []{ if (millis() - past >= 500) { stand(false); Serial << "OFF\n"; } }; break; } } void setup() { Serial.begin(9600); stand(false); } void loop() { run(); }Blink
/**/ typedef void (*pDo)(); //------------------ const byte pinLed = 13; pDo run; bool state; unsigned long past; const unsigned long time = 500; void stand(byte s) { state = s; past = millis(); switch (state) { case false: digitalWrite(pinLed, false); run = [] { if (millis() - past >= time) { stand(true); } }; break; case true: digitalWrite(pinLed, true); run = [] { if (millis() - past >= time) { stand(false); } }; break; } } //------------------------------- void setup() { pinMode(pinLed, OUTPUT); stand(false); } void loop() { run(); }Возьмем код отсюда https://www.drive2.ru/b/2867939/ и перепишем как следует. Итак исходник
//Управляем роботом с помощью BLUETOOTH. //В качестве управляющего устройства используем ANDROID устройство с программой Bluetooth RC Controller. // *********************** Установка выводов моторов ************************ int MotorLeftSpeed = 5; // Левый (А) мотор СКОРОСТЬ — ENA int MotorLeftForward = 4; // Левый (А) мотор ВПЕРЕД — IN1 int MotorLeftBack = 2; // Левый (А) мотор НАЗАД — IN2 int MotorRightForward = 8; // Правый (В) мотор ВПЕРЕД — IN3 int MotorRightBack = 7; // Правый (В) мотор НАЗАД — IN4 int MotorRightSpeed = 6; // Правый (В) мотор СКОРОСТЬ — ENB // ********************** Для управления по блютуз **************************** char btCommand = 'S';// Задаем переменную BLUETOOTH команда — по умолчанию "S" — СТОП // ********************************** SETUP *********************************** void setup () { Serial.begin (9600); //Включаем серйиный порт — скрость 9600 //****************** Настраиваем параметры выводов ARDUINO ****************** pinMode (MotorLeftForward, OUTPUT); pinMode (MotorLeftBack, OUTPUT); pinMode (MotorLeftSpeed, OUTPUT); pinMode (MotorRightForward, OUTPUT); pinMode (MotorRightBack, OUTPUT); pinMode (MotorRightSpeed, OUTPUT); } // ****************** Задаем основные направления движения ****************** void forward (int a) // ВПЕРЕД { digitalWrite (MotorRightBack, LOW); digitalWrite (MotorRightForward, HIGH); analogWrite (MotorRightSpeed, 150); digitalWrite (MotorLeftBack, LOW); digitalWrite (MotorLeftForward, HIGH); analogWrite (MotorLeftSpeed, 150); delay (a * 50); } void right (int b) // ПОВОРОТ ВПРАВО (одна сторона) { digitalWrite (MotorLeftBack, LOW); digitalWrite (MotorLeftForward, HIGH); analogWrite (MotorLeftSpeed, 200); digitalWrite (MotorRightBack, LOW); digitalWrite (MotorRightForward, LOW); delay (b * 50); } void left (int c) // ПОВОРОТ ВЛЕВО (одна сторона) { digitalWrite (MotorRightBack, LOW); digitalWrite (MotorRightForward, HIGH); analogWrite (MotorRightSpeed, 200); digitalWrite (MotorLeftBack, LOW); digitalWrite (MotorLeftForward, LOW); delay (c * 50); } void turnR (int d) // РАЗВОРОТ ВПРАВО (два стороны) { digitalWrite (MotorRightBack, HIGH); digitalWrite (MotorRightForward, LOW); digitalWrite (MotorLeftBack, LOW); digitalWrite (MotorLeftForward, HIGH); delay (d * 50); } void turnL (int e) // РАЗВОРОТ ВЛЕВО (два стороны) { digitalWrite (MotorRightBack, LOW); digitalWrite (MotorRightForward, HIGH); digitalWrite (MotorLeftBack, HIGH); digitalWrite (MotorLeftForward, LOW); delay (e * 50); } void stopp (int f) // СТОП { digitalWrite (MotorRightBack, LOW); digitalWrite (MotorRightForward, LOW); digitalWrite (MotorLeftBack, LOW); digitalWrite (MotorLeftForward, LOW); delay (f * 50); } void back (int g) // НАЗАД { digitalWrite (MotorRightBack, HIGH); digitalWrite (MotorRightForward, LOW); digitalWrite (MotorLeftBack, HIGH); digitalWrite (MotorLeftForward, LOW);; delay (g * 50); } // ********************* Управление по БЛЮТУЗ **************************** void loop() { if (Serial.available() > 0)// В случае если в "Серийном порту" есть информация { btCommand = Serial.read(); // Считываем инвормацию с "Серийного порта" Serial.println(btCommand); // Печатаем инвормацию с "Серийного порта" — необходио в слуае отладки программы switch (btCommand)// Отрабатывем в соотвествии с поступившей командой { case 'F': forward (3); // едем ВПЕРЕД break; case 'B': back (3); // едем НАЗАД break; case 'R': right (3); // поворачиваем ВПРАВО break; case 'L': left (3); // поворачиваем ВЛЕВО break; case 'S': stopp (3); // СТОП! } } }Честно - круто! А где Ваша программа? Или это задача на 100500 лет?
"Спрячем управление гусиницами в класс" и упростим управление ними
/**/ class Cl_Motor { protected : byte plusPin, minusPin, speedPin; public: Cl_Motor(byte f, byte b, byte s) : plusPin(f), minusPin(b), speedPin(s) {} void init() { pinMode(plusPin, OUTPUT); pinMode(minusPin, OUTPUT); stop(); } void forward(byte s = 150) { digitalWrite (plusPin, LOW); digitalWrite (minusPin, HIGH); analogWrite (speedPin, s); } void back(byte s = 150) { digitalWrite (plusPin, HIGH); digitalWrite (minusPin, LOW); analogWrite (speedPin, s); } void stop() { digitalWrite (plusPin, LOW); digitalWrite (minusPin, LOW); } }; //--------------------------- Cl_Motor Left (/*пин IN1*/4,/*пин IN2*/2,/*пин ENA*/5); Cl_Motor Right(/*пин IN3*/8,/*пин IN4*/7,/*пин ENB*/6); //-------------------------- void setup() { Left.init(); Right.init(); } void loop() { }И теперь вчерне накидаем код. В работе пока не проверял
/**/ class Cl_Motor { protected : byte plusPin, minusPin, speedPin; public: Cl_Motor(byte f, byte b, byte s) : plusPin(f), minusPin(b), speedPin(s) {} void init() { pinMode(plusPin, OUTPUT); pinMode(minusPin, OUTPUT); stop(); } void forward(byte s = 150) { digitalWrite (plusPin, LOW); digitalWrite (minusPin, HIGH); analogWrite (speedPin, s); } void back(byte s = 150) { digitalWrite (plusPin, HIGH); digitalWrite (minusPin, LOW); analogWrite (speedPin, s); } void stop() { digitalWrite (plusPin, LOW); digitalWrite (minusPin, LOW); } }; Cl_Motor Left (/*пин IN1*/4,/*пин IN2*/2,/*пин ENA*/5); Cl_Motor Right(/*пин IN3*/8,/*пин IN4*/7,/*пин ENB*/6); //------------------------------------------------ typedef void (*pDo)(); pDo run = [] {}; // состояния танка: стоп,вперед,назад,поворот налево,поворот направо,разворот по часовой,разворот против часовой enum state_t {sStop, sForward, sBackward, sLeft, sRight, sTurnLeft, sTurnRight} state; void stand(state_t s, unsigned long t = 100) { static unsigned long time; static unsigned long past; time = t; past = millis(); switch (s) { case sStop: Left.stop(); Right.stop(); run = [] {}; break; case sForward: Left.forward(); Right.forward(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sBackward: Left.back(); Right.back(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sLeft: Left.stop(); Right.forward(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sRight: Left.forward(); Right.stop(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sTurnLeft: Left.back(); Right.forward(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sTurnRight: Left.forward(); Right.back(); run = [] { if (millis() - past >= time)stand(sStop); }; break; } } //-------------------------- void setup() { Serial.begin (9600); //Включаем серйиный порт — скрость 9600 Left.init(); Right.init(); stand(sStop); } void loop() { run(); if (Serial.available() > 0)// В случае если в "Серийном порту" есть информация { char btCommand = Serial.read(); // Считываем инвормацию с "Серийного порта" Serial.println(btCommand); // Печатаем инвормацию с "Серийного порта" — необходио в слуае отладки программы switch (btCommand)// Отрабатывем в соотвествии с поступившей командой { case 'F': stand(sForward, 300);// едем ВПЕРЕД 0.3 сек break; case 'B': stand(sBackward, 300);//// едем НАЗАД 0.3 сек break; case 'R': stand( sRight, 300);//// поворачиваем ВПРАВО 0.3 сек break; case 'L': stand(sLeft, 300);//// поворачиваем ВЛЕВО 0.3 сек break; case 'S': stand(sStop);//// СТОП! } } }Отладил скетч. Теперь можно в сериал отправлять не только по одной команде а группу и танк будет отрабатывать их.
/**/ template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } class Cl_Motor { protected : byte plusPin, minusPin, speedPin; public: Cl_Motor(byte f, byte b, byte s) : plusPin(f), minusPin(b), speedPin(s) {} void init() { pinMode(plusPin, OUTPUT); pinMode(minusPin, OUTPUT); stop(); } void forward(byte s = 150) { digitalWrite (plusPin, LOW); digitalWrite (minusPin, HIGH); analogWrite (speedPin, s); } void back(byte s = 150) { digitalWrite (plusPin, HIGH); digitalWrite (minusPin, LOW); analogWrite (speedPin, s); } void stop() { digitalWrite (plusPin, LOW); digitalWrite (minusPin, LOW); } }; Cl_Motor Left (/*пин IN1*/4,/*пин IN2*/2,/*пин ENA*/5); Cl_Motor Right(/*пин IN3*/8,/*пин IN4*/7,/*пин ENB*/6); //------------------------------------------------ typedef void (*pDo)(); pDo run = [] {}; // состояния танка: стоп,вперед,назад,поворот налево,поворот направо,разворот по часовой,разворот против часовой enum state_t {sStop = 0, sForward, sBackward, sLeft, sRight, sTurnLeft, sTurnRight} state; void stand(state_t s, unsigned long t = 100) { static unsigned long time; static unsigned long past; state = s; time = t; past = millis(); switch (s) { case sStop: Serial << "sStop\n"; Left.stop(); Right.stop(); run = [] {}; break; case sForward: Serial << "sForward\n"; Left.forward(); Right.forward(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sBackward: Serial << "sBackward\n"; Left.back(); Right.back(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sLeft: Serial << "sLeft\n"; Left.stop(); Right.forward(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sRight: Serial << "sRight\n"; Left.forward(); Right.stop(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sTurnLeft: Serial << "sTurnLeft\n"; Left.back(); Right.forward(); run = [] { if (millis() - past >= time)stand(sStop); }; break; case sTurnRight: Serial << "sTurnRight\n"; Left.forward(); Right.back(); run = [] { if (millis() - past >= time)stand(sStop); }; break; } } //-------------------------- void setup() { Serial.begin (9600); //Включаем серйиный порт — скрость 9600 Left.init(); Right.init(); stand(sStop); } void loop() { run(); if (state == sStop) { if (Serial.available() > 0) // В случае если в "Серийном порту" есть информация { char btCommand = Serial.read(); // Считываем инвормацию с "Серийного порта" Serial << btCommand ; // Печатаем инвормацию с "Серийного порта" — необходио в слуае отладки программы switch (btCommand)// Отрабатывем в соотвествии с поступившей командой { case 'F': stand(sForward, 300);// едем ВПЕРЕД 0.3 сек break; case 'B': stand(sBackward, 300);// едем НАЗАД 0.3 сек break; case 'R': stand( sRight, 300);//поворачиваем ВПРАВО 0.3 сек break; case 'L': stand(sLeft, 300);// поворачиваем ВЛЕВО 0.3 сек break; case 'S': stand(sStop);// СТОП! } } } }Попробую показать как писать Меню на ардуине (разумеется по моей методике). Итак переменные.
/**/ //--переменые--------------------- byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; void setup() { } void loop() { }Переменные будут читаться с ЕЕPROM и меню туда записываться.
/**/ //--переменые--------------------- #include <EEPROM.h> byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; const int adr1 = 0, adr2 = 1, adr3 = 2, adr4 = 4; // void initVar() { var1 = EEPROM.read(adr1); if (var1 > maxVar1)var1 = maxVar1; if (var1 < minVar1)var1 = minVar1; EEPROM.update(adr1, var1); var2 = EEPROM.read(adr2); if (var2 > maxVar2)var2 = maxVar2; if (var2 < minVar2)var2 = minVar2; EEPROM.update(adr2, var2); var3 = EEPROM.read(adr3); if (var3 > maxVar3)var3 = maxVar3; if (var3 < minVar3)var3 = minVar3; EEPROM.update(adr3, var3); var4 = EEPROM.read(adr4); if (var4 > maxVar4)var4 = maxVar4; if (var4 < minVar4)var4 = minVar4; EEPROM.update(adr4, var4); } void setup() { initVar(); } void loop() { }Добавим первичную разметку экранов в Меню - экран для оперативного отображения информации . Обновление раз в 1 секунду. И 4 экрана для редактирования переменных которые были упомянуты ранее. И да если долго находится на экране редактирования, то автоматический возврат на главный экран.
/**/ //--переменые--------------------- #include <EEPROM.h> byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; const int adr1 = 0, adr2 = 1, adr3 = 2, adr4 = 4; // void initVar() { var1 = EEPROM.read(adr1); if (var1 > maxVar1)var1 = maxVar1; if (var1 < minVar1)var1 = minVar1; EEPROM.update(adr1, var1); var2 = EEPROM.read(adr2); if (var2 > maxVar2)var2 = maxVar2; if (var2 < minVar2)var2 = minVar2; EEPROM.update(adr2, var2); var3 = EEPROM.read(adr3); if (var3 > maxVar3)var3 = maxVar3; if (var3 < minVar3)var3 = minVar3; EEPROM.update(adr3, var3); var4 = EEPROM.read(adr4); if (var4 > maxVar4)var4 = maxVar4; if (var4 < minVar4)var4 = minVar4; EEPROM.update(adr4, var4); } //--меню-------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte strMain = 0; // главный экран const byte str1 = 1; // 1-й экран const byte str2 = 2; // 2-й экран const byte str3 = 3; // 3-й экран const byte str4 = 4; // 4-й экран byte pos; unsigned long pastMenu; void setPos(byte p) { pastMenu = millis(); pos = p; switch (p) { case strMain: Serial << "Main screen\n"; break; case str1: Serial << "1 screen\n"; break; case str2: Serial << "2 screen\n"; break; case str3: Serial << "3 screen\n"; break; case str4: Serial << "4 screen\n"; break; } } void runMenu() { switch (pos) { case strMain: if (millis() - pastMenu >= 1000) setPos(strMain); break; default: if (millis() - pastMenu >= 10000) setPos(strMain); } } //--main------------------------------ void setup() { Serial.begin(9600); initVar(); setPos(strMain); } void loop() { runMenu(); }Добавим в меню кнопки.Пока две для переключения между экранами.
/**/ //--переменые--------------------- #include <EEPROM.h> byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; const int adr1 = 0, adr2 = 1, adr3 = 2, adr4 = 4; // void initVar() { var1 = EEPROM.read(adr1); if (var1 > maxVar1)var1 = maxVar1; if (var1 < minVar1)var1 = minVar1; EEPROM.update(adr1, var1); var2 = EEPROM.read(adr2); if (var2 > maxVar2)var2 = maxVar2; if (var2 < minVar2)var2 = minVar2; EEPROM.update(adr2, var2); var3 = EEPROM.read(adr3); if (var3 > maxVar3)var3 = maxVar3; if (var3 < minVar3)var3 = minVar3; EEPROM.update(adr3, var3); var4 = EEPROM.read(adr4); if (var4 > maxVar4)var4 = maxVar4; if (var4 < minVar4)var4 = minVar4; EEPROM.update(adr4, var4); } //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); switch (s) { case false: break; case true: Do(); break; } } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; const int numBtn = 2; Cl_btn Btn[numBtn] = { Cl_btn(/*пин*/2), //кнопка верх Cl_btn(/*пин*/3) //кнопка вниз }; //--меню-------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte strMain = 0; // главный экран const byte str1 = 1; // 1-й экран const byte str2 = 2; // 2-й экран const byte str3 = 3; // 3-й экран const byte str4 = 4; // 4-й экран byte pos; unsigned long pastMenu; void setPos(byte p) { pastMenu = millis(); pos = p; switch (p) { case strMain: Serial << F("Main screen\n"); Serial << F("var1:") << var1 << F(" "); Serial << F("var2:") << var2 << F("\n"); Serial << F("var3:") << var3 << F(" "); Serial << F("var4:") << var4 << F("\n"); Btn[0].Do = [] {}; //кнопка верх Btn[1].Do = [] {setPos(str1);}; //кнопка вниз break; case str1: Serial << F("1 screen\n"); Serial << F("var1:") << var1 << F("\n"); Btn[0].Do = [] {setPos(strMain);}; //кнопка верх Btn[1].Do = [] {setPos(str2);}; //кнопка вниз break; case str2: Serial << F("2 screen\n"); Serial << F("var2:") << var2 << F("\n"); Btn[0].Do = [] {setPos(str1);}; //кнопка верх Btn[1].Do = [] {setPos(str3);}; //кнопка вниз break; case str3: Serial << F("3 screen\n"); Serial << F("var3:") << var3 << F("\n"); Btn[0].Do = [] {setPos(str2);}; //кнопка верх Btn[1].Do = [] {setPos(str4);}; //кнопка вниз break; case str4: Serial << F("4 screen\n"); Serial << F("var4:") << var4 << F("\n"); Btn[0].Do = [] {setPos(str3);}; //кнопка верх Btn[1].Do = [] {}; //кнопка вниз break; } } void runMenu() { switch (pos) { case strMain: // если находимся на главном экране то обновлять каждую секунду if (millis() - pastMenu >= 1000) setPos(strMain); break; default: // если пользователь не нажимает на кнопки, то вернуться на главный экран if (millis() - pastMenu >= 10000) setPos(strMain); } } //--main------------------------------ void setup() { Serial.begin(9600); initVar(); setPos(strMain); for (int i = 0; i < numBtn; i++)Btn[i].init(); } void loop() { runMenu(); for (int i = 0; i < numBtn; i++)Btn[i].run(); }Добавим в Меню сенсоры. К примеру потенциометры на пины A0 и А1 для имитации работы внешних датчиков.
/**/ //--переменые--------------------- #include <EEPROM.h> byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; const int adr1 = 0, adr2 = 1, adr3 = 2, adr4 = 4; // void initVar() { var1 = EEPROM.read(adr1); if (var1 > maxVar1)var1 = maxVar1; if (var1 < minVar1)var1 = minVar1; EEPROM.update(adr1, var1); var2 = EEPROM.read(adr2); if (var2 > maxVar2)var2 = maxVar2; if (var2 < minVar2)var2 = minVar2; EEPROM.update(adr2, var2); var3 = EEPROM.read(adr3); if (var3 > maxVar3)var3 = maxVar3; if (var3 < minVar3)var3 = minVar3; EEPROM.update(adr3, var3); var4 = EEPROM.read(adr4); if (var4 > maxVar4)var4 = maxVar4; if (var4 < minVar4)var4 = minVar4; EEPROM.update(adr4, var4); } //---------сенсор---------------------------- class Cl_sens { protected: const byte pin; byte val; unsigned long past; public: Cl_sens(byte p): pin(p) {} void init() { pinMode(pin, INPUT); val = analogRead(pin) / 16; past = millis(); } void run() { if (millis() - past >= 500) { val = analogRead(pin) / 16; past = millis(); } } byte read() { return val; } }; const int numSens = 2; Cl_sens Sens[numSens] = { Cl_sens(/*пин*/A0), //сенсор 1 Cl_sens(/*пин*/A1), //сенсор 2 }; //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); switch (s) { case false: break; case true: Do(); break; } } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; const int numBtn = 2; Cl_btn Btn[numBtn] = { Cl_btn(/*пин*/2), //кнопка верх Cl_btn(/*пин*/3) //кнопка вниз }; //--меню-------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte strMain = 0; // главный экран const byte str1 = 1; // 1-й экран const byte str2 = 2; // 2-й экран const byte str3 = 3; // 3-й экран const byte str4 = 4; // 4-й экран byte pos; unsigned long pastMenu; void setPos(byte p) { pastMenu = millis(); pos = p; switch (p) { case strMain: Serial << F("Main screen\n"); Serial << F("Sens1:") << Sens[0].read() << F("\n"); Serial << F("Sens2:") << Sens[1].read() << F("\n"); Btn[0].Do = [] {}; //кнопка верх Btn[1].Do = [] {setPos(str1);}; //кнопка вниз break; case str1: Serial << F("1 screen\n"); Serial << F("var1:") << var1 << F("\n"); Btn[0].Do = [] {setPos(strMain);}; //кнопка верх Btn[1].Do = [] {setPos(str2);}; //кнопка вниз break; case str2: Serial << F("2 screen\n"); Serial << F("var2:") << var2 << F("\n"); Btn[0].Do = [] {setPos(str1);}; //кнопка верх Btn[1].Do = [] {setPos(str3);}; //кнопка вниз break; case str3: Serial << F("3 screen\n"); Serial << F("var3:") << var3 << F("\n"); Btn[0].Do = [] {setPos(str2);}; //кнопка верх Btn[1].Do = [] {setPos(str4);}; //кнопка вниз break; case str4: Serial << F("4 screen\n"); Serial << F("var4:") << var4 << F("\n"); Btn[0].Do = [] {setPos(str3);}; //кнопка верх Btn[1].Do = [] {}; //кнопка вниз break; } } void runMenu() { switch (pos) { case strMain: // если находимся на главном экране то обновлять каждую секунду if (millis() - pastMenu >= 1000) setPos(strMain); break; default: // если пользователь не нажимает на кнопки, то вернуться на главный экран if (millis() - pastMenu >= 10000) setPos(strMain); } } //--main------------------------------ void setup() { Serial.begin(9600); initVar(); for (int i = 0; i < numSens; i++)Sens[i].init(); for (int i = 0; i < numBtn; i++)Btn[i].init(); setPos(strMain); } void loop() { for (int i = 0; i < numSens; i++)Sens[i].run(); for (int i = 0; i < numBtn; i++)Btn[i].run(); runMenu(); }Добавим кнопку Select и режим редактирования 4 переменных в Меню
/**/ //--переменые--------------------- #include <EEPROM.h> byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; const int adr1 = 0, adr2 = 1, adr3 = 2, adr4 = 4; // void initVar() { var1 = EEPROM.read(adr1); if (var1 > maxVar1)var1 = maxVar1; if (var1 < minVar1)var1 = minVar1; EEPROM.update(adr1, var1); var2 = EEPROM.read(adr2); if (var2 > maxVar2)var2 = maxVar2; if (var2 < minVar2)var2 = minVar2; EEPROM.update(adr2, var2); var3 = EEPROM.read(adr3); if (var3 > maxVar3)var3 = maxVar3; if (var3 < minVar3)var3 = minVar3; EEPROM.update(adr3, var3); var4 = EEPROM.read(adr4); if (var4 > maxVar4)var4 = maxVar4; if (var4 < minVar4)var4 = minVar4; EEPROM.update(adr4, var4); } //---------сенсор---------------------------- class Cl_sens { protected: const byte pin; byte val; unsigned long past; public: Cl_sens(byte p): pin(p) {} void init() { pinMode(pin, INPUT); val = analogRead(pin) / 16; past = millis(); } void run() { if (millis() - past >= 500) { val = analogRead(pin) / 16; past = millis(); } } byte read() { return val; } }; const int numSens = 2; Cl_sens Sens[numSens] = { Cl_sens(/*пин*/A0), //сенсор 1 Cl_sens(/*пин*/A1), //сенсор 2 }; //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); switch (s) { case false: break; case true: Do(); break; } } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; const int numBtn = 3; Cl_btn Btn[numBtn] = { Cl_btn(/*пин*/2), //кнопка верх Cl_btn(/*пин*/3), //кнопка вниз Cl_btn(/*пин*/4) //кнопка Select }; //--меню-------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte strMain = 0; // главный экран const byte str1 = 1; // 1-й экран const byte Estr1 = 2; // 1-й экран(редактирование) const byte str2 = 3; // 2-й экран const byte Estr2 = 4; // 2-й экран(редактирование) const byte str3 = 5; // 3-й экран const byte Estr3 = 6; // 3-й экран(редактирование) const byte str4 = 7; // 3-й экран const byte Estr4 = 8; // 3-й экран(редактирование) byte pos; byte editVar; unsigned long pastMenu; void setPos(byte p) { pastMenu = millis(); pos = p; switch (p) { case strMain: Serial << F("Main screen\n"); Serial << F("Sens1:") << Sens[0].read() << F("\n"); Serial << F("Sens2:") << Sens[1].read() << F("\n"); Btn[0].Do = [] {}; //кнопка верх Btn[1].Do = [] {setPos(str1);}; //кнопка вниз Btn[2].Do = [] {}; //кнопка Select break; case str1: Serial << F("1 screen\n"); Serial << F("var1:") << var1 << F("\n"); Btn[0].Do = [] {setPos(strMain);}; //кнопка верх Btn[1].Do = [] {setPos(str2);}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var1; setPos(Estr1); }; break; case Estr1: Serial << F("1 screen\n"); Serial << F("Edit var1:") << editVar << F("\n"); Btn[0].Do = [] { //кнопка верх if (editVar < maxVar1) editVar++; setPos(Estr1); }; Btn[1].Do = [] { //кнопка вниз if (editVar > minVar1) editVar--; setPos(Estr1); }; Btn[2].Do = [] { //кнопка Select EEPROM.write(adr1, editVar); var1 = editVar; setPos(str1); }; break; case str2: Serial << F("2 screen\n"); Serial << F("var2:") << var2 << F("\n"); Btn[0].Do = [] {setPos(str1);}; //кнопка верх Btn[1].Do = [] {setPos(str3);}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var2; setPos(Estr2); }; break; case Estr2: Serial << F("2 screen\n"); Serial << F("Edit var2:") << editVar << F("\n"); Btn[0].Do = [] { if (editVar < maxVar2) editVar++; setPos(Estr2); }; //кнопка верх Btn[1].Do = [] { if (editVar > minVar2) editVar--; setPos(Estr2); }; //кнопка вниз Btn[2].Do = [] { //кнопка Select EEPROM.write(adr2, editVar); var2 = editVar; setPos(str2); }; break; case str3: Serial << F("3 screen\n"); Serial << F("var3:") << var3 << F("\n"); Btn[0].Do = [] {setPos(str2);}; //кнопка верх Btn[1].Do = [] {setPos(str4);}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var3; setPos(Estr3); }; break; case Estr3: Serial << F("3 screen\n"); Serial << F("Edit var3:") << editVar << F("\n"); Btn[0].Do = [] { //кнопка верх if (editVar < maxVar3) editVar++; setPos(Estr3); }; Btn[1].Do = [] { //кнопка вниз if (editVar > minVar3) editVar--; setPos(Estr3); }; Btn[2].Do = [] { //кнопка Select EEPROM.write(adr3, editVar); var3 = editVar; setPos(str3); }; break; case str4: Serial << F("4 screen\n"); Serial << F("var4:") << var4 << F("\n"); Btn[0].Do = [] {setPos(str3);}; //кнопка верх Btn[1].Do = [] {}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var4; setPos(Estr4); }; break; case Estr4: Serial << F("4 screen\n"); Serial << F("Edit var3:") << editVar << F("\n"); Btn[0].Do = [] { //кнопка верх if (editVar < maxVar4) editVar++; setPos(Estr3); }; Btn[1].Do = [] { //кнопка вниз if (editVar > minVar4) editVar--; setPos(Estr3); }; Btn[2].Do = [] { //кнопка Select EEPROM.write(adr4, editVar); var4 = editVar; setPos(str4); }; break; } } void runMenu() { switch (pos) { case strMain: // если находимся на главном экране то обновлять каждую секунду if (millis() - pastMenu >= 1000) setPos(strMain); break; default: // если пользователь не нажимает на кнопки, то вернуться на главный экран if (millis() - pastMenu >= 10000) setPos(strMain); } } //--main------------------------------ void setup() { Serial.begin(9600); initVar(); for (int i = 0; i < numSens; i++)Sens[i].init(); for (int i = 0; i < numBtn; i++)Btn[i].init(); setPos(strMain); } void loop() { for (int i = 0; i < numSens; i++)Sens[i].run(); for (int i = 0; i < numBtn; i++)Btn[i].run(); runMenu(); }Теперь сделаем вывод на экран LCD1602 I2C . Кнопка Select на Главном экране вкл/выкл подсветку экрана
/**/ //--переменые--------------------- #include <EEPROM.h> byte var1, var2, var3, var4; //<- эти переменные const byte maxVar1 = 50, maxVar2 = 50, maxVar3 = 50, maxVar4 = 50; const byte minVar1 = 10, minVar2 = 10, minVar3 = 10, minVar4 = 40; const int adr1 = 0, adr2 = 1, adr3 = 2, adr4 = 4; // void initVar() { var1 = EEPROM.read(adr1); if (var1 > maxVar1)var1 = maxVar1; if (var1 < minVar1)var1 = minVar1; EEPROM.update(adr1, var1); var2 = EEPROM.read(adr2); if (var2 > maxVar2)var2 = maxVar2; if (var2 < minVar2)var2 = minVar2; EEPROM.update(adr2, var2); var3 = EEPROM.read(adr3); if (var3 > maxVar3)var3 = maxVar3; if (var3 < minVar3)var3 = minVar3; EEPROM.update(adr3, var3); var4 = EEPROM.read(adr4); if (var4 > maxVar4)var4 = maxVar4; if (var4 < minVar4)var4 = minVar4; EEPROM.update(adr4, var4); } //---------сенсор---------------------------- class Cl_sens { protected: const byte pin; byte val; unsigned long past; public: Cl_sens(byte p): pin(p) {} void init() { pinMode(pin, INPUT); val = analogRead(pin) / 16; past = millis(); } void run() { if (millis() - past >= 500) { val = analogRead(pin) / 16; past = millis(); } } byte read() { return val; } }; const int numSens = 2; Cl_sens Sens[numSens] = { Cl_sens(/*пин*/A0), //сенсор 1 Cl_sens(/*пин*/A1), //сенсор 2 }; //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); switch (s) { case false: break; case true: Do(); break; } } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; const int numBtn = 3; Cl_btn Btn[numBtn] = { Cl_btn(/*пин*/2), //кнопка верх Cl_btn(/*пин*/3), //кнопка вниз Cl_btn(/*пин*/4) //кнопка Select }; //--LCD1602----------------------------- #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); bool backlightState = 0; void invertBL() { if (!backlightState) { backlightState = 1; lcd.backlight();// Включаем подсветку дисплея } else { backlightState = 0; lcd.noBacklight();// Выключаем подсветку дисплея } } //--меню-------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte strMain = 0; // главный экран const byte str1 = 1; // 1-й экран const byte Estr1 = 2; // 1-й экран(редактирование) const byte str2 = 3; // 2-й экран const byte Estr2 = 4; // 2-й экран(редактирование) const byte str3 = 5; // 3-й экран const byte Estr3 = 6; // 3-й экран(редактирование) const byte str4 = 7; // 4-й экран const byte Estr4 = 8; // 4-й экран(редактирование) byte pos; byte editVar; unsigned long pastMenu; void setPos(byte p) { pastMenu = millis(); pos = p; switch (p) { case strMain: lcd.clear(); lcd << F("T1:") << Sens[0].read() << F(" "); lcd.setCursor(0, 1); lcd << F("T2:") << Sens[1].read() << F(" "); Btn[0].Do = [] {}; //кнопка верх Btn[1].Do = [] {setPos(str1);}; //кнопка вниз Btn[2].Do = [] {invertBL();}; //кнопка Select break; case str1: lcd.clear(); lcd << F("var1=") << var1 << F(" "); Btn[0].Do = [] {setPos(strMain);}; //кнопка верх Btn[1].Do = [] {setPos(str2);}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var1; setPos(Estr1); }; break; case Estr1: lcd.clear(); lcd << F("var1-") << editVar << F(" "); Btn[0].Do = [] { //кнопка верх if (editVar < maxVar1) editVar++; setPos(Estr1); }; Btn[1].Do = [] { //кнопка вниз if (editVar > minVar1) editVar--; setPos(Estr1); }; Btn[2].Do = [] { //кнопка Select EEPROM.write(adr1, editVar); var1 = editVar; setPos(str1); }; break; case str2: lcd.clear(); lcd << F("var2=") << var2 << F(" "); Btn[0].Do = [] {setPos(str1);}; //кнопка верх Btn[1].Do = [] {setPos(str3);}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var2; setPos(Estr2); }; break; case Estr2: lcd.clear(); lcd << F("var2-") << editVar << F(" "); Btn[0].Do = [] { if (editVar < maxVar2) editVar++; setPos(Estr2); }; //кнопка верх Btn[1].Do = [] { if (editVar > minVar2) editVar--; setPos(Estr2); }; //кнопка вниз Btn[2].Do = [] { //кнопка Select EEPROM.write(adr2, editVar); var2 = editVar; setPos(str2); }; break; case str3: lcd.clear(); lcd << F("var3=") << var3 << F(" "); Btn[0].Do = [] {setPos(str2);}; //кнопка верх Btn[1].Do = [] {setPos(str4);}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var3; setPos(Estr3); }; break; case Estr3: lcd.clear(); lcd << F("var3-") << editVar << F(" "); Btn[0].Do = [] { //кнопка верх if (editVar < maxVar3) editVar++; setPos(Estr3); }; Btn[1].Do = [] { //кнопка вниз if (editVar > minVar3) editVar--; setPos(Estr3); }; Btn[2].Do = [] { //кнопка Select EEPROM.write(adr3, editVar); var3 = editVar; setPos(str3); }; break; case str4: lcd.clear(); lcd << F("var4=") << var4 << F(" "); Btn[0].Do = [] {setPos(str3);}; //кнопка верх Btn[1].Do = [] {}; //кнопка вниз Btn[2].Do = [] { //кнопка Select editVar = var4; setPos(Estr4); }; break; case Estr4: lcd.clear(); lcd << F("var4-") << editVar << F(" "); Btn[0].Do = [] { //кнопка верх if (editVar < maxVar4) editVar++; setPos(Estr3); }; Btn[1].Do = [] { //кнопка вниз if (editVar > minVar4) editVar--; setPos(Estr4); }; Btn[2].Do = [] { //кнопка Select EEPROM.write(adr4, editVar); var4 = editVar; setPos(str4); }; break; } } void runMenu() { switch (pos) { case strMain: // если находимся на главном экране то обновлять каждую секунду if (millis() - pastMenu >= 1000) setPos(strMain); break; default: // если пользователь не нажимает на кнопки, то вернуться на главный экран if (millis() - pastMenu >= 10000) setPos(strMain); } } //--main------------------------------ void setup() { //Serial.begin(9600); initVar(); for (int i = 0; i < numSens; i++)Sens[i].init(); for (int i = 0; i < numBtn; i++)Btn[i].init(); lcd.init(); invertBL(); setPos(strMain); } void loop() { for (int i = 0; i < numSens; i++)Sens[i].run(); for (int i = 0; i < numBtn; i++)Btn[i].run(); runMenu(); }