0

Thank you everyone for help on my last post, i managed to make it work. I still keep trying to craft my skill with the arduino. This time I need to make a coffee hot plate simulation. When the sensor is not up to temp an LED will blink, once the sensor gets up to temp the LED will blink but turn off (go to low) after five seconds. This is the code i am working on. The sensor is at temp and the LED is still blinking instead of turning off. When its low it blinks as should. I think the bug is in the millis() portion of code. Any ideas would be greatly appreciated. I also ran the println screen, and the temp is consistantly around 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);    
    }
}
  • Sorry but im confused... Someone corrected my last post for me, i followed the instructions, indent than hit the space bar 4 times to activate the code block. Ill try my best to edit it through. my apologies. – Steven Sep 30 '18 at 23:41
  • so, learn how to indent the code properly .... it will help you a lot when you are debugging by showing the code in logical blocks ..... look at example code in the arduino IDE ..... your correction is a good attempt .... i'll fix it though – jsotola Sep 30 '18 at 23:45
  • reformatted the code for you .... look at the end of the code and see if the correct number of closing braces } are there ..... the code reformat will be visible when it is accepted – jsotola Sep 30 '18 at 23:54
  • I see what you are saying now. It does look nicer and easier to follow for myself and others. Ill be sure to follow this format in the future. Thank you. – Steven Oct 1 '18 at 0:17
  • the code inside else if (val == UPPER_BOUND && currentMillis - startMillis == period) { can never run, since if (val == UPPER_BOUND){ will also be true - swap the order of the first two ifs – Jaromanda X Oct 1 '18 at 1:18
1

First, let me point out a few technical issues with your program:

  1. const int sensor = 9; is wrong. On an Uno, you can only use analogRead() on the pins labeled A0 through A5.

  2. if (val == UPPER_BOUND). Never test an analog reading for exact equality. The reading is noisy, and you shouldn't expect continuity. It can very well jump from UPPER_BOUND-1 on one reading to UPPER_BOUND+1 on the next one. Instead, test for val >= UPPER_BOUND.

  3. currentMillis - startMillis == period. Again, you should not test for exact equality. This time it's for a more subtle reason that has to do with the implementation of millis(): some values (roughly one every 42) are skipped over. Here again, test for >= period.

There is also the issue, already pointed out by PhoenixRevealed, of your code defining two thresholds, while your prose only mentions one. This may well be just you being sloppy and not describing your requirements precisely enough.

Next, and since you are interested in perfecting your learning process, let me advise you on two techniques that are worth learning, because they are extremely useful for mostly any Arduino project more complex than just blinking an LED:

  1. Avoid using delay(). While the Arduino is busy delaying, it can do nothing else. For instance, it cannot monitor the temperature changes on the hot plate. In this particular case, it may well be the case that you don't care, because the hot plate is so slow that you can afford to be 400 ms late. But if you get into the habit of relying on delay(), it will bite you sooner or later. So the sooner you learn to blink an LED without using delay(), the better.

  2. Learn to program a finite state machine. This is a programming concept that is very well suited for describing reactive systems, i.e. systems that have to react to external “events”. It is a very useful concept for writing most programs that are meant to control hardware. There are quite a few tutorials out there. I would recommend The Finite State Machine, by Majenko.

Here is my interpretation of your requirements in terms of a finite state machine: your Arduino can be in any of 3 states: COLD (initial state, the plate has not reached the required temperature), WAITING (the plate reached the temperature and we are delaying five seconds), DONE (the five seconds have elapsed). The rules for changing states are:

  • COLDWAITING: when in the COLD state, if the temperature reaches a specified threshold, go to the WAITING state.
  • WAITINGDONE: when in the WAITING state, once five seconds have elapsed since we entered the state, go to the DONE state.
  • When we are in the DONE state... nothing more ever happens.

Here is the corresponding state diagram:

state diagram

Notice that once we reach the DONE state, the system gets stuck. This situation can only be recovered from by rebooting the Arduino. You may want to revise your specification in order accommodate a DONECOLD transition in some way. But from your specification as written in your question, I assume you are fine with the system getting stuck.

I tried to implement this state diagram in code, using the traditional switch/case construct. But then I got lazy and noticed that calling exit() is the easiest way of implementing the DONE state, as this function is precisely designed to get the program stuck in a do-nothing state. Then, since there are only two states left to care about, a boolean can be used as a state variable. In the end, the code I propose may not look like a finite state machine. However, it is important to notice that, event if such case, the finite state machine is a very useful concept for thinking about your program and writing down its requirements.

// 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;
        }
    }
}
  • So when using analogRead() I should never measure or read exact values? I should probably know this by now, but does A0-A5 give off a sine wave compared to a square wave compared to the PWM pins? I appreciate you all giving an explanation on what I am doing wrong. It helps me understand what I need to look for and how to go about things in later projects. – Steven Oct 1 '18 at 12:57
  • @Steven: Please, rephrase the questions in your comment in a clearer way. As written, I cannot make any sense of them. – Edgar Bonet Oct 1 '18 at 13:04
  • Does the A0-A5 pins output a sine wave instead of a square wave, like a PWM pin does? and Would that be the reason why we cannot measure exact values through analogRead()? – Steven Oct 1 '18 at 14:26
  • @Steven: 1. The A0 – A5 pins can serve as analog inputs. Otherwise you can use them as regular digital IO (i.e. output either HIGH or LOW). The Arduino Uno has no analog outputs. It cannot output a sine wave on any pin. 2. The reason you cannot measure exact values is that there is no such thing as an exact measurement in the physical world. But this is not quite the point I discuss in my answer. Instead, I am telling you to never compare a measurement with an expected value using the == operator. The key word here is “test” for exact equality. – Edgar Bonet Oct 1 '18 at 14:55
  • So I did read up on the FSM, and it does seem to be better constructed, than some of what I have been doing. And I get what your explaining now, with the A0-A5 pins. It does make sense that nothing in the physical world has an exact measurement. That is something I did not consider, and explains why with "==" I could not get the proper output i was expecting, even with making suggested edits. – Steven Oct 2 '18 at 15:48
0

I see a couple of issues with your code. The biggest problem is that you capture startMillis in setup() and then compare it with currentMillis in your loop. If it takes more than five seconds for the pot to get hot enough you will never match (currentMillis - startMillis) == period because (currentMillis - startMillis) will already be > period before you start testing it. Changing "== period" to ">= period" will fix this, but it still won't behave the way you want since you should be setting startMillis when the pot first reached the target temp, not at program startup.

Also, your code does nothing when the temp is between LOWER_BOUND and UPPER_BOUND, which I suspect is not what you want. You don't specify whether "up to temp" means at or above LOWER_BOUND or at or above UPPER_BOUND. I'm going to assume that you want the LED to flash when the temp is below LOWER_BOUND, go solid on while the temp is between LOWER_BOUND and UPPER_BOUND, and then turn completely off five seconds after the pot reaches UPPER_BOUND.

Personally I would rewrite it like this, both for functionality and readbility...

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);

            }
        }
    }
}

A couple of points about this implementation...

  1. I have not tested this code on a physical Arduino.
  2. Notice that we don't care to start storing or watching millis() until the pot first reaches UPPER_BOUND. Until then all the program cares about is the temp of the coffee.
  3. Like your original code, this implementation doesn't consider the wraparound of millis() every 50 days or so. It's unlikely a coffee pot will take over 50 days to reach the target temperature, so I wouldn't worry about it, but you should consider this situation when coding long-running applications (I refuse to call them "sketches").
  4. I based this on your original code, so like yours, once the temp reaches at least LOWER_BOUND it continuously writes to the LED pin every pass through the loop, even though it likely hasn't changed since the last iteration. If I was doing this for real I would restructure it so it only writes to the pin when the state of the LED actually changes. This is only an efficiency issue though... writing the same value to a pin repeatedly will work fine, it just isn't very elegant.
  5. If the coffee starts to cool down after reaching UPPER_BOUND then behaviour is undefined. I suspect that it would work normally when the coffee temp is low or medium, but if the coffee reached the UPPER_BOUND the second or more times I believe the LED would turn off immediately rather than after 5 seconds. It wouldn't be difficult to enhance the code to behave correctly no matter how many times the pot temp cycles up and down, but this solution was to the problem as you described it, which was a coffee pot warming up just once from cold to a target temperature.
  • I made a few changes to the code, I added the global variables for led, and sensor. I also eliminated lower bound, and only kept to measure upper bound. It worked really well. Between this code and my code, I noticed I am using a lot of unnecessary variables, and some that are useless. I found that when using both an upper and lower bound, the code would only run the lower bound, im assuming this because, i gave lower bound a value, when it does not need it, So in the program, although it kinda worked it would never work as intended because of that value. – Steven Oct 1 '18 at 12:51
  • I also believe I understand millis() a little better now, I thought millis() had to be started within the setup for it work properly. – Steven Oct 1 '18 at 12:52
  • You have received very valuable comments, all valid.However, to address your original question - once you reach upper bound and continue to apply heat - your code have no means to detect greater then upper bound value. – Jan Hus Oct 1 '18 at 20:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.