Где мой БАГ?
Спасибо всем за помощь в моем последнем посте, мне удалось заставить его работать. Я все еще продолжаю пытаться развить свои навыки с помощью arduino. На этот раз мне нужно сделать имитацию кофейной плиты. Когда датчик не соответствует температуре, индикатор будет мигать, как только датчик достигнет температуры, индикатор будет мигать, но выключится (снизится) через пять секунд. Это код, над которым я работаю. Датчик находится в режиме temp, а светодиод все еще мигает вместо того, чтобы выключаться. Когда он низкий, он мигает как следует. Я думаю, что ошибка в части кода millis (). Любые идеи будут высоко оценены. Я также запустил экран println, и температура постоянно составляет около 210-220.
const int led = 11; // hot plate led on pin 11
const int sensor = 9; // temp sensor on pin 9
const int LOWER_BOUND = 174; // lower temp
const int UPPER_BOUND = 220; // upper temp
int val = 0; // analog reading
unsigned const long period = 5000; // time it takes to turn led off at temp
unsigned long startMillis;
unsigned long currentMillis;
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
pinMode(sensor, INPUT);
startMillis = millis();
}
void loop() {
val = analogRead(sensor); // temp recorder
Serial.println(val);
currentMillis = millis();
if (val == UPPER_BOUND){
digitalWrite(led, HIGH);
}
else if (val == UPPER_BOUND && currentMillis - startMillis == period) {
digitalWrite(led, LOW);
}
else (val <= LOWER_BOUND); {
digitalWrite(led, LOW);
delay(200);
digitalWrite(led, HIGH);
delay(200);
}
}
@Steven , 👍0
Обсуждение2 ответа
Я вижу пару проблем с вашим кодом. Самая большая проблема заключается в том, что вы записываете startMillis в setup (), а затем сравниваете его с текущими миллионами в своем цикле. Если для того, чтобы горшок достаточно нагрелся, потребуется более пяти секунд, вы никогда не будете соответствовать (currentMillis - startMillis) == период, потому что (currentMillis - startMillis) уже будет > период, прежде чем вы начнете его тестировать. Изменение "== период" на ">= период" исправит это, но оно все равно будет вести себя не так, как вы хотите, так как вы должны устанавливать startMillis, когда банк впервые достиг целевой температуры, а не при запуске программы.>
Кроме того, ваш код ничего не делает, когда температура находится между НИЖНЕЙ и ВЕРХНЕЙ ГРАНИЦЕЙ, что, как я подозреваю, не то, что вам нужно. Вы не указываете, означает ли "до температуры" значение на уровне или выше НИЖНЕГО ПРЕДЕЛА или на уровне или выше ВЕРХНЕГО ПРЕДЕЛА. Я собираюсь предположить, что вы хотите, чтобы светодиод мигал, когда температура ниже НИЖНЕЙ границы, продолжайте гореть, пока температура находится между НИЖНЕЙ ГРАНИЦЕЙ и ВЕРХНЕЙ ГРАНИЦЕЙ, а затем полностью выключите через пять секунд после того, как горшок достигнет ВЕРХНЕЙ границы.
Лично я бы переписал его вот так, как для функциональности, так и для удобства чтения...
int val = 0; // analog reading
unsigned const long period = 5000; // time it takes to turn led off at temp
// ---We don't need this till later, zero it so the loop knows not to use it till then
unsigned long startMillis = 0;
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
pinMode(sensor, INPUT);
}
void loop() {
val = analogRead(sensor); // temp recorder
Serial.println(val);
if ( val <= LOWER_BOUND ) {
// ---Coffee temp is low, flash the LED
digitalWrite(led, LOW);
delay(200);
digitalWrite(led, HIGH);
delay(200);
} else if ( val < UPPER_BOUND ) {
// ---Coffee temp is medium, turn the LED solid on (assumes switched cathode)
digitalWrite(led, LOW);
} else {
// ---Coffee is hot!
if ( !startMillis ) {
// ---Coffee just reached UPPER_BOUND... start timing (LED is already on)
startMillis = millis();
} else {
// ---Has the period timed out yet?
if ( ( millis() - startMillis ) >= period ) {
// ---More than 5 seconds has passed, make sure LED is off
digitalWrite(led, HIGH);
}
}
}
}
Пара моментов об этой реализации...
- Я не тестировал этот код на физическом Arduino.
- Обратите внимание, что мы не собираемся начинать хранение или просмотр millis() до тех пор, пока банк сначала не достигнет UPPER_BOUND. До тех пор все, о чем заботится программа, - это температура кофе.
- Как и ваш исходный код, эта реализация не учитывает обтекание millis() каждые 50 дней или около того. Маловероятно, что кофейнику потребуется более 50 дней, чтобы достичь целевой температуры, поэтому я бы не беспокоился об этом, но вам следует учитывать эту ситуацию при написании длительных приложений (я отказываюсь называть их "скетчами").
- Я основал это на вашем исходном коде, так что, как и у вас, как только температура достигает, по крайней мере, нижней границы, она непрерывно записывается на вывод светодиода при каждом проходе через цикл, хотя, вероятно, она не изменилась с последней итерации. Если бы я делал это по-настоящему, я бы перестроил его так, чтобы он записывался на вывод только тогда, когда состояние светодиода действительно меняется. Это всего лишь проблема эффективности, хотя... повторная запись одного и того же значения на пин будет работать нормально, просто это не очень элегантно.
- Если кофе начинает остывать после достижения ВЕРХНЕЙ ГРАНИЦЫ, то поведение не определено. Я подозреваю, что он будет нормально работать, когда температура кофе низкая или средняя, но если кофе достигнет ВЕРХНЕЙ границы во второй или более раз, я полагаю, что индикатор погаснет немедленно, а не через 5 секунд. Было бы нетрудно улучшить код, чтобы он вел себя правильно, независимо от того, сколько раз температура в кастрюле повышалась и понижалась, но это решение было связано с проблемой, как вы ее описали, которая заключалась в том, что кофейник разогревался только один раз от холодной до целевой температуры.
Я внес несколько изменений в код, добавил глобальные переменные для светодиода и датчика. Я также устранил нижнюю границу и сохранил только верхнюю границу. Это сработало очень хорошо. Между этим кодом и моим кодом я заметил, что использую много ненужных переменных, а некоторые из них бесполезны. Я обнаружил, что при использовании как верхней, так и нижней границы код будет запускать только нижнюю границу, я предполагаю это, потому что я дал нижней границе значение, когда оно ему не нужно, поэтому в программе, хотя она и работала, она никогда не будет работать так, как задумано, из-за этого значения., @Steven
Я также считаю, что теперь понимаю millis() немного лучше, я думал, что millis() должен быть запущен в настройке, чтобы он работал правильно., @Steven
Вы получили очень ценные комментарии, и все они справедливы.Однако, чтобы ответить на ваш первоначальный вопрос - как только вы достигнете верхней границы и продолжите применять тепло - ваш код не сможет обнаружить большее значение верхней границы., @Jan Hus
Во-первых, позвольте мне указать на несколько технических проблем с вашей программой:
датчик const int = 9;
неверно. В Uno вы можете использовать толькоanalogRead()
на выводах с меткамиот A0
доA5
.если (val == ВЕРХНИЙ ПРЕДЕЛ)
. Никогда не проверяйте аналоговые показания на точное равенство. Чтение шумное, и вы не должны ожидать непрерывности. Он очень хорошо может перейти отUPPER_BOUND-1
при одном показании кUPPER_BOUND+1
при следующем. Вместо этого проверьте значениеval >= UPPER_BOUND>
.Текущий миллис - начальный миллис == период
. Опять же, вы не должны проверять точное равенство. На этот раз по более тонкой причине, связанной с реализациейmillis()
: некоторые значения (примерно по одному на 42) пропускаются. Здесь снова выполните тест на>= период>
.
Существует также проблема, на которую уже указывал PhoenixRevealed, в вашем коде, определяющем два порога, в то время как в вашей прозе упоминается только один. Вполне возможно, что вы просто небрежны и недостаточно точно описываете свои требования.
Далее, и поскольку вы заинтересованы в совершенствовании своего процесса обучения, позвольте мне посоветовать вам два метода, которые стоит изучить, потому что они чрезвычайно полезны в основном для любого проекта Arduino, более сложного , чем просто мигание светодиода:
Избегайте использования
функции delay()
. Пока Arduino занят задержкой, он больше ничего не может сделать. Например, он не может отслеживать изменения температуры на горячей плите. В данном конкретном случае вполне может оказаться, что вам все равно, потому что горячая плита работает так медленно, что вы можете позволить себе опоздать на 400 мс. Но если у вас войдет в привычку полагаться назадержку()
, рано или поздно она вас укусит. Поэтому чем раньше вы научитесь мигать светодиодом без использованияфункции delay()
, тем лучше.Научитесь программировать конечную машину. Это концепция программирования, которая очень хорошо подходит для описания реактивных систем, т. е. систем, которые должны реагировать на внешние “события”. Это очень полезная концепция для написания большинства программ, предназначенных для управления оборудованием. Существует довольно много учебных пособий. Я бы рекомендовал Конечный автоматМайенко.
Вот моя интерпретация ваших требований с точки зрения
конечного автомата: ваш Arduino может находиться в любом из 3 состояний: ХОЛОДНОЕ
(начальное
состояние, пластина не достигла требуемой температуры), ОЖИДАНИЕ
(пластина достигла температуры, и мы задерживаем пять секунд),
ГОТОВО
(пять секунд истекли). Правила изменения состояний
таковы:
ХОЛОД
→ОЖИДАНИЕ
: вХОЛОДНОМ
состоянии, если температура достигает заданного порогового значения, перейдите в состояниеОЖИДАНИЯ
.ОЖИДАНИЕ
→ГОТОВО
: когда вы находитесь в состоянииОЖИДАНИЯ
, по истечении пяти секунд с момента входа в состояние, перейдите всостояние ГОТОВО.
- Когда мы находимся в
состоянии "ГОТОВО" ... больше ничего никогда не происходит.
Вот соответствующая диаграмма состояния:
Обратите внимание, что как только мы достигаем состояния ГОТОВО, система застревает. Из этой
ситуации можно выйти только перезагрузив Arduino. Возможно
, вы захотите пересмотреть свою спецификацию, чтобы учесть
СДЕЛАННОЕ
→ ХОЛОДНОЕ
переход в некотором роде. Но из вашей спецификации, как написано в вашем
вопросе, я предполагаю, что вас устраивает, что система застревает.
Я попытался реализовать эту диаграмму состояний в коде, используя традиционную
конструкция переключателя
/корпуса
. Но потом я обленился и заметил, что звоню
exit()
-самый простой способ реализации состояния "ГОТОВО", так как
эта функция точно предназначена для того, чтобы программа застряла в
состоянии "ничего не делать". Затем, поскольку осталось заботиться только
о двух состояниях, в качестве переменной состояния можно использовать логическое значение. В конце концов, код, который я
предлагаю, может не выглядеть как конечный автомат. Однако
важно отметить, что в таком случае конечный автомат
является очень полезной концепцией для обдумывания вашей программы и записи
ее требований.
// Pinout
const uint8_t LED = 11;
const uint8_t SENSOR = A0;
// Threshold temperature reading.
const int THRESHOLD_TEMP_READING = 220;
// Timing.
const uint32_t LED_TOGGLE_PERIOD = 200;
const uint32_t WAIT_TIME = 5000;
bool is_hot = false;
uint32_t last_led_toggle;
uint32_t wait_started;
void setup()
{
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT);
}
void loop()
{
// Read the time once per loop.
uint32_t now = millis();
// Toggle the LED state.
if (now - last_led_toggle >= LED_TOGGLE_PERIOD) {
digitalWrite(LED, !digitalRead(LED));
last_led_toggle = now;
}
if (is_hot) {
// If the plate is hot, keep blinking during WAIT_TIME.
if (millis() - wait_started >= WAIT_TIME) {
digitalWrite(LED, LOW); // turn off the LED
exit(0); // stop everything
}
} else {
// If the plate is cold, take note when it gets hot.
if (analogRead(SENSOR) >= THRESHOLD_TEMP_READING) {
is_hot = true;
wait_started = now;
}
}
}
Поэтому при использовании analogRead() Я никогда не должен измерять или читать точные значения? Наверное, я уже должен это знать, но разве A0-A5 излучает синусоидальную волну по сравнению с квадратной волной по сравнению с ШИМ-контактами? Я ценю, что вы все объяснили, что я делаю не так. Это помогает мне понять, что мне нужно искать и как действовать в последующих проектах., @Steven
@Steven: Пожалуйста, перефразируйте вопросы в своем комментарии более ясным образом. В том виде, в каком они написаны, я не могу понять их никакого смысла., @Edgar Bonet
Выводит ли вывод A0-A5 синусоидальную волну вместо квадратной волны, как это делает вывод PWM? и не является ли это причиной того, что мы не можем измерить точные значения с помощью analogRead()?, @Steven
@Steven: 1. Контакты A0 – A5 могут служить аналоговыми **входами**. В противном случае вы можете использовать их как обычный цифровой ввод-вывод (то есть выводить либо "ВЫСОКИЙ", либо "НИЗКИЙ"). Arduino Uno не имеет аналоговых выходов. Он не может выводить синусоидальную волну ни на один вывод. 2. Причина, по которой вы не можете измерить точные значения, заключается в том, что в физическом мире нет такой вещи, как измерение _exact_. Но это не совсем то, что я обсуждаю в своем ответе. Вместо этого я говорю вам никогда не сравнивать измерение с ожидаемым значением с помощью оператора==
. Ключевое слово здесь - “тест” на точное равенство., @Edgar Bonet
Поэтому я прочитал о FSM, и он действительно кажется лучше сконструированным, чем некоторые из того, что я делал. И теперь я понимаю, что вы объясняете, с помощью булавок A0-A5. Вполне логично, что ничто в физическом мире не имеет точного измерения. Это то, что я не учел, и объясняет, почему с помощью "==" я не смог получить правильный результат, который ожидал, даже сделав предложенные правки., @Steven
- Печать string and integer LCD
- Почему мои часы реального времени показывают неверное время с моего ПК?
- Arduino uno + cnc Shield v3 + драйвер шагового двигателя A4988 + AccelStepper?
- Отправьте несколько значений int из Python в Arduino, используя pySerial
- Глобальные переменные занимают много места в динамической памяти.
- (Код ультразвукового датчика: такого файла или каталога нет)
- rfid_default_keys проверить с помощью RC522
- Команда strtok() с Serial связью
Извините, но я в замешательстве... Кто-то исправил мой последний пост за меня, я последовал инструкциям, отступил, чем нажал пробел 4 раза, чтобы активировать кодовый блок. Я постараюсь сделать все возможное, чтобы отредактировать его до конца. приношу свои извинения., @Steven
итак, узнайте, как правильно отступать код ... это очень поможет вам при отладке, показывая код в логических блоках ... посмотрите на пример кода в arduino IDE ... ваша коррекция - хорошая попытка .... но я все исправлю, @jsotola
переформатируйте код для вас .... посмотрите на конец кода и посмотрите, есть ли правильное количество закрывающих скобок"}"..... переформатированный код будет виден, когда он будет принят, @jsotola
Теперь я понимаю, о чем вы говорите. Это выглядит приятнее и легче следовать для себя и других. Я обязательно буду следовать этому формату в будущем. Спасибо., @Steven
код внутри
else if (val == UPPER_BOUND && currentMillis - startMillis == period) {
никогда не может работать, так какif (val == UPPER_BOUND){
также будет true - поменяйте порядок первых двух `if, @Jaromanda Xв последнем
else
отсутствуетif
и есть stray;
это должно бытьelse if (val <= LOWER_BOUND) {
- то, как написан код, теперь выполняет else(val <= LOWER_BOUND);
... и следующий блок **всегда** выполняется, @Jaromanda Xчто также не ясно, так это то, что должен делать светодиод, когда температура выше "LOWER_BOUND", но не точно равна "UPPER_BOUND"., @Jaromanda X
и еще один последний недостаток ... вы установили " startMillis
в
setup... таким образом, через 5 секунд после включения питания, независимо от температуры,
currentMillis - startMillis >= period` всегда будет истинным (не то чтобы это имело значение для вашей текущей логики)., @Jaromanda XПока температура ниже UPPER_BOUND, светодиод должен просто мигать. При достижении или превышении температуры UPPER_BOUND светодиод должен мигать в течение 5 секунд, а затем выключаться. Я сделал изменения, предложенные Jaromanda X, и прямо сейчас, когда ниже UPPER_BOUND, он мигает, когда на или выше UPPER_BOUND светодиод остается постоянно горящим., @Steven