Jack Decker
Published

Amazon Alexa PC Boot and Heater Start

Using Amazon Alexa or the app, start a PC and operate a space heater then use a temperature sensor to regulate the heater.

IntermediateFull instructions provided6 hours173
Amazon Alexa PC Boot and Heater Start

Things used in this project

Hardware components

Argon
Particle Argon
×1
DHT22 temperature-humidity sensor
Adafruit DHT22 temperature-humidity sensor
×1
IR Receiver Module
×1
IR Emitter Module
×1
Echo Dot
Amazon Alexa Echo Dot
×1
Breadboard (generic)
Breadboard (generic)
×1
Resistor 220 ohm
Resistor 220 ohm
×1

Software apps and online services

Amazon Alexa service
IFTTT Amazon Alexa service
Particle Build Web IDE
Particle Build Web IDE

Story

Read more

Schematics

Final Breadboard Schematic Download

Final Breadboard

Code

DHT22 Temperature Sensor Testing

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <PietteTech_DHT.h>

#define DHTTYPE  DHT22
#define DHTPIN   D2

PietteTech_DHT DHT(DHTPIN, DHTTYPE);

double serverTemp;
double serverHumidity;

void setup() {
    Particle.variable("serverTemp", &serverTemp, DOUBLE);
    Particle.variable("serverHumidity", &serverHumidity, DOUBLE);
}

void loop() {
    int result = DHT.acquireAndWait(2000);
  
    serverTemp = DHT.getFahrenheit();
    serverHumidity = DHT.getHumidity();
    Particle.publish("temperature", String::format("%.1f", serverTemp));

    if (serverTemp > 0){
        Particle.publish("temperature", String::format("%.1f", serverTemp));
    }
    else{
        Particle.publish("temperature", "error");
    }
      delay(5000);
}

Wake On Lan Testing

C/C++
// Define the pins we're going to call pinMode on
int LED = D7; // This one is the built-in tiny one to the right of the USB jack

#define MAC_BYTES 6
#define REPEAT_MAC 16
#define MAGIC_HEADER_LENGTH 6

uint16_t port = 7;
IPAddress broadcastIP(255,255,255,255);
IPAddress pingIP;

uint8_t hex_to_byte(uint8_t h, uint8_t l) {
    uint8_t retval = 0x00;

    // higher nibble
    if (h >= 0x30 && h <= 0x39) { // 0-9
        retval |= (h - 0x30) << 4;
    }

    if (h >= 0x41 && h <= 0x46) { // A-F
        retval |= (h - 0x41 + 0x0A) << 4;
    }

    if (h >= 0x61 && h <= 0x66) { // a-f
        retval |= (h - 0x61 + 0x0A) << 4;
    }

    // lower nibble
    if (l >= 0x30 && l <= 0x39) { // 0-9
        retval |= l - 0x30;
    }

    if (l >= 0x41 && l <= 0x46) { // A-F
        retval |= l - 0x41 + 0x0A;
    }

    if (l >= 0x61 && l <= 0x66) { // a-f
        retval |= l - 0x61 + 0x0A;
    }

    return retval;
}

void parseMacAddress(const char* string, uint8_t* target) {
    uint8_t i = 0;
    uint8_t j = 0;
    uint8_t max = 17; // MAC String is 17 characters.
    while (i < max) {
        target[j++] = hex_to_byte(string[i], string[i + 1]);
        i += 3;
    }
}

int wake(const char* mac) {
    uint8_t contents[MAGIC_HEADER_LENGTH + REPEAT_MAC * MAC_BYTES];
    uint8_t rawMac[MAC_BYTES];

    parseMacAddress(mac, rawMac);

    UDP udp;
    udp.begin(port);
    udp.beginPacket(broadcastIP, port);

    for (int i = 0; i < MAGIC_HEADER_LENGTH; i++) {
        contents[i] = 0xFF;
    }
    for (uint8_t i = MAGIC_HEADER_LENGTH; i < sizeof contents; i++) {
        contents[i] = rawMac[(i - MAGIC_HEADER_LENGTH) % MAC_BYTES];
    }

    udp.write(contents, sizeof contents);

    udp.endPacket();
    udp.stop();

    Particle.publish("test","got to end of wake ");
    return TRUE;
}
int wakeHost(String param) {
    String MACaddress = "00:d8:61:aa:73:7a";
    Particle.publish("test","got to wake host");

    char szMacAddress[80];
    MACaddress.toCharArray(szMacAddress,80);
    Particle.publish("test", szMacAddress);
    return wake(szMacAddress);
}


void setup() {
    pinMode(LED, OUTPUT);
    Spark.function("wakeHost", wakeHost);

}

void loop() {
   
}

IR Receiver Testing

C/C++
/*
 * IRremoteLearn: IRrecvDemo - demonstrates receiving IR codes with IRrecv
 * An IR detector/demodulator must be connected to the input RECV_PIN.
 *
 * NOTE: This library expects a special non-demodulated Learner IR receiver
 * such as the Vishay TSMP58000 http://www.vishay.com/docs/82485/tsmp58000.pdf
 */


/*
 * EXAMPLE OUTPUT when paired with IRsendDemo
 *
 * Decoded SONY: 00068B92 (20 bits) Raw(41)
 * Decoded NEC: [VALUE MATCHED]: 01FE40BF (32 bits) Raw(67)
 * Decoded PANASONIC - Address: 4004 Value: 0100BCBD (48 bits) Raw(99)
 * Decoded JVC: 0000C5E8 (16 bits) Raw(35)
 * Unknown encoding: 80BC9608 (32 bits) Raw(21)  ... this is RC5, needs tuning
 * Unknown encoding: FFD81C30 (32 bits) Raw(33)  ... this is RC6, needs tuning
 */

#include <IRremoteLearn.h>

const int RECV_PIN = D3;

// Default 300ms idle timeout, 200us mark timeout
IRrecv irrecv(RECV_PIN);

// Optionally specify your own timeouts
// const unsigned long IDLE_TIMEOUT_MS = 500;
// const unsigned long MARK_TIMEOUT_US = 200;
// IRrecv irrecv(RECV_PIN, IDLE_TIMEOUT_MS, MARK_TIMEOUT_US);

decode_results results;
unsigned int codedValue = 0x64;



void setup()
{
    Serial.begin(9600);
    irrecv.enableIRIn(); // Start the receiver
}

void dump(decode_results *results) {
    // Dumps out the decode_results structure.
    // Call this after IRrecv::decode()
    int count = results->rawlen;
    if (results->decode_type == UNKNOWN) {
        Particle.publish("EncodeType:","Unknown encoding:");
        
    }
    else if (results->decode_type == NEC) {
        Particle.publish("EncodeType:","Decoded NEC: ");

        // Test for a specific value, do some action!
        codedValue = results->value;
        Particle.publish("value", String::format("%d",results->value));
        if (results->value == 	16753245) {
            Serial.print("[VALUE MATCHED]: ");
            Particle.publish("match", "match");

        }
    }
    else if (results->decode_type == SONY) {
        Particle.publish("EncodeType:","Decoded SONY: ");
    }
    else if (results->decode_type == RC5) {
        // TODO: Known issue RC5 decoding needs to be tuned!
        Particle.publish("EncodeType:","Decoded RC5: ");
    }
    else if (results->decode_type == RC6) {
        // TODO: Known issue RC6 decoding needs to be tuned!
        Particle.publish("EncodeType:","Decoded RC6: ");
    }
    else if (results->decode_type == PANASONIC) {
        Serial.print("Decoded PANASONIC - Address: ");
        Serial.print(results->address, HEX);
        Serial.print(" Value: ");
    }
    else if (results->decode_type == JVC) {
        Particle.publish("EncodeType:","Decoded JVC: ");
    }

    if (results->decode_type == DISNEY) {
        // TODO: Known issue DISNEY decoding needs to be tuned!
        // Need to add CRC check to ensure we have a valid DISNEY code.
        count = results->disney_len;
        Serial.print("Decoded DISNEY: ");
        for (int i=0; i<results->disney_len; i++) {
            Serial.printf("%02X",results->disney_data[i]);
        }
    } else {
        Serial.printf("%08X",results->value);
    }

    Serial.printlnf(" (%d bits) Raw(%d)", results->bits, count);
    Particle.publish("reciever", String::format(" (%d bits) Raw(%d)", results->bits, count));
    //Particle.publish("reciever", String::format("%.1f", results));


    // Optional deep dive into data (high,-low,high,-low)
    // for (int i = 0; i < count; i++) {
    //     Serial.printf("%s%lu,", (i%2)?"-":"",results->rawbuf[i]);
    // }
    // Serial.println();

}

void loop() {
  if (irrecv.decode(&results)) {
    dump(&results);
    irrecv.resume(); // Receive the next value
  }
}

IR Transmit Testing

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <IRremoteLearn.h>

const int SEND_PIN = D4;

// defaults to TX pin if no pin specified, see IRsend::IRsend(int txpin) header for PWM pins available
IRsend irsend(SEND_PIN);

void setup() {
    pinMode(D7, OUTPUT);
}

void loop() {
    digitalWrite(D7, HIGH);
    
    irsend.sendNEC(16753245, 32);
    delay(1000);

    // unsigned int buffer[10] = {400, 400, 1233, 800, 800};
    // irsend.sendRaw(buffer, 5, 38);
    // delay(1000);

    digitalWrite(D7, LOW);
    delay(3000);
}

IR Transmit and Receive Testing

C/C++
/*
 * IRremoteLearn: IRrecvDemo - demonstrates receiving IR codes with IRrecv
 * An IR detector/demodulator must be connected to the input RECV_PIN.
 *
 * NOTE: This library expects a special non-demodulated Learner IR receiver
 * such as the Vishay TSMP58000 http://www.vishay.com/docs/82485/tsmp58000.pdf
 */


/*
 * EXAMPLE OUTPUT when paired with IRsendDemo
 *
 * Decoded SONY: 00068B92 (20 bits) Raw(41)
 * Decoded NEC: [VALUE MATCHED]: 01FE40BF (32 bits) Raw(67)
 * Decoded PANASONIC - Address: 4004 Value: 0100BCBD (48 bits) Raw(99)
 * Decoded JVC: 0000C5E8 (16 bits) Raw(35)
 * Unknown encoding: 80BC9608 (32 bits) Raw(21)  ... this is RC5, needs tuning
 * Unknown encoding: FFD81C30 (32 bits) Raw(33)  ... this is RC6, needs tuning
 */

#include <IRremoteLearn.h>

const int RECV_PIN = D3;

// Default 300ms idle timeout, 200us mark timeout
IRrecv irrecv(RECV_PIN);

// Optionally specify your own timeouts
// const unsigned long IDLE_TIMEOUT_MS = 500;
// const unsigned long MARK_TIMEOUT_US = 200;
// IRrecv irrecv(RECV_PIN, IDLE_TIMEOUT_MS, MARK_TIMEOUT_US);

decode_results results;
unsigned int codedValue = 0x64;

const int SEND_PIN = D4;
IRsend irsend(SEND_PIN);


void setup()
{
    Serial.begin(9600);
    irrecv.enableIRIn(); // Start the receiver
    pinMode(D7, OUTPUT);
}

void dump(decode_results *results) {
    // Dumps out the decode_results structure.
    // Call this after IRrecv::decode()
    int count = results->rawlen;
    if (results->decode_type == UNKNOWN) {
        Particle.publish("EncodeType:","Unknown encoding:");
        codedValue = results->value;
        Particle.publish("value", String::format("%d", codedValue));
        
    }
    else if (results->decode_type == NEC) {
        Particle.publish("EncodeType:","Decoded NEC: ");

        // Test for a specific value, do some action!
        codedValue = results->value;
        Particle.publish("value", String::format("%d", codedValue));
        if (results->value == 16753245) {
            Serial.print("[VALUE MATCHED]: ");
            Particle.publish("match", "match");

        }
    }
}
void loop() {
    // digitalWrite(D7, HIGH);
    // irsend.sendNEC(codedValue, 32);
    
    if (irrecv.decode(&results)) {
        // dump(&results);
        // irrecv.resume(); // Receive the next value
        Particle.publish(Decoded Value:, results.value, HEX);
        irrecv.resume();
    }
    //digitalWrite(D7, LOW);
    //delay(3000);
}

Final Home Automation

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <IRremoteLearn.h>

// This #include statement was automatically added by the Particle IDE.
#include <PietteTech_DHT.h>

//WakeOnLan declarations and variables
#define MAC_BYTES 6
#define REPEAT_MAC 16
#define MAGIC_HEADER_LENGTH 6

uint16_t port = 7;
IPAddress broadcastIP(255,255,255,255);
IPAddress pingIP;

//IR declarations and variables
const int SEND_PIN = D4;
IRsend irsend(SEND_PIN);
unsigned int oncodePower[143] = {1250,415,1250,415,423,1250,1247,448,1223,427,422,1256,423,1258,422,1257,423,1257,421,1257,423,1256,1248,7026,1250,416,1254,412,422,1250,1248,406,1264,427,422,1260,421,1257,423,1257,422,1256,423,1258,422,1256,1249,7066,1251,399,1267,416,421,1249,1250,419,1252,427,422,1260,420,1258,421,1258,423,1257,422,1256,436,1242,1250,7032,1250,416,1250,418,421,1249,1250,420,1250,428,422,1258,423,1256,421,1259,422,1258,422,1257,422,1255,1249,7028,1250,417,1250,416,421,1249,1250,418,1253,425,428,1254,422,1257,423,1257,422,1258,421,1260,419,1257,1248,7028,1248,416,1251,417,420,1250,1251,418,1251,427,422,1259,422,1257,423,1256,423,1267,412,1258,426,1252,1250};
unsigned int oncodeSwitch[119] = {1250,411,1255,411,425,1244,1253,414,1255,421,427,1254,427,1252,428,1250,426,1253,1251,423,427,1253,426,7849,1253,411,1254,417,421,1245,1253,415,1255,422,427,1255,424,1254,426,1252,429,1247,1256,422,427,1253,428,7891,1253,410,1255,410,426,1247,1254,414,1254,423,427,1255,426,1255,424,1255,426,1249,1256,422,428,1251,429,7857,1253,411,1255,411,427,1244,1255,414,1256,419,429,1254,428,1251,428,1255,424,1251,1254,421,428,1253,428,7851,1255,410,1256,410,427,1245,1253,414,1257,422,428,1253,427,1252,429,1253,424,1251,1254,422,427,1254,426};
unsigned int oncodeOsc[143] = {1247,415,1250,417,422,1249,1249,420,1250,427,423,1257,424,1257,423,1257,1249,427,422,1259,422,1258,423,7804,1250,416,1250,416,422,1251,1250,420,1250,435,415,1260,423,1260,421,1256,1252,438,412,1260,422,1258,424,7857,1250,416,1251,416,424,1252,1249,420,1251,438,413,1259,423,1259,423,1256,1252,429,421,1260,424,1260,422,7818,1248,433,1237,417,422,1251,1251,422,1250,428,422,1258,426,1257,426,1256,1251,429,422,1262,421,1261,422,7810,1249,418,1250,418,421,1251,1253,420,1252,427,423,1260,424,1259,423,1259,1250,427,423,1262,422,1261,421,7812,1250,414,1255,415,422,1252,1252,419,1252,427,423,1262,423,1261,422,1258,1252,427,422,1262,422,1258,425};

//Temperature sensor declarations and variables
#define DHTTYPE  DHT22
#define DHTPIN   D2
PietteTech_DHT DHT(DHTPIN, DHTTYPE);
double serverTemp;
double accurateTemp;

//Time variables
int64_t prevTime = -60000;
int64_t currentTime;

//Final Control Variables
bool deskStart = FALSE;
bool checkHIGHTemp = FALSE;
bool highTempOverride = FALSE;

//Hex to byte function
uint8_t hex_to_byte(uint8_t h, uint8_t l) {
    uint8_t retval = 0x00;

    // higher nibble
    if (h >= 0x30 && h <= 0x39) { // 0-9
        retval |= (h - 0x30) << 4;
    }

    if (h >= 0x41 && h <= 0x46) { // A-F
        retval |= (h - 0x41 + 0x0A) << 4;
    }

    if (h >= 0x61 && h <= 0x66) { // a-f
        retval |= (h - 0x61 + 0x0A) << 4;
    }

    // lower nibble
    if (l >= 0x30 && l <= 0x39) { // 0-9
        retval |= l - 0x30;
    }

    if (l >= 0x41 && l <= 0x46) { // A-F
        retval |= l - 0x41 + 0x0A;
    }

    if (l >= 0x61 && l <= 0x66) { // a-f
        retval |= l - 0x61 + 0x0A;
    }

    return retval;
}
void parseMacAddress(const char* string, uint8_t* target) {
    uint8_t i = 0;
    uint8_t j = 0;
    uint8_t max = 17; // MAC String is 17 characters.
    while (i < max) {
        target[j++] = hex_to_byte(string[i], string[i + 1]);
        i += 3;
    }
}

//wake PC from sleep
int wake(const char* mac) {
    uint8_t contents[MAGIC_HEADER_LENGTH + REPEAT_MAC * MAC_BYTES];
    uint8_t rawMac[MAC_BYTES];

    parseMacAddress(mac, rawMac);

    UDP udp;
    udp.begin(port);
    udp.beginPacket(broadcastIP, port);

    for (int i = 0; i < MAGIC_HEADER_LENGTH; i++) {
        contents[i] = 0xFF;
    }
    for (uint8_t i = MAGIC_HEADER_LENGTH; i < sizeof contents; i++) {
        contents[i] = rawMac[(i - MAGIC_HEADER_LENGTH) % MAC_BYTES];
    }

    udp.write(contents, sizeof contents);

    udp.endPacket();
    udp.stop();

    Particle.publish("test","got to end of wake ");
    return TRUE;
}
//Particle Cloud function
int wakeHost(String param) {
    String MACaddress = "00:d8:61:aa:73:7a";
    Particle.publish("test","got to wake host");

    char szMacAddress[80];
    MACaddress.toCharArray(szMacAddress,80);
    Particle.publish("test", szMacAddress);
    if(accurateTemp < 72.0){
        deskStart = TRUE;
    }
    checkHIGHTemp = TRUE;
    return wake(szMacAddress);
}
//particle cloud function
int startHeaterPower(String param) {
    Particle.publish("test","got to heater power trigger");
    deskStart = TRUE;
    return 0;
}
int startHeaterLow(String param) {
    Particle.publish("test","got to heater low trigger");
    deskStart = TRUE;
    highTempOverride = TRUE;
    return 0;
}
int switchHeater(String param) {
    Particle.publish("test","got to heater switch trigger");
    highTempOverride = TRUE;
    return 0;
}
int heaterOscillate(String param) {
    Particle.publish("test","got to heater oscillate trigger");
    irsend.sendRaw(oncodeOsc,143,38);
    Particle.publish("test","sentOSC");
    return 0;
}


void setup() {
    Spark.function("wakeHost", wakeHost);
    Spark.function("startHeaterPower", startHeaterPower);
    Spark.function("startHeaterLow", startHeaterLow);
    Spark.function("switchHeater", switchHeater);
    Spark.function("heaterOscillate", heaterOscillate);


    Particle.variable("serverTemp", &serverTemp, DOUBLE);
}

void loop() {
    currentTime = millis();
    if(currentTime == 18446744073709551615){
        prevTime = 0;
    }
    if(currentTime - prevTime > 30000){
        int result = DHT.acquireAndWait(2000);
        serverTemp = DHT.getFahrenheit();
        if(serverTemp>0){
            accurateTemp = serverTemp;
        }    
        Particle.publish("servertemperature", String::format("%.1f", serverTemp));
        Particle.publish("accuratetemperature", String::format("%.1f", accurateTemp));
        prevTime = currentTime;
    }
    if(deskStart){
        irsend.sendRaw(oncodePower,143,38);
        deskStart = FALSE;
        Particle.publish("test","sentIR");
        delay(4000);
    }
    if(highTempOverride || (checkHIGHTemp && accurateTemp > 67.0)){
        irsend.sendRaw(oncodeSwitch,119,38);
        checkHIGHTemp = FALSE;
        highTempOverride = FALSE;
        Particle.publish("test","sentTEMPIR");
    }
}

Credits

Jack Decker
1 project • 1 follower
Contact
Thanks to Pedro Pombeiro.

Comments

Please log in or sign up to comment.