Trying to interface with an altimeter

A newbies lament:

I'm a "paper engineer", and find myself having to make something real happen at last, and fast.

I'm part of a class at the university of washington that's building a sounding rocket for a competition. We are using the Arduino board to read information from a perfect flite MAWD altimeter (spits out ascii) to an arduino for some math and PWM output (we will be driving a stepper motor).

My initial goal is go get Arduino to listen the MAWD. As a preliminary step, I am just trying to get Arduino recognize keyboard input asynchronously. I thought I could use ladyada's "goodnight moon" example for newsoftserial as a start.

Unfortunately, I find that while in the IDE (in monitor mode), I can hear her "goodnight moon" input, I cannot hear the newsoftserial output (i.e. the "hello world"), despite redirecting the output pins to 0, and 1 (i.e. the same as the built in serial).

I'm sure part of this is simple newbie fumbling. I did a couple of tutorials on blinking/ramping leds, and now am crashing into this.

Any constructive suggestions and/or directions by this community would be most appreciated.

Many thanks,

Bob

Sorry, don't have specific help, but here are a couple of opinions:

It would probably be easier to interface the arduino directly to a pressure sensor (IIRC they use a Freescale MPXAZ6115, at least on the entry level model) via the ADC than to extract altitude from the perfect flite's serial port.

I would connect the perfect flite to the Arduino's UART (pins 0 and 1) and use software serial to print out status data. This is a lot more work to interface hardware-wise, but I think it would be more reliable.

-j

kg4wsv

Many thanks for your reply. Sorry if I am confused. I'm probably going to give you a headache next.

  1. Are you suggesting the software serial new or old?

  2. And why is that going to be easier/more reliable than using the hardware serial interface?

I did manage to get the built in hardware serial to read in some key board inputs, but it appears I'm forced to work with arrays of characters. Barbaric!

I think I was initially attracted to newsoftserial because I thought I could get character strings because of the 64 byte buffer offered.

Maybe this won't be so bad (working with an array), converting to a number for calculation would be relatively straightforward. Is there a reliability problem? I am assuming that when I use the hardware serial.read command I am NOT polling, but reading from a buffer that is already filled with received data. All I have to do is read it out.

Am I correct?

On your ideal suggestion, well yes of course. If we had more time, I would certainly prefer to interface with the pressure sensor directly as well, and include an accelerometer. All of which would have to be >>calibrated<<, which would mean even more time.

However, the launch is the 25th of June, and I have to attempt what I think I have a shot of accomplishing in the time available. Thus, leaving the pressure sensor interface to others. A kluge, yes, but one born out of necessity I think. And yes, as a newbie, I think this is all I can get done in the time I have.

Many thanks,

Bob

Are you suggesting the software serial new or old?

I haven't used either extensively, but I'd go with Mikal's NewSoftwareSerial.

And why is that going to be easier/more reliable than using the hardware serial interface?

Because there's a hardware UART to handle IO, so you won't lose characters when you're busy calculating.

it appears I'm forced to work with arrays of characters. Barbaric!

Welcome to microcontrollers. We don't have the resources available to a microcomputer running a modern OS.

I was initially attracted to newsoftserial because I thought I could get character strings because of the 64 byte buffer offered.

Nope, just characters. I don't recall a function to return an array of characters (which is still not a C string), but I could have overlooked something.

converting to a number for calculation would be relatively straightforward. Is there a reliability problem?

It is, and there isn't. There are a number of posts on the forum about converting received characters to integers.

I am assuming that when I use the hardware serial.read command I am NOT polling, but reading from a buffer that is already filled with received data.

Correct. The UART is a dedicated chunk of hardware on the ATmega that does it for you. This is my concern with using software serial - that you would miss characters. I assume the perfect flite doesn't have any sort of checksum to help detect errors?

If we had more time, I would certainly prefer to interface with the pressure sensor directly as well, and include an accelerometer. All of which would have to be >>calibrated<<

Nope, all these come factory calibrated; error ranges are in the data sheet. the accelerometer is easy, just read it from the ADC and convert V to G (or read it direct from a device with an integrated ADC). Converting pressure to altitude in feet or meters would be the biggest challenge (although certainly not a show stopper) because of the floating point exponentiation.

-j

kg4wsv,

Because I am stubborn, or perhaps dense, mostly likely both, I am trying the straight hardware serial.

I am getting some responses from the Arduino, but not quite what I'm looking for. The altimeter transmitts a five digit number, plus, presumably, a carriage return. Update rate is 20 Hz. My biggest observed problems are that:

  1. Premature carriage returns. Starts out okay, but doesn't do it
    correctly after awhile. Not that I directly care about how it prints per se, but it looks like the carriage returns are being stuffed in the middle of the command array, and I DO care about that.

  2. Missed transmissions? The data from MAWD is much cleaner that
    this, and I think I'm loosing data packets. As I mentioned, the MAWD altimeter update rate is 20 Hz. Is dropped data packets likely to be the problem with straight serial? I thought this was interrupt driven. Sorry if I'm not picking something up that I should be.

Here is the code I'm running on the Arduino. I've been playing around with the size of the command array.


int command[7]; //this is the anticipated size of the MAWD word, 5
characters plus ^M carriage return + 1 extra

void setup()
{

Serial.begin(9600);
delay(1000);

Serial.println("Hello Terminal");

}

void loop ()
{
if (Serial.available() == 6 ) // wait for 6 characters
{
Serial.print("The translated altitude from the Arduino is ");
for(int i=0; i < 7; i++)
{
command = Serial.read();
_ Serial.print(command*,BYTE);_
_ command = 48; //reinitializing the array
}
Serial.flush();
Serial.println();
}
}

Now for the output, we get:

Hello Terminal
The translated altitude from the Arduino is 00234
The translated altitude from the Arduino is 00309
The translated altitude from the Arduino is 00346
The translated altitude from the Arduino is
00365
The translated altitude from the Arduino is
00346
The translated altitude from the Arduino is
00337
The translated altitude from the Arduino is 46
003
The translated altitude from the Arduino is 0328
0
The translated altitude from the Arduino is 00328
The translated altitude from the Arduino is 00318
The translated altitude from the Arduino is 00318
The translated altitude from the Arduino is
00346
The translated altitude from the Arduino is
00337
The translated altitude from the Arduino is
00355
The translated altitude from the Arduino is 6
0033
*****

If you have any suggestions about how to proceed, or the forum that would be appropriate. The newsoftserial forum seemed to be all about getting newsoftserial to properly compile, not for newbie questions.
Anyway, thanks for looking at this.
Bob_

There is no reason to call Serial.flush(), and may be part of your problem. Unlike giant operating systems that multitask, the output is not buffered, it is synchronous. (Serial.print() is done and the bytes are already fully sent when it returns.) The input however is buffered, and Serial.flush() is throwing away anything recently received but not yet processed by your sketch.

Similarly, you should check for (Serial.available() >= 6), because it might have sent you more than one full packet. If you wait for just exactly six characters to be ready, you're going to risk getting out of sync. Speaking of getting out of sync, for(int i=0; i < 7; i++) iterates seven times, not six, and you might be stealing the first character of the next packet as it becomes available.

Halley,

Hmm....

On a lark, after I posted my most recented message, I commented out the opening text of "The translated altitude..." and the last line feed.

The result was a properly formatted ascii stream, with the proper carriage return after five characters. However, I still think I'm loosing data. So I'll try your suggestion later today or tomorrow morning.

For the record, when I had the "The translated altitude..." statement, if I did not include the flush statement, the output would stop after one line. Adding the flush, and Arduino would continue to print out.

So, a question nags at me. In my code, am I working with an interrupt, or am I polling? I don't want to be polling, as the community here has made abundantly clear.

Anyway, I'll try your suggestion removing the flush in a bit (I had to disconnect my fixture for the day).

Thanks,

Bob

The Serial class is not polling-- it is driven by hardware-triggered handlers and filling the rx buffer. You are polling the state of Serial.available(), but that's an extremely lightweight operation. Since the hardware handlers manage the rx buffer, you're not going to be losing any bytes.

Not sure who in your "community" is making blanket statements like "all polling is bad."

(1) Polling is bad if you can't achieve all your other software requirements due to the polling overhead. A microcontroller program is generally focused on one task, and sometimes that task is "wait for the input to be ready."

(2) Polling is bad if you can't afford the electricity to support running the microcontroller itself. Unless you are trying to make a LiPo battery last for a week, or you are trying to go into a low-power sleep state while the red planet's dust builds up on the solar panels, you should just ignore this concern.

I would suggest that you read a line, not 6 characters. It's pretty obvious that one or more characters got dropped, as evidenced by newlines in the middle of the string. At least you'd be off in the weeds for no more than one reading.

I suspect the dropped characters are a result of spending too much time writing. Your output string is too long to write at 20Hz (do the math, it takes 1.02 seconds to print it 20 times).

As Halley points out, there is nothing wrong with polling. You just have to be aware of the limitations of your system and the requirements of your program.

-j

Halley, kg4wsv,

Thank you for the reeducation (I did this stuff, a LONG time ago, like in the late 80's)....

Scrambling around on the .cc site produced this command:

readStringUntil()

I don't know anything about this command yet. If it's what I want, where can I learn more about it? I don't see a library page for it.

Thanks for the progress to date.

Bob

kg4wsv,

So I see from the page: www.arduino.cc/playground/Interfacing/Processing that I need to build up my library skills to use the readStringUntil() command. Is this what you had in mind with your previous post? If not, please don't let me wander in the desert of buffoonery for too long....

Thanks,

Bob

The library you're pointing to is for a PC application called Processing, and a facility for Arduino called Firmata. It's not relevant to your issue at hand. (You could replace the approach here, and use Firmata instead, but I don't think you need to do that.)

Your code is close to what is required. All the suggestions above are useful changes to your code. Instead of reading six characters, kg4wsv is suggesting reading until you get a newline (a '\n' byte). Either way will work, if you stay in sync. His approach can get back in sync if it should somehow get out of sync.

(You could replace the approach here, and use Firmata instead, but I don't think you need to do that.)

Won't work, as I'm guessing he intends to fly the arduino on the rocket with the altimeter, and getting the serial cable to play out without tangling may be tough :slight_smile: (although they did it with TOW missiles).

IIRC, Firmata runs on a computer and talks to the arduino via rs232.

Here's a thread with some code I wrote to read ASCII and make an int. There are lots of other methods, many of which have been posted on the forum. There may be code in the playground as well.

-j

kg4wsv, Halley,

I won't get to the board until tomorrow afternoon. But will try to tweak the code as you suggest then.

For your information and amusement, very much yes, the arduino will fly with the altimeter. I plan on using the onboard clock to calculate our vertical speed. Together with the altitude we are calculating our total vehicle energy (i.e. 0.5 m v ^2 + m g h) .

The competition objectives are to hit ten thousand feet agl, with as much precision as possible. That means blasting past the altitude with excess power is not going to cut it. So we have an energy management system, post burn out, and have a tentative control algorithm worked up. Post apogee the energy management system is stowed for the "ride home."

So that gives you an outline of the tasks ahead for our team. Getting Arduino to reliably read data from the altimeter is of course our critical first step.

Our system is hoped to be the core of a future flight computer (working with the pressure sensor, etc., directly). This is our first time at the competition, and we are cautious.

I'll work on the board tomorrow and let you guys know. Thanks for the help and keeping me sane.

Bob

p.s. NObody on my team has done anything for real on microcontrollers. I couldn't do this (with the time available) without you guys. Thanks alot.

kg4wsv, Halley,

So now I've spend the last 4 hours working on this witch, and I'm LOW on perspective. I was following the template offered by kg4wsv.

In response to typing 123 on the IDE input while monitoring, I get:

Hello Terminal

The Number: "1" has been successfully received

The Number: "23" has been successfully received

which looks like a premature exit on the serial communications loop. What am I doing wrong here? Now I am vexed. I think the progress o meter is running backward.

Any help, including a drink (joke!), would be appreciated.

Thanks,

Bob

static byte c;
static int i;
boolean cr_test;

void setup()
{
Serial.begin(9600);
delay(1000);

Serial.println("Hello Terminal");
}

void loop ()
{
c = 0;
i = 0;
while (Serial.available()) //provided we have not encountered a carriage return
{
//if (Serial.available()) //Is there input?
// {
c = Serial.read();
//Serial.print(c,BYTE);
if (c == '\n')
{
Serial.print('\r\n');
i = 0;
Serial.println("exiting to due carriage return");
break;
}
else
{
if ((c>=48)&&(c<=57)) //Is the character an ascii digit?
{
i = i*10 + c - 48; // convert the ascii character into the appropriate digit
}
else
{
Serial.print("\r\nError: "");
Serial.print(c, BYTE);
Serial.print("" is not a digit\r\n");
i = 0;
break;
}
}
// }
}

if (i!=0)
{
Serial.print("\r\nThe Number: "");
Serial.print(i,DEC); // print the finished decimal integer to the screen
Serial.print("" has been successfully received\r\n");
}

}

You're processing whatever is available on one loop() and leaving it at that. Don't leave loop() until you see a newline.

I've not tested this:

void setup()
{
    Serial.begin(9600);
    Serial.println("Altimeter OK.");
}

long Serial_atol()
{
    char ch = 0;
    long value = 0L;
    while (1) // forever
    {
        if (!Serial.available())
            continue;
        ch = Serial.read();
        if ((ch < '0') || (ch > '9')) // not an ASCII digit?
            break;
        value = value*10 + (ch - '0');
    }
    return value;
}

void loop()
{
     long altitude = Serial_atol();
     Serial.print("Altitude: ");
     Serial.println(altitude);
}

If you gave this the following input, "0 1 2 10 23A 45\n2000 654321/", you should expect the following output:

Altitude OK.
Altitude: 0
Altitude: 1
Altitude: 2
Altitude: 10
Altitude: 23
Altitude: 0
Altitude: 45
Altitude: 2000
Altitude: 0
Altitude: 0
Altitude: 0
Altitude: 654321

For brevity I've left out handling of negative altitudes.

(Actually, if Serial class supported an ungetch() or peek() I could see implementing a bunch of these helper-parsers into the library. As it is, the parser has to eat the offending terminator, instead of ignoring it.)

Halley,

Sorry gone so long (life, and research, interfere with the rocket). The code works beautifully (with the real altimeter) with the minor modification (the if then) you see in the outer loop at the bottom of the page. This change was necessary to prevent it from printing the empty altitude information between updates.

The code works well enough for us for now. Now onto the rest of this thing.

Bob


void setup()
{
Serial.begin(9600);
Serial.println("Altimeter OK.");
}

//Many thanks to Arduino forum members Halley and kg4wsv for their help on getting me straightened out on how to structure arduino code.
//This code is a very minor modification of a piece of code generously offered by Halley. Thanks guys.

long Serial_atol()
{
char ch = 0;
long value = 0L;
while (1) // forever
{
if (!Serial.available())
continue;
ch = Serial.read();
if ((ch < '0') || (ch > '9')) // not an ASCII digit?
{
break;
}
else
{
value = value*10 + (ch - '0');
}
}
return value;
}

void loop()
{
long altitude = Serial_atol();
if (altitude!=0)
{
Serial.print("Altitude: ");
Serial.println(altitude);
}
}