Skip to content

Instantly share code, notes, and snippets.

@tomasbasham
Created June 12, 2016 15:05
Show Gist options
  • Save tomasbasham/660c84c97bda9d8acb116062c2f56ae2 to your computer and use it in GitHub Desktop.
Save tomasbasham/660c84c97bda9d8acb116062c2f56ae2 to your computer and use it in GitHub Desktop.

Revisions

  1. tomasbasham created this gist Jun 12, 2016.
    175 changes: 175 additions & 0 deletions Netflix.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,175 @@
    /**
    * Netflix
    *
    * A program that emits an IR singal to startup
    * Netflix on a smart TV, lights up an array of
    * LED lights and signals a control unit that a
    * button press has occurred using MQTT.
    *
    * Hardware: Particle Photon
    * Concept Usage: Netflix and Chill Button
    */

    SYSTEM_THREAD(ENABLED); // Run the application loop and the system loop on separate threads.

    #include "MQTT/MQTT.h"
    #include "Remote.h"

    #define SWITCH_PIN A0
    #define IR_PIN A4
    #define LOGO_PIN A5
    #define LED_PIN D7

    #ifdef digitalPinToInterrupt
    #define SWITCH_INTERRUPT digitalPinToInterrupt(SWITCH_PIN)
    #else
    #define SWITCH_INTERRUPT 10
    #endif

    // Indicators pin numbers.
    const byte indicators[] = { D0, D1, D2, D3, D4 };

    // MQTT Broker username, password and topic.
    const char *username = "rylkuomv:rylkuomv";
    const char *password = "apZ_wxgdsJuSFKzzthG5scLfF9W3X5C6";
    const char *topic = "home/netflix";

    // MQTT RAW data.
    const char *raw = "18274ef5761c95629376"; // This is to identify this button on the meassage queue. Just a random HEX string.

    // MQTT server and port.
    char *server = "chicken.rmq.cloudamqp.com";
    short port = 1883;

    // Raw infrared data can be sniffed using an IR-receiver and e.g. https://github.com/z3t0/Arduino-IRremote/blob/master/examples/IRrecvDumpV2/IRrecvDumpV2.ino
    unsigned int data[67] = { 4650, 4600, 450, 1800, 450, 1800, 450, 1800, 450, 650, 450, 650, 450, 700, 450, 650, 450, 700,
    450, 1800, 450, 1800, 450, 1800, 450, 650, 450, 700, 450, 650, 450, 700, 400, 700, 450, 650,
    450, 1800, 450, 700, 450, 650, 450, 650, 500, 650, 450, 650, 500, 650, 450, 1800, 450, 650,
    450, 1800, 450, 1800, 450, 1800, 450, 1800, 450,1800, 450, 1800, 450 }; // UNKNOWN F4BA2988

    // Infrared transmitter.
    Remote irSend(IR_PIN, LED_PIN);

    // MQTT client.
    MQTT client(server, port, NULL);

    /**
    * Initialize all the pins and their
    * impedance. Also attach interrupts
    * to transmit power over IR.
    *
    * @method setup
    */
    void setup() {

    // Indicator pin modes.
    for (int i = 0; i < sizeof(indicators) / sizeof(byte); i++) {
    pinMode(indicators[i], OUTPUT );
    }

    // Set the internal pullup resistor on
    // the input switch pin.
    pinMode(SWITCH_PIN, INPUT_PULLUP);

    // Set the logo pin to output.
    pinMode(LOGO_PIN, OUTPUT);

    // Attach an interrupt to the switch
    // pin to trigger off the IR pulse. For
    // backwards compatibility we check the
    // digitalPinToInterrupt function is
    // present.
    attachInterrupt(SWITCH_INTERRUPT, transmitPower, FALLING);
    }

    /**
    * Main loop function for the program.
    * Watch for changes on the switch pin
    * and when the switch is pressed begin
    * the light show.
    *
    * @method loop
    */
    void loop() {
    if (digitalRead(SWITCH_PIN)) {

    setLogoOn();
    setIndicatorState(HIGH);

    // Make the connection to the
    // messaging queue if not
    // currently connected.
    if (!client.isConnected()) {
    client.connect(server, username, password);
    }

    // Push an MQTT message to the
    // appropriate topic.
    if (client.isConnected()) {
    client.publish(topic, raw);
    client.disconnect();
    }

    delay(1500);
    setIndicatorState(LOW);
    setLogoOff();
    delay(500);
    } else {
    delay(100);
    }
    }

    /**
    * Transmit the pulse width sequence
    * through the IR bulb.
    *
    * @method transmitPower
    */
    void transmitPower() {
    for (int i = 0; i < 3; i++) {
    irSend.transmit(data, sizeof(data) / sizeof(data[0]));
    }
    }

    /**
    * Fade in the logo using simple delay
    * and PWM pins.
    *
    * @method setLogoOn
    */
    void setLogoOn() {
    for (int i = 0; i <= 255; i++) {
    analogWrite(LOGO_PIN, i);
    delay(5);
    }
    }

    /**
    * Fade out the logo using simple delay
    * and PWM pins.
    *
    * @method setLogoOn
    */
    void setLogoOff() {
    for (int i = 255; i >= 0; i--) {
    analogWrite(LOGO_PIN, i);
    delay(5);
    }
    }

    /**
    * Sequentially turn on the indicator
    * lights representing the dot below
    * the netflix logo.
    *
    * @method setIndicatorState
    *
    * @param {bool} state
    * The impedance of each indicator.
    */
    void setIndicatorState(bool state) {
    for (int i = 0; i < sizeof(indicators) / sizeof(byte); i++) {
    digitalWrite(indicators[i], state);
    delay(500);
    }
    }
    70 changes: 70 additions & 0 deletions Remote.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,70 @@
    #include "application.h"
    #include "Remote.h"

    const int Remote::dutyCycle = 50;
    const int Remote::carrierFrequency = 38000;
    const int Remote::period = (1000000 + carrierFrequency / 2) / carrierFrequency;
    const int Remote::highTime = period * dutyCycle / 100;
    const int Remote::lowTime = period - highTime;

    Remote::Remote(unsigned int irOutputPin, unsigned int ledPin) {
    this->irOutputPin = irOutputPin;
    this->ledPin = ledPin;

    pinMode(this->ledPin, OUTPUT);
    pinMode(this->irOutputPin, OUTPUT);

    this->signalTime = 0;
    }

    void Remote::mark(unsigned int length) {
    this->signalTime += length; // Mark ends at new signal time.

    unsigned long now = micros();
    unsigned long dur = this->signalTime - now; // Allows for rolling time adjustment due to code execution delays.

    if (dur == 0) return;

    while ((micros() - now) < dur) { // Just wait here until time is up.
    digitalWriteFast(this->irOutputPin, HIGH);
    delayMicroseconds(this->highTime - 3);
    digitalWriteFast(this->irOutputPin, LOW);
    delayMicroseconds(this->lowTime - 4);
    }
    }

    void Remote::space(unsigned int length) {
    this->signalTime += length; // Space ends at new signal time.

    unsigned long now = micros();
    unsigned long dur = this->signalTime - now; // Allows for rolling time adjustment due to code execution delays.

    if (dur == 0) return;

    while ((micros() - now) < dur); // Just wait here until time is up.
    }

    void Remote::transmit(unsigned int *data, size_t length) {

    // Indicate we are sending the signal
    digitalWriteFast(this->ledPin, HIGH);

    this->signalTime = micros(); // Keeps rolling track of signal time to avoid impact of loop & code execution delays.
    for (int i = 0; i < length; i++) {
    this->mark(data[i++]); // Also move pointer to next position.
    if (i < length) {
    this->space(data[i]); // Pointer will be moved by for loop.
    }
    }

    // Indicate we are finished sending the signal.
    digitalWriteFast(this->ledPin, LOW);

    // Wait 40 miliseconds seconds between each signal.
    // Here was cannot simply use delay when in an ISR
    // as it relies on interrupts iteslef casuing the
    // applicatio to crash. Since delayMicroseconds
    // instead relies on ASM NOP commands this is not
    // an issue.
    delayMicroseconds(40000);
    }
    61 changes: 61 additions & 0 deletions Remote.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    #ifndef REMOTE_H
    #define REMOTE_H

    class Remote {
    private:
    int irOutputPin;
    int ledPin;
    unsigned long signalTime;

    const static int dutyCycle;
    const static int carrierFrequency;
    const static int period;
    const static int highTime;
    const static int lowTime;

    /**
    * Pule the Infrared LED for a
    * specific amount of time.
    *
    * @param {unsigned int} length
    * The number of miliseconds to pulse the Infrared LED.
    */
    void mark(unsigned int length);

    /**
    * Turn off the Infrared LED for a
    * specific amount of time
    *
    * @param {unsigned int} length
    * The number of milliseconds to turn the Infrared LED off.
    */
    void space(unsigned int length);

    public:

    /**
    * Instantiate a new remote object
    * that will be used to send a NEC
    * RAW signal.
    *
    * @param {unsigned int} irOutputPin
    * The pin the Infrered LED is on.
    *
    * @param {unsigned int} ledPin
    * The pin the indicator LED is on. Defaults to the built=in LED on pin D7.
    */
    Remote(unsigned int irOutputPin, unsigned int ledPin = D7);

    /**
    * Send the NEC RAW signal.
    *
    * @param {unsigned int*} data
    * NEC RAW signal data to send.
    *
    * @para, {size_t} length
    * Number of bytes in the NEC RAW signal data.
    */
    void transmit(unsigned int *data, size_t length);
    };

    #endif