viernes, 23 de septiembre de 2016

Raspberry Pi Universal Controller - Node API (Part 4)

Intro

This is a series of posts on how to make an universal remote controller out of a Raspberry Pi and use it with a node API.
  1. Setting up LIRC
  2. Setting up IR Receiver and storing the codes
  3. Setting up IR Leds and using LIRC
  4. Create the Node API

Creating the Node API

So, I have the Raspberry Pi configured with LIRC and it's able to send the right signals to all of our devices using the IR leds attached. Now, I need a way to communicate the Pi which command I want to perform. For this, I'll create a simple Node API using express.

The code

You can just avoid all the explanation and get the code here: https://github.com/ovsleep/remote or you can stay for a (kinda) short explanation of it.

One-to-One Key Mapping Approach

At first, I though about a one-to-one mapping from the API to the remotes keys. So the urls of the API would be something like this:

http://mylocalapi:9090/{device}/{key}

So press the power on button on the TV would look like this:
http://mylocalapi:9090/tv/power_on

This have some disadvantages: 
  1. Whoever is calling this API has to know exactly the devices names and the key names
  2. For more complex actions where I require more than a key press, like watch cable (need to turn on tv, turn on receiver, turn on cable box) I would need multiple calls to the API.

Command Approach

So I came up with the command approach. Basically the API would have a single URL and it will receive a JSON with the command to execute and the data needed. So the JSON would have this structure
{
  command: string
  data: object
}

For example, if I want to watch the cable box, the JSON would be:
{
 command: 'watch'
 data: { device: 'cable' }
}

Also, I want to make this API very extensible, so I want to make it very easy to add new commands.

Having this in mind I create a command as a Node module, this is an extract of the Off command:

exports.execute = function (data) {
    //This function will be executed when the command is called
    var cmdExec = new CommandExecutor();
    
    //based on the device we want to turn off, we push device function to the commandExecutor
    switch (data.device) {
        case 'tv':
            cmdExec.addCommand(denon.off);
            cmdExec.addCommand(tv.off);
            cmdExec.addCommand(cable.off);
            break;
        case 'ac':
            cmdExec.addCommand(ac.off);
            break;
        case 'all':
            cmdExec.addCommand(denon.off);
            cmdExec.addCommand(tv.off);
            cmdExec.addCommand(cable.off);
            cmdExec.addCommand(ac.off);
            break;
    }

    //run the chained commands
    return cmdExec.execute();
}

//this will be the name of the command
exports.cmdName = 'off';

Loading the commands

Adding a command should be simple: just make a module like the one before, throw it into the Command directory and that's it. 
To do this I used require-dir module, this allow to load all the modules in a directory without specifying the name of each module
var requireDir = require('require-dir');
var commandsControllers = requireDir('./commands');
//register commands
var commands = {};
for (commandCtrl in commandsControllers) {
    cmd = commandsControllers[commandCtrl];
    if (commands[cmd.cmdName]) throw 'duplicated command: ' + cmd.cmdName;
    commands[cmd.cmdName] = cmd.execute;
}

This will go through the commands directory and register the cmdName of each module in the commands object.

So when a new request come, all I need to do is to find the command and execute it with the data provided like this:

var router = express.Router();
router.route('/remote')
    .post(function (req, res) {
        var action = req.body;
        if (commands[action.command]) {
            commands[action.command](action.data)
                .then((result) => { res.json({ message: 'OK!' }); })
                .catch((err) => { console.log(err); res.json({ message: 'FAIL!' }); });
        }
        else {
            res.json({ message: 'No Command' });
        }
    });

That's everything that the server need to do to process a command.

The Devices

These are modules representing every device that I want to control (AC/Cable Box/Receiver/TV). They are just a set of functions to execute simple operations over the device, like: turn on/off, change channel, set temperature.

Every device function returns a Promise and it uses the irsend module (an adaptation of this) or the commandExecutor module. 

The irsend module just execute the irsend command using child_process module.

The commandExecutor is just a module to easily chain the promises execute. 

So, for example, the cable device looks like this:

var irsend = require('./irsend')
const CommandExecutor = require('../commandExecutor');
const channels = {
    DISCOVERY: '1732',
    HISTORY: '1742',
    DEPORTES: '1621',
    FX: '1217',
    FOX: '1204',
    FUTBOL: '1184'
}
exports.on = function () {
    return irsend.send_once('directv', 'key_on');
};
exports.off = function () {
    return irsend.send_once('directv', 'key_off');
};
exports.ok = function () {
    return irsend.send_once('directv', 'key_ok');
};
exports.channel = function (channel) {
    var cmdExec = new CommandExecutor();
    if (channels[channel]) {
        var numberStr = channels[channel];
        for (i = 0; i < numberStr.length; i++) {
            var number = numberStr[i];
            var btn = 'KEY_' + number;
            cmdExec.addCommand(irsend.send_once_data, {remote: 'directv', key: btn}, 200);
        }
    }
    return cmdExec.execute();
};

To Summarize

The server will get a JSON with the command to execute and the data that needs to execute the command. The commands are 'complex' actions that require one or more actions over the devices. The devices uses irsend directly to resolve the actions or the commandExecutor if multiple key presses are required (like changing the channels).

You can get the code here. Please comment if you have any issues/questions/whatever using this. I'll try to respond them asap.

Raspberry PI Universal Controller - Setting up IR Leds and using LIRC (Part 3)

Intro

This is a series of posts on how to make an universal remote controller out of a Raspberry Pi and use it with a node API.
  1. Setting up LIRC
  2. Setting up IR Receiver and storing the codes
  3. Setting up IR Leds and using LIRC
  4. Create the Node API

The schematics

Now that I have the codes to emit, I need something to send them to the TV, Cable Box, whatever... Since I need to control devices that are apart from each other, I'll set up this with 3 IR leds to have a wide angle of action. 
I don't have the exact model of the IR leds (I dissemble a few remotes I had) but any cheap IR Led should do the work.
And I'll use a P2N2222AG NPN Amplifier Transistor to control the IR led from the GPIO pin.

The schematics looks like this:




In case you need a different amount of leds, you should calculate the R1. You can use this calculator: link. Using this values (not sure about this, this is my weakest point, please comment if you disagree)

Volts: 5 (output from the Pi)
Voltage Drop Across LED: 1.5 (avg. for most of the IR leds)
Desired LED Current: 20 (avg. for most of the IR leds)
How many leds connected: Set the number here


Using LIRC to send commands

Ok, we have the remote's codes in our LIRC config and we have the IR leds ready, let's test them!
Let's assume you set up a remote with the name: TV and you have a KEY_POWER in your config. Then you should be able to execute this:
>irsend send_once tv key_power
If you get this error:
irsend: could not connect to socket
irsend: No such file or directory
You'll need this first (got this from here):
>sudo lircd -d /dev/lirc0
then try again:
>irsend send_once tv key_power

If you don't see smoke coming out the Pi, we're good :). You can check if the led is working with your cellphone camera, the IR light is invisible for the naked eye, but you can see it through a cellphone camera. Also you can compare the strength of your leds light against the strength of any other remote. At first I was using a 220 resistor as R1 and 10K as R2. This was limiting the strength of the leds. 

Using 220 and 10K


Using 22 and 3.3K


At this point, you should be able to point the leds to your TV and send:
>irsend send_once tv key_power
To power it on/off ... yay!!!

Next, we'll set up a node API to get the orders and execute the commands we need.

jueves, 22 de septiembre de 2016

Raspberry PI Universal Controller - Setting up IR Receiver and Storing the Codes (Part 2)

Intro

This is a series of posts on how to make an universal remote controller out of a Raspberry Pi and use it with a node API.
  1. Setting up LIRC
  2. Setting up IR Receiver and storing the codes
  3. Setting up IR Leds and using LIRC
  4. Create the Node API

Setting up the IR Receiver 

After setting up LIRC, I need to get the codes of the remote controllers I want to use. LIRC will help a lot with this, but first I need to attach an IR sensor to the Pi for that.
I'm using a sensor I get from this pack: https://www.amazon.com/gp/product/B017YKHSAI/ref=oh_aui_detailpage_o01_s01?ie=UTF8&psc=1 but pretty much any IR sensor would do the job.



The sensor has 3 legs (S, GND, VCC), the wiring is like this:



If you're using the same sensor as I am, you should be able to just point any remote control to it, push any button and a little led in the sensor board will blink.



Back to the Pi, I need to check if LIRC is getting the codes, so I run these commands:

> sudo /etc/init.d/lirc stop
> mode2 -d /dev/lirc0

Now, I can point my remote to the sensor and press any key, and it shows something like this:

space 23224
pulse 85
space 44506
pulse 43
space 55634
pulse 55

If you see something like that, it's working!

Getting the Codes

LIRC uses a config file to know the codes it need to send for each remote. If you're lucky enough, you'll find a config file for your remote here: link 
If not, you'll have to create one. But fear not! LIRC has a pretty straight forward method to do it:

#Stop lirc service
> sudo /etc/init.d/lirc stop
#Create a new config file named remote1.conf
> irrecord -d /dev/lirc0 ~/remote1.conf

You'll be asked to press several buttons, and a few more steps to detect your remote. Then you'll be asked to record every independent key you want. You'll need this list for naming the keys. Or you can run irrecord with the -n option to name the keys whatever you want.

Side note: If you want to record an AC remote controller, have in mind that those remotes send the full status, so you can't save the independent keys. The path I took for recording the AC remote was to record every state I wanted as an independent key. For example, to set the AC for HOT 24°, I set that in the AC remote in off mode, then I recorded the key when pressing the Power On button. That would be recording the status: ON with HOT air at  24°. So I saved that as a key with name: HOT_24. And repeated that step for every status I wanted to set.

After finishing the recording, open the newly created file:

> nano ~/remote1.conf 

And change the name line to whatever the name of the remote you want, like this:

begin remote

  name  /home/pi/lircdCable.conf
  flags RAW_CODES
  eps            30
  aeps          100
...

To:

begin remote

  name  cable
  flags RAW_CODES
  eps            30
  aeps          100

Then, you'll have to copy the content of that file to /etc/lirc/lircd.conf

You'll have to repeat this steps for every remote you need. 
Here's an example of my lircd.conf:

#UNCONFIGURED
#
# To find out how to get a proper configuration file please read:
# 
# /usr/share/doc/lirc/README.Debian

begin remote

  name  tv
  bits           16
  flags SPACE_ENC|CONST_LENGTH
  eps            30
  aeps          100

  header       4563  4453
  one           605  1637
  zero          605   516
  ptrail        607
  pre_data_bits   16
  pre_data       0xE0E0
  gap          108077
  toggle_bit_mask 0x0

      begin codes
          BTN_BACK                 0xA659
          KEY_POWER                0x40BF
          KEY_MENU                 0x58A7
      end codes

end remote

begin remote
  name denon
  bits 24
  flags SPACE_ENC
  eps 30
  aeps 100
  header 3378 1673
  one 466 1215
  zero 466 374
  ptrail 470
  pre_data_bits 24
  pre_data 0x2A4C02
  gap 74648
  min_repeat 1
#  suppress_repeat 1 uncomment to suppress unwanted repeats
  toggle_bit_mask 0x0
      begin codes
          KEY_POWER 0x8A0088
          KEY_VOLUMEUP 0x80E86A
          KEY_VOLUMEDOWN 0x88E862
      end codes
end remote


Now that I have saved the remote's codes I need to build the circuit for the IR leds in the next post.

Raspberry PI Universal Controller - Setting up LIRC (Part 1)

Intro

This will be a series of posts on how to make an universal remote controller with a Raspberry Pi.
The main motivation of doing this is to be able to control every (?) device on my home from a web API. This way I'll be able to control this API from my phone, web or even using Jasper.

The hardware I'll use for this is a Raspberry Pi, a few IR leds and an IR sensor.

Disclaimer: My knowledge about electronics it's practically null, so apply this at your own risk, ha... 

For the software, I'll be using:
- Raspbian for the Pi OS (you can find hundreds of tutorials for setting that up)
- LIRC for handling the IR in/out
- Node, to expose an API for using the Pi as a remote controller.

Index

  1. Setting up LIRC
  2. Setting up IR Receiver and storing the codes
  3. Setting up IR Leds and using LIRC
  4. Create the Node API

Setting up LIRC

Installing LIRC on raspbian is pretty straight forward, I used most of the steps from here 
> sudo apt-get install lirc

Now we need to configure a few files.
Open /etc/modules with nano:
> sudo nano /etc/modules

and add these lines:
lirc_dev
lirc_rpi gpio_in_pin=23 gpio_out_pin=22

*Notice we're using pin 23 as IN and 22 for OUT, we'll have that in mind when wiring everything up.

Open /etc/lirc/hardware.conf
> sudo nano /etc/lirc/hardware.conf

And copy this:
########################################################
# /etc/lirc/hardware.conf
#
# Arguments which will be used when launching lircd

LIRCD_ARGS="--uinput"

# Don't start lircmd even if there seems to be a good config file
# START_LIRCMD=false
# Don't start irexec, even if a good config file seems to exist.
# START_IREXEC=false

# Try to load appropriate kernel modules
LOAD_MODULES=true

# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="default"

# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"

# Default configuration files for your hardware if any
LIRCD_CONF=""
LIRCMD_CONF=""

########################################################

Case you're using 3.18.x or up Raspberry Pi firmware, you'll need to modify /boot/config.txt:

>sudo nano /boot/config.txt

Add:
dtoverlay=lirc-rpi,gpio_in_pin=23,gpio_out_pin=22

After that, you'll need to reboot your Pi.

On the next post, we'll attach a IR receiver to the Pi and get the remote controls.