summaryrefslogtreecommitdiff
path: root/trasmitter
diff options
context:
space:
mode:
Diffstat (limited to 'trasmitter')
-rw-r--r--trasmitter/.gitignore3
-rw-r--r--trasmitter/Makefile10
-rw-r--r--trasmitter/README.rst.txt42
-rw-r--r--trasmitter/arduino/Makefile19
-rw-r--r--trasmitter/arduino/sender.ino93
-rw-r--r--trasmitter/sender-chip.c146
-rw-r--r--trasmitter/sender-chip.pl91
7 files changed, 404 insertions, 0 deletions
diff --git a/trasmitter/.gitignore b/trasmitter/.gitignore
new file mode 100644
index 0000000..bd6f3a9
--- /dev/null
+++ b/trasmitter/.gitignore
@@ -0,0 +1,3 @@
+/arduino/build-*/
+/sender-chip
+
diff --git a/trasmitter/Makefile b/trasmitter/Makefile
new file mode 100644
index 0000000..b31025b
--- /dev/null
+++ b/trasmitter/Makefile
@@ -0,0 +1,10 @@
+CFLAGS = -Wall
+
+all: sender-chip setcap
+
+sender-chip: sender-chip.c
+
+setcap: sender-chip
+ sudo setcap CAP_SYS_NICE=eip $<
+
+.PHONY: setcap
diff --git a/trasmitter/README.rst.txt b/trasmitter/README.rst.txt
new file mode 100644
index 0000000..78200a4
--- /dev/null
+++ b/trasmitter/README.rst.txt
@@ -0,0 +1,42 @@
+Some links:
+
+* capturing radio signals
+ http://www.stevenhale.co.uk/main/2013/08/home-automation-reverse-engineering-a-worcester-bosch-dt10rf-wireless-thermostat/
+ https://damn.technology/controlling-british-gas-wr1-receiver-arduino
+ http://rossharper.net/2015/11/decoding-a-siemens-rcr10433-thermostat-signal-to-control-a-boiler-from-a-raspberry-pi/
+
+IMPORTANT: the transmitter prefers 3.3V, it stops working after a
+while on 5V.
+
+Reveng
+======
+
+Sampled at 192k samples/second, found that the transmissions had 3
+types of (presumably square) pulses:
+
+======== ======= ========
+name samples duration
+======== ======= ========
+narrow 200 ~ 1ms
+middle 300 ~ 1.5ms
+wide 400 ~ 2ms
+======== ======= ========
+
+The two sequences are:
+
+* ``nnnnnnWnWnnMnnMnnnnnWnnnnMnMWn``
+* ``nnnnnnWnWnnMnnMnnnnnnnnnnWMMWn``
+
+Actually no, the pulses are not ½ high and ½ low, they're shaped
+weird. The source code has the correct widths.
+
+Without Arduino
+===============
+
+The GPIO / XIO pins on NextThing's CHIP, even when driven via the `slow
+sysfs interface`_, seem to be fast enough for our purposes.
+
+See the ``sender-chip.pl`` test program.
+
+If we keep the ``*/value`` file open, we get good enough timing. The
+``sender-chip.c`` program works.
diff --git a/trasmitter/arduino/Makefile b/trasmitter/arduino/Makefile
new file mode 100644
index 0000000..9815090
--- /dev/null
+++ b/trasmitter/arduino/Makefile
@@ -0,0 +1,19 @@
+ARDMK_DIR = /home/dakkar/src/Arduino-Makefile
+ARDUINO_DIR = /usr/share/arduino
+MONITOR_PORT = /dev/ttyACM*
+CURRENT_DIR = $(basename $(CURDIR))
+AVR_TOOLS_DIR = /usr
+AVRDUDE_CONF = /etc/avrdude.conf
+
+PROJECT_DIR = $(CURDIR)
+BOARD_TAG = uno
+MONITOR_BAUDRATE = 115200
+
+CFLAGS_STD = -std=gnu11
+CXXFLAGS_STD = -std=gnu++11
+CXXFLAGS += -pedantic -Wall -Wextra
+
+include $(ARDMK_DIR)/Arduino.mk
+
+check-syntax:
+ $(CXX) -c -include Arduino.h -x c++ $(CXXFLAGS) $(CPPFLAGS) -fsyntax-only $(CHK_SOURCES)
diff --git a/trasmitter/arduino/sender.ino b/trasmitter/arduino/sender.ino
new file mode 100644
index 0000000..9373875
--- /dev/null
+++ b/trasmitter/arduino/sender.ino
@@ -0,0 +1,93 @@
+const uint16_t pulseScale = 520; // usec per sample unit
+
+// alternate HIGH and LOW, first one of each array is HIGH
+const uint8_t prologue[] = {
+ 1,1, 1,1, 1,1, 1,1, 1,1, 1,1,
+ 2,3, 1,1, 2,2, 1,1, 1,1, 2,1,
+ 1,1, 1,1, 1,2, 1,1, 1,1, 1,1,
+ 1,1, 1,1,
+};
+const size_t prologueSize = sizeof(prologue) / sizeof(uint8_t);
+
+const uint8_t epilogue[] = {
+ 1,2, 2,2, 1,3, // the last value doesn't much matter
+};
+const size_t epilogueSize = sizeof(epilogue) / sizeof(uint8_t);
+
+const uint8_t onSignal[] = {
+ 2,2, 1,1, 1,1, 1,1, 1,1, 2,1, 1,1,
+};
+const size_t onSize = sizeof(onSignal) / sizeof(uint8_t);
+
+const uint8_t offSignal[] = {
+ 1,1, 1,1, 1,1, 1,1, 1,1, 2,2, 2,1,
+};
+const size_t offSize = sizeof(offSignal) / sizeof(uint8_t);
+
+const int nTxPin = 7; // Arduino digital pin you're using for radio data.
+
+void transmitArray(const uint8_t pulses[], size_t pulseCount) {
+ for(size_t idx = 0; idx < pulseCount; idx+=2) {
+ digitalWrite(nTxPin, HIGH);
+ delayMicroseconds(pulseScale*pulses[idx]);
+
+ digitalWrite(nTxPin, LOW);
+ delayMicroseconds(pulseScale*pulses[idx+1]);
+ }
+}
+
+void transmitSignal(const uint8_t signal[], size_t signalSize) {
+ transmitArray(prologue,prologueSize);
+ transmitArray(signal,signalSize);
+ transmitArray(epilogue,epilogueSize);
+}
+
+void sendTrain(const uint8_t signal[], size_t signalSize) {
+ transmitSignal(signal,signalSize);
+ delay(1000);
+ transmitSignal(signal,signalSize);
+ delay(2000);
+ transmitSignal(signal,signalSize);
+}
+
+
+/**
+ * The setup() function is called when a sketch starts. Used to initialize
+ * variables, pin modes, start using libraries, etc. The setup function will
+ * only run once, after each powerup or reset of the Arduino board.
+ */
+void setup()
+{
+ pinMode(nTxPin, OUTPUT);
+ digitalWrite(nTxPin, LOW);
+
+ Serial.begin(9600);
+ Serial.println("Press 0 to turn off heating");
+ Serial.println("Press 1 to turn on heating");
+
+}
+
+
+/**
+ * The loop() function loops consecutively, allowing the program to change and
+ * respond. Used to actively control the Arduino board.
+ */
+void loop()
+{
+ if (Serial.available() > 0)
+ {
+ int nIncomming = Serial.read();
+ if (nIncomming == 49) { // char code for 1
+ Serial.println("ON");
+ sendTrain(onSignal,onSize);
+ }
+
+ if (nIncomming == 48) { // char code for 0
+ Serial.println("OFF");
+ sendTrain(offSignal,offSize);
+ }
+
+ Serial.println("Press 0 to turn off heating");
+ Serial.println("Press 1 to turn on heating");
+ }
+} \ No newline at end of file
diff --git a/trasmitter/sender-chip.c b/trasmitter/sender-chip.c
new file mode 100644
index 0000000..7ada0cf
--- /dev/null
+++ b/trasmitter/sender-chip.c
@@ -0,0 +1,146 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sched.h>
+#include <inttypes.h>
+
+#define PIN "132"
+
+void init_pin() {
+ int fd = open("/sys/class/gpio/export",O_WRONLY);
+ char buf[16];
+ int len = sprintf(buf,"%s\n",PIN);
+ write(fd,buf,len);
+ close(fd);
+ fd = open("/sys/class/gpio/gpio" PIN "/direction",O_WRONLY);
+ write(fd,"out\n",4);
+ close(fd);
+}
+
+void deinit_pin() {
+ int fd = open("/sys/class/gpio/unexport",O_WRONLY);
+ char buf[16];
+ int len = sprintf(buf,"%s\n",PIN);
+ write(fd,buf,len);
+ close(fd);
+}
+
+int pin_fd;
+void write_pin(uint8_t value) {
+ if (!pin_fd) {
+ pin_fd = open("/sys/class/gpio/gpio" PIN "/value",O_WRONLY);
+ }
+ char buf[16];
+ int len = sprintf(buf,"%d\n",value);
+ write(pin_fd,buf,len);
+}
+
+void init_sched() {
+ struct sched_param sp;
+ sp.sched_priority=99;
+
+ // to make this work, sudo setcap CAP_SYS_NICE=eip clock
+ sched_setscheduler(0,SCHED_RR,&sp);
+}
+
+const int width=520000;
+struct timespec next_step;
+void send_array(const uint8_t values[],size_t len) {
+ if (next_step.tv_sec == 0) {
+ clock_gettime(CLOCK_REALTIME,&next_step);
+ }
+ uint8_t pin_value = 1;
+ for (int i=0; i<len; ++i) {
+ write_pin(pin_value);
+ pin_value = 1 - pin_value;
+
+ next_step.tv_nsec += width * values[i];
+
+ if (next_step.tv_nsec > 1000000000UL) {
+ next_step.tv_nsec -= 1000000000UL;
+ ++next_step.tv_sec;
+ }
+
+ int rc = clock_nanosleep(CLOCK_REALTIME,
+ TIMER_ABSTIME,
+ &next_step,
+ NULL);
+ if (rc) {
+ printf("problems: %d\n",rc);
+ }
+ }
+}
+void my_sleep(uint8_t seconds) {
+ next_step.tv_sec += seconds;
+
+ int rc = clock_nanosleep(CLOCK_REALTIME,
+ TIMER_ABSTIME,
+ &next_step,
+ NULL);
+ if (rc) {
+ printf("problems: %d\n",rc);
+ }
+}
+
+// alternate HIGH and LOW, first one of each array is HIGH
+const uint8_t prologue[] = {
+ 1,1, 1,1, 1,1, 1,1, 1,1, 1,1,
+ 2,3, 1,1, 2,2, 1,1, 1,1, 2,1,
+ 1,1, 1,1, 1,2, 1,1, 1,1, 1,1,
+ 1,1, 1,1,
+};
+const size_t prologueSize = sizeof(prologue) / sizeof(uint8_t);
+
+const uint8_t epilogue[] = {
+ 1,2, 2,2, 1,3, // the last value doesn't much matter
+};
+const size_t epilogueSize = sizeof(epilogue) / sizeof(uint8_t);
+
+const uint8_t onSignal[] = {
+ 2,2, 1,1, 1,1, 1,1, 1,1, 2,1, 1,1,
+};
+const size_t onSize = sizeof(onSignal) / sizeof(uint8_t);
+
+const uint8_t offSignal[] = {
+ 1,1, 1,1, 1,1, 1,1, 1,1, 2,2, 2,1,
+};
+const size_t offSize = sizeof(offSignal) / sizeof(uint8_t);
+
+void transmitSignal(const uint8_t signal[], size_t signalSize) {
+ send_array(prologue,prologueSize);
+ send_array(signal,signalSize);
+ send_array(epilogue,epilogueSize);
+}
+
+void sendTrain(const uint8_t signal[], size_t signalSize) {
+ transmitSignal(offSignal,offSize);
+ my_sleep(1);
+ transmitSignal(signal,signalSize);
+ my_sleep(2);
+ transmitSignal(offSignal,offSize);
+}
+
+int main(int argc,char **argv) {
+ if (argc != 2) {
+ fprintf(stderr,"thing 0/1\n");
+ return 1;
+ }
+
+ init_sched();
+ init_pin();
+ write_pin(0);
+
+ if (argv[1][0] == '1') {
+ sendTrain(onSignal,onSize);
+ }
+ else {
+ sendTrain(offSignal,offSize);
+ }
+
+ deinit_pin();
+
+ return 0;
+}
diff --git a/trasmitter/sender-chip.pl b/trasmitter/sender-chip.pl
new file mode 100644
index 0000000..f6d3dd3
--- /dev/null
+++ b/trasmitter/sender-chip.pl
@@ -0,0 +1,91 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Time::HiRes qw(usleep sleep gettimeofday tv_interval);
+
+# my $base = do {
+# open my $fh,'<','/sys/class/gpio/gpiochip405/base' or die "can't get base: $!";
+# my $line = <$fh>;
+# chomp($line);
+# $line;
+# };
+
+sub gpio_enable {
+ my ($pin,$state) = @_;
+
+ if ($state) {
+ open my $fh,'>','/sys/class/gpio/export' or die "Can't export: $!";
+ print $fh $pin,"\n";
+ }
+ else {
+ open my $fh,'>','/sys/class/gpio/unexport' or die "Can't unexport: $!";
+ print $fh $pin,"\n";
+ }
+}
+
+sub gpio_direction {
+ my ($pin,$direction) = @_;
+
+ my $file = "/sys/class/gpio/gpio$pin/direction";
+ open my $fh,'>',$file
+ or die "Can't direction $file: $!";
+ print $fh ($direction ? 'out' : 'in' ),"\n";
+}
+
+sub gpio_write {
+ my ($pin,$value) = @_;
+
+ open my $fh,'>',"/sys/class/gpio/gpio$pin/value"
+ or die "Can't write: $!";
+ print $fh $value,"\n";
+}
+
+my $pin = 132; # CSID0
+#my $pin = $base+0; # XIO0
+
+gpio_enable($pin,1);
+gpio_direction($pin,1);
+gpio_write($pin,0);
+$SIG{INT} = sub {warn"disabling";gpio_enable($pin,0) };
+END { warn"disabling";gpio_enable($pin,0) }
+
+my $width = 370; # this seems to provide pulse widths close enough to
+ # what we need, it's 106 samples at 192kHz
+
+sub send_array {
+ my $state = 1;
+ for (@_) {
+ gpio_write($pin,$state);
+ usleep($width*$_);
+ $state = $state ? 0 : 1;
+ }
+}
+
+my @prologue = (
+ 1,1, 1,1, 1,1, 1,1, 1,1, 1,1,
+ 2,3, 1,1, 2,2, 1,1, 1,1, 2,1,
+ 1,1, 1,1, 1,2, 1,1, 1,1, 1,1,
+ 1,1, 1,1,
+);
+my @epilogue = (
+ 1,2, 2,2, 1,3, # the last value doesn't much matter
+);
+
+my @on_signal = (
+ 2,2, 1,1, 1,1, 1,1, 1,1, 2,1, 1,1,
+);
+my @off_signal = (
+ 1,1, 1,1, 1,1, 1,1, 1,1, 2,2, 2,1,
+);
+
+my @train = (
+ @prologue,
+ ( $ARGV[0] ? @on_signal : @off_signal ),
+ @epilogue,
+);
+
+send_array(@train);
+sleep(1);
+send_array(@train);
+sleep(2);
+send_array(@train);