//############################################################################################################## // v6 - inclreaded LED auto turn off to 1hr + 15 seconds. Added 2nd RGBW NeoPixels in string - total 19 // v5 - added function to reset NeoPixels to off based on timer. Timer will restart any time lights are turned on as well. Disabled NeoPixel Show feature as it causes ESP to crash // v4 - added 2nd NeoPixel ring - NOT DONE // v3 - REMOVED added virtualwrite using v6x to Weather RX unit. This app reserves 60-69 virtual pin range. // v2 - integration of Adafruit NeoPixels and testing // v1 - integration of DS18S20 and Blynk, setup Blynk app. History graph and LCD display // settings Board: NodeMCU 1.0 (ESP 12E) CPU 80, 9xxK // Uses Blynk to process and display info over net from IOS/Android // NodeMCU Geekcreit ESP8266 board pinouts http://www.cnx-software.com/wp-content/uploads/2015/10/NodeMCU__v1.0_pinout.jpg // For DS18B20 Temp Sensor: // Setup reference.http://www.hobbytronics.co.uk/ds18b20-arduino // both GND and VCC go to 0V (GND). Yes! // Data pin to pin 2 then 4K7 pullup resistor from 5V to data pin // using Banggood DS18B20 Temp Sensor - http://www.banggood.com/DS18B20-Waterproof-Digital-Temperature-Temp-Sensor-Probe-1M-2M-3M-5M-10M-15M-p-983801.html // OneWire DS18S20, DS18B20, DS1822 Temperature Example // // http://www.pjrc.com/teensy/td_libs_OneWire.html // // The DallasTemperature library can do all this work for you! // http://milesburton.com/Dallas_Temperature_Control_Library // END For DS18B20 Temp Sensor //############################################################################################################## //#define BLYNK_PRINT Serial // Comment this out to disable prints and save space #include #include #include #include #include #include #define ledStat 2 //gpio 2 const char* ssid = "YourSSID"; const char* password = "YourPass"; char auth[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; //Blynk - Master - Weather - Transmitter App auth token. Make sure not to use the same Vpins as the Weather TX app! const boolean debug = false; unsigned long prevTempMillis = 0; unsigned long refreshTempInterval = 60000; //unsigned long prevRestartMillis = 0; //unsigned long restartInterval = 305000; //every 5 mins + 5 seconds so it does not cut out during last update unsigned long prevLEDResetMillis = 0; unsigned long ledResetInterval = 3615000; //361500 = 1hr + 15 secnds 915000>> every 15 mins + 15 seconds so it does not cut out during last update boolean ledsTrigOn = false; #define PIN 12 // GPIO pin 12 which = PIN 6 on NodeMCU board #define NUMPIXELS 19 // Number of pixels in strip char StrTemp_c[5]; //for conversion to String char StrTemp_f[5]; //for conversion to String Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800); //for the temp sensor OneWire ds(4); // on pin 4 (this is ESP GPIO4 or Pin 2 on the board) (a 4.7K resistor is necessary) WidgetRTC rtc; BLYNK_ATTACH_WIDGET(rtc, V5); void setup() { Serial.begin(115200); pinMode(ledStat, OUTPUT); rtc.begin(); Blynk.begin(auth, ssid, password); yield(); while (Blynk.connect() == false) { //Wait until connected if (debug) {Serial.print(".");} } if (debug) {Serial.println("Blynk connected.");} pixels.begin(); //init NeoPixels resetLEDsToOff(); } void loop() { unsigned long currentMillis = millis(); Blynk.run(); //start polling the system and reporting to the Blynk app. //scan and identify the finger when one is put on it //chkForFingerPrint(); //If Blynk is not connected or looses connection, re-establish connection if (!Blynk.connected()) { Blynk.begin(auth, ssid, password); while (Blynk.connect() == false) { //do nothing but wait if (debug) {Serial.print(".");} } if (debug) {Serial.println("Blynk had to re-connect.");} } //Refresh data and check values if (currentMillis - prevTempMillis >= refreshTempInterval) { prevTempMillis = currentMillis; // save the last time we checked yield(); //if (debug) {Serial.println("Send temperature...");} getTemp(); doTime(); //send the last time updated to Blynk blinkled(ledStat, 300, 1); } //this is used to trigger a reset to start the time from when the leds were turned on. if (ledsTrigOn) { prevLEDResetMillis = currentMillis; ledsTrigOn = false; } //force LEDs off based on interval. Interval is reset whenever leds are turned on if (currentMillis - prevLEDResetMillis >= ledResetInterval) { prevLEDResetMillis = currentMillis; // save the last time we checked yield(); if (debug) {Serial.print("CurrentMS: ");} if (debug) {Serial.println(currentMillis);} if (debug) {Serial.print("PrevMS: ");} if (debug) {Serial.println(prevLEDResetMillis);} if (debug) {Serial.print("Interval: ");} if (debug) {Serial.println(ledResetInterval);} resetLEDsToOff(); } //force restart of ESP /* if (currentMillis - prevRestartMillis >= restartInterval) { prevRestartMillis = currentMillis; // save the last time we checked yield(); Blynk.notify("Hot tub unit auto restart worked."); blinkled(ledStat, 300, 4); ESP.restart(); } */ } void resetLEDsToOff() { //turn all LEDs off //colorWipe(pixels.Color(0, 0, 0), 50); // All White for(int i=0;i, but doesn't seem worth the import // if this rounding step isn't here, the value 54.321 prints as 54.3209 // calculate rounding term d: 0.5/pow(10,places) float d = 0.5; if (value < 0) d *= -1.0; // divide by ten for each decimal place for (i = 0; i < places; i++) d/= 10.0; // this small addition, combined with truncation will round our values properly tempfloat += d; // first get value tens to be the large power of ten less than value if (value < 0) tempfloat *= -1.0; while ((tens * 10.0) <= tempfloat) { tens *= 10.0; tenscount += 1; } if (tenscount > 0) charcount += tenscount; else charcount += 1; if (value < 0) charcount += 1; charcount += 1 + places; minwidth += 1; // both count the null final character if (minwidth > charcount){ extra = minwidth - charcount; charcount = minwidth; } if (extra > 0 and rightjustify) { for (int i = 0; i< extra; i++) { outstr[c++] = ' '; } } // write out the negative if needed if (value < 0) outstr[c++] = '-'; if (tenscount == 0) outstr[c++] = '0'; for (i=0; i< tenscount; i++) { digit = (int) (tempfloat/tens); itoa(digit, &outstr[c++], 10); tempfloat = tempfloat - ((float)digit * tens); tens /= 10.0; } // if no places after decimal, stop now and return // otherwise, write the point and continue on if (places > 0) outstr[c++] = '.'; // now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value for (i = 0; i < places; i++) { tempfloat *= 10.0; digit = (int) tempfloat; itoa(digit, &outstr[c++], 10); // once written, subtract off that digit tempfloat = tempfloat - (float) digit; } if (extra > 0 and not rightjustify) { for (int i = 0; i< extra; i++) { outstr[c++] = ' '; } } outstr[c++] = '\0'; return outstr; } //************************************************************************************************************************************************ void getTemp() { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8]; float celsius, fahrenheit; char combined[45] = {0}; if ( !ds.search(addr)) { //if (debug) {Serial.println("No more addresses.");} ds.reset_search(); delay(250); return; } /* Serial.print("ROM ="); for( i = 0; i < 8; i++) { Serial.write(' '); if (debug) {Serial.print(addr[i], HEX);} } if (OneWire::crc8(addr, 7) != addr[7]) { if (debug) {Serial.println("CRC is not valid!");} return; } Serial.println(); */ // the first ROM byte indicates which chip /* switch (addr[0]) { case 0x10: if (debug) {Serial.println(" Chip = DS18S20");} // or old DS1820 type_s = 1; break; case 0x28: if (debug) {Serial.println(" Chip = DS18B20");} type_s = 0; break; case 0x22: if (debug) {Serial.println(" Chip = DS1822");} type_s = 0; break; default: if (debug) {Serial.println("Device is not a DS18x20 family device.");} return; } */ ds.reset(); ds.select(addr); ds.write(0x44, 1); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not // we might do a ds.depower() here, but the reset will take care of it. present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad //if (debug) {Serial.print(" Data = ");} //if (debug) {Serial.print(present, HEX);} //if (debug) {Serial.print(" ");} for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); //if (debug) {Serial.print(data[i], HEX);} //if (debug) {Serial.print(" ");} } //Serial.print(" CRC="); //Serial.print(OneWire::crc8(data, 8), HEX); //Serial.println(); // Convert the data to actual temperature // because the result is a 16 bit signed integer, it should // be stored to an "int16_t" type, which is always 16 bits // even when compiled on a 32 bit processor. int16_t raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; // 9 bit resolution default if (data[7] == 0x10) { // "count remain" gives full 12 bit resolution raw = (raw & 0xFFF0) + 12 - data[6]; } } else { byte cfg = (data[4] & 0x60); // at lower res, the low bits are undefined, so let's zero them if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms //// default is 12 bit resolution, 750 ms conversion time } celsius = (float)raw / 16.0; fahrenheit = celsius * 1.8 + 32.0; if (debug) {Serial.print("Temp = ");} if (debug) {Serial.print(celsius);} if (debug) {Serial.print(" Celsius, ");} if (debug) {Serial.print(fahrenheit);} if (debug) {Serial.println(" Fahrenheit");} Blynk.virtualWrite(60, celsius); //sends temperature to History Graph //Blynk.virtualWrite(61, celsius); //sends temperature to Value item in Blynk app //Blynk.virtualWrite(62, fahrenheit); //sends temperature to Value item in Blynk app if (fahrenheit < 60.0) {Blynk.notify("Notice! Hot tub temp has fallen below 60F!");} //now package and send to Weather RX app floatToString(StrTemp_c, celsius, 1); floatToString(StrTemp_f, fahrenheit, 1); //put them together //strcat(combined, StrTemp_c); //strcat(combined, "|"); //strcat(combined, StrTemp_f); //Blynk.virtualWrite(V60, combined); //send them to the Weather RX unit strcat(combined, StrTemp_c); strcat(combined, "C / "); strcat(combined, StrTemp_f); strcat(combined, "F"); Blynk.virtualWrite(61, combined); //sends temperature to Value item in Blynk app if (debug) {Serial.println(" Combined sent to Blynk. Here:");} if (debug) {Serial.println(combined);} } //************************************************************************************************************************************************ void blinkled(int whichLED, int dly, int numTimes) { for (int i = 1; i<=numTimes; i++){ digitalWrite(whichLED, LOW); // for ESP's LOW is ON delay(dly); digitalWrite(whichLED, HIGH); delay(100); } } //************************************************************************************************************************************************ void startShow(int i) { switch(i){ case 1: colorWipe(pixels.Color(0, 0, 0), 50); // Black/off break; case 2: colorWipe(pixels.Color(255, 0, 0), 50); // Red break; case 3: colorWipe(pixels.Color(0, 255, 0), 50); // Green break; case 4: colorWipe(pixels.Color(0, 0, 255), 50); // Blue break; case 5: theaterChase(pixels.Color(127, 127, 127), 50); // White break; case 6: theaterChase(pixels.Color(127, 0, 0), 50); // Red break; case 7: theaterChase(pixels.Color( 0, 0, 127), 50); // Blue break; case 8: rainbow(200); break; case 9: rainbowCycle(20); break; case 10: theaterChaseRainbow(50); break; default: colorWipe(pixels.Color(0, 0, 0), 50); // Black/off break; } } //************************************************************************************************************************************************ // Fill the dots one after the other with a color void colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i