mirror of
https://github.com/thehookup/Motorized_MQTT_Blinds.git
synced 2026-01-06 21:23:49 -05:00
316 lines
10 KiB
C++
316 lines
10 KiB
C++
/****************** ALEXA INTEGRATION LIBRARIES *************************************/
|
|
#include "fauxmoESP.h" //https://bitbucket.org/xoseperez/fauxmoesp/downloads/
|
|
#include <ESP8266mDNS.h> //if you get an error here you need to install the ESP8266 board manager
|
|
/****************** WIFI MANAGER LIBRARIES *************************************/
|
|
#include <FS.h>
|
|
#include <DNSServer.h>
|
|
#include <ESP8266WebServer.h>
|
|
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
|
|
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson//this needs to be first, or it all crashes and burns...
|
|
/****************** SKETCH SPECIFIC LIBRARIES *************************************/
|
|
#include <AH_EasyDriver.h> //http://www.alhin.de/arduino/downloads/AH_EasyDriver_20120512.zip
|
|
|
|
/****************** BASIC PACKAGE LIBRARIES *************************************/
|
|
#include <SimpleTimer.h> //https://github.com/thehookup/Simple-Timer-Library
|
|
#include <PubSubClient.h> //https://github.com/knolleary/pubsubclient
|
|
#include <ESP8266WiFi.h> //if you get an error here you need to install the ESP8266 board manager
|
|
#include <ESP8266mDNS.h> //if you get an error here you need to install the ESP8266 board manager
|
|
#include <ArduinoOTA.h> //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<char[]> 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<char[]> 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();
|
|
}
|
|
|