0

I'm very new to Arduino, and I'm trying to read data from a LaCrosse TX23 V02(X) anemometer. It's supposed to behave like this https://www.john.geek.nz/2012/08/la-crosse-tx23u-anemometer-communication-protocol/, but it's not...

I don't have an oscilloscope, so I made this code, using micros() to try to trace the output from the TX23:

void loop() {
 collectData();
}
int collectData(void){
 if (bitLength < 0){
 bitLength = 1200;// length of bit is equal to approx 1230 micro seconds;
 }
 // init sequence
 pinMode(dataPin, OUTPUT);
 Serial.print("Send LOW\n");
 digitalWrite(dataPin, LOW );
 Serial.print("Send HIGH\n");
 digitalWrite(dataPin, HIGH);
 delay(100);
 Serial.print("Send LOW\n");
 digitalWrite(dataPin, LOW );
 delay(500);
 Serial.print("Begin input mode\n");
 pinMode(dataPin, INPUT);
 int state = 0;
 int oldstate = 0;
 unsigned long oldmicros = micros();
 for (int i = 1; i<5250 ; i++) {
 state = (digitalRead(dataPin) == LOW)? 0:1;
 if (i%10 != 0) {
 Serial.print("-");
 } else {
 Serial.print("+");
 }
 delayMicroseconds(10);
 if (state != oldstate) {
 oldstate = state;
 i = 1;
 Serial.print(oldstate);
 Serial.print(" @");
 Serial.print(micros() - oldmicros);
 oldmicros = micros();
 Serial.print("\n");
 } 
 }
 Serial.print(state);
 Serial.println(micros()-oldmicros);
}

Does it have a chance to provide a quite accurate monitoring of the TX23 output? (eg is using micros() or delayMicroseconds(10) accurate ?)

asked Aug 20, 2015 at 13:30

3 Answers 3

1

You should really make use of pulseIn() to work out the bit rate of the signal. The first HIGH pulse in the header is there for timing so you know how often to poll the input.

Step one, after sending your outgoing initiation pulse, should be to wait for that first pulse (you might need a slight delay first before it starts transmitting - wait for the line to be low first):

while (digitalRead(dataPin) == HIGH); // Wait for TX start
uint32_t pulseWidth = pulseIn(dataPin, HIGH); // Get first pulse

That gives you the width of the first pulse in microseconds.

Now you can read 45 times at that period. If you first delay by half that amount before your first sample you will be roughly in the middle of the bit so you can cope with a slight amount of drift.

// Wait for the middle of the first bit
delayMicroseconds(pulseWidth / 2);
uint32_t highBlock = 0;
uint32_t lowBlock = 0;
for (int i = 0; i < 45; i++) {
 // Shift the high 32-bits left one bit
 highBlock <<= 1;
 // Place in the highest bit of the low block as the lowest
 // bit of the high block
 highBlock |= (lowBlock & 0x80000000UL ? 1 : 0); 
 // Shift the low 32-bits left one bit
 lowBlock <<= 1;
 // Add in the value of the bit from the data pin
 lowBlock |= digitalRead(dataPin);
 // Wait for the next bit
 delayMicroseconds(pulseWidth);
}

You should now have two 32-bit words which, when put together, form the entire datagram. You can now use bitwise operators to pull out the parts you are interested in.

A better, though more complex, method of doing it would be to employ a timer with an overflow period equal to the pulse width. That will afford you much more accurate timing than a simple loop with delays.

answered Aug 20, 2015 at 13:49
2
  • Thanks a lot for the input. It does not exactly answer my initial question, but it certainly helps me solve my problem reading the TX23 output ! It works, but I don't understand the first 'while (digitalRead(dataPin) == HIGH);' and the data I get with pulseIn() are the first two bits of the header, not an initial pulse... Commented Aug 20, 2015 at 16:29
  • Marking this as right answer as it helped me resolve my real problem, but other two contributions were really helpful as well. Thanks to jwpat7 and Nick Grammon. Commented Aug 25, 2015 at 9:49
1

Majenko showed a method for digitally recording the waveform, after measuring pulseWidth at the outset, and mentioned use of a timer for delays. I'll address the part of your question about using micros() while tracing TX23 output: "Does it have a chance to provide a quite accurate monitoring of the TX23 output?"

Some of the serial output that your method produces as it goes along will affect evenness of timing. For example, the Serial.print() of + after every tenth reading introduces a small timing irregularity (the two branches of the if may differ a few cycles in execution time).

More seriously, your double-read of micros() (after you print oldstate, you print micros() - oldmicros and then read micros() again) introduces a few cycles of timing error. It probably amounts to a few microseconds total over the course of recording the pulse train. Instead of reading micros() twice, say something like newmicros = micros() directly after detecting a state change, before doing any serial printing, then use newmicros in place of other micros() calls. Having serial prints before oldmicros = micros(); causes an offset error, which will be microseconds or milliseconds depending on the data rate you set in setup(). It mostly affects the reported length only of the first pulse.

What I would do for debug monitoring of the pulse train is along the following lines:

enum { timelen=44, maxpause=50000 };
unsigned long int times[timelen];
int collectData() {
 byte ntimes = 0, state=LOW;
 unsigned long int prevtime;
 pinMode(dataPin, OUTPUT);
 digitalWrite(dataPin, LOW ); // Pull DTR low to trigger conversion
 delay(500);
 pinMode(dataPin, INPUT_PULLUP); // Release DTR and set pullup
 delay(1); // Let TX23U pull DTR low
 prevtime = micros();
 while (micros() - prevtime < maxpause && ntimes < timelen) {
 if (digitalRead(dataPin) != state) {
 times[ntimes++] = prevtime = micros();
 state = 1-state;
 }
 }
 return ntimes;
}

This code is intended to record times at which state changes occur. After loop() calls collectData(), it can call a reportData() routine that will print out pulse lengths by subtracting adjacent entries of times[]. Even-indexed array entries are times of rising edges in the pulse train; odd-indexed entries are times of falling edges. Because the if (digitalRead(dataPin) != state) {...} statement isn't executed atomically, the data pin can change after being tested but before being recorded a couple of lines later. Using state = 1-state; instead of state = digitalRead(dataPin); guarantees that even indexes are for rising edges, but does not guarantee that adjacent entries correspond to the same pulse edge. That is, a noisy signal can produce misleading results.

answered Aug 20, 2015 at 17:10
0
1

I'm going to present a third way of working on this. :)

First, since I don't have a TX23 I wrote a TX23-simulator:

const byte TESTPIN = 2;
const byte test [6] = { 
 0b00000011,
 0b01111011,
 0b01010100,
 0b00010100,
 0b01001010,
 0b10111111 };
void setup ()
 {
 pinMode (TESTPIN, INPUT);
 delay (100);
 } // end of setup
void loop ()
 {
 // wait for host to pull pin low
 while (digitalRead (TESTPIN) == HIGH)
 { }
 // wait for host to release it
 while (digitalRead (TESTPIN) == LOW)
 { }
 digitalWrite (TESTPIN, HIGH);
 pinMode (TESTPIN, OUTPUT);
 for (byte i = 0; i < 6; i++)
 for (byte j = 0; j < 8; j++)
 {
 delayMicroseconds (1100);
 digitalWrite (TESTPIN, test [i] & bit (7 - j));
 }
 pinMode (TESTPIN, INPUT);
 } // end of loop

Based on your linked page about the TX23 it outputs a series of low/high pulses corresponding to the example on that page, like this:

TX23 simulator

Note that you need a 10 k pull-up resistor for this to work properly.


Now the reading code uses interrupts to detect state changes:

const byte READ_PIN = 2;
const byte NUMBER_OF_CHANGES = 48;
volatile unsigned long bitTime [NUMBER_OF_CHANGES];
volatile byte state [NUMBER_OF_CHANGES];
volatile byte count;
void stateChange ()
 {
 unsigned long now = micros ();
 if (count >= NUMBER_OF_CHANGES)
 return; // array full
 state [count] = digitalRead (READ_PIN);
 bitTime [count++] = now;
 } // end of stateChange
void setup ()
 {
 Serial.begin (115200);
 Serial.println ();
 } // end of setup
void takeReading ()
 {
 count = 0;
 digitalWrite (READ_PIN, LOW); // start reading
 pinMode (READ_PIN, OUTPUT); 
 delay (20); // pulse width
 pinMode (READ_PIN, INPUT); // release line
 EIFR = bit (INTF0); // clear flag for interrupt 0
 attachInterrupt (0, stateChange, CHANGE);
 delay (55); // let readings fill up array
 detachInterrupt (0);
 } // end of takeReading
void loop ()
 {
 takeReading ();
 if (count == 0)
 return; // nothing!
 delay (500);
 for (byte i = 0; i < count; i++)
 {
 Serial.print ("count: ");
 Serial.print ((int) i);
 Serial.print (" = ");
 Serial.print (bitTime [i]);
 Serial.print (", state = ");
 Serial.print (state [i]);
 if (i > 0)
 {
 Serial.print (", width = "); 
 unsigned long width = bitTime [i] - bitTime [i - 1];
 Serial.print (width);
 Serial.print (" (");
 int bits = width / 1000;
 for (byte k = 0; k < bits; k++)
 Serial.print (state [i - 1]);
 Serial.print (")");
 }
 Serial.println ();
 }
 Serial.println ();
 } // end of loop

Results were:

count: 0 = 10952804, state = 1
count: 1 = 10953928, state = 0, width = 1124 (1)
count: 2 = 10960616, state = 1, width = 6688 (000000)
count: 3 = 10962844, state = 0, width = 2228 (11)
count: 4 = 10963960, state = 1, width = 1116 (0)
count: 5 = 10968412, state = 0, width = 4452 (1111)
count: 6 = 10969524, state = 1, width = 1112 (0)
count: 7 = 10971752, state = 0, width = 2228 (11)
count: 8 = 10972876, state = 1, width = 1124 (0)
count: 9 = 10973988, state = 0, width = 1112 (1)
count: 10 = 10975104, state = 1, width = 1116 (0)
count: 11 = 10976216, state = 0, width = 1112 (1)
count: 12 = 10977328, state = 1, width = 1112 (0)
count: 13 = 10978440, state = 0, width = 1112 (1)
count: 14 = 10984012, state = 1, width = 5572 (00000)
count: 15 = 10985124, state = 0, width = 1112 (1)
count: 16 = 10986244, state = 1, width = 1120 (0)
count: 17 = 10987356, state = 0, width = 1112 (1)
count: 18 = 10990700, state = 1, width = 3344 (000)
count: 19 = 10991812, state = 0, width = 1112 (1)
count: 20 = 10994040, state = 1, width = 2228 (00)
count: 21 = 10995156, state = 0, width = 1116 (1)
count: 22 = 10996268, state = 1, width = 1112 (0)
count: 23 = 10997380, state = 0, width = 1112 (1)
count: 24 = 10998500, state = 1, width = 1120 (0)
count: 25 = 10999616, state = 0, width = 1116 (1)
count: 26 = 11000728, state = 1, width = 1112 (0)

This seems to agree with the test data.

answered Aug 20, 2015 at 22:30
1
  • Thanks for this, it really helps me progress and opens other possibilities to me. Commented Aug 25, 2015 at 9:47

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.