// $Id $ #include #include #include #include #include #include //#define LED_PIN 9 // activity LED, comment out to disable #define RECV_PIN 4 //rf receiver pin #ifdef F_CPU #define SYSCLOCK F_CPU // main Arduino clock #else #define SYSCLOCK 16000000 // main Arduino clock #endif #define USECPERTICK 16 // microseconds per clock interrupt tick // On a 16bit timer gives 16*65536/1000=1048ms rollover #define MARK 1 #define SPACE 0 // receiver states #define STATE_IDLE 2 #define STATE_MARK 3 #define STATE_SPACE 4 #define STATE_STOP 5 #define _GAP 10000 // Minimum gap between transmissions (uS) #define GAP_TICKS (_GAP/USECPERTICK) #define RAWBUF 200 // Length of raw duration buffer // information for the interrupt handler typedef struct { uint8_t recvpin; // pin for IR data from detector uint8_t rcvstate; // state machine unsigned int timer; // state timer, counts ticks. unsigned int rawbuf[RAWBUF]; // raw data uint8_t rawlen; // counter of entries in rawbuf } rfparams_t; volatile rfparams_t rfparams; int currentSignal; rfparams_t prevrf; static unsigned long now () { // FIXME 49-day overflow return millis() / 1000; } // initialization void enableRFIn(int recvpin) { memset((void *)&rfparams, 0, sizeof(rfparams)); rfparams.recvpin = recvpin; // initialize state machine variables rfparams.rcvstate = STATE_IDLE; cli(); // setup pulse clock timer interrupt // http://www.avrbeginners.net/architecture/timers/timers.html TCCR1A = 0; TCCR1B = _BV(WGM12) | _BV(CS10); // CTC, No Prescaler OCR1A = SYSCLOCK * USECPERTICK / 1000000; TCNT1 = 0; //Timer2 Overflow Interrupt Enable TIMSK1 = _BV(OCIE1A); sei(); // enable interrupts // set pin modes pinMode(rfparams.recvpin, INPUT); } // TIMER2 interrupt code to collect raw data. // Widths of alternating SPACE, MARK are recorded in rawbuf. // Recorded in ticks of 16 microseconds. // rawlen counts the number of entries recorded so far. // First entry is the SPACE between transmissions. // As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues. // As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts ISR(TIMER1_COMPA_vect) { uint8_t irdata = (uint8_t)digitalRead(rfparams.recvpin); rfparams.timer++; // One more tick if (rfparams.rawlen >= RAWBUF) { // Buffer overflow rfparams.rawlen = 0; //rfparams.rcvstate = STATE_STOP; } switch(rfparams.rcvstate) { case STATE_IDLE: // In the middle of a gap if (irdata == MARK) { if (rfparams.timer < GAP_TICKS) { // Not big enough to be a gap. rfparams.timer = 0; } else { // gap just ended, record duration and start recording transmission rfparams.rawlen = 0; rfparams.rawbuf[rfparams.rawlen++] = rfparams.timer; rfparams.timer = 0; rfparams.rcvstate = STATE_MARK; } } break; case STATE_MARK: // timing MARK if (irdata == SPACE) { // MARK ended, record time rfparams.rawbuf[rfparams.rawlen++] = rfparams.timer; rfparams.timer = 0; rfparams.rcvstate = STATE_SPACE; } break; case STATE_SPACE: // timing SPACE if (irdata == MARK) { // SPACE just ended, record it rfparams.rawbuf[rfparams.rawlen++] = rfparams.timer; rfparams.timer = 0; rfparams.rcvstate = STATE_MARK; } else { // SPACE if (rfparams.timer > GAP_TICKS) { // big SPACE, indicates gap between codes // Mark current code as ready for processing // Switch to STOP // Don't reset timer; keep counting space width rfparams.rawbuf[rfparams.rawlen++] = rfparams.timer; rfparams.rcvstate = STATE_STOP; } } break; case STATE_STOP: // waiting, measuring gap if (irdata == MARK) { // reset gap timer rfparams.timer = 0; } break; } } void resume() { rfparams.rcvstate = STATE_IDLE; rfparams.rawlen = 0; } void dumpRaw(unsigned int rawlen, unsigned int *rawbuf) { Serial.print("RFRX "); //id string for xap-urfrx decoder Serial.print("RFRX "); //id string for xap-urfrx decoder for (int i = 0; i < rawlen; i++) { // yes I know it was unsigned int and I could loose precision. // but a real pulse won't be using numbers > 32767 int val = (*rawbuf)*USECPERTICK; Serial.print(i%2 == 0 ? -val : val, DEC); if(i < rawlen-1) Serial.print(","); rawbuf++; } Serial.println(""); } static void activityLed (byte on) { #ifdef LED_PIN pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, !on); #endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // RF12 configuration setup code typedef struct { byte nodeId; byte group; char msg[RF12_EEPROM_SIZE-4]; word crc; } RF12Config; static RF12Config config; static char cmd; static byte value, stack[RF12_MAXDATA], top, sendLen, dest, quiet=1; static byte testbuf[RF12_MAXDATA], testCounter; static void addCh (char* msg, char c) { byte n = strlen(msg); msg[n] = c; } static void addInt (char* msg, word v) { if (v >= 10) addInt(msg, v / 10); addCh(msg, '0' + v % 10); } static void saveConfig () { // set up a nice config string to be shown on startup memset(config.msg, 0, sizeof config.msg); strcpy(config.msg, " "); byte id = config.nodeId & 0x1F; addCh(config.msg, '@' + id); strcat(config.msg, " i"); addInt(config.msg, id); strcat(config.msg, " g"); addInt(config.msg, config.group); strcat(config.msg, " @ "); static word bands[4] = { 315, 433, 868, 915 }; word band = config.nodeId >> 6; addInt(config.msg, bands[band]); strcat(config.msg, " MHz "); config.crc = ~0; for (byte i = 0; i < sizeof config - 2; ++i) config.crc = _crc16_update(config.crc, ((byte*) &config)[i]); // save to EEPROM for (byte i = 0; i < sizeof config; ++i) { byte b = ((byte*) &config)[i]; eeprom_write_byte(RF12_EEPROM_ADDR + i, b); } if (!rf12_config()) Serial.println("config save failed"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - char helpText1[] PROGMEM = "\n" "Available commands:" "\n" " i - set node ID (standard node ids are 1..26)" "\n" " b - set MHz band (4 = 433, 8 = 868, 9 = 915)" "\n" " g - set network group (RFM12 only allows 212, 0 = any)" "\n" " ..., a - send data packet to node , with ack" "\n" " ..., s - send data packet to node , no ack" "\n" " q - set quiet mode (1 = don't report bad packets)" "\n" ; static void showString (PGM_P s) { for (;;) { char c = pgm_read_byte(s++); if (c == 0) break; if (c == '\n') Serial.print('\r'); Serial.print(c); } } static void showHelp () { showString(helpText1); Serial.println("Current configuration:"); rf12_config(); } static void handleInput (char c) { if ('0' <= c && c <= '9') value = 10 * value + c - '0'; else if (c == ',') { if (top < sizeof stack) stack[top++] = value; value = 0; } else if ('a' <= c && c <='z') { Serial.print("> "); Serial.print((int) value); Serial.println(c); switch (c) { default: showHelp(); break; // case 'i': // set node id // config.nodeId = (config.nodeId & 0xE0) + (value & 0x1F); // saveConfig(); // break; // case 'b': // set band: 4 = 433, 8 = 868, 9 = 915 // value = value == 8 ? RF12_868MHZ : // value == 9 ? RF12_915MHZ : RF12_433MHZ; // config.nodeId = (value << 6) + (config.nodeId & 0x3F); // saveConfig(); // break; // case 'g': // set network group // config.group = value; // saveConfig(); // break; case 'a': // send packet to node ID N, request an ack case 's': // send packet to node ID N, no ack cmd = c; sendLen = top; dest = value; memcpy(testbuf, stack, top); break; case 'q': // turn quiet mode on or off (don't report bad packets) quiet = value; break; } value = top = 0; memset(stack, 0, sizeof stack); } else if (c > ' ') showHelp(); } // Compare two tick values, returning 0 if newval is shorter, // 1 if newval is equal, and 2 if newval is longer // Use a tolerance of 20% int compare(unsigned int oldval, unsigned int newval) { if (newval < oldval * .8) { return 0; } else if (oldval < newval * .8) { return 2; } else { return 1; } } // Compare two signals // 1 Match, 0 otherwise int compareRF() { if(prevrf.rawlen != rfparams.rawlen) return 0; for(int i=0; i 20) // print at most 20 bytes if crc is wrong n = 20; } if (config.group == 0) { Serial.print("G "); Serial.print((int) rf12_grp); } Serial.print(' '); Serial.print((int) rf12_hdr); for (byte i = 0; i < n; ++i) { Serial.print(' '); Serial.print((int) rf12_data[i]); } Serial.println(); if (rf12_crc == 0) { activityLed(1); if (RF12_WANTS_ACK) { Serial.println(" -> ack"); rf12_sendStart(RF12_ACK_REPLY, 0, 0); } activityLed(0); } } if (cmd && rf12_canSend()) { activityLed(1); Serial.print(" -> "); Serial.print((int) sendLen); Serial.println(" b"); byte header = cmd == 'a' ? RF12_HDR_ACK : 0; if (dest) header |= RF12_HDR_DST | dest; rf12_sendStart(header, testbuf, sendLen); cmd = 0; activityLed(0); } if(rfparams.rcvstate == STATE_STOP) { if(currentSignal == 0) { prevrf.rawlen = rfparams.rawlen; for(int i=0;i 3 && compareRF()) { dumpRaw(prevrf.rawlen, prevrf.rawbuf); } } currentSignal = !currentSignal; resume(); } }