So you're a Noob? Post your questions here until you graduate! Don't be shy.

User avatar
By bwze
#64377 So, I have successfully created a sketch, albeit most likely hacked together when compared to someone who actually knows what they're doing, for Arduino. I have this NodeMCU and uploaded the sketch successfully to it, but for some reason the serial monitor is not recognizing my response as expected. Would anyone have any insight into why the serial monitor is not correctly deciphering my Serial.readString entry from the "start1:" position in the void loop() of my sketch?

Code: Select all/**
   Midea AC IR driver
   driving a IR LED at 38 KHz
   This sketch controls a Midea Heat Pump using Arduino.
*/

#define Duty_Cycle 56 //in percent (10->50), usually 33 or 50
//TIP for true 50% use a value of 56, because of rounding errors
//TIP for true 40% use a value of 48, because of rounding errors
//TIP for true 33% use a value of 40, because of rounding errors
 
#define Carrier_Frequency 38000 //usually one of 38000, 40000, 36000, 56000, 33000, 30000
 
#define PERIOD (1000000+Carrier_Frequency/2)/Carrier_Frequency
#define HIGHTIME PERIOD*Duty_Cycle/100
#define LOWTIME PERIOD - HIGHTIME
#define txPinIR 4 //IR carrier output

int mode; //integer for mode value
int fanSpeed;  //integer for fan speed value
int temp;  //integer for temperature value after conversion from input_3
const int temperaturePin = 0;

String input_1;  //string for mode value input by user in void loop
String input_2;  //string for fan speed value input by user in void loop
int input_3;  //integer for temperature value input by user in void loop

void setup() {
  Serial.begin(19200);
  pinMode(txPinIR, OUTPUT);
}

void mark(unsigned long mLen) {
 if (mLen==0) return;
 unsigned long now = micros();
 while ((micros() - now) < mLen) {
 digitalWrite(txPinIR, HIGH);
 delayMicroseconds(HIGHTIME-6);
 digitalWrite(txPinIR, LOW);
 delayMicroseconds(LOWTIME-7);
 }
}

void space(unsigned long sLen) {
 if (sLen==0) return;
 while (sLen>16383) {
 delayMicroseconds(16383);
 sLen -= 16383;
 }
 delayMicroseconds(sLen);
}


 
/* The following function int emit_midea.start sets the 38 kHz pulse width modulation and sends the frame start markers.*/
void emit_midea_start() {
  mark(4200);
  space(4500);
}

/*The following function "command_midea_ac) compiles the start markers, the 6 Byte payload, and the end markers into a single task to configure the selected mode, fan speed, and temperature.*/
void command_midea_ac(byte b1, byte b2) {
  emit_midea_start(); //runs start function for IR command
  emit_midea_byte(178);
  emit_midea_byte(b1);
  emit_midea_byte(b2);
  emit_midea_end(); //runs end function for IR command
  /* the IR command is sent twice */
  emit_midea_start();
  emit_midea_byte(178);
  emit_midea_byte(b1);
  emit_midea_byte(b2);
  emit_midea_end(); //runs end function for IR command
}

/*The following function "command_midea_ac_follow_me" compiles the start markers, the 6 Byte payload, and the end markers into a single task to configure the AC to use the thermistor temperature sensor in the remote for setpoint management.
The Follow Me translation from snooping the voltage signal from the remote to the heat pump is as follows: 10111010(byte1=value of 186)01000101(byte1 inverse)01011010(byte2=value of 90)10100101(byte2 inverse)01010010(byte3=value of 82)10101101(byte3 inverse) */
void command_midea_ac_follow_me() {
  emit_midea_start(); //runs start function for IR command
  emit_midea_byte(186);
  emit_midea_byte(90);
  emit_midea_byte(82);
  emit_midea_end(); //runs end function for IR command
  /* the IR command is sent twice */
  emit_midea_start(); //runs start function for IR command
  emit_midea_byte(186);
  emit_midea_byte(90);
  emit_midea_byte(82);
  emit_midea_end(); //runs end function for IR command
}

/* The following function int emit_midea_end sends the frame end markers essentially ending the command_midea_ac function. Mark is IR LED on and Space is IR LED off.*/
void emit_midea_end() {
  mark(550);
  space(4500);
}

/*The following function is used by the command_midea_ac function to selectively broadcast 0s or 1s for each bit in each byte
  that is held in the "byte b" variable. It works by first converting the variables to binary, then using a mask at each bit
  location in the variable byte to test it for a particular condition. This is done with an if statement. The first thing that
  happens is the IR LED is turned on for 450 mlliseconds (i.e. irsend.mark(450)). Then the bits are examined.....for example,
  if you have a variable byte of 1001, with a mask byte of 1000, you would typically compare the left most bit in the variable
  byte with the left most bit in the mask byte. Since the "&" operator is used in the code below (i.e. cur "&" mask), the two
  bits are compared to see if they are both 1 and if so, then according to the code, the first condition of the if statement
  is satisfied and the IR LED is turned off for 1700 milliseconds (i.e. irsend.space(1700) representing a 1. After the first
  loop through, the 1 in the mask is advanced one space to the right (i.e. the mask is now 0100 instead of 1000) as indicated
  by the "mask >>=1" portion of the code and the if statement is ran again. Again, the IR LED is turned on for 450 milliseconds
  and then next two bits are compared to each other (i.e. 1"0"01 is compared to 0"1"00). Since they are not both 1, the first
  condition of the if statement is not met and the else condition is used which turns off the IR LED for 600 milliseconds
  representing a 0. This process continues until the test (i.e. i<8) is satisfied and this particular byte payload is sent.
  In this particular communication method, each byte is first sent "as is" and then its compliment is sent. The tilde symbol
  "~" is used to change every bit in the variable byte to its opposite (e.g. 1001 becomes 0110) and the same process as
  described above is ran again.*/
void emit_midea_byte(byte b) {
     int i;
     byte cur = b;
     byte mask = 0x80;

     for (i = 0;i < 8;i++) {
         mark(450);
         if (cur & mask)
             space(1700);
         else
             space(600);
         mask >>= 1;
     }
     cur = ~b;
     mask = 0x80;
     for (i = 0;i < 8;i++) {
         mark(450);
         if (cur & mask)
             space(1700);
         else
             space(600);
         mask >>= 1;
         
     }
}

void loop() {

start:
  Serial.println("Which AC mode (case sensitive)?");  //prompt user for specific mode they wish the AC to operate in
  Serial.println("cool, heat, auto, dry, or off?");  //prompt user for specific mode they wish the AC to operate in
  Serial.println("");  //enters a blank line
  Serial.println("You may also choose 1 for cool/auto/72, or 2 for cool/auto/84.");  //gives the user a quick config option
  Serial.println("");  //enters a blank line
start1:
  while (Serial.available() == 0) { }; //wait for user input
  input_1 = Serial.readString();  //this will fill the string value for "input_1"
  if (input_1 == "cool") { //checks to see if the user input matches "cool"
    mode = 0; //if "cool" is entered, the mode integer value is set to 0
  }
  else if (input_1 == "heat") { //checks to see if the user input matches "heat"
    mode = 12; //if "heat" is entered, the mode integer value is set to 12
  }
  else if (input_1 == "auto") { //checks to see if the user input matches "auto"
    mode = 8; //if "auto" is entered, the mode integer value is set to 8
    fanSpeed = 1; //in "auto" moode, the fan speed can't be selected, this value has to be entered
    Serial.println("You can't choose a fan speed for the auto mode.");  //instructs user that they can't choose a fan speed for "auto"
    Serial.println("");  //enters a blank line
    goto start3;
  }
  else if (input_1 == "dry") { //checks to see if the user input matches "dry"
    mode = 4; //if "dry" is entered, the mode integer value is set to 4
    fanSpeed = 1; //in "dry" moode, the fan speed can't be selected, this value has to be entered
    Serial.println("You can't choose a fan speed for the dry mode.");  //instructs user that they can't choose a fan speed for "dry"
    Serial.println("");  //enters a blank line
    goto start3;
  }
  else if (input_1 == "off") { //checks to see if the user input matches "off"
    command_midea_ac(123, 224);  //if "off" is entered, command_midea_ac is ran to turn AC off
    Serial.println("");  //enters a blank line
    Serial.println("The AC is now off.");  //informs the user that the AC is now in the off mode
    Serial.println("");  //enters a blank line
    goto start;  //after the command_midea_ac is ran, this returns the user to the mode prompt located directly after start:
  }
  else if (input_1 == "1"){
    command_midea_ac(191,112);
    delay (2000);
    command_midea_ac_follow_me();
    Serial.println("The AC is set to cool, with fan to auto, and temp is 72.");
    Serial.println("");  //enters a blank line
    goto start;
  }
  else if (input_1 == "2"){
    command_midea_ac(191,128);
    delay (2000);
    command_midea_ac_follow_me();
    Serial.println("The AC is set to cool, with fan to auto, and temp is 84.");
    Serial.println("");  //enters a blank line
    goto start;
  }
  else {
    Serial.println("You either misspelled the mode, ");  //if neither "cool", "heat", "auto", "dry", or "off" is entered, the user is given this instruction
    Serial.println("used the incorrect case, ");  //if neither "cool", "heat", "auto", "dry", or "off" is entered, the user is given this instruction
    Serial.println("or entered an incorrect value.");  //if neither "cool", "heat", "auto", "dry", or "off" is entered, the user is given this instruction
    Serial.println("");  //enters a blank line
    Serial.println("Try again:");  //prompts user to try again
    Serial.println("");  //enters a blank line
    goto start1;  //this returns the user the the start1: position to try again
  }

  Serial.println("Which fan speed (case sensitive)?");  //prompt user for a specific fan speed
  Serial.println("low, medium, high, or auto?");  //prompt user for a specific fan speed
  Serial.println("");  //enters a blank line
start2:
  while (Serial.available() == 0) { }; //wait for user input
  input_2 = Serial.readString();  //this will fill the string value for "input_2"
  if (input_2 == "low") { //checks to see if the user input matches "low"
    fanSpeed = 9; //if "low" is entered, the fanSpeed integer value is set to 9
  }
  else if (input_2 == "medium") { //checks to see if the user input matches "medium"
    fanSpeed = 5; //if "medium" is entered, the fanSpeed integer value is set to 5
  }
  else if (input_2 == "high") { ///checks to see if the user input matches "high"
    fanSpeed = 3; //if "high" is entered, the fanSpeed integer value is set to 3
  }
  else if (input_2 == "auto") { ////checks to see if the user input matches "auto"
    fanSpeed = 11; //if "auto" is entered, the fanSpeed integer value is set to 11
  }
  else {
    Serial.println("You either misspelled the fan speed, ");  //if neither "low", "medium", "high", or "auto" is entered, the user is given this instruction
    Serial.println("used the incorrect case, ");  //if neither "low", "medium", "high", or "auto" is entered, the user is given this instruction
    Serial.println("or entered an incorrect value.");  //if neither "low", "medium", "high", or "auto" is entered, the user is given this instruction
    Serial.println("");  //enters a blank line
    Serial.println("Try again:");  //prompts user to try again
    Serial.println("");  //enters a blank line
    goto start2;  //this returns the user the the start2: position to try again
  }

start3:
  Serial.println("Enter an even temp between 62 & 86.");  //prompt user for specific temperature value
  Serial.println("");  //enters a blank line
  while (Serial.available() == 0) { }; //wait for user to input something
  input_3 = Serial.parseInt();  //this will fill the integer value for "input_3"
  if ((input_3%2!=0)||(input_3<62||(input_3>86))) {  //the condition of this if statment first tests to see if the value is even, then if it is in the allowed range
    Serial.println("You either entered a temp that's not even ");  //prompt user that they entered an odd temp or one that's out of the allowed range
    Serial.println("or that's out of range.");  //prompt user that they entered an odd temp or one that's out of the allowed range
    Serial.println("");  //enters a blank line
    Serial.println("Try again:");  //prompts user to try again
    goto start3;  //this returns the user the the start3: position to try again
  }
  else {
    /*This is a lookup table for converting temperature entry from the "start 3" section directly above into a numerical value which corresponds to the Midea IR protocol. */
    switch (input_3){
    case 62: {temp=0;}
    break;
    case 64: {temp=1;}
    break;
    case 66: {temp=3;}
    break;
    case 68: {temp=2;}
    break;
    case 70: {temp=6;}
    break;
    case 72: {temp=7;}
    break;
    case 74: {temp=5;}
    break;
    case 76: {temp=4;}
    break;
    case 78: {temp=12;}
    break;
    case 80: {temp=13;}
    break;
    case 82: {temp=9;}
    break;
    case 84: {temp=8;}
    break;
    case 86: {temp=10;}
    break;
    }
  }
 
  Serial.print("You are currently in ");
  Serial.print(input_1);
  Serial.println(" mode");
  Serial.print("with a fan speed set to ");
  if (input_2 == "") {
    Serial.print("auto");
  }
  else {
    Serial.print(input_2);
  }
  Serial.println(" and ");
  Serial.print("a temperature setpoint of ");
  Serial.print(input_3);
  Serial.println(".");
  Serial.println("");
  Serial.println("");

  command_midea_ac(fanSpeed << 4 | 15, temp << 4 | mode);

  delay (2000);
 
  command_midea_ac_follow_me;
}

User avatar
By martinayotte
#64380
Code: Select allwhile (Serial.available() == 0) { };

You should not do some blocking code like that, it prevent the rest of ESP OS from running properly.
Even under Arduino umbrela, loop() function code should not last long. If you have to wait for long preiod of time, it is better to have some flags and watch its value changes and doing tasks accordingly.
User avatar
By bwze
#64388 So don't mean to sound too needy, but what would you recommend in this case to wait for a user response from the serial monitor? Again, your comment about timeliness not withstanding, this works flawlessly with an Uno.

I'd continue to use the Uno but I want to connect this to a WiFi network for remote control via a TCP/IP connection and the ESP8266 seems like a perfect fit.