Project tutorial

VenetianMover: Alexa-Controlled Blinds © GPL3+

Create a device to control the tilt of Venetian blinds using your voice.

  • 1,155 views
  • 0 comments
  • 7 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)

Apps and online services

About this project

Overview

All the necessary files for making the VenetianMover device can be downloaded from this repository. All the files are also attached individually below.

Making the Case

The case for the device can be 3D printed using the STL files below if you have access to a 3D printer, or they can be purchased from an online 3D printing service, such as Shapeways. If creating the parts with a fused filament fabrication process, print the rod-gear with a high-density infill (90%) so the hook can be screwed into it later.

Once you have the pieces of the case, the hook attachment can be made by bending the wire into a hook shape and screwing it into the rod-gear. It may also be necessary to secure the hook with super glue.

Wiring

In general wiring is as simple as following the schematic wiring diagram using a breadboard and jumper wires. In addition, the following components will require soldering:

  • Motor driver breakout board
  • Motor encoder

Solder the 0.1" headers that are packaged with the motor driver onto the motor driver breakout board by first inserting the headers into the breakout board so that the board can rest flat, and then soldering each pin.

Once the above step is complete, solder the motor power supply wires by first inserting the wires and bending them so that they stay secure, and then applying the soldering iron and solder. Once soldered, any excess wire can be removed with wire cutters.

The motor encoder also requires soldering. We've chosen to solder wires onto all encoder connections even though our project does not use all of them. First insert the wires into the circular holes and bend the wire to secure it. Then solder the individual wires and trim any excess.

Once complete, the encoder can be installed on the motor and the motor tabs soldered to the tab connections on the encoder. The motor tabs are shown in the image below:

The power for the device is supplied by the 6V power supply. To attach this to the circuit, cut off the end of the female connector that comes with the power supply and push the wires into the breadboard.

Registering the Device

Go to VenetianMover and login with your Amazon account. Click "Add Device" and fill in the device name:

Click save and then click the download link that appears:

Replace the file named connection_info.h with the new file (in directory VenetianMover/arduino/aws_iot_esp8266).

Open up connection_info.h and add your WiFi connection details (network name/SSID and password) in the appropriate locations:

Calibrating for the Blinds

You will need to determine how many turns of the rod are necessary for your blinds to go from completely closed to completely open. Once determined, adjust the MAX_ROTATIONS variable in the file named motor_control_arduino.ino.

Loading the Code

Using the Arduino IDE, load the code named motor_control_arduino.ino onto the Arduino Micro. To do this, start by selecting the correct board under Tools > Board > Arduino Micro, connect the board via USB, select the appropriate COM port under Tools > Port and press the upload button.

To set up the ESP8266, it is necessary to first configure the Arduino IDE to work with the new board. Follow the instructions below:

  • Open Preferences (File > Preferences)
  • Enter http://arduino.esp8266.com/stable/package_esp8266com_index.json into the Additional Board Manager URLs field.
  • Go to Tools > Board > Boards Manager and search for "esp8266".
  • Select the latest version and click the install button.
  • Select the ESP8266 board from the Tools > Board menu.
  • Set Tools -> Upload Speed -> 921600

Open the aws_iot_esp8266.ino sketch, ensuring that cdecode_p.h, cdecode_p.c, and connection_info.h are also opened in the Arduino IDE.

Install the necessary PubSub library into the Arduino IDE by using menu Sketch > Include Library > Manage Libraries..., searching for "pubsubclient", and clicking install.

For this project, the MQTT (messaging protocol) buffer size of the PubSubClient Library must be increased. To do this, find the location the PubSubClient Library was installed to (typically Arduino/libraries/PubSubClient/src) and open the file PubSubClient.h. Change the MQTT_MAX_PACKET_SIZE to 256:

Physically connect the ESP8266 (via USB), select the appropriate COM port, and upload the sketch. Note that if the COM port doesn't show up, you may need to install the USB to UART drivers from here.

Now, test that everything is working correctly. Open the Serial Monitor (Tools > Serial Monitor) and ensure the output contains "PubSubClient connecting to AWS endpoint. connected". This means that everything is set up correctly. If nothing is displayed in the Serial Monitor, you may have missed the output. Reset or power-cycle the ESP8266 and watch the Serial Monitor again.

Adding the Case

Begin this step by pressing the motor gear onto the motor shaft. Ensure that the wider part of the gear faces the motor.

Next, place the rod-gear (rod with the attached gear) and motor into the left half of the case. Check that the gears mesh nicely. Adjust their positioning if needed. Also make sure that the wires attached to the motor exit the top of the case neatly (and are not crimped or squished).

Put the magnet into the round receptacle in the right case half.

Finally slide the left half of the case onto the right half and push them together.

Important: The code assumes that the blinds start at the 50% position between open and closed. Before attaching the device to your blinds, ensure your blinds are fully open (slats horizontal). This should also be done any time the Arduino is reset or power-cycled.

Open the blinds so the slats are horizontal, hook the wire through the rotating hook on your blinds and secure the device to the top of the blinds with the magnet.

Setting Up Alexa

To set up the device with Alexa, you will need the Alexa app which can be found on the Play Store for Android, or the iTunes Store for iOS. Sign into the app, go to Skills, and search for "VenetianMover". Tap Enable and follow the prompts to link your Amazon account. A confirmation, such as the one below, should be shown.

When prompted to, discover your devices.

Using the Device

Ask Alexa to open your blinds:

Alexa, set my blinds to ten-percent.

Congratulations, now Alexa can control your blinds!

Code

motor_control_arduino.inoArduino
Code which gets loaded onto the Arduino
/* VenetianMover Arduino code
 *  
 * Code to connect your VenetianMover device to the ESP8266
 * If you don't have a VenetianMover device, create one following the instructions at
 * https://www.hackster.io/85993/venetianmover-alexa-controlled-blinds-1aee7e
 *
 * VenetianMover Copyright (C) 2018 Jeremy Webb and Andrew Nelson
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const int MAX_ROTATIONS = 8;

// counter for encoder pulses
volatile int enc_cnt = 0;
// counter for pulses from ESP8266
volatile int pulse_cnt = 0;
// indicates ESP8266 is currently sending pulses
volatile bool pulsing = false;
// indicates the motor position is bieng updated
volatile bool updating_motor_pos = false;

const int COUNTS_PER_ROTATION = 747;
const int MAX_MOTOR_POS = MAX_ROTATIONS*COUNTS_PER_ROTATION;

const int MIN_MOTOR_POS = 0;

// current position of the motor
int current_motor_pos = MAX_MOTOR_POS/2;

void setup() {
  // serial debug
  Serial.begin(115200);
  
  // encoder input interrupt
  attachInterrupt(digitalPinToInterrupt(7), risingEnc, RISING);

  // motor control
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(3, OUTPUT);

  // EPS8266 interface
  attachInterrupt(digitalPinToInterrupt(0), risingPulseStartStop, RISING);
  attachInterrupt(digitalPinToInterrupt(1), pulseCount, RISING);

}

void loop() {
  if(updating_motor_pos) {
    updateMotorPos();
  }   
}

/* updateMotorPos - determines amount to move motor based on current position and
 *  new position requested
 *  
 *  does not return a value
 */
void updateMotorPos() {
  int abs_motor_pos = 0;
  int relative_motor_pos = 0;
  // calculate absolute positional value
  abs_motor_pos = int((float(pulse_cnt)/100.0)*MAX_MOTOR_POS);
  // calculate relative amount to turn motor
  if(abs_motor_pos > current_motor_pos) {
    relative_motor_pos = abs_motor_pos - current_motor_pos;
    current_motor_pos += relative_motor_pos;
    turnMotor(relative_motor_pos, true);
  } else if(abs_motor_pos < current_motor_pos) {
    relative_motor_pos = current_motor_pos - abs_motor_pos;
    current_motor_pos -= relative_motor_pos;
    turnMotor(relative_motor_pos, false);
  }
  // stop updating motor
  updating_motor_pos = false;
}

/* turnMotor - controls motor rotation based on current position
 *  
 *  does not return a value
 */
void turnMotor(int relative_motor_pos, bool dir) {
  enc_cnt = 0;
  if(dir) {
    // turn motor
    digitalWrite(4, LOW);
    digitalWrite(5, HIGH);
    analogWrite(3, 255);
    while(enc_cnt < relative_motor_pos);
  } else {
    // turn motor
    digitalWrite(4, HIGH);
    digitalWrite(5, LOW);
    digitalWrite(3, HIGH);
    while(enc_cnt < relative_motor_pos);
  }
  // stop motor
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
}       

/* risingEnc - Pin change interrupt based on the rising edge of the encoder input
 *  
 *  does not return a value
 */
void risingEnc() {
  enc_cnt++;
}

/* risingPulseStartStop - Pin change interrupt based on the rising edge of the start / stop pulse pin
 *  
 *  does not return a value
 */
void risingPulseStartStop() {
  if(pulsing) {
    Serial.println(pulse_cnt);
    updating_motor_pos = true;
  } else {
    pulse_cnt = 0;
  }
  pulsing = !pulsing;
}

/* pulseCount - Pin change interrupt to detect a rising edge on the pulse_count pin
 *  
 *  does not return a value
 */
void pulseCount() {
  if(pulsing) {
    pulse_cnt++;
  }
}
aws_iot_esp8266.inoArduino
Code which gets loaded onto the ESP8266
/* VenetianMover ESP8266 code
 *  
 * Code to connect your VenetianMover device to AWS IOT
 * If you don't have a VenetianMover device, create one following the instructions at
 * https://www.hackster.io/85993/venetianmover-alexa-controlled-blinds-1aee7e
 * 
 * Change MQTT_MAX_PACKET_SIZE to 256 in Arduino/libraries/PubSubClient/src/PubSubClient.h
 * Update the variables in connection_info.h
 *
 *  VenetianMover Copyright (C) 2018 Jeremy Webb and Andrew Nelson
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <time.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "cdecode_p.h"
#include "connection_info.h"

// Buffer size for the binary certificates
// If any of your certificates have string lengths larger than this,
// you will need to increase this value
const unsigned int BINARY_CERT_BUFFER_SIZE = 1600;
const unsigned char MAX_CONNECTION_ATTEMPTS = 20;
// requested setpoint of current device
uint8_t requestedDeviceState = 50;
// cloud needs updating
bool cloudNeedsUpdating = false;

// Physical pins for communicating with Arduino
const uint8_t PULSE_PIN = 12;
const uint8_t START_STOP_PULSE_PIN = 13;

WiFiClientSecure wiFiClient;
void processMsg(char* topic, byte* payload, unsigned int len);
PubSubClient pubSubClient(awsEndpoint, 8883, processMsg, wiFiClient);

const PROGMEM char* rootCert = \
//-----BEGIN CERTIFICATE-----
"MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB" \
"yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL" \
"ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp" \
"U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW" \
"ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0" \
"aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL" \
"MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW" \
"ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln" \
"biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp" \
"U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y" \
"aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1" \
"nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex" \
"t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz" \
"SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG" \
"BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+" \
"rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/" \
"NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E" \
"BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH" \
"BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy" \
"aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv" \
"MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE" \
"p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y" \
"5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK" \
"WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ" \
"4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N" \
"hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq";
//-----END CERTIFICATE-----

void setup() {
  Serial.begin(115200); Serial.println();

  Serial.print("Connecting to "); Serial.print(ssid);Serial.println();
  WiFi.begin(ssid, password);
  WiFi.waitForConnectResult();
  Serial.println("WiFi connected");

  // set pulse pins as outputs
  pinMode(PULSE_PIN, OUTPUT);
  pinMode(START_STOP_PULSE_PIN, OUTPUT);

  // Synchronize time useing SNTP. This is necessary to verify that
  // the TLS certificates offered by the server are currently valid.
  Serial.print("Setting time using SNTP");
  configTime(8 * 3600, 0, "pool.ntp.org", "time.nist.gov");
  time_t now = time(nullptr);
  while (now < 8 * 3600 * 2) {
    delay(500);
    now = time(nullptr);
  }
  Serial.println();
  struct tm timeinfo;
  gmtime_r(&now, &timeinfo);
  Serial.print("Current time: ");
  Serial.print(asctime(&timeinfo));

  uint8_t binaryCert[BINARY_CERT_BUFFER_SIZE];
  // Convert and load client certificate
  int len = b64decode(certificatePemCrt, binaryCert);
  wiFiClient.setCertificate(binaryCert, len);

  // Convert and load private key
  len = b64decode(privatePemKey, binaryCert);
  wiFiClient.setPrivateKey(binaryCert, len);

  // Convert and load root certificate
  len = b64decode(rootCert, binaryCert);
  wiFiClient.setCACert(binaryCert, len);
}

void loop() {
  pubSubCheckAndReconnect();
  updateCloudIfNeeded();
  delay(1000);
}

/* pubSubCheckAndReconnect Check if there is still an active connection
 *  If not, reconnect
 *  Also process the PubSub loop
 *  
 *  returns void
 */
void pubSubCheckAndReconnect() {
  if (!pubSubClient.connected()) {
    Serial.print("PubSubClient connecting to AWS endpoint.");
    unsigned char connection_attempts = 0;
    while(!pubSubClient.connect(awsThingName.c_str())) {
      ++connection_attempts;
      if(connection_attempts > MAX_CONNECTION_ATTEMPTS) {
          // restart to try and fix the issue
          ESP.restart();
      }
      Serial.print(" .");
    }
    // check root certificate
    if(!wiFiClient.verifyCertChain(awsEndpoint)) {
      // server could not be verified using root certificate, disconnect
      Serial.println();
      Serial.println("Root certificate doesn't match the one provided by server. Disconnecting");
      pubSubClient.disconnect();
      return;
    }
    Serial.println(" connected");

    const String subTopic = "$aws/things/" + awsThingName + "/shadow/update/delta";
    if(!pubSubClient.subscribe(subTopic.c_str())){
      Serial.println("Error subscribing to topic");
    }
  }
  pubSubClient.loop();
}

/* updateCloudIfNeeded Check if the device status on the cloud needs to be updated
 *  and update it
 *  
 *  returns void
 */
void updateCloudIfNeeded() {
    if(cloudNeedsUpdating) {
        if(!updateCloudState(String(requestedDeviceState))) {
          Serial.println("Couldn't update AWS IOT cloud");
          cloudNeedsUpdating = false;
        }
    }
}

/* processMsg Handle a message sent from a subscribed topic
 *  
 *  returns void
 */
void processMsg(char* topic, byte* payload, unsigned int length) {
  // find position of updated tilt digits in msg
  const char* updateKeyword = "\"tilt\":";
  char* setPosition = strstr((char*)payload, updateKeyword);
  if(setPosition == NULL) {
    Serial.println("Error, no tilt in message");
    return;
  }
  setPosition += strlen(updateKeyword); // add length of "tilt": 
  // find the length of the number
  uint8_t num_len = strspn(setPosition, "1234567890");
  // char array of size 4 because 100 is max and a null terminator is needed
  char tilt_num[4];
  tilt_num[num_len] = '\0'; // add null terminator
  // copy only tilt value into tilt_num
  strncpy(tilt_num, setPosition, num_len);
  // save requested setpoint
  requestedDeviceState = atoi(tilt_num);
  Serial.println("Sending Pulses: ");
  Serial.println(requestedDeviceState);
  sendPulses(requestedDeviceState);
  cloudNeedsUpdating = true;
}

/* updateCloudState Send a state update to AWS IOT
 *  
 *  returns true if sending the update was successful, false otherwise
 */
bool updateCloudState(String tilt_value) {
  String msg = "{\"state\" : {\"reported\" : {\"tilt\" : " + tilt_value + "}}}";
  String topic = "$aws/things/" + awsThingName + "/shadow/update";
  return pubSubClient.publish(topic.c_str(), msg.c_str(), false);
}

/* sendPulses sends a series of pulses between 0 to and 99
 *  to the Arduino to indiciate the position of the blinds
 *  
 *  returns true when complete
 */
bool sendPulses(uint8_t numPulses) {
  // inidicate the start of a series of pulses
  digitalWrite(START_STOP_PULSE_PIN, HIGH);
  delay(20);
  digitalWrite(START_STOP_PULSE_PIN, LOW);
  // send the series of digital pulses
  for(uint8_t i=0; i < numPulses; ++i) {
    digitalWrite(PULSE_PIN, HIGH);
    delay(5);
    digitalWrite(PULSE_PIN, LOW);
    delay(5);
  }
  // inidicate the stop of a series of pulses
  digitalWrite(START_STOP_PULSE_PIN, HIGH);
  delay(20);
  digitalWrite(START_STOP_PULSE_PIN, LOW);
  return true;
}

/* b64decode Convert a String into binary
 *  
 * Author: Anthony Elder 
 * License: Apache License v2
 *
 * Modified by Jeremy Webb
 */
int b64decode(const char* b64Text, uint8_t* output) {
  base64_decodestate s;
  base64_init_decodestate(&s);
  int cnt = base64_decode_block_p(b64Text, strlen(b64Text), (char*)output, &s);
  return cnt;
}
connection_info.hArduino
Connection information loaded onto ESP8266. Replace this file with the one downloaded when registering your device.
/* VenetianMover ESP8266 code - connection_info.h
 * 
 * Code to connect your VenetianMover device to AWS IOT
 * 
 * Change MQTT_MAX_PACKET_SIZE to 256 in Arduino/libraries/PubSubClient/src/PubSubClient.h
 * Update the variables below
 *
 * Author: Jeremy Webb and Andrew Nelson
 */

// WiFi connection credentials
const char* ssid = "*******";
const char* password = "*******";

// thing name on AWS IOT
const String awsThingName = "ThingName";
// AWS IOT endpoint
// Can be found under the "Interact" tab of your thing
const char* awsEndpoint = "*************.iot.us-east-1.amazonaws.com";

// Replace the client certificate, private key, and root certificate
// below with your own.
// Add a double-quote character at the start of each line and a 
// double-quote and backslash at the end of each line.

// xxxxxxxxxx-certificate.pem.crt
const PROGMEM char* certificatePemCrt = \
//-----BEGIN CERTIFICATE-----
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
//-----END CERTIFICATE-----

// xxxxxxxxxx-private.pem.key
const PROGMEM char* privatePemKey = \
//-----BEGIN RSA PRIVATE KEY-----
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
//-----END RSA PRIVATE KEY-----
cdecode_p.cC/C++
Decoder library source code that should be loaded onto the ESP8266
/*
cdecoder_p.c - c source to a base64 decoding algorithm implementation

This is part of the libb64 project, and has been placed in the public domain.
For details, see http://sourceforge.net/projects/libb64
*/

#include "cdecode_p.h"

int
base64_decode_value (char value_in)
{
  static const char decoding[] =
    { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,
    -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
    17, 18, 19, 20, 21, 22, 23,
    24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
    36, 37, 38, 39, 40, 41, 42,
    43, 44, 45, 46, 47, 48, 49, 50, 51
  };
  static const char decoding_size = sizeof (decoding);
  value_in -= 43;
  if (value_in < 0 || value_in > decoding_size)
    return -1;
  return decoding[(int) value_in];
}

void
base64_init_decodestate (base64_decodestate * state_in)
{
  state_in->step = step_a;
  state_in->plainchar = 0;
}

int
base64_decode_block_p (const char *code_in, const int length_in,
		     char *plaintext_out, base64_decodestate * state_in)
{
  const char *codechar = code_in;
  char *plainchar = plaintext_out;
  signed char fragment;

  *plainchar = state_in->plainchar;

  switch (state_in->step)
    {
      while (1)
	{
    case step_a:
	  do
	    {
	      if (codechar == code_in + length_in)
		{
		  state_in->step = step_a;
		  state_in->plainchar = *plainchar;
		  return plainchar - plaintext_out;
		}
	      fragment = (char) base64_decode_value (pgm_read_byte_near(codechar++));
	    }
	  while (fragment < 0);
	  *plainchar = (fragment & 0x03f) << 2;
    case step_b:
	  do
	    {
	      if (codechar == code_in + length_in)
		{
		  state_in->step = step_b;
		  state_in->plainchar = *plainchar;
		  return plainchar - plaintext_out;
		}
	      fragment = (char) base64_decode_value (pgm_read_byte_near(codechar++));
	    }
	  while (fragment < 0);
	  *plainchar++ |= (fragment & 0x030) >> 4;
	  *plainchar = (fragment & 0x00f) << 4;
    case step_c:
	  do
	    {
	      if (codechar == code_in + length_in)
		{
		  state_in->step = step_c;
		  state_in->plainchar = *plainchar;
		  return plainchar - plaintext_out;
		}
	      fragment = (char) base64_decode_value (pgm_read_byte_near(codechar++));
	    }
	  while (fragment < 0);
	  *plainchar++ |= (fragment & 0x03c) >> 2;
	  *plainchar = (fragment & 0x003) << 6;
    case step_d:
	  do
	    {
	      if (codechar == code_in + length_in)
		{
		  state_in->step = step_d;
		  state_in->plainchar = *plainchar;
		  return plainchar - plaintext_out;
		}
	      fragment = (char) base64_decode_value (pgm_read_byte_near(codechar++));
	    }
	  while (fragment < 0);
	  *plainchar++ |= (fragment & 0x03f);
	}
    }
  /* control should not reach here */
  return plainchar - plaintext_out;
}
cdecode_p.hC Header File
Header file for the decoder library that should be loaded onto the ESP8266
/*
cdecode_p.h - c header for a base64 decoding algorithm

This is part of the libb64 project, and has been placed in the public domain.
For details, see http://sourceforge.net/projects/libb64

These files have been modified for arduino to read the input data from 
program memory.
*/  

#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#endif
  
#ifndef BASE64_CDECODE_H
#define BASE64_CDECODE_H

#ifdef __cplusplus
extern "C" {
#endif

typedef enum 
{ 
step_a, step_b, step_c, step_d 
} base64_decodestep;

 
typedef struct 
{
  
base64_decodestep step;
  
char plainchar;
 
} base64_decodestate;

 
void base64_init_decodestate (base64_decodestate * state_in);

 
int base64_decode_value (char value_in);

 
int base64_decode_block_p (const char *code_in, const int length_in,
			    char *plaintext_out,
			    base64_decodestate * state_in);

#ifdef __cplusplus
}
#endif
 
#endif	/* BASE64_CDECODE_H */

Custom parts and enclosures

Case: Right Half
Case: Left Half
Motor Gear
Gear which mounts on motor shaft.
Rod-Gear
Piece which meshes with the Motor Gear and attaches to the blinds hook.
Case Clip
Clip which holds the case together

Schematics

Circuit Diagram
Venetianmoverschematic g99arvdjxx
VUI Diagram
Voice User Interface Diagram to show Alexa flow
Vui diagram xu1fdp7aar

Comments

Similar projects you might like

Secure Package Delivery Trunk for Your Front Porch

Project tutorial by Team Castle Locker

  • 2,515 views
  • 1 comment
  • 15 respects

Smart Blinds

Project tutorial by Froz3nArcher

  • 13,939 views
  • 3 comments
  • 47 respects

Monocle: View & Control IP Cameras with Alexa & Arduino

Project tutorial by Team Monocle

  • 3,783 views
  • 2 comments
  • 11 respects

Scent-terrific Smart Candle

Project tutorial by Darian Johnson

  • 2,895 views
  • 0 comments
  • 32 respects

Android App-Based Home Automation System Using IOT

Project tutorial by Team Autoshack

  • 24,826 views
  • 17 comments
  • 75 respects

Blinds (Or Any AC Power Motor) Control

Project tutorial by gomecin

  • 11,575 views
  • 23 comments
  • 54 respects
Add projectSign up / Login