Посмотрите/Оцените/Покритикуйте/Похвалите (класс для светодиода)
- Войдите на сайт для отправки комментариев
Пт, 04/01/2019 - 18:59
Доброго времени суток и с наступившим НГ. Решил делать свой первый самостоятельный проект на ардуинке. Среди прочего потребуется "помигать светодиодами" (почему бы и нет :) ). По классике в первой версии delay()... Но потом понял что это не хорошо, да и неудобно. Погуглил, но ничего особо не нашел (да и не особо старался, если честно). Попробовал написать класс самостоятельно. Оно вроде работает, но все же прошу оценить/подсказать, ежели что не так.
И на сколько такой класс может быть востребован? Есть идея доделать его до уровня библиотеки. Стоит ли продолжать, так сказать.
class LED {
public:
LED(byte ledPin = LED_BUILTIN);
void ON();
void OFF();
void autoBlink(bool state, word delayON = 1000, word delayOFF = 1000);
bool autoBlink() {return _autoBlink; };
bool state() { return _state; } ;
void update();
private:
byte _ledPin;
bool _state = false;
bool _autoBlink = false;
word _delayON, _delayOFF;
unsigned long interval, cMillis, pMillis;
};
LED::LED(byte ledPin) {
_ledPin = ledPin;
pinMode(ledPin, OUTPUT);
}
void LED::ON() {
_state = true;
digitalWrite(_ledPin, HIGH);
}
void LED::OFF() {
_state = false;
digitalWrite(_ledPin, LOW);
}
void LED::autoBlink(bool state, word delayON, word delayOFF) {
_autoBlink = state;
_delayON = delayON;
_delayOFF = delayOFF;
if (state)
pMillis = millis();
}
void LED::update() {
if (_autoBlink) {
cMillis = millis();
interval = cMillis - pMillis;
interval = abs(interval);
if (!_state && interval >= _delayOFF) {
pMillis = cMillis;
ON();
}
else if (_state && interval >= _delayON) {
pMillis = cMillis;
OFF();
}
}
}
LED led;
void setup() {
led.autoBlink(true, 100, 2000);
}
void loop() {
led.update();
}
Ах да, забыл написать, что можно делать. Можно включить светодиод. Можно выключить светодиод. Можно включить автоматическое моргание с заданными интервалами горения и не горения...
Осталось вам освоить автоматное программирование и можете писать программы посложнее.
Ну и так далее http://arduino.ru/forum/programmirovanie/klassy-arduino-po-qwone-dlya-chainikov
Осталось вам освоить автоматное программирование и можете писать программы посложнее.
Ну я нутром чуял, что велосипед изобретаю :) Ну зато потренировался. Я так понимаю из конструктивной критики у меня только setup() и loop()? или еще что то?
Ну я нутром чуял, что велосипед изобретаю :)
Велосипед уже есть, титановый :-)
Евжений, а что должна сделать функция abs() с аргументом типа unsigned long? Что-то Вы там с миллис намудрили по-моему.
Ну я нутром чуял, что велосипед изобретаю :) Ну зато потренировался. Я так понимаю из конструктивной критики у меня только setup() и loop()? или еще что то?
Ну, для начала, очень неплохо. Разве что строчка N48 лишняя. Но на библиотеку, пока, не тянет. Начинайте использовать в своих проектах. По мере добавления функционала, может и дорастет до библиотеки.
Ну я нутром чуял, что велосипед изобретаю :) Ну зато потренировался. Я так понимаю из конструктивной критики у меня только setup() и loop()? или еще что то?
Евжений, сколько Вам лет?
Это я к тому, чтобы понять как с Вами разговаривать.
Ну да, с abs и unsigned я погорячился :) Спасибо.
Лет мне 40. Программирую я давно. Вот с ардуино у меня "первая любовь"... Да и c++ не мой фаворит :)
Рассматривайте мою тему не как эталон, или "классику". Просто я раньше вас начал копать в этом направлении. И эта тема это "часть моих изысканий" . И если бы я сейчас писал этот класс, то писал так.
/**/ class Cl_Led { protected: byte pin; unsigned long past, time; enum state_t {sOFF = 0, sON, sOFF_Blink, sON_Blink} state; void stand(state_t s) { state = s; past = millis(); switch (state) { case sOFF: digitalWrite(pin, LOW); break; case sON: digitalWrite(pin, HIGH); break; case sOFF_Blink: digitalWrite(pin, LOW); break; case sON_Blink: digitalWrite(pin, HIGH); break; } } public: Cl_Led(byte p): pin(p) {} void init() { pinMode(pin, OUTPUT); stand(sOFF); } void run() { switch (state) { case sOFF: break; case sON: break; case sOFF_Blink: if (millis() - past >= time) stand(sON_Blink); break; case sON_Blink: if (millis() - past >= time) stand(sOFF_Blink); break; } } void ON() { stand(sON); } void OFF() { stand(sOFF); } void blink(unsigned long t = 1000) { time = t; stand(sON_Blink); } }; //-----компоновка--------------------------- Cl_Led Led(/*пин*/13); //-----main()--------------------------- void setup() { Led.init(); Led.blink(200); } void loop() { Led.run(); } /**/А конструктивно это или нет решайте сами.
Ну, если давно программируете, то ... держите :))) То что подростку списалось бы ...
Про abs не буду ибо уже боян. А про остальное, поехали...
Давайте разбираться почему
Про дальнейшую экономию я говорить не буду, т.к. это уже паранойя, хотя там есть ещё куда расти, но то, о чём написано выше – абсолютно ничем не оправданный расход памяти
1. Про память - согласен полностью... Не поспоришь :)
2. а вот тут поспорю. Тут конечно и ваша правда есть, но мне ничего не мешает написать так:
Переопределить параметры "моргания", а в следующий раз просто вызвать led.autoBlink(true); Что как бы и не запрещено. Параметр есть - надо отработать.
3. На сколько я проникса классами - в привате описываются внутренние параметры, которые не должны переопределяться из-вне, так сказать. Я уже понял, что на захват мира не тянет. А для разового проэкта - даже слишком хорошо.
з.ы. А вообще я разочаровался :( Не не в ардуинке... В результате который я получил. Раньше у меня был delay()... ну фиг с ним 2-3 раза, что замедляло скорость работы проекта, но на функционал не влияло. Сейчас в погоне за красивым кодом запилил класс, который не замедляет выполнение, что привело к тому, что работать задумка перестала. В итоге ее надо теперь замедлить принудительно. Если писать delay () - то перестает работать корректно моргание. т.е. надо теперь хитрую "замедлялку"... опять небось класс. в итоге проэкт в котором всего было 50 строк разматывается в нечто очень большое и ужасное.
Что бы не было лишних вопросов - что поломалось: фоторезистор снимает показания. маленькие колебания мне не нужны и плавное изменение в течении длительного времени (аля восход и закат солнца), а надо было отследить именно резкое изменение освещенности - включение/выключение света. А лампа с которой я тут эксперементирую с диммером. И получается, что пока я кручу диммер показания плавно растут... и фильтруются.
qwone, дайте ссылку уже, где почитать. Гуглил, но хотелось бы прям шоб носом ткнули :)
2. а вот тут поспорю. Тут конечно и ваша правда есть, но мне ничего не мешает написать так:
Переопределить параметры "моргания", а в следующий раз просто вызвать led.autoBlink(true); Что как бы и не запрещено. Параметр есть - надо отработать.
напрасно спорите. Вы не до конца разобрались с языком. Вызов led.autoBlink(false); абсолютно идентичен вызову led.autoBlink(false, 1000, 1000); Нет никакой разницы от слова совсем.
Так что, поверьте, здесь спорить неочем. Если Вы не согласны - перечитайте описание языка и читайте пока не поймёте. Эти два присваивания у Вас выполнятся ВСЕГДА, т.е. при выключении они - лишние.
3. На сколько я проникса классами - в привате описываются внутренние параметры, которые не должны переопределяться из-вне, так сказать.
Почитайте ещё и про protected и попытйтесь понять разницу.
Сейчас в погоне за красивым кодом запилил класс, который не замедляет выполнение, что привело к тому, что работать задумка перестала. В итоге ее надо теперь замедлить принудительно. Если писать delay () - то перестает работать корректно моргание. т.е. надо теперь хитрую "замедлялку"... опять небось класс. в итоге проэкт в котором всего было 50 строк разматывается в нечто очень большое и ужасное.
Что бы не было лишних вопросов - что поломалось: фоторезистор снимает показания. маленькие колебания мне не нужны и плавное изменение в течении длительного времени (аля восход и закат солнца), а надо было отследить именно резкое изменение освещенности - включение/выключение света. А лампа с которой я тут эксперементирую с диммером. И получается, что пока я кручу диммер показания плавно растут... и фильтруются.
Ничего не понял, но если Вам нужно фильтровать значения, так фильруйте, а не замедляйте программу. Напишите фильтр и пусть фильтрует :)
напрасно спорите. Вы не до конца разобрались с языком. Вызов led.autoBlink(false); абсолютно идентичен вызову led.autoBlink(false, 1000, 1000); Нет никакой разницы от слова совсем.
Так что, поверьте, здесь спорить неочем. Если Вы не согласны - перечитайте описание языка и читайте пока не поймёте. Эти два присваивания у Вас выполнятся ВСЕГДА, т.е. при выключении они - лишние.
Ничего не понял, но если Вам нужно фильтровать значения, так фильруйте, а не замедляйте программу. Напишите фильтр и пусть фильтрует :)
Так вот в том то и дело. Было кода мало - все работало. Дописал еще чуть-чуть... теперь надо еще дописывать... Хотя блин все уже работало и без класса. Вот в чем спич.
qwone, дайте ссылку уже, где почитать. Гуглил, но хотелось бы прям шоб носом ткнули :)
Но скорее Вам надо что-то виде автомата Пуха. Но такой темы нет. Не написал. Так что почитайте это #27 и #26
Может поможет. Да и не пугайтесь объема кода и текста исходника . Главное что бы вы в этом объеме не потерялись.
Мне вот интересно, неужели никто из кодописателей никогда не управлял яркостью светодиода посредством ШИМ?
Или никогда не подключал светодиоды через 595 регистр?
Если для включения/выключения светодиода всегда используется digitalWrite(), то класс здесь совершенно не нужен, т.к. явно избыточен. А вот если необходимо единообразно работать с двумя-тремя сотнями светодиодов, часть из которых подключены непосредственно к Ардуине, а часть - к регистрам (или внешнему ШИМ-контроллеру), то ради такой унификации может иметь смысл и написать класс.
Должен же класс иметь хоть какой-то практический смысл кроме чисто учебного.
и еще, тут никто не сказал, счас у тебя светодиод зажигается высоким уровнем, а это не всегда так. Иногда устройство, а светодиод это именно исполнительное устройство, управляется нулем, а не единицей. Как это отразить в классе?
Каким боком? У Вас есть метод без параметров? Нет. А значит при следующем включении Вы снова передадите параметры, и они снова будут присваиваться, а Ваше "заранешнее определение" будет выброшено на помойку. Если Вы хотите передавать для следующего, то это надо было так и писать.
Так вот в том то и дело. Было кода мало - все работало. Дописал еще чуть-чуть... теперь надо еще дописывать... Хотя блин все уже работало и без класса. Вот в чем спич.
Если использовать классы там, где они не нужны и так, что они перестают быть объктами ООП, то от них никогда толку не бывает. Это тот самый случай. Очень удачный пример.
Спасибо за оценку. Постараюсь все учесть.
qwone, автомат получился у меня? Или так же как и класс (я его подправил. попытку на "универсальность" выкинул - он под конкретную задачу теперь)... На сколько я понял - нам надо пройти по цепочке - никуда не сворачивая, вополняя каждое действие (исключения возможны конечно, но не в данном случае). Итак, автоматический освежитель воздуха. Ну да... захотелось :) Ждем включения света, ждем выключения света, пшикаем, пауза (1 минута - что бы ребенок щелкающий светом не устроил газовую камеру). После паузы - опять ждем включения света. Вроде можно просто сразу ждать выключения... Но мне с включением больше нравится.
#define diffLight 200 #define pinLight A0 unsigned long pMillis = 0; int lastLight = - 1; bool activateFresh = false; bool pause = false; enum actions { act_WAITON, act_WAITOFF, act_FRESH, act_PAUSE } state; class LED { protected: bool blink = false; word delayON, delayOFF; public: LED() { pinMode(LED_BUILTIN, OUTPUT); }; bool state() { return digitalRead(LED_BUILTIN); }; void ON (bool disableBlink = true) { if (disableBlink) LED::blink = false; digitalWrite(LED_BUILTIN, HIGH); }; void OFF(bool disableBlink = true) { if (disableBlink) LED::blink = false; digitalWrite(LED_BUILTIN, LOW); }; void autoBlink(word delayON = 100, word delayOFF = 2000) { if (!blink) { LED::blink = true; LED::delayON = delayON; LED::delayOFF = delayOFF; } }; void update() { if ( LED::blink ) { if (!LED::state() && millis() - LED::pMillis >= LED::delayOFF ) { LED::pMillis = millis(); LED::ON (false); } if ( LED::state() && millis() - LED::pMillis >= LED::delayON ) { LED::pMillis = millis(); LED::OFF(false); } } }; private: unsigned long pMillis; }; LED led; void setup() { Serial.begin(9600); } void loop() { led.update(); switch (state) { case act_WAITON: led.autoBlink(); if (millis() - pMillis >= 3000) { pMillis = millis(); if (lightON()) state = act_WAITOFF; } break; case act_WAITOFF: led.ON(); if (millis() - pMillis >= 3000) { pMillis = millis(); if (lightOFF()) state = act_FRESH; } break; case act_FRESH: state = act_PAUSE; fresh(); break; case act_PAUSE: led.OFF(); if (millis() - pMillis >= 60000) state = act_WAITON; break; default: pMillis = millis(); state = act_WAITON; } } bool lightON() { int diff = difference(); if (diff > 0 && diff > diffLight) return true; else return false; } bool lightOFF() { int diff = difference(); if (diff < 0 && -diff > diffLight) return true; else return false; } // Вык - Вкл + int difference() { int result; int currentLight = analogRead(pinLight); if ( lastLight < 0 ) result = 0; else result = currentLight - lastLight; Serial.print("result: "); Serial.print(result); Serial.print(" = "); Serial.print(lastLight); Serial.print(" - "); Serial.println(currentLight); lastLight = currentLight; return result; } void fresh() { Serial.println(); Serial.println("!!! FRESH !!!"); Serial.println(); }Евжений, зря Вы выбросили метод stand(). Эта функция очень сильно упрощает код и читаемость текста. Имеено с нее я и перешел на автоматное программирование в среде Ардуино.
Для пшикалки надо 3 состояния Ready,ON,Wait - готовность , пшик, и пауза блокирования. А а внутри метода inject - вспрыск проверять состояние Ready и stand(ON) если автомат в этом состоянии и газововой атаки нет.
А это, вообще, что? :)))
Может, нормально написать?
Метод класса bool state() не изменяет данные класса, по этому должен быть декларирован как bool state() const;
иначе использовать этот метод в константном методе другого класса не получится (если не указан -fpermissive).
Для отладочных плат на ядре авр в платформ.тхт любят злоупотреблять этой опцией, и когда с авра переключаемся на другую плату, например на esp8266 неожиданно начинаем получать ошибку
class A { public: bool my_led_state() const { return this->led.state(); } LED led; }; /opt/arduino-1.8.7/hardware/esp8266com/esp8266/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-g++ -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -I/opt/arduino-1.8.7/hardware/esp8266com/esp8266/tools/sdk/include -I/opt/arduino-1.8.7/hardware/esp8266com/esp8266/tools/sdk/lwip2/include -I/opt/arduino-1.8.7/hardware/esp8266com/esp8266/tools/sdk/libc/xtensa-lx106-elf/include -I/tmp/arduino_build_760268/core -c -w -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections -DF_CPU=80000000L -DLWIP_OPEN_SRC -DTCP_MSS=536 -DARDUINO=10807 -DARDUINO_ESP8266_NODEMCU -DARDUINO_ARCH_ESP8266 "-DARDUINO_BOARD=\"ESP8266_NODEMCU\"" -DESP8266 -I/opt/arduino-1.8.7/hardware/esp8266com/esp8266/cores/esp8266 -I/opt/arduino-1.8.7/hardware/esp8266com/esp8266/variants/nodemcu /tmp/arduino_build_760268/sketch/sketch_jan06a.ino.cpp -o /tmp/arduino_build_760268/sketch/sketch_jan06a.ino.cpp.o /tmp/arduino_modified_sketch_643936/sketch_jan06a.ino: In member function 'bool A::my_led_state() const': sketch_jan06a:110:30: error: passing 'const LED' as 'this' argument of 'bool LED::state()' discards qualifiers [-fpermissive] return this->led.state(); ^ exit status 1 passing 'const LED' as 'this' argument of 'bool LED::state()' discards qualifiers [-fpermissive]И чего он (compiller) хочет от нас??? не давно же всё собиралось.
Метод класса stand() для внутренего использования внутри класса. Так что он всегда private или protected . Для внешнего всегда должны присутсвовать обертки. Так что const тут по желанию и не больше.
A вот state это свойство. Оно не должно быть константным . Потому что автомат.
Евжений, А вот структура скетча должна по моему мнению такой
/**/ class Cl_Fresher { protected: public: void init() {} void run() {} void fresh() {} }; //-------Компоновка------------- const int diffLight = 200; const byte LightPin = /*пин*/A0; enum state1_t {sNoLight, sLight} state1; Cl_Fresher Fresher; //------------------------- void setup() { state1 = sNoLight; Fresher.init(); } void loop() { int tmp = analogRead(LightPin); switch (state1) { case sNoLight: if (tmp <= diffLight) { state1 = sLight; Fresher.fresh(); } break; case sLight: if (tmp > diffLight+20) { state1 = sNoLight; } break; } Fresher.run(); }Метод класса stand() для внутренего использования внутри класса. Так что он всегда private или protected . Для внешнего всегда должны присутсвовать обертки. Так что const тут по желанию и не больше.
A вот state это свойство. Оно не должно быть константным . Потому что автомат.
а не о enum actions { act_WAITON, act_WAITOFF, act_FRESH, act_PAUSE } state;
В классе A инкапсулирован экземпляр класса LED и у этого экземпляра вызывается метод state() и вызов выполняется из константного метода класса A.
Тот скетч не мой. Так почему я не скажу. Вот мой скетч
/**/ class Cl_Fresher { protected: byte pin; unsigned long past; const unsigned long timeFresh = 1000; /*время пшика*/ const unsigned long timeWait = 2000; /*время паузы после пшика*/ enum state_t {sReady, sFresh, sWait} state; void stand(state_t s) { state = s; past = millis(); switch (state) { case sReady: break; case sFresh: digitalWrite(pin, HIGH); break; case sWait: digitalWrite(pin, LOW); break; } } public: Cl_Fresher(byte p): pin(p) {} void init() { pinMode(pin, OUTPUT); stand(sWait); } void run() { switch (state) { case sReady: break; case sFresh: if (millis() - past >= timeFresh)stand(sWait); break; case sWait: if (millis() - past >= timeWait)stand(sReady); break; } } void fresh() { if (state == sReady)stand(sFresh); } }; //-------Компоновка------------- const int diffLight = 200; const byte LightPin = /*пин*/A0; enum state1_t {sNoLight, sLight} state1; Cl_Fresher Fresher(/*пин*/13); //------------------------- void setup() { state1 = sNoLight; Fresher.init(); } void loop() { int tmp = analogRead(LightPin); switch (state1) { case sNoLight: if (tmp <= diffLight) { state1 = sLight; Fresher.fresh(); } break; case sLight: if (tmp > diffLight + 20) { state1 = sNoLight; } break; } Fresher.run(); }