Differenze tra le versioni di "Some experiments on Radio Remote Controls"
m |
|||
Riga 168: | Riga 168: | ||
== Arduino and mm53200 == | == Arduino and mm53200 == | ||
+ | |||
+ | Using the same modules above and the same wiring I wrote a sketch to open the gate of my garden and to receive the input from a remote control designed for garages and gates. | ||
+ | |||
+ | There is a quite common encoding (at least in Italy): MM53200. It gets its name from the integrated encoder/decoder by National Semiconductor. | ||
+ | There are many clones like UM3750, UM86409. | ||
+ | |||
+ | The following sketch can send and receive MM53200 codes (and much more)... | ||
+ | |||
+ | <source lang=C> | ||
+ | /* (c) 2016 Renzo Davoli */ | ||
+ | /* Licensed under the LGPLv2.1+ (inspired by rc-switch-2.52) */ | ||
+ | #define MAX_CHANGES 67 | ||
+ | #define INPUT_INTERRUPT 0 | ||
+ | #define OUTPUT_PIN 10 | ||
+ | #define PREAMBLE 10700 | ||
+ | #define SHORTDELAY 300 | ||
+ | #define LONGDELAY 590 | ||
+ | #define NTIMES 12 | ||
+ | |||
+ | unsigned int timings[MAX_CHANGES]; | ||
+ | unsigned int changeCount; | ||
+ | |||
+ | unsigned char val(unsigned int timing) { | ||
+ | if (timing > 250 && timing < 350) | ||
+ | return 1; | ||
+ | else if (timing > 550 && timing < 650) | ||
+ | return 0; | ||
+ | else | ||
+ | return 255; | ||
+ | } | ||
+ | |||
+ | int decodeMM53200() { | ||
+ | unsigned char code[12]; | ||
+ | int i; | ||
+ | if (changeCount != 25) | ||
+ | return 0; | ||
+ | for (i=0; i<12; i++) { | ||
+ | if (val(timings[2*i+1]) > 1 || (code[i]=val(timings[2*i+2])) > 1) | ||
+ | return 0; | ||
+ | } | ||
+ | Serial.print("MM53200 code: "); | ||
+ | for (i=0; i<12; i++) | ||
+ | Serial.print(code[i]); | ||
+ | Serial.println(); | ||
+ | } | ||
+ | |||
+ | void handleInterrupt() { | ||
+ | static long lastTime; | ||
+ | static long duration; | ||
+ | static unsigned int repeatCount; | ||
+ | |||
+ | long time = micros(); | ||
+ | duration=time-lastTime; | ||
+ | if (duration > 5000 && duration > timings[0] - 200 && duration < timings[0] + 200) { | ||
+ | int i; | ||
+ | repeatCount++; | ||
+ | changeCount--; | ||
+ | if (repeatCount == 2) { | ||
+ | decodeMM53200(); | ||
+ | repeatCount = 0; | ||
+ | } | ||
+ | changeCount = 0; | ||
+ | } else if (duration > 5000) { | ||
+ | changeCount = 0; | ||
+ | } | ||
+ | if (changeCount >= MAX_CHANGES) { | ||
+ | changeCount = 0; | ||
+ | repeatCount = 0; | ||
+ | } | ||
+ | timings[changeCount++] = duration; | ||
+ | lastTime=time; | ||
+ | } | ||
+ | |||
+ | void sendMM53200(int value) { | ||
+ | int mask; | ||
+ | digitalWrite(OUTPUT_PIN,LOW); | ||
+ | delayMicroseconds(PREAMBLE); | ||
+ | digitalWrite(OUTPUT_PIN,HIGH); | ||
+ | delayMicroseconds(SHORTDELAY); | ||
+ | for (mask=1<<11; mask; mask>>=1) { | ||
+ | if (value & mask) { | ||
+ | digitalWrite(OUTPUT_PIN,LOW); | ||
+ | delayMicroseconds(SHORTDELAY); | ||
+ | digitalWrite(OUTPUT_PIN,HIGH); | ||
+ | delayMicroseconds(LONGDELAY); | ||
+ | } else { | ||
+ | digitalWrite(OUTPUT_PIN,LOW); | ||
+ | delayMicroseconds(LONGDELAY); | ||
+ | digitalWrite(OUTPUT_PIN,HIGH); | ||
+ | delayMicroseconds(SHORTDELAY); | ||
+ | } | ||
+ | } | ||
+ | digitalWrite(OUTPUT_PIN,LOW); | ||
+ | } | ||
+ | |||
+ | void sendcode(int value) { | ||
+ | int i=NTIMES; | ||
+ | Serial.println(value, BIN); | ||
+ | while (i--) | ||
+ | sendMM53200(value); | ||
+ | } | ||
+ | |||
+ | void bruteforce(void) { | ||
+ | int value; | ||
+ | for (value=0; value<4096; value++) | ||
+ | sendcode(value); | ||
+ | } | ||
+ | |||
+ | void inbyte(int inchar) { | ||
+ | static value = 0; | ||
+ | switch (inchar) { | ||
+ | case '\r': | ||
+ | sendcode(value); | ||
+ | value=0; | ||
+ | break; | ||
+ | case 0x02: | ||
+ | bruteforce(); | ||
+ | break; | ||
+ | case '1': | ||
+ | value++; | ||
+ | case '0': | ||
+ | value <<= 1; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void setup() { | ||
+ | Serial.begin(115200); | ||
+ | attachInterrupt(INPUT_INTERRUPT, handleInterrupt, CHANGE); | ||
+ | pinMode(OUTPUT_PIN, OUTPUT); | ||
+ | digitalWrite(OUTPUT_PIN,LOW); | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | while (Serial.available()) { | ||
+ | inbyte(Serial.read()); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
== Raspberry PI TX for mm53200 == | == Raspberry PI TX for mm53200 == | ||
== Raspberry PI TX for avidsen sockets == | == Raspberry PI TX for avidsen sockets == |
Versione delle 17:08, 4 giu 2016
Arduino and Radio Controller Sockets
I have bought a kit by avidsen including a socket and a remote.
My goal was twice. I wanted to control avidsen sockets using an arduino and to get commands from a avidsen remote.
For that I have taken a set of RX and TX 433.92Mhz modules. This kind of modules are very common on e-bay.
They are usually provided without an antenna: they need a 17.3 cm wire soldered to the ANT labelled terminal.
There is already a library named RC-Switch. It can be downloaded from github. This is its latest release: https://github.com/sui77/rc-switch/releases/tag/v2.52.
The library includes a set a of examples. I have created a specific sketch for my avidsen set.
The wiring between the Arduino and TX/RX modules is:
TX has three pins (left to right watching the component side of the PCB, there are clear labels): DATA <-> Arduino GPIO 2 Vcc <-> Arduino 5v GND <-> Arduino GND RX has four pins (left to right watching the component side of the PCB, the labels are printed on the reverse side of the PCB) Vcc <-> Arduino 5v DATA <-> Arduino GPIO 10 DATA <-> (the two DATA pins are connected together, so either can be used). GND <-> Arduino GND
#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();
#define BINARY 0
#define SWITCH 1
int mode=SWITCH;
void setup() {
Serial.begin(115200);
mySwitch.enableReceive(0); // Receiver on inerrupt 0 => that is pin #2
mySwitch.enableTransmit(10);
}
void incodeProcess(unsigned long code) {
static char outbuf[25];
int i;
switch (mode) {
case BINARY:
for (i=0; i<24; i++, code>>=1)
outbuf[23-i] = (code & 1) ? '1' : '0';
break;
case SWITCH:
outbuf[5]=outbuf[11]=' ';
outbuf[13]=0;
switch (code & 0xF) {
case 0x1: outbuf[12]='1';break;
case 0x4: outbuf[12]='0';break;
default: return;
}
code>>=4;
for (i=0; i<5; i++, code>>=2)
outbuf[10-i] = (code & 1) ? '0' : '1';
for (i=0; i<5; i++, code>>=2)
outbuf[4-i] = (code & 1) ? '0' : '1';
break;
}
Serial.print(outbuf);
Serial.println();
}
void inlineProcess(char *line) {
if (line[5] == ' ' && line[11] == ' ' && line[13] == 0) {
line[5] = line[11] = 0;
switch (line[12]) {
case '1':
mySwitch.switchOn(line,line+6);
break;
case '0':
mySwitch.switchOff(line,line+6);
break;
default:
return;
}
} else
mySwitch.send(line);
}
void inbyte(int inchar) {
static char inbuf[64];
static int pos=0;
if (inchar == '\r' || pos == 64) {
inbuf[pos]=0;
inlineProcess(inbuf);
pos=0;
} else if (inchar < ' ') {
switch (inchar) {
case 0x2: //ctrl-b
mode=BINARY;
break;
case 0x17: //ctrl-W
mode=SWITCH;
break;
}
} else
inbuf[pos++]=inchar;
}
void loop() {
if (mySwitch.available()) {
incodeProcess(mySwitch.getReceivedValue());
mySwitch.resetAvailable();
}
while (Serial.available()) {
inbyte(Serial.read());
}
}
When the sketch has been compiled and loaded it is possible to interact with it using the "Serial Monitor" of the Arduino IDE or a terminal emulator. (my favuorite is screen). Set it to 115200 baud, end of line=CR.
The command for screen is simply
screen /dev/ttyUSB0 115200
I have set up the dip switches of the remote to the system code '11111' and the socket to system code '11111', unit code '00010' (it is operated by the D buttons of the remote)
Now when I push a button of the remote I get a line like the following:
11111 10000 1 11111 10000 0 11111 01000 1 11111 01000 0 11111 00100 1 11111 00100 0 11111 00010 1 11111 00010 0
The first group of 5 bits is the code of the remote (it is the configuration of the 5 switches hidden in the battery compartment of the remote. The second group is the letter A=10000, B=01000, C=00100, D=00010 (maybe other remotes have a fifth button). The final bit is ON (1) or OFF (1).
So I can use the remote to activate any process, just by checking the pattern received.
viceversa if I type in the code of my socket in the terminal emulator followed by 1 or 0 return (CR) the trasmitter sends the code. Please note that the sketch does not echo what is typed in. When I type:
11111 00010 1
nothing appears on the terminal but the socket gets switched on, and
11111 00010 0
switches it off.
The sketch has been designed to use arduino as a device of a host computer. A daemon could parse the input lines (commands from remote controls) and trigger actions when required by the configuration. The same demon can also write the code of a socket to turn on of off as in the example above. Given this idea of usage, echoing the input would be inconsistent: the demon would receive its own commands, too.
Arduino and mm53200
Using the same modules above and the same wiring I wrote a sketch to open the gate of my garden and to receive the input from a remote control designed for garages and gates.
There is a quite common encoding (at least in Italy): MM53200. It gets its name from the integrated encoder/decoder by National Semiconductor. There are many clones like UM3750, UM86409.
The following sketch can send and receive MM53200 codes (and much more)...
/* (c) 2016 Renzo Davoli */
/* Licensed under the LGPLv2.1+ (inspired by rc-switch-2.52) */
#define MAX_CHANGES 67
#define INPUT_INTERRUPT 0
#define OUTPUT_PIN 10
#define PREAMBLE 10700
#define SHORTDELAY 300
#define LONGDELAY 590
#define NTIMES 12
unsigned int timings[MAX_CHANGES];
unsigned int changeCount;
unsigned char val(unsigned int timing) {
if (timing > 250 && timing < 350)
return 1;
else if (timing > 550 && timing < 650)
return 0;
else
return 255;
}
int decodeMM53200() {
unsigned char code[12];
int i;
if (changeCount != 25)
return 0;
for (i=0; i<12; i++) {
if (val(timings[2*i+1]) > 1 || (code[i]=val(timings[2*i+2])) > 1)
return 0;
}
Serial.print("MM53200 code: ");
for (i=0; i<12; i++)
Serial.print(code[i]);
Serial.println();
}
void handleInterrupt() {
static long lastTime;
static long duration;
static unsigned int repeatCount;
long time = micros();
duration=time-lastTime;
if (duration > 5000 && duration > timings[0] - 200 && duration < timings[0] + 200) {
int i;
repeatCount++;
changeCount--;
if (repeatCount == 2) {
decodeMM53200();
repeatCount = 0;
}
changeCount = 0;
} else if (duration > 5000) {
changeCount = 0;
}
if (changeCount >= MAX_CHANGES) {
changeCount = 0;
repeatCount = 0;
}
timings[changeCount++] = duration;
lastTime=time;
}
void sendMM53200(int value) {
int mask;
digitalWrite(OUTPUT_PIN,LOW);
delayMicroseconds(PREAMBLE);
digitalWrite(OUTPUT_PIN,HIGH);
delayMicroseconds(SHORTDELAY);
for (mask=1<<11; mask; mask>>=1) {
if (value & mask) {
digitalWrite(OUTPUT_PIN,LOW);
delayMicroseconds(SHORTDELAY);
digitalWrite(OUTPUT_PIN,HIGH);
delayMicroseconds(LONGDELAY);
} else {
digitalWrite(OUTPUT_PIN,LOW);
delayMicroseconds(LONGDELAY);
digitalWrite(OUTPUT_PIN,HIGH);
delayMicroseconds(SHORTDELAY);
}
}
digitalWrite(OUTPUT_PIN,LOW);
}
void sendcode(int value) {
int i=NTIMES;
Serial.println(value, BIN);
while (i--)
sendMM53200(value);
}
void bruteforce(void) {
int value;
for (value=0; value<4096; value++)
sendcode(value);
}
void inbyte(int inchar) {
static value = 0;
switch (inchar) {
case '\r':
sendcode(value);
value=0;
break;
case 0x02:
bruteforce();
break;
case '1':
value++;
case '0':
value <<= 1;
break;
}
}
void setup() {
Serial.begin(115200);
attachInterrupt(INPUT_INTERRUPT, handleInterrupt, CHANGE);
pinMode(OUTPUT_PIN, OUTPUT);
digitalWrite(OUTPUT_PIN,LOW);
}
void loop() {
while (Serial.available()) {
inbyte(Serial.read());
}
}