Где мой БАГ?

Спасибо всем за помощь в моем последнем посте, мне удалось заставить его работать. Я все еще продолжаю пытаться развить свои навыки с помощью 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);    
    }
}

, 👍0

Обсуждение

Извините, но я в замешательстве... Кто-то исправил мой последний пост за меня, я последовал инструкциям, отступил, чем нажал пробел 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


2 ответа


0

Я вижу пару проблем с вашим кодом. Самая большая проблема заключается в том, что вы записываете 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);

            }
        }
    }
}

Пара моментов об этой реализации...

  1. Я не тестировал этот код на физическом Arduino.
  2. Обратите внимание, что мы не собираемся начинать хранение или просмотр millis() до тех пор, пока банк сначала не достигнет UPPER_BOUND. До тех пор все, о чем заботится программа, - это температура кофе.
  3. Как и ваш исходный код, эта реализация не учитывает обтекание millis() каждые 50 дней или около того. Маловероятно, что кофейнику потребуется более 50 дней, чтобы достичь целевой температуры, поэтому я бы не беспокоился об этом, но вам следует учитывать эту ситуацию при написании длительных приложений (я отказываюсь называть их "скетчами").
  4. Я основал это на вашем исходном коде, так что, как и у вас, как только температура достигает, по крайней мере, нижней границы, она непрерывно записывается на вывод светодиода при каждом проходе через цикл, хотя, вероятно, она не изменилась с последней итерации. Если бы я делал это по-настоящему, я бы перестроил его так, чтобы он записывался на вывод только тогда, когда состояние светодиода действительно меняется. Это всего лишь проблема эффективности, хотя... повторная запись одного и того же значения на пин будет работать нормально, просто это не очень элегантно.
  5. Если кофе начинает остывать после достижения ВЕРХНЕЙ ГРАНИЦЫ, то поведение не определено. Я подозреваю, что он будет нормально работать, когда температура кофе низкая или средняя, но если кофе достигнет ВЕРХНЕЙ границы во второй или более раз, я полагаю, что индикатор погаснет немедленно, а не через 5 секунд. Было бы нетрудно улучшить код, чтобы он вел себя правильно, независимо от того, сколько раз температура в кастрюле повышалась и понижалась, но это решение было связано с проблемой, как вы ее описали, которая заключалась в том, что кофейник разогревался только один раз от холодной до целевой температуры.
,

Я внес несколько изменений в код, добавил глобальные переменные для светодиода и датчика. Я также устранил нижнюю границу и сохранил только верхнюю границу. Это сработало очень хорошо. Между этим кодом и моим кодом я заметил, что использую много ненужных переменных, а некоторые из них бесполезны. Я обнаружил, что при использовании как верхней, так и нижней границы код будет запускать только нижнюю границу, я предполагаю это, потому что я дал нижней границе значение, когда оно ему не нужно, поэтому в программе, хотя она и работала, она никогда не будет работать так, как задумано, из-за этого значения., @Steven

Я также считаю, что теперь понимаю millis() немного лучше, я думал, что millis() должен быть запущен в настройке, чтобы он работал правильно., @Steven

Вы получили очень ценные комментарии, и все они справедливы.Однако, чтобы ответить на ваш первоначальный вопрос - как только вы достигнете верхней границы и продолжите применять тепло - ваш код не сможет обнаружить большее значение верхней границы., @Jan Hus


1

Во-первых, позвольте мне указать на несколько технических проблем с вашей программой:

  1. датчик const int = 9; неверно. В Uno вы можете использовать только analogRead() на выводах с метками от A0 до A5.

  2. если (val == ВЕРХНИЙ ПРЕДЕЛ). Никогда не проверяйте аналоговые показания на точное равенство. Чтение шумное, и вы не должны ожидать непрерывности. Он очень хорошо может перейти от UPPER_BOUND-1 при одном показании к UPPER_BOUND+1 при следующем. Вместо этого проверьте значение val >= UPPER_BOUND>.

  3. Текущий миллис - начальный миллис == период. Опять же, вы не должны проверять точное равенство. На этот раз по более тонкой причине, связанной с реализацией millis(): некоторые значения (примерно по одному на 42) пропускаются. Здесь снова выполните тест на >= период>.

Существует также проблема, на которую уже указывал PhoenixRevealed, в вашем коде, определяющем два порога, в то время как в вашей прозе упоминается только один. Вполне возможно, что вы просто небрежны и недостаточно точно описываете свои требования.

Далее, и поскольку вы заинтересованы в совершенствовании своего процесса обучения, позвольте мне посоветовать вам два метода, которые стоит изучить, потому что они чрезвычайно полезны в основном для любого проекта Arduino, более сложного , чем просто мигание светодиода:

  1. Избегайте использования функции delay(). Пока Arduino занят задержкой, он больше ничего не может сделать. Например, он не может отслеживать изменения температуры на горячей плите. В данном конкретном случае вполне может оказаться, что вам все равно, потому что горячая плита работает так медленно, что вы можете позволить себе опоздать на 400 мс. Но если у вас войдет в привычку полагаться на задержку(), рано или поздно она вас укусит. Поэтому чем раньше вы научитесь мигать светодиодом без использования функции delay(), тем лучше.

  2. Научитесь программировать конечную машину. Это концепция программирования, которая очень хорошо подходит для описания реактивных систем, т. е. систем, которые должны реагировать на внешние “события”. Это очень полезная концепция для написания большинства программ, предназначенных для управления оборудованием. Существует довольно много учебных пособий. Я бы рекомендовал Конечный автоматМайенко.

Вот моя интерпретация ваших требований с точки зрения конечного автомата: ваш Arduino может находиться в любом из 3 состояний: ХОЛОДНОЕ (начальное состояние, пластина не достигла требуемой температуры), ОЖИДАНИЕ (пластина достигла температуры, и мы задерживаем пять секунд), ГОТОВО (пять секунд истекли). Правила изменения состояний таковы:

  • ХОЛОДОЖИДАНИЕ: в ХОЛОДНОМ состоянии, если температура достигает заданного порогового значения, перейдите в состояние ОЖИДАНИЯ.
  • ОЖИДАНИЕГОТОВО: когда вы находитесь в состоянии ОЖИДАНИЯ, по истечении пяти секунд с момента входа в состояние, перейдите в состояние ГОТОВО.
  • Когда мы находимся в состоянии "ГОТОВО" ... больше ничего никогда не происходит.

Вот соответствующая диаграмма состояния:

state diagram

Обратите внимание, что как только мы достигаем состояния ГОТОВО, система застревает. Из этой ситуации можно выйти только перезагрузив 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