PROJECT - ESP8266 YA _isvhsopen_indicator_light

@lukecyca started off the ESP8266 project here:

I tried using the suggested tools but had no luck getting them installed on my Linux box. So I went looking for other options and was hoping for something Arduino IDE like as that is what I am used to using.

First I found that Adafruit had set up some libraries/hardware files that you could link to from your existing Arduno IDE: Using Arduino IDE | Adafruit HUZZAH ESP8266 breakout | Adafruit Learning System I tried this and while the code would compile, the upload would freeze.

Kept looking and found this: GitHub - esp8266/Arduino: ESP8266 core for Arduino I downloaded the Windows binary and it worked like a charm.

Out of the complete lack of creativity on my part I decided to port @lukecyca example onto the Arduino IDE. You can download it here: YAisVHSopen1.zip (2.6 KB)

The code basically monitors the vhs door api and lights LEDs as expected. I was having various issues so the serial monitor tracks various things. Keep in mind I am no programmer so I sort of cobbled together something from various places to parse the text from the site. Learned a bunch about control characters and such. I also added code to track various connection issues that I was (still) having. Handling errors and exceptions is more work than the prime taskā€¦

Did run into a few issues:

  1. While the average power draw of the module seems less than 100 mA it draws a lot more when the radio fires off. I was using a small Solarbotics Breadboard Power Supply and it seemed that because itā€™s filter capacitor was small, it could not handle the spike in current draw. This didnā€™t seem to affect the processor but the radio would lose connectivity to my AP. Running off a larger power supply and adding larger filter caps seemed to solve this issue.

  2. While I seemed to fix the above issues I find that the system will still lose itā€™s connection to my AP and requires a reset. Again the serial monitor and processor seem fine but the program can no longer connect to the api site and I can no longer ping the unit on my network. Seems to happen randomly after several hours. Prior to moving to a better power supply it was happening every few minutes so perhaps it is related. One thing I tried to add was a reset through an API call but that feature is no yet working on the IDE. Another way to do it would be to use a GPIO pin to reset the module if once the connections start to fail. However Iā€™d like to figure out why this is happening. Is it my AP (Apple AirPort), is it the type of WIFI connection Iā€™m using (not sure if itā€™s B, G or N), is it my carrier PCB (seems pretty solid), is it a bad batch of modules (same thing happens of 3 different one but they all came from AliExpress)

  3. Even when I connect properly my parsing seems to fail a small percentage of times. Iā€™d like to think itā€™s the api host but manual testing from a browser never fails. More likely itā€™s my code or possibly a glitch in the Arduino/ESP port. Again Iā€™m no programmer so there may be much better ways to get what I want doneā€¦

Here is the breadboard set up (note various extra caps):

Here is a crooked screenshot:

And here is the code (also in attached zip file), I tried to add lots of comments to help others figure out what I was doing:


    // YA_isVHSopen_1.91   Bob Johnson May 8, 2015
    // Test program using the Windows version (1.6.1-p1)of the Arduino based IDE for the ESP8266 module
    // You can get it here: https://github.com/esp8266/Arduino
    // Mashup of WiFiClient example and idea stolen from lukecyca: https://github.com/vhs/esp-isvhsopen
    // Client goes to VHS api site and grabs isvhsopen result (either "open" or "closed")
    // Code should leave no doubt that I am not a programmer
    // Green OPEN LED connected to GPIO12 via 330 ohm resister
    // Red CLOSED LED connected to GPIO13 via 330 ohm resister
    // I used an ESP-07 from here:
    // http://www.aliexpress.com/item/10pcs-ESP8266-serial-WIFI-model-ESP-07-Authenticity-Guaranteed-WIFI-module/32264981572.html
    // I used a breakout board from here:
    // https://www.tindie.com/products/Ba0sh1/esp8266-esp-0712-full-io-breadboard-adapter/
    // Make sure you use a decent power supply, connections failing are an indication of power issues
    
    
    
    // these are all the libraries, defines, constant declarations, etc
    
    #include <ESP8266WiFi.h>
    
    // this is where you put the SSID and PW for your AP
    const char* ssid     = "put your SSID here";
    const char* password = "put your password here";
    
    // this is the host and path to the data we want
    const char* host = "api.vanhack.ca";
    const char* url = "/s/vhs/data/door.txt";
    
    // count for connnection fail reboot
    float check_count;    // # of times we check the web site
    float fail_count;    // # of times we didn't get get a http connection to server
    float fail_percent;     // used to calculate pertage times for open and closed
    float open_count;    // # of times VHS was open when we checked
    float closed_count;    // # of times VHS was closed when we checked
    float open_percent;     // used to calculate pertage times for open and closed
    float closed_percent;     // used to calculate pertage times for open and closed
    float bad_data_percent;     // used to calculate pertage times for open and closed
    float bad_data_count;     // used to calculate pertage times for open and closed
    int flag;     // flag to track if we got expected data
    
    
    // these are the GPIO pins used to control the LEDs
    #define OPEN_LED 12
    #define CLOSED_LED 13
    
    // this is the time delay in milliseconds in between checking the VHS API site.
    // 60 seconds is good to avoid overloading it (cuz all the cool kids are makign one of these)
    #define WAIT 58000
    
    
    // this is the start of the setup section
    void setup() {
    
      // this sets up the pins as outputs
      pinMode(OPEN_LED, OUTPUT);    // set up green led output
      pinMode(CLOSED_LED, OUTPUT);  // set up red led output
    
      // this initializes the serial port for monitoring
      Serial.begin(115200);
      delay(10);
       
      Serial.println();
      Serial.println("####################################");
      Serial.println("         YA_isVHSopen_1.91");
      Serial.println("####################################");
      Serial.print("Connecting To: ");
      Serial.println(ssid);
    
      // this starts up the WiFi network connection
      WiFi.begin(ssid, password);
      
      // this keeps trying until connected
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
    
      // this tells you your network connection information
      Serial.println("");
      Serial.println("WiFi Connected!!");
      Serial.print("IP Address: ");
      Serial.println(WiFi.localIP());
      Serial.println("####################################");
      Serial.println();
    }
    
    
    // this is the start of the main loop
    void loop() {
    
    
      //this sets up up the tcp connection to the server
      WiFiClient client;
      const int httpPort = 80;
      delay(50);    // delay to try to fix seemingly random freezes
    
      // this tells you if it can't connect to the server
      if (!client.connect(host, httpPort)) {
        digitalWrite(OPEN_LED, LOW);    // turn off any leds to avoid showing bad data
        digitalWrite(CLOSED_LED, LOW);
        fail_count++;    // increment count to track how many times it fails
        Serial.println();
        Serial.println("####################################");
        Serial.print("Connection Failed ");
        Serial.print(fail_count, 0);
        Serial.println(" Times...  Crap");
        Serial.println("####################################");
        return;
      }
    
      delay(50);    // delay to try to fix seemingly random freezes
      check_count++;    // increment check count
      // this will send the http request to the server
      client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                   "Host: " + host + "\r\n" +
                   "Connection: close\r\n\r\n");
      delay(50);
    
    
      Serial.println("###################################################################");
      Serial.print("Checking: ");
      Serial.print(host);
      Serial.print(url);
    
      // this will grab text reply from server one line at a time
      while (client.available()) {
        delay(50);
        String line = client.readStringUntil('\r');
        delay(50);
        // if the line starts with "Date" then it prints it cuz it's the server timestamp in GMT
        if (line.startsWith("\nDate")) {
    
          Serial.println(line);    // this prints the date line
    
        }
    
        // this parses the line to see if it ends with "closed" and acts accordingly
        if (line.endsWith("closed")) {
          closed_count++;    // increment closed count
          closed_percent = 100 * (closed_count / check_count);
          digitalWrite(OPEN_LED, LOW);
          digitalWrite(CLOSED_LED, HIGH);
          flag = 1;    // set the flag so we know we got good data
          Serial.println();
          Serial.println("Sorry, the VHS is CLOSED right now :-(");
    
    
        }
    
        // this parses the line to see if it ends with "open" and acts accordingly
        if (line.endsWith("open")) {
          open_count++;    // increment open count
          open_percent = 100 * (open_count / check_count);
          digitalWrite(OPEN_LED, HIGH);
          digitalWrite(CLOSED_LED, LOW);
          flag = 1;    // set the flag so we know we got good data
          Serial.println();
          Serial.println("The VHS is OPEN right now, come on down!!");
        }
    
      }
    
      // if we didn't get good data then print an error message and clear LEDs
    
      if (flag == 0) {
        bad_data_count++;    //increment bad data count
        bad_data_percent = 100 * (bad_data_count / check_count);
        digitalWrite(OPEN_LED, LOW);    // turn off any leds to avoid showing bad data
        digitalWrite(CLOSED_LED, LOW);
        Serial.println();
        Serial.println("###################################################################");
        Serial.println("Didn't get expected data from API... Crap");
        Serial.println("Can't tell if VHS is OPEN or CLOSED!!");
    
    
      }
      bad_data_percent = 100 * (bad_data_count / check_count);    // update % even if we don't call it
      closed_percent = 100 * (closed_count / check_count);
      open_percent = 100 * (open_count / check_count);
      fail_percent = 100 * (fail_count / check_count);
    
      flag = 0;    // clear the flag for the next loop
    
      Serial.println();
    
    
      //Serial.print("You've been checking the VHS status for ");
      
    Serial.print("I've checked the VHS status ");
      Serial.print(check_count, 0);
      Serial.print(" times in the last ");  
      Serial.print(check_count/60, 2);
      Serial.println(" hours.");
    
      Serial.print("It has been OPEN for ");
      Serial.print(open_count, 0);
      Serial.print(" minutes. [");
      Serial.print(open_percent, 2);
      Serial.println("% of the time]");
    
      Serial.print("It has been CLOSED for ");
      Serial.print(closed_count, 0);
      Serial.print(" minutes. [");
      Serial.print(closed_percent, 2);
      Serial.println("% of the time]");
    
      Serial.print("I couldn't get the data for ");
      Serial.print(bad_data_count, 0);
      Serial.print(" minutes. [");
      Serial.print(bad_data_percent, 2);
      Serial.println("% of the time]");
      
      Serial.print("I couldn't get to the site for ");
      Serial.print(fail_count, 0);
      Serial.print(" minutes. [");
      Serial.print(fail_percent, 2);
      Serial.println("% of the time]");
    
      Serial.println("###################################################################");
      Serial.println();
      Serial.println();
      Serial.println();
      Serial.println();
      delay(WAIT);    // wait 60 seconds to check again
    }
3 Likes

Very cool, Bob!
It needs an amber ā€˜pizzaā€™ LED.
In the past, Iā€™ve also seen it read ā€œcloseā€ (but no cigar), rather
than ā€œclosedā€.

Works great! Iā€™ve switched mine over to your Arduino-based code.

I had some trouble getting esp8266 support working in the Arduino IDE. The esptool-ck tool (which it uses under the hood) doesnā€™t work for me. It appears to flash the chip, but doesnā€™t correctly erase the memory (more info). So I just edited the arduino IDE config files to make it use esptool.py instead, which works perfectly every time. Now I can upload sketches from the IDE. Awesome!

If yours is not crapping out every 8 hours or so (and you are running he same code and using the same libraries) then either itā€™s my modules or my carrier board that must be causing my issueā€¦

Your sketch is working pretty well for me. It runs for hours with no problems. Occasionally the board resets, but it connects again immediately and keeps on going. Itā€™s only after quite a few hours of running, and Iā€™ve noticed the same with my ESP8266s programmed with the Espressif IoT SDK.

Iā€™m having tons of trouble with esp8266/Arduino in general though. Sketches randomly crash (and reset the chip) all the time. I notice you peppered delay(50) all around your sketch with a comment alluding to random crashes. Can you elaborate on that?

Iā€™m not experiencing total crashes but I am experiencing:

  1. The radio system seemingly hanging. The program still runs, I get serial output that shows it going through the code but it can no longer make a connection via the WiFi. When this happens I can no longer ping the ESP from a remote PC via WiFi either. This seems to happen after running for a few hours and seems worse with a closer AP (in the same room) than with one further away (my neighbours house).

  2. Using the string commands to parse the returned data seems to fail sometimes. I print out the parsed string and even though is shows the correct data, the code says it isnā€™t. I donā€™t have a terminal app that shows non printing characters so there is a chance that a line feed or return is showing up where it shouldnā€™t be.

  3. Sometimes it just canā€™t make a port 80 connection to the site. Any time this happens I have tried doing the same from a browser on a PC connected through the same AP and never have a failure so I suspect itā€™s the device rather than the ISP connection or service.

In regards to the code and the ESP8266/Arduino:

a) I have yet to check my AP logs to see if the ESP actually loses association with it during this time (which would perhaps mean an issue with the WiFi code) or if it stays associated (which could mean a issue with the TCP stack).

b) I added the various delay(50) in the event that the WiFi subsystem was crapping out due to not being serviced enough. The docs mentioned that if a loop in your app took longer than 50 mS it could impact the WiFi. I figured better safe than sorry. It didnā€™t seem to make any difference.

c) The ESP8266/Arduino project is pretty young and the compiles binaries lag behind the various updates. You can manually update the libraries but I wanted to make it easier for others to run the code without doing anything extra. A new precompiled build is suppose to be out in the next few days and may address bugs/features that are causing some of these issues.

d) My modules were from AliExpress and only cost $3.00 each. Iā€™m sure there are QC issues at this price point and the ones I have may just be crapā€¦ I have decent (I think) carrier boards and did notice a power supply issue using a small breadboard power supply. Using a 3 Amp power supply and adding extra decoupling and filter caps around the carrier board made things better but not perfect. I have 5 modules and they all seem to have the same issue.

e) As a work around I may just hook a spare GPIO to the reset on the module. Then when I get several connection failures in a row Iā€™ll just reset the module. There is a call ā€œESP.reset()ā€ that will also do this but it is not yet working from the Arduino IDE (though this is one of the things that shoudl be fixed in the next release). This may be my only option with these modules and while not very elegant will work fine for the project in mind and the price pointā€¦

Wouldnā€™t this have exactly opposite of the desired effect? If itā€™s not interrupt driven, Iā€™d expect you to have to call into the WiFi/TCP library at least every 50ms so it can do its housekeeping.

I believe part of the delay() function is allowing the housekeeping of various subsystemsā€¦

Yes, internally delay() calls yield() which allows housekeeping tasks to run, including feeding the watchdog timer.

Still investigatingā€¦

My issue was that I had a floating RX pin, and the version of esp8266-Arduino that I was using had a bug that would crash the chip when anything was received (Issue #219). Combined, it meant that as the floating pin generated UART garbage, the chip would put it into its buffer and eventually hit this bug, resetting the chip at random times. I pulled the software fix, added a stronger pull-up, and now itā€™s stable.

Iā€™ve been running sketches for more than 8 hours with no wifi-related problems, so I havenā€™t hit your issue, @packetbob. Iā€™ll let you know if I do.