├── .gitignore ├── requirements.txt ├── LICENSE ├── README.md ├── arduirc.py └── src └── sketch.ino /.gitignore: -------------------------------------------------------------------------------- 1 | .build/ 2 | lib/ 3 | timings.yml 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==3.10 2 | docopt==0.6.1 3 | pyserial==2.7 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Stavros Korokithakis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ArduiRC 2 | ======= 3 | 4 | Remotely control IR/RF devices from a computer with Arduino. 5 | 6 | Rationale 7 | --------- 8 | The reason for this library is explained in [a post I wrote about it](http://www.stavros.io/posts/control-rf-devices-with-arduino/). Basically, I needed my home server to be able to control RF devices, and here we are. 9 | 10 | Usage 11 | ----- 12 | To use ArduiRC, you need to perform two steps: 13 | 14 | 1. Install the Arduino sketch on the device so it can transmit the codes (after you've set up the Arduino with the proper hardware). 15 | 2. Use `arduirc.py` to transfer the code you want to transmit to the Arduino for transmission. 16 | 17 | To install the sketch to the Arduino, either use the IDE to compile and upload `sketch.ino`, or use the amazing [Ino](http://inotool.org/): 18 | 19 | ino build 20 | ino upload 21 | 22 | Afterwards, use `arduirc.py` to send the timings (which you can retrieve by reading the post above): 23 | 24 | ./arduirc.py "625 625 1292 646 1292 625 1292 646 1271 667 1271 646 1271 646" 25 | 26 | You can also define the timings in a YAML file so you don't have to enter the numbers each time: 27 | 28 | ./arduirc.py garage open 29 | 30 | The `timings.yml` file for the above command might look something like this: 31 | 32 | garage: 33 | open: 625 625 1292 646 1292 625 1292 646 1271 667 1271 646 1271 646 34 | close: 625 625 1292 625 625 1292 646 1292 625 1292 625 1292 667 1271 646 35 | ac: 36 | toggle: 625 625 1292 646 1292 625 1292 625 1292 667 1271 646 127 37 | 38 | Hopefully this will be enough to get you started. 39 | -------------------------------------------------------------------------------- /arduirc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Usage: 3 | arduirc.py [options] (
| ) 4 | 5 | Options: 6 | -h --help show this help and exit 7 | -v --version show version and exit 8 | -t --timings-file=FILE specify the timings file to use [default: timings.yml] 9 | -s --socket=SOCKET specify the socket to connect to [default: /dev/ttyACM0] 10 | -w --wait wait two seconds for the Arduino to reset. 11 | -b --baud-rate=RATE the baud rate to connect with [default: 9600] 12 | -r --repeat=REPEAT repeat the command REPEAT times [default: 3] 13 | -d --delay=DELAY delay between repeats (in usec) [default: 10000] 14 | -p --pin=PIN which Arduino pin to write to [default: 3] 15 | """ 16 | 17 | import serial 18 | import sys 19 | import time 20 | import yaml 21 | 22 | from docopt import docopt 23 | 24 | 25 | def main(arguments): 26 | if arguments.get("
") and arguments.get(""): 27 | section = arguments["
"] 28 | command = arguments[""] 29 | 30 | try: 31 | timing_dict = yaml.load(open(arguments["--timings-file"])) 32 | except IOError: 33 | sys.exit("Error opening timings file.") 34 | 35 | if section not in timing_dict: 36 | sys.exit("Unknown section.") 37 | 38 | if command not in timing_dict[section]: 39 | sys.exit("Unknown command.") 40 | 41 | raw_timings = timing_dict[section][command] 42 | elif arguments.get(""): 43 | raw_timings = arguments[""] 44 | 45 | timings = [int(timing) for timing in raw_timings.split()] 46 | output = "".join([chr(int(round(timing / 25.0))) for timing in timings]) 47 | 48 | com = serial.Serial(arguments["--socket"], int(arguments["--baud-rate"]), timeout=0.2) 49 | 50 | if arguments["--wait"]: 51 | print "Waiting for the Arduino to reset..." 52 | time.sleep(2) 53 | 54 | pin = int(arguments["--pin"]) 55 | repeat = int(arguments["--repeat"]) 56 | delay = int(round(int(arguments["--delay"]) / 100.0)) 57 | 58 | if delay > 255 or delay < 1: 59 | sys.exit("Delay must be between 100 and 25500.") 60 | 61 | if pin > 13 or pin < 0: 62 | sys.exit("Pin must be between 0 and 13.") 63 | 64 | if repeat > 255 or repeat < 1: 65 | sys.exit("Repeat must be between 1 and 255.") 66 | 67 | # chr(1) is the command (command 1, send timings). 68 | com.write(chr(1) + chr(pin) + chr(repeat) + chr(delay) + output + chr(0)) 69 | print com.read(1000) 70 | 71 | if __name__ == "__main__": 72 | arguments = docopt(__doc__, version="0.1.0") 73 | main(arguments) 74 | -------------------------------------------------------------------------------- /src/sketch.ino: -------------------------------------------------------------------------------- 1 | #define LED 13 2 | #define TIMING_MULTIPLIER 25 3 | #define DELAY_MULTIPLIER 100 4 | #define MAX_MESSAGE_LENGTH 100 5 | 6 | unsigned char gMessage[MAX_MESSAGE_LENGTH] = { 0 }; 7 | unsigned char gCounter = 0; 8 | 9 | void transmit(char pin, unsigned char message[], unsigned char repeat, unsigned int intraDelay) { 10 | int status = LOW; 11 | unsigned int out = 0; 12 | unsigned char i, j; 13 | 14 | pinMode(pin, OUTPUT); 15 | 16 | // Send the message. 17 | for (j = 0; j < repeat; j++) { 18 | for (i = 0; i < MAX_MESSAGE_LENGTH; i++) { 19 | if (message[i] == 0) break; 20 | 21 | if (status == HIGH) { 22 | status = LOW; 23 | } else { 24 | status = HIGH; 25 | } 26 | digitalWrite(pin, status); 27 | out = message[i] * TIMING_MULTIPLIER; 28 | delayMicroseconds(out); 29 | } 30 | 31 | // Reset, just in case. 32 | digitalWrite(pin, LOW); 33 | delayMicroseconds(intraDelay); 34 | } 35 | } 36 | 37 | // Create the callback function 38 | void cmdSend(char pin, unsigned char message[], unsigned char repeat, unsigned int intraDelay) { 39 | unsigned int out = 0; 40 | unsigned char i; 41 | 42 | digitalWrite(LED, HIGH); 43 | 44 | Serial.print("Sending on pin "); 45 | Serial.print(pin, DEC); 46 | Serial.print(", repeating "); 47 | Serial.print(repeat, DEC); 48 | Serial.print(" times, for "); 49 | Serial.print(intraDelay); 50 | Serial.println(" microseconds."); 51 | 52 | // Print the received timings to the console. 53 | for (i = 0; i < MAX_MESSAGE_LENGTH; i++) { 54 | if (message[i] == 0) break; 55 | out = message[i] * TIMING_MULTIPLIER; 56 | Serial.print(out); 57 | Serial.print(" "); 58 | } 59 | Serial.print("\r\n"); 60 | 61 | transmit(pin, message, repeat, intraDelay); 62 | 63 | digitalWrite(LED, LOW); 64 | Serial.println("Command sent."); 65 | } 66 | 67 | void setup() { 68 | Serial.begin(9600); 69 | 70 | pinMode(LED, OUTPUT); 71 | } 72 | 73 | void loop() { 74 | unsigned char character = 0; 75 | 76 | while (Serial.available()) { 77 | if (gCounter >= MAX_MESSAGE_LENGTH) gCounter = 0; 78 | 79 | character = Serial.read(); 80 | gMessage[gCounter++] = character; 81 | 82 | // Process the message if we receive a null. 83 | if (character != 0) continue; 84 | 85 | // Don't process early if the command is 1, because pin may also be a 0. 86 | if ((gMessage[0] == 1) && (gCounter < 3)) continue; 87 | 88 | switch (gMessage[0]) { 89 | case 1: 90 | cmdSend(gMessage[1], &gMessage[4], gMessage[2], gMessage[3] * DELAY_MULTIPLIER); 91 | break; 92 | } 93 | 94 | gCounter = 0; 95 | } 96 | } 97 | --------------------------------------------------------------------------------