В моем случае без разницы какие номера у входов и следуют ли они по порядку или нет.В случае переопределения входа
#define TOP_SWITCH 10
твой код перестанет работать, а моему фиолетово.
Angbor пишет:
а вот это не понял, можно поподробнее.
4 линии данных, которые идут к D4-D7 индикатора, можно использовать и для записи в регистр светодиодов. Пока ты не дергаешь выводы E, R/W, RS, индикатору все равно, что происходит на шине данных. Выдай на неё данные для светодиодов и используй clockPin или latchPin для записи в регистр светодиодов. У ардуины освободится еще пара ног.
Angbor пишет:
по сути приведеного тобой кода, IDE-шка ругается сильно
Опечатался, надо PROGMEM , а не PROGRAM. Смысл: разместить массив в памяти програм, а не памяти данных.
const int arr[NUM] PROGMEM={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
Спасибо, Andy, попозже еще раз попробую с твоим советом про массив пинов.
Про использование дисплея... Как-то не хочется все лепить в кучу. А в чем выгода такого варианта по сравнению с двумя регистрами?
В моем случае я могу еще добавить безболезненно 4 диода. Мне вобще понравились регистры :) Я думал использовать регистр и для поплавковых выключателей. Их сейчас 4, а так можно будет и до 8 использовать на разные нужды.
Ннапример у меня на трех-ходовом вентиле есть концевые выключатели, определяющие докрутился ли привод вентиля до упора или нет. И бывает, что если заряда в кондерах привода не хватает, то привод вентиль не докрутит. Также линия предварительной прокачки скважины с реле давления (чтобы в случае засора линии не пожечь насос). можно определять, не разомкнулось ли оно.
А сейчас вот думаю над куском кода, чтобы не ежемилисекундно тыркать
//=========
const long BLINK_DELAY = 200;
const long READ_PIN_DELAY = 100;
const long REGISTR_DELAY = 150;
unsigned long previousMillis = 0;
...
...
...
void loop() {
unsigned long currentMillis = millis();
int differences = currentMillis - previousMillis;
if (differences >= READ_PIN_DELAY/*100*/) {
pinReading();
}
if (differences >= REGISTR_DELAY /*150*/) {
regWork();
}
if (differences >= BLINK_DELAY /*200*/) {
blinkS();
previousMillis = currentMillis;
}
}
Немного смущает, что обновление происходит только один раз... Но городить под каждый случай отдельную переменную как-то тяжело выходит..
если посмотрите на картинку выше - подключил через регистр LCD и релешу - еле еле вобщем-то
В схеме присутствуют разного рода кривульки, по хорошему от них надо избавляться. Вообще, хотелось бы увидеть схему электрическую, по монтажной схеме мне не все понятно.
Angbor пишет:
Про использование дисплея... Как-то не хочется все лепить в кучу. А в чем выгода такого варианта по сравнению с двумя регистрами?
В моем случае я могу еще добавить безболезненно 4 диода. Мне вобще понравились регистры :)
Это как бы не лепка в кучу, а попытка структурировать схему и код. Взгляни на устройство компа: шина данных (и не только она) проходит по всем устройствам. Кроме того, запас по ногам надо иметь, любой проект имеет тенденцию к развитию.
В твоем случае светодиоды моргают при каждом сдвиге, пусть это не видно глазу, но это так. В моем случае ты так же можешь добавить еще регистр и светодиоды или реле или еще что нибудь.
Angbor пишет:
А сейчас вот думаю над куском кода, чтобы не ежемилисекундно тыркать
У тебя константы размещены в памяти данных, в этом нет необходимости. Сделай так:
я думал раньше, что определение типа define - это указатель на константу
а оказывается можно функцию туда забабахать.... круто...
однако
а вот код с массивом не робит. компилятор не ругается, т.е. с объявылением все ОК, но цикл возвращает что-то не то, диоды загораются не правильно, точнее вообще каша
попробовал для эксперимента выводить в порт
несколько циклов 0111, потом 1111, потом снова 0111
при этом никакие контакты физически не замыкаются вообще
возвращаю свой код с костылем i-2 и все опять работает как надо...
switchState[i]=GET(i) это значит присвоить значение, возвращаемое GET(i) в switchState[i] - а это либо 1 (HIGH), либо 0 (LOW)
и поскольку был глюк, я просто выводил в порт значение, возвращаемое этим самым GET(i)
for (byte i=0; i<NUM; i++)
{
switchState[i]=GET(i);
}
я изменил на
Serial.println(sw);
for (byte i=0; i<NUM; i++)
{
int sw = GET(i);
Serial.print(sw);
delay(200);
}
и вот собственно возвращается странный набор состояний пинов: 0111, потом спустя несколько циклов 1111, хотя я не замыкаю и не размыкаю никакие кнопки.
если же я возвращаю свой старый код, то все работает
откуда такие значения? ведь вроде там должно быть 2, 3, 4, 5...
если же я определяю массив так
const int arr[] ={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
то все работает...
это что же за PROGMEM такой?...
В общем-то, переменная int sw объявлена внутри цикла, и возвращает значение только одной ноги. Я ожидал увидеть 16-разрядное значение либо 0 либо -1 либо нечто подобное. Что такое 1111 и 0111 мне не понятно. Добавь в вывод Serial.print(i), Serial.print(arr[i]), Serial.print(digitalRead(arr[i])), что бы понять где проблема.
Цитата:
это что же за PROGMEM такой?
Рекомендуют для PROGMEM
#include <avr/pgmspace.h>
Можешь оставить массив в памяти данных. Это на случай нехватки памяти.
ну судя по тому, что с таким объявлением
const int arr[] ={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
все работает, дело именно в использовании PROGMEM
а что там может быть не так?
вернул PROGMEM, сделал вывод
вот результат, собственно
добавил, не помогло :(
да и ладно, пока ведь все помещается, программа готова на половину, а использовано только 27%
а вот с таймерами работает все прекрасно, спасибо за идею
нужна помощь, уже просто голову сломал над тем, как уйти от delay
логика вот. работает но с delay
bool checkPause() {
time = millis();
if (TIMER(t4) > PUMP_DELAY * 1000) { // если таймер простоя превышен
if (!twvIsOpen) {
// если вентиль не открыт
digitalWrite(THREE_WAY_VALWE, TWV_ON);
Serial.println("VALWE OPEN");
twvIsOpen = true;
return false;
}
if (twvIsOpen) {
delay(TWV_OPENING);// минуту как бы ждем открытия вентиля
pumpIsOn = true;
Serial.println("PUMP OPEN"); // включаем насос
digitalWrite(PUMP, PUMP_ON);
delay (TWV_DELAY);// 15 минут как бы прокачиваем
digitalWrite(THREE_WAY_VALWE, TWV_OFF);
Serial.println("VALWE CLOSE");
twvIsOpen = false;
CLR_TIMER(t4);
return false;
}
}
}
void pumpOnOff (int onOrOff) {
if ((onOrOff == 1) && !pumpIsOn && checkPause()) {
digitalWrite(PUMP, PUMP_ON);
pumpIsOn = true;
}
if ((onOrOff == 0) && pumpIsOn) {
digitalWrite(PUMP, PUMP_OFF);
CLR_TIMER(t4);
pumpIsOn = false;
}
}
когда начинаю "в лоб" заменять delay и писать if b т.д. просто дебри получаются и постоянно сбиваюсь
может просто сам подход неверный?
смысл такой: если таймер простоя первышен, открываем вентиль слива, ждеим минуту пока откроется, включаем насос, качаем 15 минут. потом закрываем вентиль (трехходовый ему всеравно и насос уже выключать не надо, он уже на закачку направлен)
Для наглядности нарисуй граф состояний, нечто вроде этого:
где: кружочки это состояния, стрелочки это переходы по условию из состояния в состояние. Дай осмысленные названия состояниям, каждой стрелочке опиши условия перехода и действия, которые необходимо выполнить при переходе. Мысленно походи по графу, убедись, что все соответствует твоим хотелкам.
я вот задумался сильно о прерываниях - на леонардо как раз 4 штуки пинов с контролем внешних прерываний... может их задействовать для обработки состояний поплавковых выключателей...
задумался сильно о прерываниях - на леонардо как раз 4 штуки пинов с контролем внешних прерываний... может их задействовать для обработки состояний поплавковых выключателей...
Нет смысла. Периодичность изменения сигналов от поплавков измеряется часами и минутами. Прерывания разумно использовать, когда сигнал меняется быстрее, чем основной цикл. Хотя, если цель "сделать не как у всех" , то можно и по прерываниям :)
почитал описания сложностей, которые могут возникнуть при использовании прерываний и некоторые ограничения, и подумал, что не стоит.
а потом прочел фразу "если 10 событий из 10 важные, то ни одно из 10 не важно"... и понял, что прерывания мне точно не нужны, а вот с таймером поигрался немного. в данном случае проверка раз в милисекунду
class WaterTimer
{
private:
long timerLength; // длительность таймера
bool timerState; // Текущее состояние On или Off
unsigned long previousMillis;
public:
WaterTimer(long tLength)
{
timerLength = tLength;
timerState = false;
previousMillis = 0;
}
void setTimerState(bool stateChang) {
timerState = stateChang;
}
bool getTimerState(unsigned long currentMillis)
{
if ((timerState) && (currentMillis - previousMillis >= timerLength))
{
previousMillis = currentMillis;
return true;
}
else {
return false;
}
}
};
WaterTimer timer1(1000);
WaterTimer timer2(500);
WaterTimer timer3(800);
void setup()
{
Serial.begin(9600);
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
}
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
if (timer1.getTimerState(currentMillis)) {
Serial.println("TIMER 3H");
}
if (timer2.getTimerState(currentMillis)) {
Serial.println("TIMER 15M");
}
if (timer3.getTimerState(currentMillis)) {
Serial.println("TIMER 1M");
}
}
void loop()
{
delay(1000);
Serial.println("ALL is ON");
timer1.setTimerState(true);
timer2.setTimerState(true);
timer3.setTimerState(true);
delay(10000);
timer1.setTimerState(false);
timer2.setTimerState(false);
timer3.setTimerState(false);
Serial.println("ALL is OFF");
delay(3000);
}
любопытно, учитывая у меня три таймера в проекте, вполне вариант. пока не вижу подвожных камней
Есть один, class WaterTimer надо несколько изменить, метод setTimerStstate() перезапускает таймер только если он истек, т.е. если вызвать метод setTimerStstate(true) за минуту до того, как истечет 3-х часовой таймер, то событие от таймера произойдет через минуту, а не через 3 часа.
В предложенном коде есть еще одна засада, которая рано или поздно приведет к сбою или зависанию программы - рекурсия функции Serial.print((). Функция вызывается и в основном цикле и в прерывании. Нужно либо избегать доступа к одному и тому же объекту и из основного цикла и из прерывания, либо запрещать прерывания на время доступа из основного цикла.
Правило хорошего тона при программировании - минимум в прерываниях, максимум в основном цикле.
ну Delphi меня не напугал, и С++ не страшнее. Вот asm я несколько раз пробовал осилить, но за неимением серьезной мотивации и области применения каждый раз забрасывал.
Angbor пишет:
Есть один, class WaterTimer надо несколько изменить, метод setTimerStstate() перезапускает таймер только если он истек, т.е. если вызвать метод setTimerStstate(true) за минуту до того, как истечет 3-х часовой таймер, то событие от таймера произойдет через минуту, а не через 3 часа.
а и правда что...
на самом деле я понял, что get и set в данном случае неправильные идеи - нужно ON и OFF со сбросом при каждом OFF.
вот сделал примерный прикид по состояниям
T3H - трехчасовой таймер, T15M - 15-ти минутный и т.д.
3W - трехходовой вентиль
всего 4 основных состояния + 2 аварийных
Что касается serial.print - то это я просто для отладки временно воткнул
сейчас еще немного подумаю и выложу вариант класса уже как он будет в реальном коде использоваться
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
if (timer1.getTimerState(currentMillis)) {
blinkS();
}
}
срабатывает без проблем, хотя эта самая функция blinkS() находится сильно после установки этого прерывания по таймеру
а если я начинаю двигать объявления туда-сюда, то
void blinkS() {
// мигаем
for (int i = 0; i < 4 ; i++) {
if ((blinkF[i] == 1) && (switchState[i]) == 1) {
ledsBurn = ledsBurn ^ ledS[i];
}
}
}
WaterTimer timer1(300, &blinkS());
timer1.setTimerState(true);
// Прерывание вызывается один раз в миллисекунду,
// ищет любые новые данные, и сохраняет их
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
if (timer1.getTimerState(currentMillis)) {
blinkS();
}
}
приводит к
waterSystem_with_timers:193: error: lvalue required as unary '&' operand
waterSystem_with_timers:194: error: 'timer1' does not name a type
exit status 1
lvalue required as unary '&' operand
может я просто не так передаю указатель на функцию?
при этом компилятором принимается только такой вариант объявления аргумента для указателя на функцию в конструкторе
если указывать любые другие варианты типа (void) (*f)() и любые другие комбинации - не робит
я думаю, это уже не принципиально, как и сложности с PROGMEM, пусть так, а то кучу время потрачу на разбирательства с глючным ARDUINO IDE :)
я опустил не относящиеся моменты и сами функции. Оставил только прототипы. Но в коде, который я запускаю, понятное дело, это все присутствует.
вот этот код, который я привел - работает. если же в конструктор подсовывать не указатель, а так, как у тебя, то не работает.
первое отличие, которое я вижу между твоим вариантом и моим, у меня это коструктор без void, у тебя же это функция, возвращающая void и находящаяся вне класса.
ну да ладно, я пока это на стоп поставлю, мой вариант тоже работает. еще одна заморочка - вынесение переменных и функций в билиотеку. Полезли глюки, но я понял, что если я с ними буду разбираться, то до конца кода не дойду. Улучшения позже. Сначала чтобы просто все было реализовано, пусть и простыня на километр :)
Я слегка уперся в логический тупик. Короче, у меня первоначально логика была построена на состояниях датчиков и соответственно уровне воды в баке. А потом я нарисовла схему (выше по теме) - в ней логика от состояния насоса и вентиля, а датчики являются условием перехода из одного состояния в другое... И оно как-то плавно одно в другое не переделывается и друг с другом не стыкуется...
То ли пропробовать доделать в первичной логике, то ли оставить и начать по-другому...
Ну вроде все так, я вот переписал сам, вроде аналогично
СОСТОЯНИЯ
0: выключены все реле.
1: включено реле насоса.
2: включен трехходовой клапан
3: включены все реле
4А: ошибка неверная комбинация датчиков.
4Б: ошибка перелив воды.
В описании действий подразумеваем но не указываем, что при включении и выключении насоса будут устанавливаться и сбрасываться соответствующие флаги и выводится соответствующая информация на дисплей и/или LED
Датчики размещены соответственно схеме:
датчик перелива
датчик полного бака
датчик половины бака
датчик пустого бака (в норме это состояние только при первом включении системы)
0–>1 (бак пустой полностью || уровень ниже половины) && 3ч не истек
останавливаем 3ч таймер
включаем насос
0–>2 (бак пустой полностью || уровень ниже половины) && 3ч истек
останавливаем 3ч таймер
открываем клапан
запускам таймер 1м
нда.. короче, пытался все-таки свою старую логику доделать - в итоге непонятный глюк - стал срабатывать непредсказуемо датчик перелива. Причем бессистемно. Короче, запутался совсем :)
Так что сохранил все это дело и начал с чистого листа :) Сейчас поробую с твоим вариантом с case
столкнулся с неожиданым для "абстрактного" программера на Delphi казусом.
Начался аппаратный глюк. Вдруг начал срабатывать датчик перелива, несистемно. То ли оптопара сгорела, то ли... на этом мысль останавливается. Так что сегодня буду пересобирать железо, хочу перенести входную группу оптореле на отдельную платку и распаять их уже. За одно проверить номиналы резисторов :)
взял эмуляцию, добавил туда считывание датчиков и все остальное - в иоге цикл пролетает первый вариант (пусто и таймер влючен) и попадает в вариант, где таймер выключен.
При этом я не могу никак отловить место, где и кто таймер 3-х часовой выключает...
по сути приведеного тобой кода, IDE-шка ругается сильно
я не знаток С++, сидел всегда в Delphi :) так что я ориентируюсь на компилятор именно ардуинского IDE
ей нужно так: int PROGRAM[NUM]
а не так: int arr[NUM] PROGRAM
а вот это не может выкурить ни IDE ни я
digitalRead(arr[b])
В моем случае без разницы какие номера у входов и следуют ли они по порядку или нет.
В случае переопределения входа
твой код перестанет работать, а моему фиолетово.
4 линии данных, которые идут к D4-D7 индикатора, можно использовать и для записи в регистр светодиодов. Пока ты не дергаешь выводы E, R/W, RS, индикатору все равно, что происходит на шине данных. Выдай на неё данные для светодиодов и используй clockPin или latchPin для записи в регистр светодиодов. У ардуины освободится еще пара ног.
Опечатался, надо PROGMEM , а не PROGRAM. Смысл: разместить массив в памяти програм, а не памяти данных.
Спасибо, Andy, попозже еще раз попробую с твоим советом про массив пинов.
Про использование дисплея... Как-то не хочется все лепить в кучу. А в чем выгода такого варианта по сравнению с двумя регистрами?
В моем случае я могу еще добавить безболезненно 4 диода. Мне вобще понравились регистры :) Я думал использовать регистр и для поплавковых выключателей. Их сейчас 4, а так можно будет и до 8 использовать на разные нужды.
Ннапример у меня на трех-ходовом вентиле есть концевые выключатели, определяющие докрутился ли привод вентиля до упора или нет. И бывает, что если заряда в кондерах привода не хватает, то привод вентиль не докрутит. Также линия предварительной прокачки скважины с реле давления (чтобы в случае засора линии не пожечь насос). можно определять, не разомкнулось ли оно.
А сейчас вот думаю над куском кода, чтобы не ежемилисекундно тыркать
Немного смущает, что обновление происходит только один раз... Но городить под каждый случай отдельную переменную как-то тяжело выходит..
Что думаешь?
В схеме присутствуют разного рода кривульки, по хорошему от них надо избавляться. Вообще, хотелось бы увидеть схему электрическую, по монтажной схеме мне не все понятно.
В моем случае я могу еще добавить безболезненно 4 диода. Мне вобще понравились регистры :)
Это как бы не лепка в кучу, а попытка структурировать схему и код. Взгляни на устройство компа: шина данных (и не только она) проходит по всем устройствам. Кроме того, запас по ногам надо иметь, любой проект имеет тенденцию к развитию.
В твоем случае светодиоды моргают при каждом сдвиге, пусть это не видно глазу, но это так. В моем случае ты так же можешь добавить еще регистр и светодиоды или реле или еще что нибудь.
У тебя константы размещены в памяти данных, в этом нет необходимости. Сделай так:
я думал раньше, что определение типа define - это указатель на константу
а оказывается можно функцию туда забабахать.... круто...
однако
а вот код с массивом не робит. компилятор не ругается, т.е. с объявылением все ОК, но цикл возвращает что-то не то, диоды загораются не правильно, точнее вообще каша
попробовал для эксперимента выводить в порт
несколько циклов 0111, потом 1111, потом снова 0111
при этом никакие контакты физически не замыкаются вообще
возвращаю свой код с костылем i-2 и все опять работает как надо...
Это команда препроцессору типа replace, перед компиляцией пробежаться по тексту и заменить совпадающие выражения определением.
Как-то не увязывается у меня 16 разрядная переменная sw с тем, что выведено в порт. По логике вещей цикл должен выглядеть так:
так у меня так и выглядит, как у вас.
если я все правильно понимаю, то
switchState[i]=GET(i) это значит присвоить значение, возвращаемое GET(i) в switchState[i] - а это либо 1 (HIGH), либо 0 (LOW)
и поскольку был глюк, я просто выводил в порт значение, возвращаемое этим самым GET(i)
я изменил на
и вот собственно возвращается странный набор состояний пинов: 0111, потом спустя несколько циклов 1111, хотя я не замыкаю и не размыкаю никакие кнопки.
если же я возвращаю свой старый код, то все работает
Обнаружил, что arr[i] возвращает
arr[0] 6
arr[1] 0
arr[2] 0
arr[3] -256
откуда такие значения? ведь вроде там должно быть 2, 3, 4, 5...
если же я определяю массив так
const int arr[] ={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
то все работает...
это что же за PROGMEM такой?...
В общем-то, переменная int sw объявлена внутри цикла, и возвращает значение только одной ноги. Я ожидал увидеть 16-разрядное значение либо 0 либо -1 либо нечто подобное. Что такое 1111 и 0111 мне не понятно. Добавь в вывод Serial.print(i), Serial.print(arr[i]), Serial.print(digitalRead(arr[i])), что бы понять где проблема.
#include <avr/pgmspace.h>
Можешь оставить массив в памяти данных. Это на случай нехватки памяти.
ну судя по тому, что с таким объявлением
const int arr[] ={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
все работает, дело именно в использовании PROGMEM
а что там может быть не так?
вернул PROGMEM, сделал вывод
вот результат, собственно
вместо 2, 3, 4, и 5 там 6, 0, 0 и -256 :/
вот объявление
проверил, нигде больше в коде BOTTOM_ и т.д. не переопределяются и не перписываются.
Похоже все дело в
#include <avr/pgmspace.h>
Добавь эту строку в начало проекта. С ней должно работать...
добавил, не помогло :(
да и ладно, пока ведь все помещается, программа готова на половину, а использовано только 27%
а вот с таймерами работает все прекрасно, спасибо за идею
нужна помощь, уже просто голову сломал над тем, как уйти от delay
логика вот. работает но с delay
когда начинаю "в лоб" заменять delay и писать if b т.д. просто дебри получаются и постоянно сбиваюсь
может просто сам подход неверный?
смысл такой: если таймер простоя первышен, открываем вентиль слива, ждеим минуту пока откроется, включаем насос, качаем 15 минут. потом закрываем вентиль (трехходовый ему всеравно и насос уже выключать не надо, он уже на закачку направлен)
может просто сам подход неверный?
0-исходное, все реле выключены.
1-закачка воды в бак, включено реле 1.
2-слив воды перед наполнением бака, включено реле 2.
3-аварийное состояние...
Далее опиши условия перехода из одного состояния в другое и какие действия надо предпринять, например:
0->1 нажатие кнопки 1 или средний датчик уровня =0, включить реле 1, включить светодиод 1.
0->2 истек 3 часовой таймер, запустить 15 минутный таймер.
2->1 истек 15 минутный таймер, включить реле 1, включить светодиод 2.
1->0 верхний датчик уровня =1, выключить реле 1, запустить 3х часовой таймер.
1->3 датчики уровня в странном состоянии, выключить все реле, включить светодиод "авария".
Теперь пример код:
спасибо огромное!
вчера вечером на этой же мысли остановился - описать состояния системы. сегодня днем попробую, отпишусь
Для наглядности нарисуй граф состояний, нечто вроде этого:
где: кружочки это состояния, стрелочки это переходы по условию из состояния в состояние. Дай осмысленные названия состояниям, каждой стрелочке опиши условия перехода и действия, которые необходимо выполнить при переходе. Мысленно походи по графу, убедись, что все соответствует твоим хотелкам.
Смешно конечно, и банально. Но сложность кода превысила порог просто "с наскока". Так что погрузился в чтение некоторой литературы :)
В частности меня интересует вопрос прерываний и таймеров.
я вот задумался сильно о прерываниях - на леонардо как раз 4 штуки пинов с контролем внешних прерываний... может их задействовать для обработки состояний поплавковых выключателей...
задумался сильно о прерываниях - на леонардо как раз 4 штуки пинов с контролем внешних прерываний... может их задействовать для обработки состояний поплавковых выключателей...
Нет смысла. Периодичность изменения сигналов от поплавков измеряется часами и минутами. Прерывания разумно использовать, когда сигнал меняется быстрее, чем основной цикл. Хотя, если цель "сделать не как у всех" , то можно и по прерываниям :)
:)
почитал описания сложностей, которые могут возникнуть при использовании прерываний и некоторые ограничения, и подумал, что не стоит.
а потом прочел фразу "если 10 событий из 10 важные, то ни одно из 10 не важно"... и понял, что прерывания мне точно не нужны, а вот с таймером поигрался немного. в данном случае проверка раз в милисекунду
любопытно, учитывая у меня три таймера в проекте, вполне вариант. пока не вижу подвожных камней
значит С++ не пугает.... :)
В предложенном коде есть еще одна засада, которая рано или поздно приведет к сбою или зависанию программы - рекурсия функции Serial.print((). Функция вызывается и в основном цикле и в прерывании. Нужно либо избегать доступа к одному и тому же объекту и из основного цикла и из прерывания, либо запрещать прерывания на время доступа из основного цикла.
Правило хорошего тона при программировании - минимум в прерываниях, максимум в основном цикле.
на самом деле я понял, что get и set в данном случае неправильные идеи - нужно ON и OFF со сбросом при каждом OFF.
вот сделал примерный прикид по состояниям
T3H - трехчасовой таймер, T15M - 15-ти минутный и т.д.
3W - трехходовой вентиль
всего 4 основных состояния + 2 аварийных
Что касается serial.print - то это я просто для отладки временно воткнул
сейчас еще немного подумаю и выложу вариант класса уже как он будет в реальном коде использоваться
а вот на чем я просто сломал голову, так это над тем, как в конструктор класса передать указатель на функцию... уже просто крыша отъезжает :)
и потом
но теперь если я указываю
получаю сообщение, что нету такого blinkS()...
blinkS() должна быть объявлена раньше, чем её вызов.
хм...
не понимаю. А почему тогда
срабатывает без проблем, хотя эта самая функция blinkS() находится сильно после установки этого прерывания по таймеру
а если я начинаю двигать объявления туда-сюда, то
приводит к
может я просто не так передаю указатель на функцию?
Короче, вот что получилось. Это работает. Но порассуждаю, потому что не понимаю в полной мере, почему это работает, а это не гуд.
т.е. я объявил указатель для функции, возвращабщей void и не принимающей параметры, потом в конструктор добавил второй параметр void f()
в конструкторе указателю присвоил то, что получил из аргумента
Хотя код и работает, но я не понимаю до конца, что именно я указываю здесь как параметр? указатель? но по записи то же функция...
далее
ну тут просто добавил запуска функции через указатель на нее
добавил прототип функции, установил указатель на "блинк" и передал в параметр.
т.е. основной вопрос, объявив функцию в которой аргумент выглядит как void f() - я передаю указатель? если да, то почему это так?
Нет разницы передавать функцию в качестве параметра или указатель на неё, в любом случае передается указатель.
попробовал в конструкторе оставить все как есть, но передать в параметре функцию вот так без создания указателя
вместо того как было (с у казателем)
и получил: waterSystem_with_timers:71: error: invalid use of void expression
Оба вызова timer1 равнозначны
на второе объвление компилятор все равно ругается :(
error: invalid use of void expression
А так не ругается: timer1(300, blinkS);
Разные компиляторы по разному компиляют.
ругань наступает на любой из четырех вариантов :(
мне кажется, что это не только из-за объявления экземпляра класса, а скорее из-за конструктора
видимо для такого варианта компилятор требует именно формат указателя.
при этом компилятором принимается только такой вариант объявления аргумента для указателя на функцию в конструкторе
если указывать любые другие варианты типа (void) (*f)() и любые другие комбинации - не робит
я думаю, это уже не принципиально, как и сложности с PROGMEM, пусть так, а то кучу время потрачу на разбирательства с глючным ARDUINO IDE :)
проект закончу, посмотрю в сторону Code::Blocks
Специально скачал ардуино IDE, что бы проверить
компиляется без ошибок (Arduino 1.6.7)
да, если размещать объявление экземпляра класса в loop
но если поместить это до loop и до setup то возникают проблемы
Не пойму в чем проблема, так тоже компиляется.
Выложи проблемный код.
я опустил не относящиеся моменты и сами функции. Оставил только прототипы. Но в коде, который я запускаю, понятное дело, это все присутствует.
вот этот код, который я привел - работает. если же в конструктор подсовывать не указатель, а так, как у тебя, то не работает.
первое отличие, которое я вижу между твоим вариантом и моим, у меня это коструктор без void, у тебя же это функция, возвращающая void и находящаяся вне класса.
Взял твой код и в строках 52-54 заменил указатели на имена функций.
и работает?
На счет работает не знаю, но компиляется без ошибок.
загадка...
спасибо за участие в процессе, очень поддерживает
ну да ладно, я пока это на стоп поставлю, мой вариант тоже работает. еще одна заморочка - вынесение переменных и функций в билиотеку. Полезли глюки, но я понял, что если я с ними буду разбираться, то до конца кода не дойду. Улучшения позже. Сначала чтобы просто все было реализовано, пусть и простыня на километр :)
Я слегка уперся в логический тупик. Короче, у меня первоначально логика была построена на состояниях датчиков и соответственно уровне воды в баке. А потом я нарисовла схему (выше по теме) - в ней логика от состояния насоса и вентиля, а датчики являются условием перехода из одного состояния в другое... И оно как-то плавно одно в другое не переделывается и друг с другом не стыкуется...
То ли пропробовать доделать в первичной логике, то ли оставить и начать по-другому...
Давай распишем твою диаграмму, а то мне не все понятно.
Состояние 0: выключены все реле.
Состояние 1: включено реле насоса.
Состояние 2: включение трехходового клапана.
Состояние 3: включен трехходовый клапан и реле насоса.
Состояние 4А: ошибка неверная комбинация датчиков.
Состояние 4Б: ошибка перелив воды.
Условия переходов между состояниями и действия:
0->1 уровень воды ниже среднего датчика и таймер 3ч не истек - включить реле насоса.
0->2 уровень воды ниже среднего датчика и истек таймер 3ч - подать напряжение на трехходовый клапан, запустить таймер 1 мин.
1->0 уровень воды достиг верхнего датчика - отключить насос, запустить таймер 3ч.
2->3 истек таймер 1 мин - включить реле насоса, запустить таймер 15 мин.
3->1 истек таймер 15 мин - снять напряжение с трехходового клапана.
из всех состояний ->4A неверная комбинация датчиков - выключить всё, индикация ошибки.
2->4Б перелив воды - выключить всё, индикация ошибки.
4А->0 ручное вмешательство? - выкл индикатор ошибки.
4Б->0 ручное вмешательство? - выкл индикатор ошибки.
При переходе в состояния 4А и 4Б по идее надо запускать 3 часовой таймер.
Ну вроде все так, я вот переписал сам, вроде аналогично
СОСТОЯНИЯ
0: выключены все реле.
1: включено реле насоса.
2: включен трехходовой клапан
3: включены все реле
4А: ошибка неверная комбинация датчиков.
4Б: ошибка перелив воды.
В описании действий подразумеваем но не указываем, что при включении и выключении насоса будут устанавливаться и сбрасываться соответствующие флаги и выводится соответствующая информация на дисплей и/или LED
Датчики размещены соответственно схеме:
датчик перелива
датчик полного бака
датчик половины бака
датчик пустого бака (в норме это состояние только при первом включении системы)
0–>1 (бак пустой полностью || уровень ниже половины) && 3ч не истек
останавливаем 3ч таймер
включаем насос
0–>2 (бак пустой полностью || уровень ниже половины) && 3ч истек
останавливаем 3ч таймер
открываем клапан
запускам таймер 1м
2–>3 истек таймер 1м
остановить таймер 1м
запустить таймер 15м
включаем насос
3–>1 истек таймер 15м
остановить таймер 15м
закрываем клапан
1–>0 бак полный
останавливаем насос
включаем таймер 3ч
0,1,2,3–>4А неверная комбинация датчиков
включаем таймер 3ч
все выключить
индикация ошибки
0,1,2,3–>4Б перелив
включаем таймер 3ч
все выключить
индикация ошибки
Переход из 4А и 4Б по идее произойдет автоматически после устранения неполадок датчиков и/или перелива.
Описать какая схема действует сейчас?
замечательно, теперь перекладываем в код
хотя неверную комбинацию датчиков и перелив можно вынести из switch
нда.. короче, пытался все-таки свою старую логику доделать - в итоге непонятный глюк - стал срабатывать непредсказуемо датчик перелива. Причем бессистемно. Короче, запутался совсем :)
Так что сохранил все это дело и начал с чистого листа :) Сейчас поробую с твоим вариантом с case
так, ну вот сделал некую болванку, эмуляция комбинаций датчиков и таймеров ввожу через параллельный порт
вроде бы все работает.
действительно, обработка аварийных ситуаций напрашивается куда-нибудь отдельно, ну или в функцию вынести.
столкнулся с неожиданым для "абстрактного" программера на Delphi казусом.
Начался аппаратный глюк. Вдруг начал срабатывать датчик перелива, несистемно. То ли оптопара сгорела, то ли... на этом мысль останавливается. Так что сегодня буду пересобирать железо, хочу перенести входную группу оптореле на отдельную платку и распаять их уже. За одно проверить номиналы резисторов :)
упс.. таки-я их (4N35) пожег... При 5 вольт надо 370 Ом.
что-то я в тупике очередном :)
взял эмуляцию, добавил туда считывание датчиков и все остальное - в иоге цикл пролетает первый вариант (пусто и таймер влючен) и попадает в вариант, где таймер выключен.
При этом я не могу никак отловить место, где и кто таймер 3-х часовой выключает...