Need some coding help

Hi all,
I keep having to take time away from my little dryer project, so I haven’t looked at it in a few weeks. I did some refactoring of my code, but I’m messing up my logic somehow…

What I’m making is an IoT clothes dryer, and I’m getting stuck on updating the settings from the menu. What I want to happen is update the settings using the lcd > that updates a variable called temp selected > if current temp selected != previous temp selected, then temp_setpoint is set equal to temp_selected > temp setpoint controls how hot it gets in the dryer.

What is actually happening is that I can change the temp_selected, but it doesn’t affect the temp_setpoint

time=615, currentMode=2, inside_temp=77.00, inside_moisture=42.00, average= 41, time_selected=60, time_remaining=0, temp_selected=130, temp_setpoint=101, heater on?=0, motor on?=1, button pressed?=1, heater timeout?=1, cooldown start=0

Also, the time_remaning != time_selected, which it should. I’m doing something wrong, but I can’t figure out what it is :confused:

full code is below.
Thanks!
JR


    // This #include statement was automatically added by the Particle IDE.
    #include "Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.h"
    #include "Adafruit_RGBLCDShield/Adafruit_MCP23017.h"
    // This #include statement was automatically added by the Particle IDE.
    #include "Adafruit_DHT/Adafruit_DHT.h"
    //dryer v3 uses the adafruit dht library since the pietteTech lib seems to have stopped working? -it just reads -5.00 all the time
    #include "math.h" 
    #define DHTTYPE DHT11       // Sensor type DHT11/21/22/AM2301/AM2302
    #define DHTPIN 3     // what pin we're connected to
    #define ON 0x7
    #define OFF 0x0
    DHT dht(DHTPIN, DHTTYPE); //adafruit dht init
    Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
    //initialize icons
    byte icons[3][7] = {{ 0x0, 0x4, 0x6, 0x1f, 0x6, 0x4, 0x0},
      {0x0, 0xe, 0x15, 0x17, 0x11, 0xe, 0x0},
      {0x4, 0xe, 0xe, 0x1e, 0x1b, 0xb, 0x6}
    };
    //! Index into the bitmap array for the  icons.
    const int ARROW_ICON_IDX = 0;
    const int CLOCK_ICON_IDX = 1;
    const int FLAME_ICON_IDX = 2;
    
    //constants
    const long meas_interval = 3000;
    const int motorPin = 7;
    const int heaterPin = A5;
    const long heater_timeout_interval = 120000; //2 minute lockout
    const long time_interval = 3000; //check how much time left every 3 seconds
    const long temp_interval = 3000; //check what to set temp to every 3 seconds
    
    
    //variables
    uint8_t clicked_buttons;
    int menu = 0;
    unsigned long previous_print_millis;
    const unsigned long printInterval = 3000;
    unsigned long heater_last_used_millis;
    int motorState = LOW;
    int heaterState = LOW;
    boolean heater_timeout = false;
    unsigned long previous_meas_millis = 0;
    unsigned long previous_time_millis = 0;
    unsigned long previous_temp_millis = 0;
    float inside_temp;
    float inside_moisture;
    int time_remaining = 60;
    int temp_setpoint = 101;
    unsigned long currentMillis;
    unsigned long cooldown_start_millis = 0;
    unsigned long cooldown_interval = 300000;
    
    //button variables
    int buttonPin = 6;         // the number of the input pin
    int buttonReading;           // the current reading from the input pin
    int buttonPrevious = LOW;
    
    //variables and constants for moving average of moisture
    const int numReadings = 10;
    int readings[numReadings];      // the readings from the analog input
    int reading_idx = 0;                  // the index of the current reading
    int total = 0;                  // the running total
    int average_moisture = 0;                // the average
    
    //variables for time and temp selections
    String run_selected;
    int temp_selected;
    int time_selected;
    int prev_temp_selected = 0;
    int prev_time_selected = 0;
    int run_type = 0;
    int prev_time_remaining = 0;
    unsigned long previous_run_millis = 0;
    unsigned long time_elapsed = 0;
    
    enum OperatingMode{
      STANDBY_MODE,
      RUN_MODE,
      COOL_MODE,
      STOP_MODE
    };
    
    OperatingMode currentMode = STANDBY_MODE;
    OperatingMode lastMode = STANDBY_MODE;
    
    //*********************** setup ********************************//
    void setup() 
    {
      Serial.begin(9600);
      lcd.begin(16, 2);
      lcd.setBacklight(ON);
      lcd.createChar(ARROW_ICON_IDX, icons[ARROW_ICON_IDX]);
      lcd.createChar(CLOCK_ICON_IDX, icons[CLOCK_ICON_IDX]);
      lcd.createChar(FLAME_ICON_IDX, icons[FLAME_ICON_IDX]);
      pinMode(motorPin, OUTPUT);
      pinMode(heaterPin, OUTPUT);
      pinMode(buttonPin, INPUT);
      dht.begin(); //adafruit dht start
      for (int thisReading = 0; thisReading < numReadings; thisReading++) //initialize counter for moving average of moisture
        readings[thisReading] = 0;
      lcd.setCursor(0, 0);
      lcd.write(FLAME_ICON_IDX);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print(" Welcome to ");
      lcd.write(CLOCK_ICON_IDX);
      lcd.write(FLAME_ICON_IDX);
      lcd.setCursor(2, 1);
      lcd.print("Dryerbot9000");
      delay(2000);
      lcd.clear();
      menu_fun();
    }
    //
    //
    //********************************* loop *****************************//
    //
    //
    ??
    void loop() 
    {
      delay(10);
      currentMillis = millis();
      read_button_clicks();
      menu_fun();
      digitalWrite(motorPin, motorState);
      digitalWrite(heaterPin, heaterState);
      evaluateSensors();  //force a state change on evaluation of your sensor data, you can even do this at an interval
      print_fun();
    
    
    
      if (lastMode != currentMode)
      {
        //doSomething ?
        lastMode = currentMode;
      }
      switch(currentMode){
        case (STANDBY_MODE):
          motorState = LOW;
          heaterState = LOW;
          menu = 0;
          break;
        case (RUN_MODE):
          menu = 999;
          motorState = HIGH;
          //do HEAT_MODE stuff
          //check time remaining 
          //check temp setting
          check_time_remaining();
          check_temp_setting();
          heater_timeout_check();
          if (inside_temp <= temp_setpoint && heater_timeout == false) {
            heaterState = HIGH;
          }
          else {
            heaterState = LOW;
          }
          break;
        case (COOL_MODE):
          motorState = HIGH;
          // do COOL_MODE stuff
          break;
        case (STOP_MODE):
          motorState = LOW;
          heaterState = LOW;
          menu = 0;
          // do STOP_MODE stuff
          break;
      } 
    }
    
    void evaluateSensors(){ 
      buttonReading = digitalRead(buttonPin);
      total = total - readings[reading_idx];
      if (currentMillis - previous_meas_millis >= meas_interval) {
        previous_meas_millis = currentMillis;
        readings[reading_idx] = read_inside_moisture();
        read_inside_temp();
      }
      total = total + readings[reading_idx];
      reading_idx = reading_idx + 1;
      if (reading_idx >= numReadings){
        reading_idx = 0;
      }
      average_moisture = total / numReadings;
    
      if (buttonReading == LOW){
        currentMode = STANDBY_MODE;
      }
    
      if (buttonReading == HIGH && time_remaining >0){
        currentMode = RUN_MODE;
      }
    
      if (buttonReading == HIGH && time_remaining <=0 && time_remaining > -5){
        currentMode = COOL_MODE;
      }
    
      if (buttonReading == HIGH && time_remaining <= -5) {
        currentMode = STOP_MODE;
      }
    }
    
    int check_time_remaining(){
      if (prev_time_selected != time_selected){
        prev_time_selected = time_selected;
        if(prev_time_selected == 'auto'){
          auto_time();
        }
        else{ 
          time_remaining = time_selected;
        }
      }
      else{ 
        time_remaining = prev_time_selected - (currentMillis/60000);
      }
    }
    
    int check_temp_setting(){
      if (prev_temp_selected != temp_selected){
        prev_temp_selected = temp_selected;
        if(prev_temp_selected =='auto'){
          auto_heat();
        }
        else{
          temp_setpoint = temp_selected;
        }
      }
    }
    
    
    int auto_time(){
      time_remaining = map(average_moisture, 35,52,0,60);
      return time_remaining;
    }
    
    
    int auto_heat() {
      if (average_moisture < 35)
      {
        temp_setpoint = 0;
      }
      else
      {
        temp_setpoint = 10 * (constrain(map(average_moisture, 35, 52, 100, 160), 100, 160) / 10);
      }
      return temp_setpoint;
    }
    
    
    
    float read_inside_temp() {
      // Read temperature as farenheit
      inside_temp = dht.getTempFarenheit();
      // Check if any reads failed and exit early (to try again).
      if (isnan(inside_temp)) {
        //lcd.print("Failed to read from DHT sensor!");
        Serial.println("Failed to read from DHT sensor!");
      }
      return inside_temp; 
    }
    
    float read_inside_moisture() {
      inside_moisture = dht.getHumidity();
      if (isnan(inside_moisture)) {
        //lcd.print("Failed to read from DHT sensor!");
        Serial.println("Failed to read from DHT sensor!");
      }
      return inside_moisture;
    }
    
    boolean heater_timeout_check() {
      //heaterState = digitalRead(heaterPin);
      if (heaterState == HIGH) {
        heater_last_used_millis = currentMillis;
        heater_timeout = false;
      }
    
      else if (heaterState == LOW && (currentMillis - heater_last_used_millis >= heater_timeout_interval)) {
        heater_timeout = false;
      }
      else {
        heater_timeout = true;
      }
      return heater_timeout;
    }
    
    
    void read_button_clicks() {
      lcd.setBacklight(ON);
      static uint8_t last_buttons = 0;
      uint8_t buttons = lcd.readButtons();
      clicked_buttons = (last_buttons ^ buttons) & (~buttons);
      last_buttons = buttons;
      /*if (clicked_buttons) {
        menu_fun();
      }*/
    
    }
    int prev_menu=-1;
    
    int menu_fun() {
      if (clicked_buttons & BUTTON_RIGHT) {
        menu++;
      }
      if (clicked_buttons & BUTTON_LEFT) {
        menu--;
      }
      if (menu > 2 && menu < 100) {
        menu = 0;
      }
      if (menu < 0) {
        menu = 2;
      }
      if (menu > 106 && menu < 200) {
        menu = 101;
      }
      if (menu < 101 && menu > 99) {
        menu = 106;
      }
      if (menu > 205 && menu < 300) {
        menu = 201;
      }
      if (menu < 201 && menu > 199) {
        menu = 205;
      }
      switch (menu) {
        case 0: //time
          //lcd.clear();
          lcd.setCursor(0,0);
          lcd.write(ARROW_ICON_IDX);
          lcd.print("time ");
          lcd.print("heat         ");
          //if (time_selected && temp_selected){
          lcd.setCursor(0,1);
          lcd.print("time:");
          lcd.print(time_selected);
          lcd.print(" temp:");
          lcd.print(temp_selected);//}
          if (clicked_buttons & (BUTTON_SELECT | BUTTON_DOWN)) {
            lcd.clear();
            menu=101;
          }
          break;
        case 1: //heat
        lcd.setCursor(0,0);
          lcd.print(" time");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("heat        ");
          //if (time_selected && temp_selected){
          lcd.setCursor(0,1);
          lcd.print("time:");
          lcd.print(time_selected);
          lcd.print(" temp:");
          lcd.print(temp_selected);//}
          if (clicked_buttons & (BUTTON_SELECT | BUTTON_DOWN)) {
            lcd.clear();
            menu = 201;
          }
          //prev_menu = 1;
          break;
        case 101: //time auto
          lcd.setCursor(0,0);
          lcd.write(CLOCK_ICON_IDX);
          lcd.print("time           ");
          lcd.setCursor(0, 1);
          lcd.write(ARROW_ICON_IDX);
          lcd.print("Auto 90 60 45 30 15");
          
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            time_selected = -99;
            lcd.clear();
            lcd.print("Auto Time");
            delay(1000);
            lcd.clear();
            menu =1;
          }
          break;
        case 102: //time 90
          lcd.setCursor(0,0);
          lcd.write(CLOCK_ICON_IDX);
          lcd.print("time           ");
          lcd.setCursor(0, 1);
          lcd.print(" Auto");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("90 60 45 30 15");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            time_selected = 90;
            lcd.clear();
            lcd.print("90 Minutes");
            delay(1000);
            lcd.clear();
            menu =1;
          }
          break;
        case 103: //time 60
          lcd.setCursor(0,0);
          lcd.write(CLOCK_ICON_IDX);
          lcd.print("time           ");
          lcd.setCursor(0, 1);
          lcd.print(" Auto 90");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("60 45 30 15");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            time_selected = 60;
            lcd.clear();
            lcd.print("60 Minutes");
            delay(1000);
            lcd.clear();
            menu = 1;
          }
          break;
        case 104: //time 45
          lcd.setCursor(0,0);
          lcd.write(CLOCK_ICON_IDX);
          lcd.print("time           ");
          lcd.setCursor(0, 1);
          lcd.print(" Auto 90 60");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("45 30 15");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            time_selected = 45;
            lcd.clear();
            lcd.print("45 Minutes");
            delay(1000);
            lcd.clear();
            menu =1;
          }
          break;
        case 105: //time30
          lcd.setCursor(0,0);
          lcd.write(CLOCK_ICON_IDX);
          lcd.print("time           ");
          lcd.setCursor(0, 1);
          lcd.print("90 60 45");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("30 15  ");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            time_selected = 30;
            lcd.clear();
            lcd.print("30 Minutes");
            delay(1000);
            lcd.clear();
            menu =1;
          }
          break;
        case 106:  //time 15
          lcd.setCursor(0,0);
          lcd.write(CLOCK_ICON_IDX);
          lcd.print("time           ");
          lcd.setCursor(0, 1);
          lcd.print("90 60 45 30");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("15  ");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            time_selected = 15;
            lcd.clear();
            lcd.print("15 Minutes");
            delay(1000);
            lcd.clear();
            menu =1;
          }
          break;
        case 201: //heat auto
          lcd.setCursor(0,0);
          lcd.write(FLAME_ICON_IDX);
          lcd.print("heat           ");
          lcd.setCursor(0, 1);
          lcd.write(ARROW_ICON_IDX);
          lcd.print("Auto Hi Med Low Off");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            temp_selected = -99;
            lcd.clear();
            lcd.print("Auto heat");
            delay(1000);
            lcd.clear();
            menu =0;
          }
    
          break;
        case 202:  //heat hi
          lcd.setCursor(0,0);
          lcd.write(FLAME_ICON_IDX);
          lcd.print("heat           ");
          lcd.setCursor(0, 1);
          lcd.print(" Auto");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("Hi Med Low Off");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            temp_selected = 150;
            lcd.clear();
            lcd.print("High heat");
            delay(1000);
            lcd.clear();
            menu = 0;
          }
          break;
        case 203: //heat med
          lcd.setCursor(0,0);
          lcd.write(FLAME_ICON_IDX);
          lcd.print("heat           ");
          lcd.setCursor(0, 1);
          lcd.print(" Auto Hi");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("Med Low Off");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            temp_selected = 130;
            lcd.clear();
            lcd.print("Medium heat");
            delay(1000);
            lcd.clear();
            menu = 0;
          }
          break;
        case 204:  //heat low
          lcd.setCursor(0,0);
          lcd.write(FLAME_ICON_IDX);
          lcd.print("heat           ");
          lcd.setCursor(0, 1);
          lcd.print("Hi Med");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("Low Off  ");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            temp_selected = 110;
            lcd.clear();
            lcd.print("Low heat");
            delay(1000);
            lcd.clear();
            menu = 0;
            
          }
          break;
        case 205: //heat off
          lcd.setCursor(0,0);
          lcd.write(FLAME_ICON_IDX);
          lcd.print("heat           ");
          lcd.setCursor(0, 1);
          lcd.print("Hi Med Low");
          lcd.write(ARROW_ICON_IDX);
          lcd.print("Off  ");
          if (clicked_buttons & BUTTON_UP) {
            lcd.clear();
            menu = 0;
          }
          if (clicked_buttons & BUTTON_SELECT) {
            temp_selected = 32;
            lcd.clear();
            lcd.print("Heat Off");
            delay(1000);
            lcd.clear();
            menu = 0;
          }
          break;
          
        case 999: //display in run mode
         lcd.setCursor(0,0);
         lcd.write(CLOCK_ICON_IDX);
          lcd.print(":");
          lcd.print(time_remaining);
          lcd.print(" dry:");
          lcd.print(map(average_moisture,35,100,100,0));
          lcd.print(" ");
          lcd.print(currentMode);
          //if (time_selected && temp_selected){
          lcd.setCursor(0,1);
          lcd.print("time:");
          lcd.print(time_selected);
          lcd.print(" temp:");
          lcd.print(temp_selected);//}
          if (clicked_buttons) {
            lcd.clear();
            menu=0;
          }
          break;
      }
    }
    void print_fun(){
      if (currentMillis - previous_print_millis >= printInterval) {
        previous_print_millis = currentMillis;
    
        Serial.print("time=");
        Serial.print(currentMillis / 1000);
        Serial.print(", ");
        Serial.print("currentMode=");
        Serial.print(currentMode);
        Serial.print(", ");
        Serial.print("inside_temp=");
        Serial.print(inside_temp);
        Serial.print(", ");
        Serial.print("inside_moisture=");
        Serial.print(inside_moisture);
        Serial.print(", average= ");
        Serial.print(average_moisture);
        Serial.print(", ");
        Serial.print("time_selected=");
        Serial.print(time_selected);
        Serial.print(", ");
        Serial.print("time_remaining=");
        Serial.print(time_remaining);
        Serial.print(", ");
        Serial.print("temp_selected=");
        Serial.print(temp_selected);
        Serial.print(", ");
        Serial.print("temp_setpoint=");
        Serial.print(temp_setpoint);
        Serial.print(", ");
        Serial.print("heater on?=");
        Serial.print(heaterState);
        Serial.print(", ");
        Serial.print("motor on?=");
        Serial.print(motorState);
        Serial.print(", ");
        Serial.print("button pressed?=");
        Serial.print(buttonReading);
        Serial.print(", ");
        Serial.print("heater timeout?=");
        Serial.print(heater_timeout);
        Serial.print(", cooldown start=");
        Serial.println(cooldown_start_millis / 1000);
        /*
        lcd.clear();
          lcd.setCursor(0, 0);
        lcd.print("tr=");
        lcd.print(time_remaining);
        lcd.print(" ");
        lcd.print("h=");
        lcd.print(heaterState);
        lcd.print("ts=");
        lcd.print(temp_setpoint);
            lcd.setCursor(0, 1);
        lcd.print("temp=");
        lcd.print(inside_temp);
        lcd.print(" ");
        lcd.print("moist=");
        lcd.print(inside_moisture);
        */
      }
    }

This is a lot of code to plough through.

The first step for you should be to narrow down the possible location of the problem in your code.
Try to add some Serial.print() debug statements wherever you intend to set your variables and check wheater they are really set correctly and when/where they start loosing their desired content.

After you have done that but can’t find an explanation why this happens, you might ask here.

One thing I saw in your code was this

    //
    //
    //********************************* loop *****************************//
    //
    //
    ??
    void loop() 

Does this even build?

And you have several functions that should return an int but don’t feature a return statement at all.

1 Like

It builds fine, the ?? was accidental as I was copying this over
Sometimes you type things and it goes in the wrong window, y’know . :slight_smile:

I thought I wouldn’t need a return if I’m using a global variable. I’ll add some returns and see if that works.
In my print statements, I can see that temp_selected is getting updated, but not temp_setpoint. Is that likely because I’m not returning in the section below?

int check_temp_setting(){
  if (prev_temp_selected != temp_selected){
    prev_temp_selected = temp_selected;
    if(prev_temp_selected =='auto'){
      auto_heat();
    }
    else{
      temp_setpoint = temp_selected;
    }
  }
}

Thanks,
JR

If you don’t want to return anything you should declare your function void check_temp_setting(), but if you declare it int ... you need to return an int

Try something like this

int check_temp_setting(){
Serial.printlnf("1. sel: %d, prev %d, set %d", temp_selected, prev_temp_selected, temp_setpoint);

  if (prev_temp_selected != temp_selected){
    prev_temp_selected = temp_selected;
    if(prev_temp_selected =='auto'){
      auto_heat();
    }
    else{
Serial.printlnf("2. sel: %d, prev %d, set %d", temp_selected, prev_temp_selected, temp_setpoint);
      temp_setpoint = temp_selected;
Serial.printlnf("3. sel: %d, prev %d, set %d", temp_selected, prev_temp_selected, temp_setpoint);
    }
  }
Serial.printlnf("4. sel: %d, prev %d, set %d", temp_selected, prev_temp_selected, temp_setpoint);

  return temp_selected;
}

To see if things get updated as intended and then follow along in your other code, if some other statement ruins your variables (even some mem violation might cause this - missing returns can cause this).

Thanks for the help @ScruffR, with the advice on print statements, I was able to figure out that check_temp_setting wasn’t being called because I was calling it in RUN_MODE, which it wasn’t entering because time_remaining was never > 0.

I’ve fixed that and a few other issues that crop up with the LCD, but now I’m back to an issue that I had a while ago - I want to be able to treat ‘auto’ as a special case of setting the time and temp, but if I keep the time_selected as int I can’t check against the string ‘auto’ because ‘auto’ becomes some very long integer. What is the best way to handle that? Earlier, I was trying to just use the numbers as strings, and convert to int when I needed to do math.

Thanks again,
JR

I don’t quite see your problem there.
The way you use 'auto' there (which is not really a C string, which would be written as "auto") is just a bunch of bytes. All four of them can be pulled together into one 32bit integer and hence will give you a large number.
But if you use this as “magic number” you can still use it as you intended. You only need to check for your variables being != 'auto' before you do any int operations on it or print the value.

This is not a string compare but checking one integer against another, which you provide as a “base 256” number.
Just as you can write if (48 == 0x30) you can write if (48 == '0') - both would be true.

Thanks for the help again,
The thing about it not being a real string (I’d forgotten that c “” is string and ‘’ is an array of chars, cursed Python backwards masking!) was the solution. Once made a real string, then temp_selected.toInt() does the thing it says and I’m off to the races.

In case anyone is curious, here’s the whole batch of code. I may yet need to edit it to support a different LCD, but maybe not, since the i2c lcd seems to be running much better than it had (knock on wood).

Thanks,
JR

// This #include statement was automatically added by the Particle IDE.
#include "blynk/blynk.h"
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.h"
#include "Adafruit_RGBLCDShield/Adafruit_MCP23017.h"
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_DHT/Adafruit_DHT.h"
//dryer v3 uses the adafruit dht library since the pietteTech lib seems to have stopped working? -it just reads -5.00 all the time
#include "math.h" 
#define DHTTYPE DHT11       // Sensor type DHT11/21/22/AM2301/AM2302
#define DHTPIN 3     // what pin we're connected to
#define ON 0x7
#define OFF 0x0
#define BLYNK_PRINT Serial
#include "blynk/BlynkSimpleParticle.h"

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "6302c6854861441a82dd0586fa4f9e5d";
DHT dht(DHTPIN, DHTTYPE); //adafruit dht init
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
//initialize icons
byte icons[3][7] = {{ 0x0, 0x4, 0x6, 0x1f, 0x6, 0x4, 0x0},
  {0x0, 0xe, 0x15, 0x17, 0x11, 0xe, 0x0},
  {0x4, 0xe, 0xe, 0x1e, 0x1b, 0xb, 0x6}
};
//! Index into the bitmap array for the  icons.
const int ARROW_ICON_IDX = 0;
const int CLOCK_ICON_IDX = 1;
const int FLAME_ICON_IDX = 2;

//constants
const long meas_interval = 3000;
const int motorPin = 7;
const int heaterPin = A5;
const long heater_timeout_interval = 120000; //2 minute lockout
const long time_interval = 3000; //check how much time left every 3 seconds
const long temp_interval = 3000; //check what to set temp to every 3 seconds


//variables
uint8_t clicked_buttons;
int menu = 0;
unsigned long previous_print_millis;
const unsigned long printInterval = 3000;
unsigned long heater_last_used_millis;
int motorState = LOW;
int heaterState = LOW;
boolean heater_timeout = false;
unsigned long previous_meas_millis = 0;
unsigned long previous_time_millis = 0;
unsigned long previous_temp_millis = 0;
float inside_temp;
float inside_moisture;
int time_remaining = 60;
int temp_setpoint = 101;
unsigned long currentMillis;
unsigned long cooldown_start_millis = 0;
unsigned long cooldown_interval = 300000;

//button variables
int buttonPin = 6;         // the number of the input pin
int buttonReading;           // the current reading from the input pin
int buttonPrevious = LOW;
int swPin = 5;
int HWswitch;
int SWswitch;

//variables and constants for moving average of moisture
const int numReadings = 10;
int readings[numReadings];      // the readings from the analog input
int reading_idx = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average_moisture = 0;                // the average

//variables for time and temp selections
String run_selected;
String temp_selected = "auto";
String time_selected = "auto";
String prev_temp_selected = 0;
String prev_time_selected = 0;
int run_type = 0;
int prev_time_remaining = 0;
unsigned long previous_run_millis = 0;
unsigned long time_elapsed = 0;

enum OperatingMode{
  STANDBY_MODE,
  RUN_MODE,
  COOL_MODE,
  STOP_MODE
};

OperatingMode currentMode = STANDBY_MODE;
OperatingMode lastMode = STANDBY_MODE;
const char* currentModeText[] = {"Standby","Run","Cooldown","Stop"};
//*********************** setup ********************************//
void setup() 
{
  Serial.begin(9600);
  delay(1000);
  lcd.begin(16, 2);
  lcd.setBacklight(ON);
  lcd.createChar(ARROW_ICON_IDX, icons[ARROW_ICON_IDX]);
  lcd.createChar(CLOCK_ICON_IDX, icons[CLOCK_ICON_IDX]);
  lcd.createChar(FLAME_ICON_IDX, icons[FLAME_ICON_IDX]);
  pinMode(motorPin, OUTPUT);
  pinMode(heaterPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  dht.begin(); //adafruit dht start
  for (int thisReading = 0; thisReading < numReadings; thisReading++) //initialize counter for moving average of moisture
    readings[thisReading] = 0;
  lcd.setCursor(0, 0);
  lcd.write(FLAME_ICON_IDX);
  lcd.write(CLOCK_ICON_IDX);
  lcd.print(" Welcome to ");
  lcd.write(CLOCK_ICON_IDX);
  lcd.write(FLAME_ICON_IDX);
  lcd.setCursor(2, 1);
  lcd.print("Dryerbot9000");
  delay(2000);
  lcd.clear();
  menu_fun();
  Blynk.begin(auth);
}
//
//
//********************************* loop *****************************//
//
//
void loop() 
{
  delay(10);
  currentMillis = millis();
  read_button_clicks();
  menu_fun();
  digitalWrite(motorPin, motorState);
  digitalWrite(heaterPin, heaterState);
  evaluateSensors();  //force a state change on evaluation of your sensor data, you can even do this at an interval
  print_fun();
  Blynk.run();

  if (lastMode != currentMode)
  {
    //doSomething ?
    if(currentMode ==COOL_MODE){
        Blynk.notify("Cooldown Mode");
    }
    else if(currentMode == STOP_MODE){
        Blynk.notify("Stopping");
    }
    else if(currentMode == RUN_MODE){
        Blynk.notify("Starting");
    }
    lastMode = currentMode;
  }
  switch(currentMode){
    case (STANDBY_MODE):
      motorState = LOW;
      heaterState = LOW;
      //menu = 0;
      break;
    case (RUN_MODE):
      cooldown_start_millis = currentMillis;
      motorState = HIGH;
      heater_timeout_check();
      if (inside_temp <= temp_setpoint && heater_timeout == false) {
        heaterState = HIGH;
      }
      else {
        heaterState = LOW;
      }
      break;
    case (COOL_MODE):
      motorState = HIGH;
      // do COOL_MODE stuff
      break;
    case (STOP_MODE):
      motorState = LOW;
      heaterState = LOW;
      // do STOP_MODE stuff
      break;
  } 
}
/*blynk functions for setting time*/
BLYNK_WRITE(V6) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    time_selected = "auto";
  }
}

BLYNK_WRITE(V7) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    time_selected = 90;
  }
}

BLYNK_WRITE(V8) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    time_selected = 60;
  }
}

BLYNK_WRITE(V9) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    time_selected = 30;
  }
}

/*blynk functions for setting temp*/
BLYNK_WRITE(V10) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    temp_selected = "auto";
  }
}

BLYNK_WRITE(V11) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    temp_selected = 150;
  }
}

BLYNK_WRITE(V12) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    temp_selected = 120;
  }
}

BLYNK_WRITE(V13) //Button Widget is writing to pin V1
{
  int pinData = param.asInt(); 
  if (pinData == HIGH){
    temp_selected = 0;
  }
}




void sendDryness()
{
  // This function sends Arduino up time every 1 second to Virtual Pin (V5)
  // In the app, Widget's reading frequency should be set to PUSH
  // You can send anything with any interval using this construction
  // Don't send more that 10 values per second
  Blynk.virtualWrite(V0, currentModeText[currentMode]);
  Blynk.virtualWrite(V1, map(average_moisture,35,100,100,0));
  Blynk.virtualWrite(V2, time_remaining);
  Blynk.virtualWrite(V3, temp_setpoint);
  Blynk.virtualWrite(V4, HWswitch);
  Blynk.virtualWrite(V5, SWswitch);
}

bool read_on_off(){
  HWswitch = digitalRead(buttonPin);
  SWswitch = digitalRead(swPin);
  bool prev_buttonReading;
  if (HWswitch == HIGH && SWswitch == HIGH){
    buttonReading = HIGH;
    prev_buttonReading = buttonReading;
  }
  else{
    buttonReading = LOW;
    prev_buttonReading = buttonReading;
  }
return buttonReading;
}

void evaluateSensors(){ 
  read_on_off();
  total = total - readings[reading_idx];
  if (currentMillis - previous_meas_millis >= meas_interval) {
    previous_meas_millis = currentMillis;
    readings[reading_idx] = read_inside_moisture();
    read_inside_temp();
    check_time_remaining();
    check_temp_setting();
    sendDryness();
  }
  total = total + readings[reading_idx];
  reading_idx = reading_idx + 1;
  if (reading_idx >= numReadings){
    reading_idx = 0;
  }
  average_moisture = total / numReadings;

  if (buttonReading == LOW){
    currentMode = STANDBY_MODE;
  }

  if (buttonReading == HIGH && time_remaining >0){
    currentMode = RUN_MODE;
  }

  if (buttonReading == HIGH && time_remaining <=0 && time_remaining > -5){
    currentMode = COOL_MODE;
  }
  if (currentMode == COOL_MODE && (currentMillis - cooldown_start_millis) > 300000){
    currentMode = STOP_MODE;
  }
  if (buttonReading == HIGH && time_remaining <= -5) {
    currentMode = STOP_MODE;
  }
}

int check_time_remaining(){
  Serial.println("check_time_remaining");
  if (prev_time_selected != time_selected){
    Serial.println("prev_time_selected != time_selected");
    prev_time_selected = time_selected;
    if(time_selected == "auto"){
      Serial.println("auto_time");
      auto_time();
    }
    else{ 
      Serial.println("time_remaining = time_selected.toInt()");
      time_remaining = time_selected.toInt();
    }
  }
  else if(prev_time_selected == "auto"){
    Serial.println("auto_time2");
    auto_time();
  }
  else{ 
    time_remaining = prev_time_selected.toInt() - (currentMillis/60000);
  }
  return time_remaining;
}

int check_temp_setting(){
  if (prev_temp_selected != temp_selected){
    prev_temp_selected = temp_selected;
    if(temp_selected =="auto"){
      auto_heat();
    }
    else{
      temp_setpoint = temp_selected.toInt();
    }
  }
  else if (temp_selected == "auto"){
    auto_heat();
  }
  else {
    temp_setpoint = temp_selected.toInt();
  }
  return temp_setpoint;
}


int auto_time(){
  Serial.println("auto time");
  time_remaining = constrain(map(average_moisture, 35,52,0,60),0,60);
  return time_remaining;
}


int auto_heat() {
  Serial.println("auto heat");
  if (average_moisture < 35)
  {
    temp_setpoint = 0;
  }
  else
  {
    temp_setpoint = 10 * (constrain(map(average_moisture, 35, 52, 100, 160), 100, 160) / 10);
  }
  return temp_setpoint;
}



float read_inside_temp() {
  // Read temperature as farenheit
  inside_temp = dht.getTempFarenheit();
  // Check if any reads failed and exit early (to try again).
  if (isnan(inside_temp)) {
    //lcd.print("Failed to read from DHT sensor!");
    Serial.println("Failed to read from DHT sensor!");
  }
  return inside_temp; 
}

float read_inside_moisture() {
  inside_moisture = dht.getHumidity();
  if (isnan(inside_moisture)) {
    //lcd.print("Failed to read from DHT sensor!");
    Serial.println("Failed to read from DHT sensor!");
  }
  return inside_moisture;
}

boolean heater_timeout_check() {
  //heaterState = digitalRead(heaterPin);
  if (heaterState == HIGH) {
    heater_last_used_millis = currentMillis;
    heater_timeout = false;
  }

  else if (heaterState == LOW && (currentMillis - heater_last_used_millis >= heater_timeout_interval)) {
    heater_timeout = false;
  }
  else {
    heater_timeout = true;
  }
  return heater_timeout;
}



void read_button_clicks() {
  lcd.setBacklight(ON);
  static uint8_t last_buttons = 0;
  uint8_t buttons = lcd.readButtons();
  clicked_buttons = (last_buttons ^ buttons) & (~buttons);
  last_buttons = buttons;
  /*if (clicked_buttons) {
    menu_fun();
  }*/

}
int prev_menu=-1;
int menu_last_clicked_millis=0;
int menu_fun() {
  if (!clicked_buttons && (currentMillis - menu_last_clicked_millis >= 60000)){
    menu = 999;
  }
  if(clicked_buttons){
    menu_last_clicked_millis = currentMillis;
    if(menu == 999){
      lcd.clear();
      menu = 0;
    }
  }
  if (clicked_buttons & BUTTON_RIGHT) {
    menu++;
  }
  if (clicked_buttons & BUTTON_LEFT) {
    menu--;
  }
  if (menu > 2 && menu < 100) {
    menu = 0;
  }
  if (menu < 0) {
    menu = 2;
  }
  if (menu > 106 && menu < 200) {
    menu = 101;
  }
  if (menu < 101 && menu > 99) {
    menu = 106;
  }
  if (menu > 205 && menu < 300) {
    menu = 201;
  }
  if (menu < 201 && menu > 199) {
    menu = 205;
  }
  switch (menu) {
    case 0: //time
      //lcd.clear();
      lcd.setCursor(0,0);
      lcd.write(ARROW_ICON_IDX);
      lcd.print("time ");
      lcd.print("heat         ");
      //if (time_selected && temp_selected){
      lcd.setCursor(0,1);
      lcd.print("time:");
      lcd.print(time_selected);
      lcd.print(" temp:");
      lcd.print(temp_selected);//}
      if (clicked_buttons & (BUTTON_SELECT | BUTTON_DOWN)) {
        lcd.clear();
        menu=101;
      }
      break;
    case 1: //heat
    lcd.setCursor(0,0);
      lcd.print(" time");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("heat        ");
      //if (time_selected && temp_selected){
      lcd.setCursor(0,1);
      lcd.print("time:");
      lcd.print(time_selected);
      lcd.print(" temp:");
      lcd.print(temp_selected);//}
      if (clicked_buttons & (BUTTON_SELECT | BUTTON_DOWN)) {
        lcd.clear();
        menu = 201;
      }
      //prev_menu = 1;
      break;
    case 101: //time auto
      lcd.setCursor(0,0);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print("time           ");
      lcd.setCursor(0, 1);
      lcd.write(ARROW_ICON_IDX);
      lcd.print("Auto 90 60 45 30 15");
      
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        time_selected = "auto";
        lcd.clear();
        lcd.print("Auto Time");
        delay(1000);
        lcd.clear();
        menu =1;
      }
      break;
    case 102: //time 90
      lcd.setCursor(0,0);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print("time           ");
      lcd.setCursor(0, 1);
      lcd.print(" Auto");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("90 60 45 30 15");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        time_selected = 90;
        lcd.clear();
        lcd.print("90 Minutes");
        delay(1000);
        lcd.clear();
        menu =1;
      }
      break;
    case 103: //time 60
      lcd.setCursor(0,0);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print("time           ");
      lcd.setCursor(0, 1);
      lcd.print(" Auto 90");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("60 45 30 15");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        time_selected = 60;
        lcd.clear();
        lcd.print("60 Minutes");
        delay(1000);
        lcd.clear();
        menu = 1;
      }
      break;
    case 104: //time 45
      lcd.setCursor(0,0);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print("time           ");
      lcd.setCursor(0, 1);
      lcd.print(" Auto 90 60");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("45 30 15");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        time_selected = 45;
        lcd.clear();
        lcd.print("45 Minutes");
        delay(1000);
        lcd.clear();
        menu =1;
      }
      break;
    case 105: //time30
      lcd.setCursor(0,0);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print("time           ");
      lcd.setCursor(0, 1);
      lcd.print("90 60 45");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("30 15  ");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        time_selected = 30;
        lcd.clear();
        lcd.print("30 Minutes");
        delay(1000);
        lcd.clear();
        menu =1;
      }
      break;
    case 106:  //time 15
      lcd.setCursor(0,0);
      lcd.write(CLOCK_ICON_IDX);
      lcd.print("time           ");
      lcd.setCursor(0, 1);
      lcd.print("90 60 45 30");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("15  ");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        time_selected = 15;
        lcd.clear();
        lcd.print("15 Minutes");
        delay(1000);
        lcd.clear();
        menu =1;
      }
      break;
    case 201: //heat auto
      lcd.setCursor(0,0);
      lcd.write(FLAME_ICON_IDX);
      lcd.print("heat           ");
      lcd.setCursor(0, 1);
      lcd.write(ARROW_ICON_IDX);
      lcd.print("Auto Hi Med Low Off");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        temp_selected = "auto";
        lcd.clear();
        lcd.print("Auto heat");
        delay(1000);
        lcd.clear();
        menu =0;
      }

      break;
    case 202:  //heat hi
      lcd.setCursor(0,0);
      lcd.write(FLAME_ICON_IDX);
      lcd.print("heat           ");
      lcd.setCursor(0, 1);
      lcd.print(" Auto");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("Hi Med Low Off");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        temp_selected = 150;
        lcd.clear();
        lcd.print("High heat");
        delay(1000);
        lcd.clear();
        menu = 0;
      }
      break;
    case 203: //heat med
      lcd.setCursor(0,0);
      lcd.write(FLAME_ICON_IDX);
      lcd.print("heat           ");
      lcd.setCursor(0, 1);
      lcd.print(" Auto Hi");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("Med Low Off");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        temp_selected = 130;
        lcd.clear();
        lcd.print("Medium heat");
        delay(1000);
        lcd.clear();
        menu = 0;
      }
      break;
    case 204:  //heat low
      lcd.setCursor(0,0);
      lcd.write(FLAME_ICON_IDX);
      lcd.print("heat           ");
      lcd.setCursor(0, 1);
      lcd.print("Hi Med");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("Low Off  ");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        temp_selected = 110;
        lcd.clear();
        lcd.print("Low heat");
        delay(1000);
        lcd.clear();
        menu = 0;
        
      }
      break;
    case 205: //heat off
      lcd.setCursor(0,0);
      lcd.write(FLAME_ICON_IDX);
      lcd.print("heat           ");
      lcd.setCursor(0, 1);
      lcd.print("Hi Med Low");
      lcd.write(ARROW_ICON_IDX);
      lcd.print("Off  ");
      if (clicked_buttons & BUTTON_UP) {
        lcd.clear();
        menu = 0;
      }
      if (clicked_buttons & BUTTON_SELECT) {
        temp_selected = 32;
        lcd.clear();
        lcd.print("Heat Off");
        delay(1000);
        lcd.clear();
        menu = 0;
      }
      break;
      
    case 999: //display in run mode
     lcd.setCursor(0,0);
     lcd.write(CLOCK_ICON_IDX);
      lcd.print(":");
      lcd.print(time_remaining);
      lcd.print(" dry:");
      lcd.print(map(average_moisture,35,100,100,0));
      lcd.print(" ");
      lcd.print(currentMode);
      //if (time_selected && temp_selected){
      lcd.setCursor(0,1);
      lcd.print("time:");
      lcd.print(time_selected);
      lcd.print(" temp:");
      lcd.print(temp_selected);//}
      if (clicked_buttons) {
        lcd.clear();
        menu=0;
      }
      break;
  }
}
void print_fun(){
  if (currentMillis - previous_print_millis >= printInterval) {
    previous_print_millis = currentMillis;

    Serial.print("time=");
    Serial.print(currentMillis / 1000);
    Serial.print(", ");
    Serial.print("currentMode=");
    Serial.print(currentMode);
    Serial.print(", ");
    Serial.print("inside_temp=");
    Serial.print(inside_temp);
    Serial.print(", ");
    Serial.print("inside_moisture=");
    Serial.print(inside_moisture);
    Serial.print(", average= ");
    Serial.print(average_moisture);
    Serial.print(", ");
    Serial.print("time_selected=");
    Serial.print(time_selected);
    Serial.print(", ");
    Serial.print("time_remaining=");
    Serial.print(time_remaining);
    Serial.print(", ");
    Serial.print("temp_selected=");
    Serial.print(temp_selected);
    Serial.print(", ");
    Serial.print("temp_setpoint=");
    Serial.print(temp_setpoint);
    Serial.print(", ");
    Serial.print("heater on?=");
    Serial.print(heaterState);
    Serial.print(", ");
    Serial.print("motor on?=");
    Serial.print(motorState);
    Serial.print(", ");
    Serial.print("button pressed?=");
    Serial.print(buttonReading);
    Serial.print(", ");
    Serial.print("heater timeout?=");
    Serial.print(heater_timeout);
    Serial.print(", cooldown start=");
    Serial.println(cooldown_start_millis / 1000);
    /*
    lcd.clear();
      lcd.setCursor(0, 0);
    lcd.print("tr=");
    lcd.print(time_remaining);
    lcd.print(" ");
    lcd.print("h=");
    lcd.print(heaterState);
    lcd.print("ts=");
    lcd.print(temp_setpoint);
        lcd.setCursor(0, 1);
    lcd.print("temp=");
    lcd.print(inside_temp);
    lcd.print(" ");
    lcd.print("moist=");
    lcd.print(inside_moisture);
    */
  }
}