#include #include "hcs200.h" /** Pulse width lengths used by the transmitter. A pulse width is the time * between a transition from 0 to 1 or from 1 to 0 of the input pin. */ enum RbType { RB_SHORT = 0, // pulse_width RB_LONG, // 2 * pulse_width RB_ERROR }; /** Determine if 'pulse' is a short pulse, RB_SHORT or a long one, RB_LONG. * Normally, a short pulse is the same as the 'tx_clock' and a long pulse is * twice as long. However, timing problems with servicing interrupts means * that we need to add some fudge factors. */ int HCS200::classify(unsigned pulse) { int d = pulse - tx_clock; if (d < -100) return RB_ERROR; else if (d < 100) return RB_SHORT; else { d -= tx_clock; if (d < -100) return RB_ERROR; else if (d < 100) return RB_LONG; else return RB_ERROR; } } #define IN_RANGE(x, min, max) (x >= min && x <= max) void HCS200::on_isr(uint8_t value) { unsigned long timestamp = micros(); unsigned long pulse_width = timestamp - last_timestamp; int d; switch (rx_state) { case RS_PREAMBLE_START: // value == 0 preamble_count = 1; rx_state = RS_PREAMBLE_LOW; tx_clock = pulse_width; break; case RS_PREAMBLE_LOW: // value == 1 preamble_count++; d = pulse_width - tx_clock; rx_state = (IN_RANGE(d, -100, 100)) ? RS_PREAMBLE_HIGH : RS_NOSYNC; break; case RS_PREAMBLE_HIGH: // value == 0 preamble_count++; d = pulse_width - tx_clock; if (IN_RANGE(d, -100, 100)) rx_state = (preamble_count == 23) ? RS_PREAMBLE_HEADER : RS_PREAMBLE_LOW; else rx_state = RS_NOSYNC; break; case RS_PREAMBLE_HEADER: // header should be low for 10*tx_clock d = pulse_width - 10 * tx_clock; if (value == 1 && IN_RANGE(d, -100, 100)) { rx_state = RS_DATA; rx_bit_count = 0; memset(rx_buf, 0, sizeof(rx_buf)); } else rx_state = RS_NOSYNC; break; case RS_DATA: if (value == 1) { int first = classify(last_pulse_width); int second = classify(pulse_width); // received a 1 bit if (first == RB_SHORT && second == RB_LONG) { int idx = rx_bit_count / 32; rx_buf[idx] >>= 1; rx_buf[idx] |= 0x80000000; rx_bit_count++; } // received a 0 bit else if (first == RB_LONG && second == RB_SHORT) { int idx = rx_bit_count / 32; rx_buf[idx] >>= 1; rx_bit_count++; } else rx_state = RS_NOSYNC; // we ignore the last bit as it's always "1" // instead we use the raising edge as trigger to stop if (rx_bit_count == MAX_BITS - 1) rx_state = RS_COMPLETED; } break; } // check outside of the state machine // this is important as otherwise otherwise we always miss the start if (rx_state == RS_NOSYNC && value == 1) rx_state = RS_PREAMBLE_START; last_timestamp = timestamp; last_pulse_width = pulse_width; } void HCS200::reset() { rx_state = RS_NOSYNC; } bool HCS200::decode(HCS200_Keycode &out) { if (rx_state != RS_COMPLETED) return false; out.encrypted = rx_buf[0]; out.serial = rx_buf[1] & 0x0FFFFFFF; out.buttons = (rx_buf[1] >> 28) & 0xF; out.lowbat = rx_buf[2] & 0x80000000; return true; } void HCS200::print_state(Print &stream) { stream.print("rx_state="); stream.print(rx_state, DEC); stream.print(", rx_bit_count="); Serial.print(rx_bit_count, DEC); stream.print(", tx_clock="); stream.print(tx_clock, DEC); stream.print(", preamble_count="); stream.println(preamble_count, DEC); } void HCS200_Keycode::print(Print &stream) { stream.print("Keyfob# "); stream.print(serial, HEX); stream.print(", buttons:"); if (buttons & BM_S0) stream.print(" 1"); if (buttons & BM_S1) stream.print(" 2"); if (buttons & BM_S2) stream.print(" 3"); if (buttons & BM_S3) stream.print(" 4"); stream.print(", lowbat="); stream.print(lowbat, DEC); stream.print(", code="); stream.print(encrypted, HEX); stream.print("\n"); } // pulse width according to chip datasheet #define PULSE_WIDTH 400 // pulse width according to rtl_433 implementation // run width: rtl_433 -R 131 //#define PULSE_WIDTH 370 inline void HCS200_Keycode::send(bool value, std::function setOutput) { if (!value) { setOutput(HIGH); delayMicroseconds(PULSE_WIDTH); delayMicroseconds(PULSE_WIDTH); setOutput(LOW); delayMicroseconds(PULSE_WIDTH); } else { setOutput(HIGH); delayMicroseconds(PULSE_WIDTH); setOutput(LOW); delayMicroseconds(PULSE_WIDTH); delayMicroseconds(PULSE_WIDTH); } } void HCS200_Keycode::send(std::function setOutput) { uint32_t val; // preamble for(unsigned short i = 0; i < 11; i++) { setOutput(HIGH); delayMicroseconds(PULSE_WIDTH); setOutput(LOW); delayMicroseconds(PULSE_WIDTH); } setOutput(HIGH); delayMicroseconds(PULSE_WIDTH); // header setOutput(LOW); delayMicroseconds(PULSE_WIDTH * 10); // encrypted val = this->encrypted; for(unsigned short i = 0; i < 32; i++) { send(val & 0x01, setOutput); val >>= 1; } // serial val = this->serial; for(unsigned short i = 0; i < 28; i++) { send(val & 0x01, setOutput); val >>= 1; } // buttons val = this->buttons; for(unsigned short i = 0; i < 4; i++) { send(val & 0x01, setOutput); val >>= 1; } // lowbat send(this->lowbat, setOutput); // RPT send(1, setOutput); // guard time setOutput(LOW); delayMicroseconds(PULSE_WIDTH * 39); }