/* Created 10 Oct. 2018 by Peter Chodyra */ /*Vertical blind turn range 180 deg 84 beeds, 1 rotation = 16 beeds 5.25 rotations = 180 deg 2.62 rotations = 90 deg 1.31 rotations or 21 beeds = 45 deg */ #include #include "WiFiManager.h" //https://github.com/tzapu/WiFiManager #include #include #include //https://github.com/esp8266/Arduino //needed for library #include #include //#define DEBUG //IMPORTANT Connect ULN2003 PINs to NodeMCU D pins in order PIN1 -> D1, PIN2 -> D5, PIN3 -> D2, PIN4 -> D6 #define PIN1 D1 #define PIN2 D2 #define PIN3 D5 #define PIN4 D6 // ------------------------------------------------------------------------ // ############################# Global Definitions ######################### // ------------------------------------------------------------------------ int photocellPin = A0; // the cell and 100K pulldown are connected to a4 int MAXSteps = 0; //Global variable fo rthe maximum steps to open blinds = 100% open const int moveSteps = 100; //Steps to advance the motor at setup const int stepsPerRevolution = 2048; //this has been changed to 2048 for the 28BYJ-48 int movesLeft = 0; //This hold the current remaining percent of move to make, this will be used in main loop to prevent blocking behaviour of teh stepper function int target = 0; int advanceAmount = 1; //for non blocking stepper set the advance amount each itteration // ------------------------------------------------------------------------ // ############################# EEPROM Definitions ######################### // ------------------------------------------------------------------------ int addr = 0; //EEPROM Address memory space for the current_position of the blinds struct EEPROMStruct { int MAXstate; int NOWstate; int OPENstate; int LUXstate; } eepromVar; //const int revolutions = 1; // ------------------------------------------------------------------------ // ############################# HTML Definitions ######################### // ------------------------------------------------------------------------ const String HTMLheader = ""\ ""\ ""\ ""\ "
"; const String HTMLfooter = "
"; const String HTMLheading ="
"\ "

KOOBEE blinds
Control Panel

"\ "
"; const String HTMLsetup = "
"\ "

Setup

"\ "

Use the MORE and LESS buttons to adjust the OPEN limit of the blinds. Click SAVE to save your settings. RESET will reset the blinds to a closed position. Clicking MORE or LESS will move the motor by 100 steps. To increase the number of steps and advance faster, change the query string in the browser URL to e.g.\\api\\position?move=1000.

"\ " "\ " "\ " "\ " "\ " "\ " "\ " "\ " "\ " "\ " "\ " "\ " "\ "
"\ " "\ "
"\ " "\ "
"\ "
"; const String HTMLhelp = "
"\ "

Information

"\ "

To control the motor via WiFi and the builtin API, use the following GET and PUT API calls.

"\ "

"\ "

    "\ "
  • /api/lux - GET : returns the light level as {\"lightlevel\":240}
  • "\ "
  • /api/status - GET : returns the motor position in % {\"position\":20}
  • "\ "
  • /api/blinds?open=[x] - PUT : moves the motor to position x%
  • "\ "
"\ "

"\ "
"; const String HTMLsavedone = "

Setup Complete!

"; const String HTMLresetdone = "

Reset Complete!

"; const String HTMLmove = "
"\ "

Operation

"\ "

Click on the % buttons to move the blinds to the desired open state. Ensure you have gone through the setup process first.

"\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ ""\ "
"\ "
"; const String HTMLmenu = "
"\ "
"\ ""\ ""\ "" ""\ ""\ "
"; String HTMLstatus =""; //Stepper setup const int clockwise = 1; const int counterclockwise = -1; // initialize the stepper library on pins 1 through 4: Stepper myStepper(stepsPerRevolution, PIN1,PIN2,PIN3,PIN4); //note the modified sequence D1, D5, D2, D6 //Web server setup ESP8266WebServer server(80); //Web server object. Will be listening in port 80 (default for HTTP) // ------------------------------------------------------------------------ // ############################# getHTMLstatus() ############################# // ------------------------------------------------------------------------ void getHTMLstatus(){ eepromVar.LUXstate = getLux(photocellPin); HTMLstatus ="

"\ "Open LIMIT is set to: "+String(eepromVar.MAXstate)+"
"\ "Current position is: "+String(eepromVar.NOWstate)+"
"\ "Open position is: "+String(eepromVar.OPENstate)+"%
"\ "Light level is: "+String(eepromVar.LUXstate)+"

"; } // ------------------------------------------------------------------------ // ############################# handleRoot() ############################# // ------------------------------------------------------------------------ void handleRoot() { server.send(200, "text/plain", HTMLheader+HTMLhelp+HTMLsetup+HTMLmove+HTMLfooter); } void handleNotFound() { String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } // ------------------------------------------------------------------------ // ############################# getBlindsPosition() ############################# // ------------------------------------------------------------------------ int getBlindsPosition(){ return eepromVar.OPENstate; } // ------------------------------------------------------------------------ // ############################# jsonOutput() ############################# // ------------------------------------------------------------------------ String jsonOutput(String jName,int jValue){ String output; StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root[jName] = jValue; root.printTo(output); return output; } // ------------------------------------------------------------------------ // ############################# advanceBlinds() ############################# // ------------------------------------------------------------------------ void advanceBlinds() { //non blocking stepper move int advanceSteps = map(advanceAmount,0,100,0,eepromVar.MAXstate); int targetSteps = map(target,0,100,0,eepromVar.MAXstate); if (targetSteps-eepromVar.NOWstate > 0){ //move the motor clockwise eepromVar.OPENstate=eepromVar.OPENstate+advanceAmount; eepromVar.NOWstate=eepromVar.NOWstate+advanceSteps; //move the motor myStepper.step(advanceSteps * clockwise); } else if (targetSteps-eepromVar.NOWstate < 0) { //move the motor counterclockwise eepromVar.OPENstate=eepromVar.OPENstate-advanceAmount; eepromVar.NOWstate=eepromVar.NOWstate-advanceSteps; //move the motor myStepper.step(advanceSteps * counterclockwise); } else { //is 0 and no meed to move the blinds } EEPROM.put(addr, eepromVar); // write the data to EEPROM boolean ok2 = EEPROM.commit(); #ifdef DEBUG Serial.println((ok2) ? "Commit OK" : "Commit failed"); #endif //Power off the motor digitalWrite(PIN1,LOW); digitalWrite(PIN2,LOW); digitalWrite(PIN3,LOW); digitalWrite(PIN4,LOW); } // ------------------------------------------------------------------------ // ############################# moveBlinds() ############################# // ------------------------------------------------------------------------ void moveBlinds(int percent){ //map percentage to number of steps where 2860 is fully open or 100% int newSteps = map(percent, 0,100,0,eepromVar.MAXstate); if (newSteps != eepromVar.NOWstate) { myStepper.step(newSteps - eepromVar.NOWstate); eepromVar.NOWstate = newSteps; eepromVar.OPENstate=percent; EEPROM.put(addr, eepromVar); // write the data to EEPROM boolean ok2 = EEPROM.commit(); #ifdef DEBUG Serial.println((ok2) ? "Commit OK" : "Commit failed"); #endif } else { //do nothing newSteps = currentSteps } #ifdef DEBUG Serial.print("Moving Percent : "); Serial.print(percent); Serial.print(" Moving Steps : "); Serial.print(newSteps-eepromVar.NOWstate); Serial.print(" currentSteps is: "); Serial.print(eepromVar.NOWstate); Serial.print(" newSteps is: "); Serial.print(newSteps); Serial.print(" MAXSteps is: "); Serial.println(eepromVar.MAXstate); #endif #ifdef DEBUG Serial.print("Blinds are at "); Serial.print(percent); Serial.println("%"); #endif //Power off the motor digitalWrite(PIN1,LOW); digitalWrite(PIN2,LOW); digitalWrite(PIN3,LOW); digitalWrite(PIN4,LOW); } // ------------------------------------------------------------------------ // ############################# setBlindsMore() ############################# // ------------------------------------------------------------------------ void setBlindsMore(){ #ifdef DEBUG Serial.print("Moving Steps to: "); Serial.println(moveSteps*clockwise); #endif myStepper.step(moveSteps*clockwise); eepromVar.MAXstate = eepromVar.MAXstate + moveSteps; eepromVar.NOWstate = eepromVar.NOWstate + moveSteps; eepromVar.OPENstate=100; //During setup always assume the current position is at 100% open #ifdef DEBUG Serial.print("Blinds are at "); Serial.print(eepromVar.MAXstate); Serial.println(" steps"); #endif //Power off the motor digitalWrite(PIN1,LOW); digitalWrite(PIN2,LOW); digitalWrite(PIN3,LOW); digitalWrite(PIN4,LOW); } // ------------------------------------------------------------------------ // ############################# setBlindsLess() ############################# // ------------------------------------------------------------------------ void setBlindsLess(){ #ifdef DEBUG Serial.print("Moving Steps to: "); Serial.print(moveSteps*counterclockwise); #endif if (eepromVar.NOWstate != 0) { myStepper.step(moveSteps*counterclockwise); eepromVar.MAXstate = eepromVar.MAXstate - moveSteps; eepromVar.NOWstate = eepromVar.NOWstate - moveSteps; //MAXSteps=MAXSteps-moveSteps; //saveEEPROMState(MAXSteps, addr2); //Store the current blind position in the non volotile memory //saveEEPROMState(MAXSteps, addr); //Save the currentSteps position eepromVar.OPENstate=100; //During setup always assume the current position is at 100% open } else { //can not go past 0 steps } #ifdef DEBUG Serial.print(" Blinds are at "); Serial.print(eepromVar.MAXstate); Serial.println(" steps"); #endif //Power off the motor digitalWrite(PIN1,LOW); digitalWrite(PIN2,LOW); digitalWrite(PIN3,LOW); digitalWrite(PIN4,LOW); } // ------------------------------------------------------------------------ // ############################# saveBlindsPosition() ############################# // ------------------------------------------------------------------------ void saveBlindsPosition(){ eepromVar.OPENstate=100; //At the completion of the setup the current position is at 100% open EEPROM.put(addr, eepromVar); // write the data to EEPROM boolean ok2 = EEPROM.commit(); #ifdef DEBUG Serial.println((ok2) ? "Commit OK" : "Commit failed"); #endif } // ------------------------------------------------------------------------ // ############################# resetBlindsPosition() ############################# // ------------------------------------------------------------------------ void resetBlindsPosition(){ //Reset the position of the blinds to 0 eepromVar.MAXstate=0; eepromVar.NOWstate=0; eepromVar.OPENstate=0; EEPROM.put(addr, eepromVar); // write the data to EEPROM boolean ok2 = EEPROM.commit(); #ifdef DEBUG Serial.println((ok2) ? "Commit OK" : "Commit failed"); #endif } // ------------------------------------------------------------------------ // ############################# getLux() ############################# // ------------------------------------------------------------------------ int getLux(int Pin){ // the analog reading from the sensor divider int photocellReading = analogRead(Pin); //Convert phocell analog to lux int lux = map(photocellReading, 0, 1023, 0, 800); #ifdef DEBUG Serial.print("Analog reading RAW = "); Serial.print(photocellReading); // the raw analog reading Serial.print(" LUX= "); Serial.println(lux); #endif return lux; } // ------------------------------------------------------------------------ // ############################# handleOpenArgs() ############################# // ------------------------------------------------------------------------ void handleOpenArgs(){ String message = ""; if (server.arg("open")== ""){ //Parameter not found message = "Open Argument not found"; }else{ //Parameter found message = "Manual input trigered Open Argument = "; message += server.arg("open"); //Gets the value of the query parameter //Move the blinds //moveBlinds(atoi(server.arg("open").c_str())); target=atoi(server.arg("open").c_str()); //target to open to movesLeft=abs(eepromVar.OPENstate-target); //change the global variale to percent to move the blinds } #ifdef DEBUG //server.send(200, "text/plain", message); //Returns the HTTP response #endif } // ------------------------------------------------------------------------ // ############################# handleMoveArgs() ############################# // ------------------------------------------------------------------------ void handleMoveArgs(){ String message = ""; int moveSteps = 0; //Ths value can either be positive -> clockwise rotation or negative -> counetrclockwise rotation if (server.arg("move")== ""){ //Parameter not found message = "Move Argument not found"; }else{ //Parameter found message = "Manual input trigered Open Argument = "; message += server.arg("move"); //Gets the value of the query parameter //Move the blinds moveSteps = atoi(server.arg("move").c_str()); if (eepromVar.NOWstate + moveSteps >= 0) { myStepper.step(moveSteps); eepromVar.MAXstate = eepromVar.MAXstate + moveSteps; eepromVar.NOWstate = eepromVar.NOWstate + moveSteps; //MAXSteps=MAXSteps-moveSteps; //saveEEPROMState(MAXSteps, addr2); //Store the current blind position in the non volotile memory //saveEEPROMState(MAXSteps, addr); //Save the currentSteps position eepromVar.OPENstate=100; //During setup always assume the current position is at 100% open } else { //can not go past 0 steps } } //Power off the motor digitalWrite(PIN1,LOW); digitalWrite(PIN2,LOW); digitalWrite(PIN3,LOW); digitalWrite(PIN4,LOW); } // ------------------------------------------------------------------------ // ############################# setup() ############################# // ------------------------------------------------------------------------ void setup() { digitalWrite(PIN1,OUTPUT); digitalWrite(PIN2,OUTPUT); digitalWrite(PIN3,OUTPUT); digitalWrite(PIN4,OUTPUT); // set the speed (needed to be reduced for the 28BYJ-48): myStepper.setSpeed(6); // initialize the serial port: Serial.begin(115200); Serial.println(); //Setup WiFiManager WiFiManager wfManager; //Switchoff debug mode wfManager.setDebugOutput(false); //exit after config instead of connecting wfManager.setBreakAfterConfig(true); //tries to connect to last known settings //if it does not connect it starts an access point with the specified name //here "AutoConnectAP" with password "password" //and goes into a blocking loop awaiting configuration if (!wfManager.autoConnect()) { Serial.println("failed to connect, resetting and attempting to reconnect"); delay(3000); ESP.reset(); delay(5000); } //if you get here you have connected to the WiFi Serial.println("connected... "); //server.reset(new ESP8266WebServer(WiFi.localIP(), 80)); //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; server.on("/", [](){ //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLstatus+HTMLmenu+HTMLfooter); }); server.on("/api/help", []() { //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLhelp+HTMLstatus+HTMLmenu+HTMLfooter); }); //hande querystring /blinds?open=20 server.on("/api/blinds",[](){ handleOpenArgs(); //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLmove+HTMLstatus+HTMLmenu+HTMLfooter); }); server.on("/api/setup", [](){ //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLsetup+HTMLstatus+HTMLmenu+HTMLfooter); }); server.on("/api/position", [](){ //When ?move=100 is passed move the blinds 100 steps handleMoveArgs(); //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLsetup+HTMLstatus+HTMLmenu+HTMLfooter); }); server.on("/api/save", [](){ saveBlindsPosition(); //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLsavedone+HTMLstatus+HTMLmenu+HTMLfooter); }); server.on("/api/reset", [](){ resetBlindsPosition(); //String HTMLstatus ="

LIMIT is set to: "+String(eepromVar.MAXstate)+"
"+"Current position is: "+String(eepromVar.NOWstate)+"

"; getHTMLstatus(); server.send(200, "text/html", HTMLheader+HTMLheading+HTMLresetdone+HTMLstatus+HTMLmenu+HTMLfooter); }); server.on("/api/lux", []() { eepromVar.LUXstate = getLux(photocellPin); server.send(200, "application/json", jsonOutput("lightlevel",eepromVar.LUXstate)); }); server.on("/api/status", [](){ server.send(200, "application/json", jsonOutput("position",getBlindsPosition())); }); server.onNotFound(handleNotFound); server.begin(); Serial.println("HTTP server started"); Serial.print("local ip: "); Serial.println(WiFi.localIP()); Serial.print("MAC: "); Serial.println(WiFi.macAddress()); eepromVar.MAXstate = -1; eepromVar.NOWstate = -1; eepromVar.OPENstate = -1; eepromVar.LUXstate = -1; EEPROM.begin(sizeof(EEPROMStruct)); EEPROM.get(addr, eepromVar); //read the eeprom structure //currentSteps = getEEPROMState(addr); //Get previous steps number //MAXSteps = getEEPROMState(addr2); //Get previous MAXsteps number if (eepromVar.MAXstate < 0 || eepromVar.NOWstate < 0 || eepromVar.OPENstate < 0) { //eeprom has been corrupted or not set eepromVar.MAXstate = 0; eepromVar.NOWstate = 0; eepromVar.OPENstate = 0; moveBlinds(0); //rest blind position to closed on startup EEPROM.put(addr, eepromVar); } eepromVar.LUXstate = getLux(photocellPin); //if (currentSteps == 0) { //device was reset // eepromVar.NOWstate = 0; // moveBlinds(0); //rest blind position to closed on startup // EEPROM.put(addr, eepromVar); // } #ifdef DEBUG Serial.print(" currentSteps is: "); Serial.print(eepromVar.NOWstate); Serial.print(" MAXSteps is: "); Serial.println(eepromVar.MAXstate); #endif //Power off the motor digitalWrite(PIN1,LOW); digitalWrite(PIN2,LOW); digitalWrite(PIN3,LOW); digitalWrite(PIN4,LOW); } // ------------------------------------------------------------------------ // ############################# loop() ############################# // ------------------------------------------------------------------------ void loop() { if(movesLeft != 0) { //move the steper motor by advanceAmount advanceBlinds(); #ifdef DEBUG Serial.print("movesLeft is: "); Serial.println(movesLeft); #endif movesLeft=movesLeft-advanceAmount; } //else wait for webserver request //Handle server requests server.handleClient(); }