1
\$\begingroup\$

Trying to decode serial data (encoded using proprietary method) from a RF module.

Having switched back and forth between the fast-sampling (4x the fastest freq) and of late an interrupt based method, I've reached a dead-end and not sure what all I am doing wrong, because apparently people have been successful in decoding ISM-band RF data (Manchester, NRZ, VirtualWire and HT12E/PT2260-62 encoded), in software.

In my case, the RF modules's AGC (which I've no idea how to turn off, since there's no data-sheet, and the co. doesn't respond to emails... a cheap east-asian make) picks up noise and has the fastest level-transitions in durations ~40us, although valid (encoded data) transitions not lesser than 400us. My ISR has an elementary task of measuring the duration of last state to reject the spurious noise, and save only those transitions that could possibly be part of the encoded data. So far, I have not used a circular-queue (which is what I plan as the next step), but before doing that, I did a small experiment. In the ISR I check the state of a flag to see if it is true, and if true I do Serial.print("x"), else I set it true. In the loop(), I check if flag is true, then do Serial.print("-") and set it false. The flag is declared volatile. I find that I get very long continous streams of "x"s and very rare "-". My ISR is triggering on every transition of pin-2 (i.e. external interrupt#0).

Now my question is, would adding a queue really solve my problem, because apparently the ISR is running far too frequently and thereby starving my loop() of cycles. Counting the distance between the "x"s and "-", I was hoping to find the ideal queue length, but I can't figure out a pattern. Is there something inherently wrong in my approach ?

An outline of the source code is here.

\$\endgroup\$
  • \$\begingroup\$ Both answers are quite relevant to what I am doing. Chris' answer helped me remove Serial.print()s & debug more effectively using pin-toggling & watching it on my logic-analyzer. It also confirmed my approach towards using circular-buffer as correct. Supercat's answer, I think could be the big leap forward, but I am not sure how to implement it, as the schematic isn't clear in my mind. Reducing noise electronically, allows me to handle interrupts at a lot more practical rate on the Arduino. In the end, I had to choose 1 answer, so I picked one which was provided earlier. Wish I could pick 2. \$\endgroup\$ – icarus74 Dec 20 '11 at 14:47
1
\$\begingroup\$

You probably want to take the transmission time (and software UART management methods) of the Serial.print() routine into account.

If you have access to a scope, toggling I/O pins would be a lot faster. You could toggle one in the ISR and the other in the loop.

Or you could increment a counter in the ISR and sample/clear it in the loop (inside an interrupt disable/re-enable) then print it (after re-enabling interrupts). More sophisticated would be to only print the number if it exceeds the previous maximum.

However, such tests are fundamentally flawed unless the testing code constitutes a small fraction of the loop operations - just figuring out how many interrupts can occur during a max-check/print routine is meaningless in terms of the ultimate operation which would presumably be doing something else in the loop.

Generally, you will want to use circular buffering if you are acquiring data via an interrupt which must be aggregated into a larger assembly (bits into bytes, or bytes into buffers) and then acted upon in a way that is at all time consuming. It shouldn't be too hard to change your buffer size later to optimize memory consumption, especially if you keep them power-of-two sized. You may want to build in overflow alarms, and try running the finished system with an artificially reduced buffer size.

Do pay very careful attention to ensuring that the ISR can't jump in the middle of where the loop trying to update the buffer's tracking variables - if you do a read-modify-write, you'll need to protect it.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks @Chris. I do not have a scope, but I do have a logic-analyzer (OpenBench). It's limited sample memory is what might be a constraint, but I guess it might still give some valuable clues, right ? The pin toggling thing is an excellent idea. Could you elaborate the para "such tests are fundamentally flawed...", not sure I got it. In my case, I am indeed assembling the signal patterns to identify a bit, and then to create a codeword (/packet) from it, so the circular buffer seems apt. \$\endgroup\$ – icarus74 Dec 19 '11 at 16:34
  • \$\begingroup\$ A test isn't valid unless the test conditions approximate (or limit) the eventual usage conditions. If the code in the loop right now is primarily the testing code, that won't give you an accurate impression of how bad things could get when there's application code in the loop as well. \$\endgroup\$ – Chris Stratton Dec 19 '11 at 17:21
  • \$\begingroup\$ @Christ thanks for clarifying. Here is what my logic-analyzer shows (annotated), and notice the glitches I found (ch#1), which could potentially break my logic since the tiny HIGH signal doesn't correspond to a change in signal-state of the the RF receiver output (ch#0), and how loop() is being called quite frequently. \$\endgroup\$ – icarus74 Dec 19 '11 at 19:26
  • \$\begingroup\$ and here is the code outline. \$\endgroup\$ – icarus74 Dec 19 '11 at 20:23
1
\$\begingroup\$

Many IR and RF modules are designed to yield optimal results with a signal having some particular duty cycle, and will set their switching threshold to roughly average_received_level/(desired_duty_cycle*2). For example, if a device is designed to receive a signal with a 1/3 duty cycle, the average signal level received would be 1/3 of the transmit level, and so the formula would set a threshold of 1/2 the transmit level (leaving a noise margin of +/- half the transmit level). If one were to send such a device a signal with a 1/2 duty cycle, it would set the threshold to 2/3 of the transmit level, leaving only 1/3 of the transmit level as noise margin. If a receiver is designed for manchester-encoded data (50% duty cycle), the threshold will simply be the average receive level. If you're getting more noise on one side or the other, it might be worthwhile to find out whether your receiver is designed for the duty cycle that's being sent.

I also would suggest taking the output from the receiver and feeding it through an RC filter. This may yield glitch rejection much better than could be achieved with software alone. If, for example, during the interval that a zero is being transmitted, the signal from the receiver is low 99% of the time, but it happens to have glitches at the moments that the CPU samples it, the CPU may falsely think it's high all during that time, but the RC filter circuit would correctly output a continuous low. If you use an RC filter circuit, it may be good to use a biasing resistor to ensure that the delay on rising and falling edges will be roughly equal. If the processor's input switches at 1/3 rail, for example, add a biasing resistor to Vss so that a "high" from the radio will appear as 2/3 VDD.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks @supercat. Should've added in my query that I have fairly limited electronics knowledge, as I am a CS major, so I'd have to admit that I am having some difficulty understanding the first paragraph, especially the relationship between duty_cycle, transmit_level and noise. However, I'd try to do some self-study to understand it. As for the 2nd paragraph, I think I do understand the rationale behind using an RC filter to "smooth out" the signal, filtering noise spikes, although tempted to ask if similar correction happens while 1 is being transmitted as well ? \$\endgroup\$ – icarus74 Dec 19 '11 at 17:02
  • \$\begingroup\$ would be great if you could point me to some schematic, including the biasing resistor. \$\endgroup\$ – icarus74 Dec 19 '11 at 17:06
  • \$\begingroup\$ @icarus74: The vast majority of the noise will be "added" to the signal as it travels through the air. It's likely that the transmit module has an internal filtering of the transmit data, though for a rather different reason: keying a (e.g.) 1MHz transmitter on and off "smoothly" at a rate of 1khz will cause it to out signals in the range of 999,000Hz to 1,001,000Hz (i.e. the carrier frequency +/- the modulation rate). Switching the signal on and off abruptly would also output unwanted signals that have 1/3 the carrier amplitude and a frequency +/- 3x the switching rate, ... \$\endgroup\$ – supercat Dec 19 '11 at 17:07
  • \$\begingroup\$ ...as well as one at 1/5 the amplitude and +/- 5x the switching rate, 1/7 the amplitude and +/- 7x the switching rate, etc. If the fastest signals you're interested in are 400us, I would suggest that you wire a 1k resistor between the transmitter and the processor, and a capacitor of 0.22uF (as a first approximation, choose the cap and resistor such that the capacitance in farads times the resistance in ohms yields roughly half the desired time in seconds). \$\endgroup\$ – supercat Dec 19 '11 at 17:11
  • \$\begingroup\$ thanks for explaining this further. In your last comment, is it right to infer that by 'transmitter' you mean the circuit-block that is generating the signals being fed directly into the uC ? In my case, I'd then assume it to mean the "RF RX module", right ? \$\endgroup\$ – icarus74 Dec 19 '11 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.