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 ?)
3 Answers 3
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.
-
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...alci– alci2015年08月20日 16:29:38 +00:00Commented 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.alci– alci2015年08月25日 09:49:53 +00:00Commented Aug 25, 2015 at 9:49
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.
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:
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.
-
Thanks for this, it really helps me progress and opens other possibilities to me.alci– alci2015年08月25日 09:47:38 +00:00Commented Aug 25, 2015 at 9:47