/****************** ALEXA INTEGRATION LIBRARIES *************************************/ #include "fauxmoESP.h" //https://bitbucket.org/xoseperez/fauxmoesp/downloads/ #include //if you get an error here you need to install the ESP8266 board manager /****************** WIFI MANAGER LIBRARIES *************************************/ #include #include #include #include //https://github.com/tzapu/WiFiManager #include //https://github.com/bblanchon/ArduinoJson//this needs to be first, or it all crashes and burns... /****************** SKETCH SPECIFIC LIBRARIES *************************************/ #include //http://www.alhin.de/arduino/downloads/AH_EasyDriver_20120512.zip /****************** BASIC PACKAGE LIBRARIES *************************************/ #include //https://github.com/thehookup/Simple-Timer-Library #include //https://github.com/knolleary/pubsubclient #include //if you get an error here you need to install the ESP8266 board manager #include //if you get an error here you need to install the ESP8266 board manager #include //ArduinoOTA is now included with the ArduinoIDE /***************** START USER CONFIG SECTION *********************************/ /***************** START USER CONFIG SECTION *********************************/ /***************** START USER CONFIG SECTION *********************************/ /***************** START USER CONFIG SECTION *********************************/ //define your default values here, if there are different values in config.json, they are overwritten. char alexa_id[20]; char rotations[6]; char current_position[6] = "0"; #define STEPPER_SPEED 35 //Defines the speed in RPM for your stepper motor #define STEPPER_STEPS_PER_REV 1028 //Defines the number of pulses that is required for the stepper to rotate 360 degrees #define STEPPER_MICROSTEPPING 0 //Defines microstepping 0 = no microstepping, 1 = 1/2 stepping, 2 = 1/4 stepping #define DRIVER_INVERTED_SLEEP 1 //Defines sleep while pin high. If your motor will not rotate freely when on boot, comment this line out. #define STEPPER_DIR_PIN D6 #define STEPPER_STEP_PIN D7 #define STEPPER_SLEEP_PIN D5 #define STEPPER_MICROSTEP_1_PIN 14 #define STEPPER_MICROSTEP_2_PIN 12 /***************** END USER CONFIG SECTION *********************************/ /***************** END USER CONFIG SECTION *********************************/ /***************** END USER CONFIG SECTION *********************************/ /***************** END USER CONFIG SECTION *********************************/ /***************** END USER CONFIG SECTION *********************************/ WiFiClient espClient; PubSubClient client(espClient); SimpleTimer timer; AH_EasyDriver shadeStepper(STEPPER_STEPS_PER_REV, STEPPER_DIR_PIN ,STEPPER_STEP_PIN,STEPPER_MICROSTEP_1_PIN,STEPPER_MICROSTEP_2_PIN,STEPPER_SLEEP_PIN); fauxmoESP fauxmo; //Global Variables bool shouldSaveConfig = false; bool boot = true; int currentPosition = 0; int newPosition = 0; bool moving = false; char positionPublish[50]; //callback notifying us of the need to save config void saveConfigCallback () { Serial.println("Should save config"); shouldSaveConfig = true; } void configPortal() { Serial.println("mounting FS..."); if (SPIFFS.begin()) { Serial.println("mounted file system"); if (SPIFFS.exists("/storedPosition.json")) { File storedFile = SPIFFS.open("/storedPosition.json", "r"); if (storedFile) { Serial.println("opened config file"); size_t size = storedFile.size(); // Allocate a buffer to store contents of the file. std::unique_ptr buf(new char[size]); storedFile.readBytes(buf.get(), size); DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.parseObject(buf.get()); json.printTo(Serial); if (json.success()) { Serial.println("\nparsed json"); strcpy(current_position, json["current_position"]); String tempPos = String((char *)current_position); currentPosition = tempPos.toInt(); newPosition = tempPos.toInt(); boot = false; } else { Serial.println("failed to load json config"); } } } if (SPIFFS.exists("/config.json")) { //file exists, reading and loading Serial.println("reading config file"); File configFile = SPIFFS.open("/config.json", "r"); if (configFile) { Serial.println("opened config file"); size_t size = configFile.size(); // Allocate a buffer to store contents of the file. std::unique_ptr buf(new char[size]); configFile.readBytes(buf.get(), size); DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.parseObject(buf.get()); json.printTo(Serial); if (json.success()) { Serial.println("\nparsed json"); strcpy(alexa_id, json["alexa_id"]); strcpy(rotations, json["rotations"]); } else { Serial.println("failed to load json config"); } } } } else { Serial.println("failed to mount FS"); } //end read // The extra parameters to be configured (can be either global or just in the setup) // After connecting, parameter.getValue() will get you the configured value // id/name placeholder/prompt default length WiFiManagerParameter custom_alexa_id("ID", "Unique Alexa ID", alexa_id, 20); WiFiManagerParameter custom_rotations("rotations", "Rotations (12 recommended)", rotations, 6); //WiFiManager //Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifiManager; //set config save notify callback wifiManager.setSaveConfigCallback(saveConfigCallback); //add all your parameters here wifiManager.addParameter(&custom_alexa_id); wifiManager.addParameter(&custom_rotations); //reset settings - for testing if(digitalRead(D1) == LOW) { wifiManager.resetSettings(); } //set minimu quality of signal so it ignores AP's under that quality //defaults to 8% //wifiManager.setMinimumSignalQuality(); //sets timeout until configuration portal gets turned off //useful to make it all retry or go to sleep //in seconds //wifiManager.setTimeout(120); //fetches ssid and pass and tries to connect //if it does not connect it starts an access point with the specified name //here "AutoConnectAP" //and goes into a blocking loop awaiting configuration if (!wifiManager.autoConnect("Blinds_Setup")) { Serial.println("failed to connect and hit timeout"); delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } //if you get here you have connected to the WiFi Serial.println("connected..."); //read updated parameters strcpy(alexa_id, custom_alexa_id.getValue()); strcpy(rotations, custom_rotations.getValue()); //save the custom parameters to FS if (shouldSaveConfig) { Serial.println("saving config"); DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); json["alexa_id"] = alexa_id; json["rotations"] = rotations; File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { Serial.println("failed to open config file for writing"); } json.printTo(Serial); json.printTo(configFile); configFile.close(); //end save } } void processStepper() { if (newPosition > currentPosition) { #if DRIVER_INVERTED_SLEEP == 1 shadeStepper.sleepON(); #endif #if DRIVER_INVERTED_SLEEP == 0 shadeStepper.sleepOFF(); #endif shadeStepper.move(80, FORWARD); currentPosition++; moving = true; } if (newPosition < currentPosition) { #if DRIVER_INVERTED_SLEEP == 1 shadeStepper.sleepON(); #endif #if DRIVER_INVERTED_SLEEP == 0 shadeStepper.sleepOFF(); #endif shadeStepper.move(80, BACKWARD); currentPosition--; moving = true; } if (newPosition == currentPosition && moving == true) { #if DRIVER_INVERTED_SLEEP == 1 shadeStepper.sleepOFF(); #endif #if DRIVER_INVERTED_SLEEP == 0 shadeStepper.sleepON(); #endif String temp_str = String(currentPosition); temp_str.toCharArray(positionPublish, temp_str.length() + 1); Serial.println("saving position"); DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); json["current_position"] = positionPublish; File storedFile = SPIFFS.open("/storedPosition.json", "w"); if (!storedFile) { Serial.println("failed to open config file for writing"); } json.printTo(Serial); json.printTo(storedFile); storedFile.close(); moving = false; } Serial.println(currentPosition); Serial.println(newPosition); } //Run once setup void setup() { pinMode(D1, INPUT_PULLUP); Serial.begin(115200); configPortal(); shadeStepper.setMicrostepping(STEPPER_MICROSTEPPING); // 0 -> Full Step shadeStepper.setSpeedRPM(STEPPER_SPEED); // set speed in RPM, rotations per minute #if DRIVER_INVERTED_SLEEP == 1 shadeStepper.sleepOFF(); #endif #if DRIVER_INVERTED_SLEEP == 0 shadeStepper.sleepON(); #endif WiFi.mode(WIFI_STA); ArduinoOTA.setHostname(alexa_id); ArduinoOTA.begin(); delay(10); if(alexa_id != "Echo Control ID") { fauxmo.createServer(true); // not needed, this is the default value fauxmo.setPort(80); // This is required for gen3 devices fauxmo.enable(true); fauxmo.addDevice(alexa_id); fauxmo.onSetState([](unsigned char device_id, const char * device_name, bool state, unsigned char value) { if (strcmp(device_name, alexa_id)==0) { if(state) { int rots = strtoul(rotations, NULL, 10); newPosition = map(value, 0, 255, 0, rots); } else { newPosition = 0; } } }); } timer.setInterval(((1 << STEPPER_MICROSTEPPING)*5800)/STEPPER_SPEED, processStepper); Serial.print("Device Name is "); Serial.println(alexa_id); } void loop() { fauxmo.handle(); ArduinoOTA.handle(); timer.run(); }