Jeremy WebbAndrew Nelson
Published © GPL3+

VenetianMover: Alexa-Controlled Blinds

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

BeginnerFull instructions provided3 hours1,485

Things used in this project

Hardware components

Micro Metal Gearmotor
×1
Motor Encoder
×1
Arduino Micro
×1
SparkFun ESP8266 Thing - Dev Board
SparkFun ESP8266 Thing - Dev Board
×1
Adafruit Motor Driver
×1
Solderless Breadboard Full Size
Solderless Breadboard Full Size
×1
6V DC Power Supply
×1
Male/Male Jumper Wires
×1
Round Magnet
×1
16 Gauge Floral Wire
×1
Super Glue
×1

Software apps and online services

Arduino IDE
Arduino IDE
Alexa Voice Service
Amazon Alexa Alexa Voice Service

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

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

VUI Diagram

Voice User Interface Diagram to show Alexa flow

Code

motor_control_arduino.ino

Arduino
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.ino

Arduino
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.h

Arduino
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.c

C/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.h

C 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 */

Credits

Jeremy Webb

Jeremy Webb

4 projects • 5 followers
Andrew Nelson

Andrew Nelson

1 project • 0 followers

Comments