Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conrad RS-200 Sockets / Europe Supplies Ltd. RS-200 #231

Open
ddTech opened this issue May 17, 2018 · 15 comments
Open

Conrad RS-200 Sockets / Europe Supplies Ltd. RS-200 #231

ddTech opened this issue May 17, 2018 · 15 comments

Comments

@ddTech
Copy link

ddTech commented May 17, 2018

I'm currently working with older remotes and sockes of type RS-200 from Conrad.
I have several of them and in one of the boxes I found a receipt from 2003. So they're 15 years old (It's May 2018 as I write these lines).

Still, they seem to be quite reliable (though not beeing used for serval years) and I have around 15 of them. The system also came with wall mount switches and special receivers for overhead lights were available. Besides ON/OFF switches, dimmable sockets were available as well.

If one of the sockets appears to be dead it's likely just the bigger 22.2kOhm resistor that quit service after working as a "heating element" for several years. A really easy and cheap repair.

The buttons of the transmitter can be revived in most cases by removing the "crispy frog" cover - which is not soldered and cleaning the contacts.

Finally, the socket can be "hacked" quite easily and used as a receiver.

The transmitter has a four digit code which has to be / can be freely chosen after entering the batteries. So there are no switches or jumpers to set that code permanently. Likewise the sockets have to learn this code after each power down. It's a process of seconds, but has to be done if You remove the socket from the mains for more than 30 seconds. This gives You the freedom to easily change how things work together but can be annoying after a power down.

I've been struggling with the protocol as I was using a poor receiver which gave me reasonable results with another type of transmitters, but garbage with the conrad ones.
(see issu #228 post 3).

Most of the decoding has been prepared years ago by two guys in a German forum (https://forum.fhem.de/index.php/topic,12286.0.html See posts by RxTs and BeRo). So the credit goes to them. I just put the bits together, solved a few quirks and made it work in a micro controller like an arduino.

I'm not sure if I should integrate the new protocol into rc-switch as it differs from the others, would require additional methods and will probably not have a huge demand. Maybe add it as an option which can be included/excluded with define statements. I will however post my findings here, so that all the information is available.

Just let me know, what You think.

Frank

@ddTech
Copy link
Author

ddTech commented May 17, 2018

Telegram structure

The telegram has a length of 26 bits
"1"s are 500µs HIGH pulses followed by 3300µs LOW
"0"s are 1300µs HIGH pulses as well followed by 3300 LOW
a longer silence is appended to each telegram. this pause is 26000µ after the first two telegrams and 36.000µs from then on.
I guess this was necessary to decide between ON/OFF and dimming.

The telegram packet (telegram+silence) is repeatedly being sent as long as the button is pressed down.
When the button is released, a different telegram will be sent for three times.
So there are two types of messages being sent by the transmitter: ON/OFF and UpClick or TOGGLE.

Wall mount transmitters only have four buttons for the four channels. I have not personally investigated them yet, but would guess, they only send the TOGGLE signal.

So lets further digg into the telegram.

The following screenshot shows the OFF action of Button no. 3 from a transmitter (actually my code that I simultaniously sent and received at the same time) with a transmitter-code of 3-4-2-4.

grafik

this can be cut into the following pieces:

Header  4  2  4  3 P  3 OFF  Check
011001 11 01 11 10 1 010 01 000010

Header
The telegram starts with a constant header of six bits: 011001.

Transmitter code
The next eight bits represent the zero based transmitter code in reverse order.
In my example the code is 3-4-2-4 which is reversed to 4-2-4-3.
Zero based it's 3-1-3-2 or 11 01 11 10.

Parity bit
The next bit is a parity bit for ODD parity over the following five bits.

Button pressed
The parity bit is followed by three bits for the button being pressed. There are four individual rows (1-4) and a "master" for "all ON / OFF". That's why three bits are needed. Buttons are zero based as well. The values are: 1 = 0b000, 2 = 0b001, 3 = 0b010, 4 = 0b011, 6 = Master = 0b101
So its 010 in my example.

Action
Now there are two bits for the action. This can be ON = 0b00, OFF = 0b01 or TOGGLE = 11

Checksum
So far so good - nice and easy. But now it gets dirty.
I'm almost sure this is not the algorithm the manufacturer uses and I have no clue how BeRo (see previous post) made that one up, but with a bit of clarification it now works properly for every combination I've tested.

The telegram finishes with a checksum of six bit. The checksum (naturally) differs between ON and OFF but is always identical for OFF and TOGGLE.

sidekick
Whenever I write "is" or "always" You may read something like "seems to be" or "as far as I know" or "as it appears" as this is all more or less guessing and observation.
/sidekick

Calculation of the checksum uses the following elements:
BUTTON: 1 = 0b0001 (1) ; 2 = 0b1010 (10); 3 = 0b1011 (11); 4 = 0b0100 (4); 6 = 0b0110 (6) - 6 is "master"

Yes, button two and three get values "ten" and "eleven" and not 0b10 and 0b11 (didn't I say it gets dirty).

TRANSMITTER CODE : now from left to right, zero based (1-2-3-4 --> 0-1-2-3 or 1-1-1-1 -> 0-0-0-0)
ACTION: ON = 0; OFF and TOGGLE = 1 (dirty)
MASK: 0x0F (cut off everything over 15, thus practically making two bits unused)

CheckSum = ( BUTTON + TC1 + (TC2 * 4) + TC3 + (TC4 * 4) + (ACTION * 0x0C) ) & MASK

Using that on my example:
Button: 3
Transmitter code: 3-4-2-4 -> 2-3-1-3
Action: OFF

CheckSum = (11 + 2 +(34) + 1 + (34) + (1 * 0x0C) ) & 0x0F = 0b000010 = 2

Weird, but the correct answer.

So that's to the protocol. Let me know if there are questions.

regards

Frank

@ddTech
Copy link
Author

ddTech commented May 17, 2018

Code
I've created a test code so that You can test these sockets without the need to write the code Yourself.
It is not my prettiest piece of code but it should compile and work.

Just compile, upload and open the serial monitor. Now You should be able to control any of these RS200 Sockets with a six digit code The code is TransmitterKey+Button+Action.

i.e. 123421 in order to switch a socket ON that has learned button no.2 of key code 1234.
Enter 123420 in order to switch it off again.

By the way, this way sockets can also learn codes for transmitters You don't have set up.

Github refuses my zipped upload so here it is in plain text:



/*
 * 2018-05-17
 * Test code for Conrad RS-200 433MHz remote sockets, also labeled "Euroope Supplies Ltd. RS-200"
 * (c) 2018 Frank Dietrich - ddtech(at)gmx(dt)de
 * 
 * For in depth information and documentation see  
 * https://github.com/sui77/rc-switch/issues/231
 * https://forum.fhem.de/index.php?topic=12286.
 * see posts from BeRo und RxTx in the latter. They did all the important preparation.
 * 
 * 
 * This code provides all necessary functionality to test and understand
 * the communication to the sockets. The code could / should be optimized
 * and does not do much error correction. 
 * 
 * 
 * USAGE:
 * 
 * - Adjust the digital PIN to the one You connected to Your 433 MHz transmitter
 * - Compile and upload
 * - open serail monitor and enter six digit code for the socket You like to address 
 *   The code TTTTBA   where TTTT represents the transmitter code, B the button number 
 *   and A the action being 1= ON and 2 = OFF. 
 *   Thus 123421 will switch the socket listening to transmitter 1234 for button no. 2 ON 
 * 
 */


// display info in serial monitor
#define DisplayInfo


// =================== custom settings ========================//

// Pin used for transmission of the signal
// adjust to Your needs
const uint8_t SENDPIN = 10;

// how often should the telegram be sent?
// 
uint8_t nRepeatTransmit = 5;


// =================== constants ================================  //

// timings that work well for me. The timing is not
// too critical. A 650µs / 1950µs timing worked as well
const uint16_t PULSE_HIGH_ONE = 500;
const uint16_t PULSE_HIGH_ZERO = 1300;
const uint16_t PULSE_LOW = 3300;


// Each telegram ends with a lOW signal of 26000µs or 36000µs
// First two messages are followed by a 26000µs break. After that
// the break is 36000µs. 
// As each pulse ends with a 3300µs LOW, we need to subtract that
// from the total length of the pause. i.e. these values are 3300µs 
// shorter
const uint16_t PULSE_SYNC_SHORT = 23000;
const uint16_t PULSE_SYNC_LONG  = 33000;


// Every transmission ends with an "upclick" message when the 
// button is being released. That telegram will be sent
// three times. That telegram sends a "TOGGLE" state
const uint8_t UPCLICK_VAL = 0b11;     // Buttonwert für Toggle, bzw. Upcklick bin 0b11 = 3
const uint8_t UPCLICK_REPEAT = 3;

void sendPair(unsigned long timeLow, unsigned long timeHigh);
void sendPair(unsigned long timeLow);


// ==================== global variables =================== //
//char code[4] = "1111";
String cBuff = "";
uint8_t nBuffLen = 0;


void setup() {

  // wait for Serial to appear on Leonardos
  unsigned long now = millis();
  while (! Serial && millis() < now + 15000 ) {}
  
  Serial.begin(115200);

  pinMode(SENDPIN, OUTPUT);
  digitalWrite(SENDPIN, LOW);
  
  Serial.println(F("Enter six digit code in order to address a socket"));
  Serial.println(F("The format is TTTTBA, with T = Transmitter code B = Button no. and A = Action"));
  Serial.println(F("Example: 123421 sends transmitter code 1234 and switches socket no.2 ON. 123420 switches it OFF again"));
  
}

void loop() {

  delay(500);
  // see if the user entered something in the serial monitor
  checkInput();
}


void checkInput(){
  char inChar;
  while (Serial.available()){
    inChar = (char)Serial.read();  

  if (inChar == 13){
      unsigned long fullCode = 0, fullCodeUpClick = 0;
      // Serial.println("CR");
      // Serial.print("Buffer: ");
      // Serial.println(cBuff);

      // Als pointer druckt es die chars ab pos + x
      // bei 12410 wäre das "410";      
      //Serial.print(&cBuff[0]+3); 

      // und so den numerischen wert der Array-Zelle:
      // 52 bei 123410  = 49+2
      //Serial.print(cBuff[0]+3); 
      //Serial.print(" ");
      //Serial.print(cBuff[4]);
      //Serial.print(" ");
      //Serial.println(cBuff[5]);

      if (cBuff[0] == 'r'){
        // raw byte string
                
        fullCode = 0;
        for (uint8_t i=1; i< cBuff.length(); i++){
          fullCode <<= 1;
          fullCode |= cBuff[i]-48;
        }
        Serial.println( fullCode );
        send( fullCode, cBuff.length() -1);        
        
      } else {
        // Eingabe PIN Button on/off ( 123410 = Key 1234, Button 1, ON, | 123411 = Key 1234, Button 1, OFF )
        char code[4] = {cBuff[0], cBuff[1], cBuff[2], cBuff[3]};

        // I guess having 1 for ON and 0 for OFF is more intuitive       
        // fullCode = getCode_RS200(code, cBuff[4]-48, cBuff[5]-48);
        uint8_t nAction = (cBuff[5]=='0') ? 1 : 0;
                               
        fullCode = getCode_RS200(code, cBuff[4]-48, nAction);
        // prepare the upclick code as well so that it can be sent
        // immediatelly after the the repetitions are done
        fullCodeUpClick = getCode_RS200(code, cBuff[4]-48, UPCLICK_VAL );  
        send( fullCode, 26, nRepeatTransmit );

        // Nach den Wiederholungen werden jetzt noch drei Telegramme mit dem "UpClick" (Toggle) geschickt
        // fullCode = getCode_RS200(code, cBuff[4]-48, UPCLICK_VAL );  
        send(fullCodeUpClick, 26, UPCLICK_REPEAT ); 
        
      }
      
      cBuff = "";
      
    } else if (inChar == 10){
      //Serial.println("LF");
      
    } else {      
      cBuff+=inChar;
    }
  }    
}


void encodeTransCode(unsigned long *nFullCode, uint8_t *nFullLen, char* transCode){
    /* Encoding the transmitter code in reverse order and zero based
     * 1-2-3-4 -> 4-3-2-1
     * where 4 = 0b11, 3 = 0b10, 2 = 0b01 and 1 = 0b00
     */
  
    uint8_t nCodeLen = 4, i;
 
    for ( i = 0; i < nCodeLen; i++ ){
      //Serial.print( code[i] );
      //Serial.print( "->" );
      //Serial.println( code[i]-48 );

      *nFullCode <<= 2UL;
      *nFullLen+= 2;
      if (transCode[3-i]-49 > 0){
        *nFullCode |= (transCode[3-i]-49);    // zero based
      }          
    }      
}


void encodeAction(unsigned long *nFullCode, uint8_t *nFullLen, uint8_t tnButton, uint8_t tnAction ){
    /* Encoding der Aktion, d.h. gedrückter Knopf + Aktion (ON / OFF / UP)
     * Es gibt ein odd parity Bit. Die high-bits dafür werden bei der 
     * Ermittlung gleich mitgezählt.
     */

    /*  Encoding the action (ON / OFF / UP) 
     *  high bits will be counted as we go in order to generate the ODD parity bit
     *  which will be prepended
     */
    uint8_t highBits = 0;    
    uint8_t action  = 0;

    // bits 5,4,3 sind button 1-4 oder 6 (Master) 
    if (tnButton > 1){
      action |= ((tnButton-1) << 2) ;
      
      if (tnButton == 6 || tnButton == 4){
        highBits+=2;
      } else {
        highBits++;
      }

      /* which actually is
      if (tnButton-1 == 5 || tnButton-1 == 3){
        ....
      }
      */

      /* or this
      switch(tnButton-1){
        case 1:   // button 2
          nHighBits ++;
          break;
        case 2:   // button 3
          nHighBits ++;
          break;
        case 3:   // button 4
          nHighBits += 2;
          break;
        //case 4:   // button 5 - does not exist but for completeness
        //  nHigBits += 2;
        //  break;
        case 5:      // button 6 - "All"
          nHighBits += 2;
          break;
      }
      */
      
    }

    // action: 0b00 = ON pressed, 0b01 = OFF pressed, 0b11 = released
    if (tnAction >0){
      action |= tnAction;
      highBits += (tnAction==1) ? 1: 2 ;
    }

    /* alternatively we could count high bits later
     * where val should be action
    
    while(bits > 0) {
      highBits += (val & 1 ? 1 : 0);
      val >>= 1;
      bits--;
    }
    */
            
    // prepend with parity bit 
    if (highBits % 2 == 0){
      action |= 1 << 5; 
    }

    // now append the action to fullCode
    *nFullCode <<= 6UL;
    *nFullCode |= action;
    *nFullLen += 6;
}


void createCheckSum(unsigned long *nFullCode, uint8_t *nFullLen, char* transCode, uint8_t tnButton, uint8_t tnAction ){

  /* Nach dem Beitrag von BeRo in oben genanntem thread:
   *  Wie berechnet sich die Checksumme?
    CS = ROW + (HC1) + (HC2 * 4) + (HC3) + (HC4 * 4) + (Schaltaktion * 0x0c)
    ROW ist die Taste die betätigt wurde.
    ROW1 = 1
    ROW2 = 10
    ROW3 = 11
    ROW4 = 4 
    ROW6 = 6 ROW6 gibt es nur beim Handsender ROW5 gibt es nicht.    

    transCode is zero based
    Das Ergebnis ist ein vier-Bit Wert, der ggf. mit 0x0f maskiert werden muss.

    2018-05-16
    Ich habe länger mit dem Code gekämpft, da er nur für den ersten Button funktioniert hat, 
    was daran lag, dass ich Button 2 und 3 mit "10" und "11" als Binärwerte (also 0b10 und 0b11) gelesen habe. 
    Tatsächlich meint er aber 10 (zehn) und 11 (elf). Damit funktionier die Routine jedenfalls.

    Falsche Ergebnisse gibt es jedoch bei den Toggle-Aktionen (0b11). Hier soll die gleiche Checksumme, wie beim 
    OFF herauskommen. Erreicht wird das vermutlich in der eingebauten Funktion durch Verwendung des Paritäts-Bits, 
    das in diesem Fall immer steht, und im OFF-Fall immer liegt. 
    Hier verwende ich der Einfachheit halber die Werte 0 für tnAction == 0 und 1 für andere. 

    Das ist alles ziemlich "schmutzig" und vermutlich nicht die Originalmethode, führt aber in allen von mir getesteten 
    Kombinationen zum richtigen Ergebnis.      
    
   */

   uint8_t nButtonVal = tnButton;
   //uint8_t nActVal = (tnAction > 0) ? 1 : 0;   // or simply use the last bit

   // With buttons 1, 4 and 6 (master), their position can be used
   // for the checksum calculation. For buttons 2 and 3 however 
   // decimal values of 10 and 11 respectively have to be used.   
   // As an alternative a constant array holding the button
   // values could be used, but the following works fine. 
   if (nButtonVal == 2 || nButtonVal == 3){
    nButtonVal = 8 + nButtonVal;  // -> 10 oder 11
   }   
   
   *nFullCode <<= 6UL;
   // uint16_t checkSum = ( nButtonVal + (transCode[0]-49) + ((transCode[1]-49)*4) + (transCode[2]-49) + ((transCode[3]-49)*4) + (nActVal * 0x0c) ) & 0x0f;
   // *nFullCode |= checkSum;
   //*nFullCode |= ( nButtonVal + (transCode[0]-49) + ((transCode[1]-49)*4) + (transCode[2]-49) + ((transCode[3]-49)*4) + (nActVal * 0x0c) ) & 0x0f;
   *nFullCode |= ( nButtonVal + (transCode[0]-49) + ((transCode[1]-49)*4) + (transCode[2]-49) + ((transCode[3]-49)*4) + ((tnAction & 0b01) * 0x0c) ) & 0x0f;
   *nFullLen += 6;        
}



unsigned long getCode_RS200(char * transCode, uint8_t tnButton, uint8_t tnAction){

    unsigned long fullCode = 0;
    uint8_t fullLen = 0;

    // Header ist immer gleich
    fullCode += 0b011001;
    fullLen  = 6;
    //Serial.println("Header");
    //Serial.println(fullCode, BIN);

    encodeTransCode(&fullCode, &fullLen, transCode); 

    encodeAction(&fullCode, &fullLen, tnButton, tnAction);
    
    createCheckSum(&fullCode, &fullLen, transCode, tnButton, tnAction);
    #ifdef DisplayInfo
      Serial.print("Button ");
      Serial.print(tnButton);
      Serial.print(" ");
      Serial.println( (tnAction==0) ? "ON" : ((tnAction==3) ? "released" : "OFF"));
      Serial.print("0"); // as the leading 0 is being swallowed 
      Serial.print(fullCode, BIN);
      // and the long equivalent as well
      Serial.print(" (");
      Serial.print(fullCode);
      Serial.println(")");
    #endif  

    return fullCode;  
}



void send( unsigned long fullCode, uint8_t fullLen) {
   send( fullCode, fullLen, nRepeatTransmit );
}

void send( unsigned long fullCode, uint8_t fullLen, uint8_t tnRepeat ){
  
  for (int nRepeat = 0; nRepeat < tnRepeat; nRepeat++) {
    
    for (int i = fullLen-1; i >= 0; i--) {
      if (fullCode & (1L << i))        
        sendOneBit();
      else        
        sendZeroBit();
    }

    // Sync-Signal wird nachgestellt. Die ersten beiden Male
    // mit 26ms, danach mit 36 ms
    // Dabei endet jeder Impuls ja mit einem 3300er low
    // d.h. der von hier ausgesendet kann um diesen Wert kürzer sein.
    
    sendPart( (nRepeat < 2) ? PULSE_SYNC_SHORT : PULSE_SYNC_LONG, false );
    
  }  
}


void sendOneBit(){ 
  sendPair(PULSE_LOW, PULSE_HIGH_ONE);  
}

void sendZeroBit(){
  sendPair(PULSE_LOW, PULSE_HIGH_ZERO);
}


void sendPair(unsigned long timeLow, unsigned long timeHigh){ 
  sendPart(timeHigh, true);
  sendPart(timeLow, false);  
}  


void sendPart(unsigned long tnTime, bool tlHigh){
    if (tnTime > 0){
      unsigned long now = micros();
      digitalWrite(SENDPIN, tlHigh);
      while (micros() < now + tnTime){};  
    }   
}


HTH

regards

Frank

@Martin-Laclaustra
Copy link

Martin-Laclaustra commented May 17, 2018

You may improve your interpretation (and even try to make it work with rcswitch) with the following hints:

  • lets look for approximate single number relations of the durations that you provide:
  1. base timing: 550 us
  2. 500... 1... 550
  3. 1300... 2... 1100
  4. 3300... 6... 3300
  5. 36000... 65... 35750
    (you could try to find other base timing and natural number factors)
  • we will need to assume that only the central repeats carry information.
  • check if for every button/code the first or last "up" signal duration remains always the same. That is not "data" but "sync". It is usually the last one, but I was not sure after reading your explanation, as it is the first one the one that you describe as invariant. The rest is "data". You have 25 bits of data that you have to interpret.
  • if the first "up" duration is the one invariant, the protocol is "reversed", that mean the bits go in pairs of low-up durations. Otherwise it is a "normal" protocol, and the bits go in pairs of up-low durations.
  • in the first case ("reversed") the protocol would look like this:
    { 550, { 65, 2 }, { 6, 2 }, { 6, 1 }, true }
  • in the second case ("normal") the protocol would look like this:
    { 550, { 2, 65 }, { 2, 6 }, { 1, 6 }, false }
  • once you have decided which of these interpretations is the correct, it would help if you provided as many 25-bit codes for the different buttons (with their name) as possible to let have everyone have a bird-view of the protocols used. The German forum went a long way in the interpretation, but it might be possible to go further, as the engineers who designed the sockets used probably simpler approaches.
  • the dirty part, once you have selected the correct 25 bits, it is probably a simplified use of the encoding chip. These have a combination of binary and trinary bits, which are encoded in pairs. So the data bits go in pairs (this is not consistent with 25 bits, that will need rethinking), from which the last (of first) 4, 6, or 8 might be trinary (you can learn about this by reading spec datasheets of the encoding chips). For the sake of simplifying the wiring, engineers use all of them as binary, so 0 -> 00 always, but for part of the code 1 -> 01, while for the rest 1 -> 11. Please keep in mind that you might have got the code "inverted" as the long "up duration" might be the 1. Then you should binary negate your numbers before interpreting them.
  • Even without interpreting the codes, it would help if you report whether sending them with rcswitch and the protocols (or variations) above succeeds to operate your sockets. Interpreting the codes is only needed to avoid the recording phase (which as you report is difficult with this brand).
    I hope this helps.

@ddTech
Copy link
Author

ddTech commented May 17, 2018

Martin,
thanks, yes the goal is to integrate it into RC-Switch. I actually had a closer look into the class before I started tinkering with the protocol, so I had that in mind but in order to get a first working prototype it was easier for me to just write an individual piece of code. And I wanted to make the information available, so that it's "there" in case some needs it.
Now, with a working model, I can try to port it to rc-switch. Guess I'll have to fork the current version and see how far I get.

Concerning Sync / data:
the first six bits are an invariant "header". So one could say the code has six bits of sync information followed by 20 bits of data followed by a longer separation LOW (26ms / 36ms). This pause is not part of the sync procedure as the signal never begins with it.
One could abandon the first telegram and pretend the pause precedes the header. But that's not the truely the way it is.

I'll start playing with rc-switch and see how I get along.

regards

Frank

@Martin-Laclaustra
Copy link

the first six bits are an invariant "header".

That is common among sockets of the same brand.
But it is important to assign the first high duration to the sync bit.
Does the last high duration change across the different buttons? If the answer is "yes", the first high duration belongs to to the sync bit.

So one could say the code has six bits of sync information followed by 20 bits of data followed by a longer separation LOW (26ms / 36ms).

No. That is not how rcswitch works.

This pause is not part of the sync procedure as the signal never begins with it.

It actually is.

One could abandon the first telegram and pretend the pause precedes the header.

You need at least one repeat to have a transmission detected. That is how rcswitch identifies a signal... by the gap length (and you have no gap if you have no signal before). We do not know if all sockets rely on these same method for detection, but using it to create the signal does work!

But that's not the truely the way it is.

who cares...

Guess I'll have to fork the current version and see how far I get.

I don't think so.
You should start with the protocols that I propose above with the unmodified rcswitch, emit, record the signal, see how much it does differ from your original one, and see if it does trigger your sockets.

@ddTech
Copy link
Author

ddTech commented May 17, 2018

Hacking the Socket

As mentioned earlier, it is pretty easy to "hack" the socket and misuse it as a 433MHz receiver in case You need one better than the el cheapo MX-RMs.

No, I will not put a five page disclaimer here, telling You that mains power can be lethal and that You will be getting damn close to that mains power if You open the socket.
When You tinker around with stuff like that, You're either smart enough to know what You're doing or a complete idiot. In both cases the information is useless.

However, I'd like to draw Your attention to the fact that there is no galvanic separation between the high and low voltage parts.
Why is that important? Well, if You try to probe the signal with an oszilloscope or logic analyzer there is a good 50/50 chance to fry the chip, Your scope, logic analyzer, usb-port or all of them, as soon as You connect the ground connector unless Your device is battery operated or separated from mains via a transformer.

How come? Mains power normally has two wires (plus a third ground in case of metal enclosures), phase (the one that "bites") and neutral. Neutral and Ground can be seen as one (in older houses like ours they are in fact tied together). Directly connecting phase to neutral or ground shorts the circuit and blows up the fuse or the weakest parts in line.

Now to our RC-200. The low DC voltage is derived from mains AC high voltage via resistors and some rectifiers but not galvanically separated. As it can be plugged in either, way it is possible, that DC ground (-) is (more or less) directly connected to mains phase. Ground of Your Oscilloscope or USB-Port however ist connected to mains ground. Thus connecting the ground probe to DC (-) might short the circuit as if You connected phase directly to ground.

So make sure to plug in the RC-200 in a way that phase is on the switched side (the right side with my version) and do NOT use the ground probe unless You know what You are doing. If plugged in correctly You will still see the signal, as all devices share common ground.

conrad_rs-200_receiver_signal

If You measure the voltage between the pads marked + and - with Your battery powered (!!) volt meter You should read something between 1.5 and 3 V depending on the "noise" and the latency of Your meter. If so can connect Your oscilloscope or logic analyzer with one probe to the pad marked +

Pressing a button on the transmitter should bring up something like this:

conrad_rs-200_receiver_signal_2

You see the wavy modulation onto 50Hz mains.

Logic analyzer works as well (also no ground probe)

bild6_abgriff_dose

The signal is not limited to Conrad RS-200 transmitters. You will measure the raw 433MHz signal and the socket can work as a basic receiver.

As they say: Don't try this at home! or do it at Your own risk.

@ddTech
Copy link
Author

ddTech commented May 18, 2018

That is common among sockets of the same brand.
But it is important to assign the first high duration to the sync bit.
Does the last high duration change across the different buttons? If the answer is "yes", the first high duration belongs to to the sync bit.

I'm afraid, I don't get it....

The signal starts right into the atmospheric noise. like that:
grafik

So yes, for detection it makes sense to wait for the first longer break. Seeing it that way, the signal starts with a long LOW followed by six invariant high low packets.
grafik

The seventh high belongs to the transmitter code. It is invariant throughout all buttons of the transmitter but varies between transmitters (or settings).

The German forum went a long way in the interpretation, but it might be possible to go further, as the engineers who designed the sockets used probably simpler approaches.

I'm almost sure that is the case. The checksum code is awful and I can't imagine it's meant to be that way. I've spent some time to find a better solution trying 2s complement on various combinations etc. but without success.

Please keep in mind that you might have got the code "inverted" as the long "up duration" might be the 1. Then you should binary negate your numbers before interpreting them.

The long high being the "one" to me also seemed to be more intuitive. So I played with inverted signals as well. That way I could ommit some of the backwards approaches which seem to be counterintuitive as well. But as I did not find a working solution for the checksum I had to continue with the working parts.

I don't think so.
You should start with the protocols that I propose above with the unmodified rcswitch, emit, record the signal, see how much it does differ from your original one, and see if it does trigger your sockets.

I can try that, but what about the checksum?

once you have decided which of these interpretations is the correct, it would help if you provided as many 25-bit codes for the different buttons (with their name) as possible to let have everyone have a bird-view of the protocols used.

I could write a few lines that generate them all and try to verify some of them.

Here are some I played with. All of them are verified.



PIN 	Butt  	Func 	Code (r für direkte Eingabe)	Header	Key				Par	But	Act	Checksum	Dec

1111	1	ON	01100100000000100000000001	011001	00	00	00	00	1	000	00	000001		 1
1111	1	OFF	01100100000000000001001101	011001	00	00	00	00	0	000	01	001101		13		
1111	1	TGL	01100100000000100011001101	011001	00	00	00	00	1	000	11	001101		13

1111	2	ON	01100100000000000100001010	011001	00	00	00	00	0	001	00	001010		10
1111	2	OFF	01100100000000100101000110	011001	00	00	00	00	1	001	01	000110		 6
1111	2	TGL	01100100000000000111000110	011001	00	00	00	00	0	001	11	000110		 6

===================================================
1234    1   	ON	01100111100100100000000011	011001	11	10	01	00	1	000	00	000011		 3
1234	1	OFF	01100111100100000001001111	011001	11	10	01	00	0	000	01	001111		15
1234	1	TGL	01100111100100100011001111	011001	11	10	01	00	1	000	11	001111		15

1234	2	ON	01100111100100000100001100	011001	11	10	01	00	0	001	00	001100		12
1234	2	OFF	01100111100100100101001000	011001	11	10	01	00	1	001	01	001000		 8
1234	2	TGL	01100111100100000111001000	011001	11	10	01	00	0	001	11	001000		 8

1234	3	ON	01100111100100001000001101	011001	11	10	01	00	0	010	00	001101		13
1234	3	OFF	01100111100100101001001001	011001	11	10	01	00	1	010	01	001001		 9
1234	3	TGL	01100111100100001011001001	011001	11	10	01	00	0	010	11	001001		 9

1234	4	ON	01100111100100101100000110	011001	11	10	01	00	1	011	00	000110		 6
1234	4	OFF	01100111100100001101000010	011001	11	10	01	00	0	011	01	000010		 2
1234	4       TGL	01100111100100101111000010	011001	11	10	01	00	1	011	11	000010		 2

1234    ALL	ON	01100111100100110100001000	011001	11	10	01	00	1	101	00	001000		 8
1234	ALL	OFF	01100111100100010101000100	011001	11	10	01	00	0	101	01	000100		 4
1234	ALL	TGL	01100111100100110111000100	011001	11	10	01	00	1	101	11	000100		 4

===================================================

And here the inverted versions in case 0s are 1s and 1s are 0s


1111	1	ON	10011011111111011111111110	100110	11	11	11	11	0	111	11	111110		62
1111	1	OFF	10011011111111111110110010	100110	11	11	11	11	1	111	10	110010		50	
1111	1	TGL	10011011111111011100110010	100110	11	11	11	11	0	111	00	110010		50

1111	2	ON	10011011111111111011110101	100110	11	11	11	11	1	110	11	110101		53
1111	2	OFF	10011011111111011010111001	100110	11	11	11	11	0	110	10	111001		57
1111	2	TGL	10011011111111111000111001	100110	11	11	11	11	1	110	00	111001		57


===================================================
1234    1  	ON	10011000011011011111111100	100110	00	01	10	11	0	111	11	111100		60
1234	1	OFF	10011000011011111110110000	100110	00	01	10	11	1	111	10	110000		48
1234	1	TGL	10011000011011011100110000	100110	00	01	10	11	0	111	00	110000		48

1234	2	ON	10011000011011111011110011	100110	00	01	10	11	1	110	11	110011		51
1234	2	OFF	10011000011011011010110111	100110	00	01	10	11	0	110	10	110111		55
1234	2	TGL	10011000011011111000110111	100110	00	01	10	11	1	110	00	110111		55

1234	3	ON	10011000011011110111110010	100110	00	01	10	11	1	101	11	110010		50
1234	3	OFF	10011000011011010110110110	100110	00	01	10	11	0	101	10	110110		54
1234	3	TGL	10011000011011110100110110	100110	00	01	10	11	1	101	00	110110		54

1234	4	ON	10011000011011010011111001	100110	00	01	10	11	0	100	11	111001		57
1234	4	OFF	10011000011011110010111101	100110	00	01	10	11	1	100	10	111101		61
1234	4       TGL	10011000011011010000111101	100110	00	01	10	11	0	100	00	111101		61

1234    ALL	ON	10011000011011001011110111	100110	00	01	10	11	0	010	11	110111		55
1234	ALL	OFF	10011000011011101010111011	100110	00	01	10	11	1	010	10	111011		59
1234	ALL	TGL	10011000011011001000111011	100110	00	01	10	11	0	010	00	111011		59


@Martin-Laclaustra
Copy link

I can try that, but what about the checksum?

try:
mySwitch.send("01100100000000100000000001");

I think rcswitch should be separated in diferent "levels". One to deal with the transmission (hardware), one to deal with the encoding (what is called "protocols"), and one to deal with the brands and the translation between the socket "settings" and the way the actual codes that must be sent are generated (functions ".switchOn" and alike).
You can advance faster in the use and improvement of the library if you solve separately each level (at least that is my experience). In your case, I would advice to solve the "protocols" level first, and face the way the whole code is created later (that level would be useful for those not able to record the transmitter).
Good luck.

@ddTech
Copy link
Author

ddTech commented May 18, 2018

Yes, baby steps :-)

it would help if you provided as many 25-bit codes for the different buttons (with their name) as possible to let have everyone have a bird-view of the protocols used.

Meanwhile I've created a list of all possible 3840 combinations, easy to read and with all necessary information. Human readable as well as compact binary and split binary. Code and checksum also in octal values. The codes have been created on the micro controller and reported back via serial.

I've done it for current assumption (short pulse = logical one) as well as for the inverted variant (long pulse = logical one).

After that, I randomly picked ON-codes and tested them against a socket (ON is necessary for a socket to "learn" the code) and switched it OFF again with the corresponding OFF-code.

The validated codes have been added to a third list.
So far, none of the codes I've tested failed.

RS-200_AllCodes.txt
RS-200_AllCodes_inverted.txt
RS-200_Validated.txt

regards

Frank

@ddTech
Copy link
Author

ddTech commented May 19, 2018

Martin,

first test with RC-Switch.

I've added a new protocol to my local copy with the following settings:

{ 260, { 100, 5 }, { 13, 5 }, { 13, 2 }, true }

and created a simple test sketch

#include "RCSwitch.h"

RCSwitch mySwitch = RCSwitch(); 

void setup() {
   // Transmitter is connected to Arduino Pin #10  
  mySwitch.enableTransmit(10); 
  mySwitch.setProtocol(7)  ;
  mySwitch.setRepeatTransmit(7);
}

void loop() {

  // key 1234
  
  //Switch 1
  mySwitch.send("01100111100100100000000011");
  delay(1000);
  mySwitch.send("01100111100100000001001111");
  
  // Switch 2  
  mySwitch.send("01100111100100000100001100");
  delay(1000);
  mySwitch.send("01100111100100100101001000");

  // Switch 3
  mySwitch.send("01100111100100001000001101");
  delay(1000);
  mySwitch.send("01100111100100101001001001");

  // Switch 4
  mySwitch.send("01100111100100101100000110");
  delay(1000);
  mySwitch.send("01100111100100001101000010");

  // All 
  mySwitch.send("01100111100100110100001000");
  delay(3000);
  mySwitch.send("01100111100100010101000100");  
}

... and it toggles through all four switches, then switches all on at once and off again after 3s.

This way it is now sending 27 bits, as I do send the full 26 bits and have an additional "0" included in the sync bit. Later I would just omit the first bit of the header.
The sync timing is not correct as well. delayMicroseconds() obviously does not support delays that long. I realized that a few days ago, when I started my measurements. That's why I did while (...) in my timing procedure. I'm getting something like 9.5 ms.
So the protocol is not identical to the one from the handheld, but the sockets do accept it.

grafik

That's for the first tests :-)

regards

Frank

@ddTech
Copy link
Author

ddTech commented May 20, 2018

I've created a simple demo that works with the current RC-Switch class and uses codes from my list of codes.


/*
 * 2018-05-20
 * Simple RC-Switch demo for usage with Conrad RS-200 switches
 * (c) Frank Dietrich ddtech|gmx|de
 * 
 * 
 * Usage: 
 * Pick an ON/OFF combination from the list with all codes that I posted here:
 * https://github.com/sui77/rc-switch/files/2017604/RS-200_AllCodes.txt
 * 
 * Select a pair that matches Your key / button - combination and replace the
 * codeON and codeOFF with the last 25 bytes of them (i.e. omit the first "0"  
 * from the 26byte string.
 * Compile and upload. The outlet should now toggle between ON and OFF
 * 
 */


#include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch(); 

// These are the codes for 1234 button 2
const char* codeON = "1100111100100000100001100";
const char* codeOFF = "1100111100100100101001000"; 

void setup() {
  Serial.begin(9600);
  
  // Transmitter is connected to Arduino Pin #10  
  mySwitch.enableTransmit(10); 
    
  mySwitch.setProtocol( {260, { 100, 5 }, {  13,  5 }, { 13,  2 }, true} )  ;

  // a repetition of 7 works OK for me, You may choose a higher or lower value
  // if You like
  mySwitch.setRepeatTransmit(7);

}


void loop() {
  
  mySwitch.send(codeON);
  delay(1000);

  mySwitch.send(codeOFF);
  delay(1000);
  
}

RemoteSwitch_433_RS200_rc-swithch_Demo.zip

It does not use the upClick or toggle, but normal outlets don't seem to care.

grafik

Frank

@Martin-Laclaustra
Copy link

Good job!

@nothingTVatYT
Copy link

The code works, my plugs are switching and I'm so happy. Many thanks to both of you.

As I wrote in the other issue I had a broken, not properly contacted jump wire so I guess I would have had better results sooner when the sender module would have been sending all the time I tested.

@ddTech
Copy link
Author

ddTech commented May 27, 2018

perfect :-)

@GHPS
Copy link

GHPS commented Jan 29, 2020

So far so good - nice and easy. But now it gets dirty.
I'm almost sure this is not the algorithm the manufacturer uses

Sometimes it's hopeful to check a different solution to a given problem.
Years ago messlinger reverse engineered the Conrad RS200 protocol
for his Python project and came up with a different checksum formula.

https://github.com/messlinger/mbug_devel/blob/master/py/mbug_dev/mbug_2151_target.py
(See the RS200 class)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants