// 2011-07-03 http://opensource.org/licenses/mit-license.php // // Based upon the "Decoder for 433 MHz OOK pulses" by Jean-Clause Wippler // and on protocol information from Ralph van Nellestijn (even though I think this is not // entirely correct). // // http://code.google.com/p/arduino-nodo/issues/attachmentText?id=33&aid=1076664673169665292&name=kaku+new.txt&token=0d087fd841e9c7765855036f09669d67 // // To work out of the box, this code expects the OOK433Plug to be in port 3 of your JeeNode. // It reports some information back over the serial port. // // Basically it listens for A-type KlikAanKlikUit commands, and "inverts" the command after // two seconds. (so, if you turn a light on, it will go off after 2 seconds, and vice // versa). // // Code needs major cleaning, and a lot could have been done better. But hey, it's only an // example, it's 03:00 and I really need to go to bed now, and I only started ATmega // programming 3 days ago. Need any more reasons :-P // // So enjoy, and shut up about it... :-) // #include #include // needed to avoid a linker error :( enum { UNKNOWN, RB1 , RB2 , RB3 , RB4 , RB5 , RB6 , RB7 , RB8 , RB9 , RB10, RB11, RB12, RB13, RB14, RB15, RB16, RB17, RB18, RB19, RB20, RB21, RB22, RB23, RB24, RB25, RB26, RB27, RB28, RB29, RB30, RB31, RB32, DONE }; // Various states we go through ("unknown", one state per bit, and "done") struct { uint8_t state; uint32_t data; } KAKU; // Holds the KAKU message enum { Tu, T1, T5, T10 }; // Pulse types (Tu is undefined pulse) uint8_t backBuffer[] = { Tu, Tu, Tu, Tu }; // Buffer for last 4 pulses Port radio (3); // We expect the OOK433Plug to be in Port 3 of the JeeNode ISR(PCINT1_vect) { // width is the pulse length in usecs, for either polarity static uint16_t last; uint16_t width = micros() - last; last += width; // We try to pick up message data, until a message is complete if (KAKU.state != DONE) { // Translate the current pulse (we use rather large ranges, to make are reader more sensitive. Seems to work better) // // (The send function uses some delays that I picked up over the air when testing with a remote control, in case // you want to know a little more... You'll also notice that my T1, T5, T10 definition is actually not entirely // correct, as there is also a T1/2 :-) But the ranges here are big enough to pick up a T1/2 as a T1, and it // really doesn't matter that much (except when sending I guess)) uint8_t pulse; if ((width >= 100) && (width <= 600)) pulse = T1; else if ((width >=800) && (width <= 1800)) pulse = T5; else if ((width >=2000) && (width <= 3000)) pulse = T10; else { // Weird pulse length, abort any reading that is going on, and start searching for START/SYNC sequence again KAKU.state = UNKNOWN; pulse = Tu; } // Update the buffer, and store the pulse we just read (we want to know about the last 4 readings) backBuffer[3] = backBuffer[2]; backBuffer[2] = backBuffer[1]; backBuffer[1] = backBuffer[0]; backBuffer[0] = pulse; // Are we reading data bits already? if ((KAKU.state >= RB1) && (KAKU.state <= RB32)) { // We can NEVER have a T10 while reading data bits if (pulse == T10) { // Received an unexpected pulse, abort (but do not clear the buffer, it may contain part of a START/SYNC sequence!) KAKU.state = UNKNOWN; } else { // No reason to abort (yet :-)). Data bits always consist of 4 pulses, so wait until we have 4. if (backBuffer[3] != Tu) { // Buffer full, let's see if it is a valit bit if ( (backBuffer[3] == T5) && (backBuffer[2] == T1) && (backBuffer[1] == T1) && (backBuffer[0] == T1)) { // High bit KAKU.data = (KAKU.data << 1) | 1; KAKU.state++; backBuffer[3] = Tu; backBuffer[2] = Tu; backBuffer[1] = Tu; backBuffer[0] = Tu; } else if ( (backBuffer[3] == T1) && (backBuffer[2] == T1) && (backBuffer[1] == T5) && (backBuffer[0] == T1)) { // Low bit KAKU.data = (KAKU.data << 1) | 0; KAKU.state++; backBuffer[3] = Tu; backBuffer[2] = Tu; backBuffer[1] = Tu; backBuffer[0] = Tu; } else { // Error, data invalid, abort (but do not clear the buffer, it may contact part of a START/SYNC sequence!) KAKU.state = UNKNOWN; } } } } // Looking for START/SYNC? (this is INTENTIONALLY put AFTER the bits reading part, although on hindsight probably // not really required...) if (KAKU.state == UNKNOWN) { // START/SYNC sequence found? if ( (backBuffer[2] == T1) && (backBuffer[1] == T10) && (backBuffer[0] == T1) ) { // Yes! We can start reading the bits now KAKU.state = RB1; // Clear the buffer backBuffer[3] = Tu; backBuffer[2] = Tu; backBuffer[1] = Tu; backBuffer[0] = Tu; // Clear the data KAKU.data = 0; } } } } void sendBit(uint8_t bit) { // Delays picked up from a "real life" capture from a remote control if (bit == 1) { // High bit delayMicroseconds(1180); radio.digiWrite(1); delayMicroseconds(360); radio.digiWrite(0); delayMicroseconds(184); radio.digiWrite(1); delayMicroseconds(348); radio.digiWrite(0); } else { // Low bit delayMicroseconds(188); radio.digiWrite(1); delayMicroseconds(352); radio.digiWrite(0); delayMicroseconds(1192); radio.digiWrite(1); delayMicroseconds(352); radio.digiWrite(0); } } void sendKAKUMessage(uint32_t houseAddress, uint8_t group, uint8_t action, uint8_t code) { // Send START/SYNC bits // dummy flip radio.digiWrite(0); delayMicroseconds(10044); // <-- Not sure if this is required, but the remote waits // this amount of uSecs between the messages it sends, // and you always need to send a message twice... // So I guess it's a good idea to use this number :-) radio.digiWrite(1); // START/SYNC delayMicroseconds(356); radio.digiWrite(0); delayMicroseconds(2544); radio.digiWrite(1); delayMicroseconds(364); radio.digiWrite(0); // Now send the house bits uint8_t bitCount; for (bitCount = 0; bitCount<26; bitCount++) sendBit( (houseAddress >> (25 - bitCount)) & 1 ); // Group bit sendBit(group); // Action bit sendBit(action); // Code bits for (bitCount = 0; bitCount<4; bitCount++) sendBit( (code >> (3 - bitCount)) & 1 ); } void setup() { Serial.begin(57600); Serial.println("\n[KAKU type-A reader]"); radio.mode2(INPUT); // enable pin change interrupts on PC2 PCMSK1 = bit(2); PCICR |= bit(PCIE1); KAKU.state = UNKNOWN; } void loop() { if (KAKU.state == DONE) { // We got a message! Announce it! Serial.println("We got a KAKU TYPE-A message!"); Serial.println(); // Printing the bits Serial.println("Bits: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31"); Serial.print (" "); for (uint8_t currBit = 0; currBit < 32; currBit++) { // Serial.print(" "); // Get currBit bit uint16_t bit = ( (KAKU.data >> (31 - currBit)) & 1); Serial.print(bit); } Serial.println(); Serial.println(); // Printing message information (yes yes, I need to read up on the Serial.print functions...) Serial.print("House (address): "); // House address is the first 26 bits uint32_t house = (KAKU.data >> 6); Serial.println(house); Serial.print("Group : "); uint16_t group = (KAKU.data >> 5) & 1; Serial.println(group); Serial.print("Action : "); uint16_t action = (KAKU.data >> 4) & 1; Serial.println(action); Serial.print("Code : "); uint16_t code = KAKU.data & 15; Serial.println(code); Serial.println(); // Reverse the action in 2 seconds // Put us in send mode, so we won't be bothered in the meantime radio.mode(OUTPUT); // Wait vor 2 seconds delay(2000); // Now reverse the command, and send it Serial.println("Reversing your command, whohahahaaaa!"); Serial.println(); uint8_t newAction = (action + 1) & 1; // We NEED to send it twice, or it won't work! sendKAKUMessage(house, group, newAction, code); sendKAKUMessage(house, group, newAction, code); // Ready to receive a new message! KAKU.state = UNKNOWN; // Put us in "listen" mode again... radio.mode2(INPUT); } }