Arduino UNO not reading data from Parallax Altimeter.
murrayl
Posts: 9
I am attempting to get the arduino Parallax altimeter working with my Arduino UNO.
I have wired it per the diagram given in the example code.
I have verified a good consistent 5V in on the altimeter power leads. I am powering with a 9V battery.
I have checked the pins, with the Arduino UNO Analog 4 is connected to the SDA pin.
Analog 5 is connected to SCL pin.
I am using the Arduino IDE 1.0.5 with the Arduino UNO R3 SMD
The i2C routine appears to be reading back all zeroes from the altimeter module.
I turned on the debug code in the function AquireAveragedSampleCM().
It is labeled //MAPGPS.
When it prints the pressure/pressure converted/temperature, they all read zero.
This would lead me to the conclusion that either I've done something wrong, or something is not initialized properly.
It looks like the i2caddress comes from the code and appears to be set in the constructor to 0xee>>1 which I guess makes it 77.
I'm new to arduino and am rusty from a few years of not programming. I did embedded programming for many years.
I'm hoping it is some simple initialization.
I am using the 1.0 code downloaded from the Parallax site next to the diagram for the Arduino UNO.
I would be grateful for any help you could give!.
I have wired it per the diagram given in the example code.
I have verified a good consistent 5V in on the altimeter power leads. I am powering with a 9V battery.
I have checked the pins, with the Arduino UNO Analog 4 is connected to the SDA pin.
Analog 5 is connected to SCL pin.
I am using the Arduino IDE 1.0.5 with the Arduino UNO R3 SMD
The i2C routine appears to be reading back all zeroes from the altimeter module.
I turned on the debug code in the function AquireAveragedSampleCM().
It is labeled //MAPGPS.
When it prints the pressure/pressure converted/temperature, they all read zero.
This would lead me to the conclusion that either I've done something wrong, or something is not initialized properly.
It looks like the i2caddress comes from the code and appears to be set in the constructor to 0xee>>1 which I guess makes it 77.
I'm new to arduino and am rusty from a few years of not programming. I did embedded programming for many years.
I'm hoping it is some simple initialization.
I am using the 1.0 code downloaded from the Parallax site next to the diagram for the Arduino UNO.
I would be grateful for any help you could give!.
Comments
I went through the data sheet of the proc and the altimeter module and from what I can see, all the addresses are correct and the values being written look write. The debug code seems to indicate that there is no data coming back from the part. When it tries to read the coeffcients from the part, there are zero bytes in the rxbuffer for the Wire object.
uint16_t ReadCoefficient(const uint8_t coefNum)
{
uint16_t rC=0;
Wire.beginTransmission(i2cAddr_);
Wire.write(cmdPromRd_ + coefNum * 2); // send PROM READ command
Wire.endTransmission();
Wire.requestFrom(i2cAddr_, static_cast<uint8_t>(2));
if(Wire.available() >= 2) <<<<<<<<<<<<<< This is zero.
{
uint16_t ret = Wire.read(); // read MSB and acknowledge
uint16_t rC = 256 * ret;
ret = Wire.read(); // read LSB and not acknowledge
rC = rC + ret;
return rC;
}
#ifdef DEBUG
else
{
Serial.println("No data available in ReadCoefficient()"); <<<<< I'm hitting this. Leads me to the conclusion the part is not answering.
}
#endif
I put in a loop before the code in question while (Wire.available()==0); and the code stops hung there forever (no data incoming).
I verified that I never saw any data coming in. Below the code, I'm showing the output when the while loop is not present, (once again no data back from part.)
I am going to try to find time at work on Monday to hook up an oscilliscope and see if I can see the I2C requests going out (to verify it isn't a problem with the UNO board itself.
I am hoping you see something obvious in the code, but since it has worked for others, my assumption is that it is my hardware.
Output if I don't have the while 1 loop waiting on data.
i2cAddr_: 76
No data available in ReadCoefficient()
Coefficient 1 : 0
Coefficient 2 : 0
Coefficient 3 : 0
Coefficient 4 : 0
Coefficient 5 : 0
Coefficient 6 : 0
0
0
No data available in cmdAdc()
No data available in cmdAdc()
pressure: 0, pressConv: 0, temperature: 0
No data available in cmdAdc()
No data available in cmdAdc()
pressure: 0, pressConv: 0, temperature: 0
No data available in cmdAdc()
No data available in cmdAdc()
pressure: 0, pressConv: 0, temperature: 0
No data available in cmdAdc()
No data available in cmdAdc()
pressure: 0, pressConv: 0, temperature: 0
Centimeters: 1166.00, Feet: 38.25
http://learn.parallax.com/KickStart/29124
The Learn site version is known to return raw values that will at least demonstrate proper wiring and operation of the board.
There's no need to power your Uno with a 9V battery if you're connected to a PC via USB and relying on debugging through the Serial window. The board will derive its power from USB, which is generally a more reliable power source.
Franklin, your code and the code in the Intersema class are opposite. They are doing do-nothing loops until the Wire buffer is not empty. Murray mentioned it's an Uno board and the 1.0.5 Arduino IDE.
The reason I changed the code is of the way that Wire.available() works. It returns the number of bytes waiting in the RX buffer. So if there is nothing waiting in the RX buffer (which there is zero bytes) then the command while(Wire.available()) will always return zero (false) and you will immediately drop out of the loop. If there is data in the buffer you will loop there forever since no one will read it. (Because you are blocking before the read code below it.)
The code while (Wire.available()==0) will wait in a tight loop until some data has arrived, then drop out, and go into the routines below.
Also from some of my reading, it looks like the read routines are blocking with a timeout and so should be adequate to the task.
I had put the code in with the hope that it was a timing problem and waiting until data is ready would help.
From my reading it isn't necessary. See more in the response below to Gordon.
[LRM] I did further testing the other day. I put it on an oscilloscope. I can see the I2C signal coming out of the Arduino UNO fine. I decoded the serial data stream and it appears to be correct. I see the reset sequence, followed after a while by the EEPROM read sequence. I don't see anything after the EEPROM read (to get the calibration constants) although it's possible the delay is too great for the scope trigger to catch it, but since there is no micro I would think the response would be almost immediate (i.e. a pure memory read). As far as I can tell I'm not seeing a peep from the altimeter. One other oddity is that in the datasheet for the chipset, it says the EEPROM data will come out on SDO. They show the data being clocked out on SDO in the clock / data diagram. I'm assuming since people have it working they tied SDO/SDI together on the board.
So as far as I can see here is my analysis. It is one of the following:
- Bad altimeter board: I ordered another and it is in route. If mine is zapped this will solve the problem.
- Wrong Address: address I see used is 0x76 which looks right, and matched what I saw on the oscilloscope but there is one pin that has to match the way a pin is tied on the board. The PS (protocol select) has to be high to select I2c. It also calls out that in I2C mode that the CSB pin should be connected either to VDD or GND but don't leave it unconnected. It may be CSB is floating and not matching up. CSB allows a chip select line if you have multiple chips on the bus.
- Code has a problem: Since others have it working, this is least likely.
jim
//
//changed to using 3.3v for power and this works great as of 1/1/14
//reads sensor, displays to monitor, writes to card
//responds to network
//
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>
#define ADDRESS 0x76
const int chipSelect = 4;
uint32_t D1 = 0;
uint32_t D2 = 0;
int64_t dT = 0;
int32_t TEMP = 0;
int64_t OFF = 0;
int64_t SENS = 0;
int32_t P = 0;
uint16_t C[7];
float Temperature;
float Pressure;
float f;
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0x90, 0xA2, 0xDA, 0x00, 0xD8, 0x83 };
IPAddress ip(192,168,15,33);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
void setup() {
// Disable internal pullups, 10Kohms are on the breakout
PORTC |= (1 << 4);
PORTC |= (1 << 5);
Wire.begin();
Serial.begin(9600);
delay(100);
initial(ADDRESS);
Serial.print("Initializing SD card...");
// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
//return;
}
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
//File dataFile = SD.open("datalog.txt", FILE_WRITE);
delay(1000);
delay(5000);
}//end of setup loop here
void loop()
{
compute();
output();
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each
client.print("Temperature is ");
client.print(f);
client.println(" F,");
client.print("Pressure is ");
client.print(Pressure);
client.print(" mb");
client.println("<br />");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}delay(15000); //should make writes to card every 15 sec
}
long getVal(int address, byte code)
{
unsigned long ret = 0;
Wire.beginTransmission(address);
Wire.write(code);
Wire.endTransmission();
delay(10);
// start read sequence
Wire.beginTransmission(address);
Wire.write((byte) 0x00);
Wire.endTransmission();
Wire.beginTransmission(address);
Wire.requestFrom(address, (int)3);
if (Wire.available() >= 3)
{
ret = Wire.read() * (unsigned long)65536 + Wire.read() * (unsigned long)256 + Wire.read();
}
else {
ret = -1;
}
Wire.endTransmission();
return ret;
}
void initial(uint8_t address)
{
Serial.println();
Serial.println("PROM COEFFICIENTS ivan");
Wire.beginTransmission(address);
Wire.write(0x1E); // reset
Wire.endTransmission();
delay(10);
for (int i=0; i<6 ; i++) {
Wire.beginTransmission(address);
Wire.write(0xA2 + (i * 2));
Wire.endTransmission();
Wire.beginTransmission(address);
Wire.requestFrom(address, (uint8_t) 6);
delay(1);
if(Wire.available())
{
C[i+1] = Wire.read() << 8 | Wire.read();
}
else {
Serial.println("Error reading PROM 1"); // error reading the PROM or communicating with the device
}
Serial.println(C[i+1]);
}
Serial.println();
}
void compute(){
D1 = getVal(ADDRESS, 0x48); // Pressure raw
D2 = getVal(ADDRESS, 0x58);// Temperature raw
dT = D2 - ((uint32_t)C[5] << 8);
OFF = ((int64_t)C[2] << 16) + ((dT * C[4]) >> 7);
SENS = ((int32_t)C[1] << 15) + ((dT * C[3]) >> 8);
TEMP = (int64_t)dT * (int64_t)C[6] / 8388608 + 2000;
if(TEMP < 2000) // if temperature lower than 20 Celsius
{
int32_t T1 = 0;
int64_t OFF1 = 0;
int64_t SENS1 = 0;
T1 = pow(dT, 2) / 2147483648;
OFF1 = 5 * pow((TEMP - 2000), 2) / 2;
SENS1 = 5 * pow((TEMP - 2000), 2) / 4;
if(TEMP < -1500) // if temperature lower than -15 Celsius
{
OFF1 = OFF1 + 7 * pow((TEMP + 1500), 2);
SENS1 = SENS1 + 11 * pow((TEMP + 1500), 2) / 2;
}
TEMP -= T1;
OFF -= OFF1;
SENS -= SENS1;
}
Temperature = (float)TEMP / 100;
f = round(Temperature*9.0/5.0 +32.0);
P = ((int64_t)D1 * SENS / 2097152 - OFF) / 32768;
Pressure = (float)P / 100;
}
void output(){
Serial.print(" Actual TEMP= ");
Serial.print(f);
Serial.print(" F Actual PRESSURE= ");
Serial.print(Pressure);
Serial.println(" mb");
Serial.print("D2 RAW (Temp)= ");
Serial.print(D2);
Serial.print(" D1 RAW (Pressure)= ");
Serial.println(D1);
Serial.println();
File dataFile = SD.open("datalog.txt", FILE_WRITE);
dataFile.print("Actual TEMP= ");
dataFile.print(f);
dataFile.print(" F, Actual PRESSURE= ");
dataFile.println(Pressure);
dataFile.print(" mb");
dataFile.close();
}
I tried a 2nd altimeter to see if it was damaged, no joy.
As for your 3.3v comment, did you get any data back from the altimeter at all when you were using 5V?
It should be 5V tolerant.
Also did you change the power and data levels, or just supply 3.3V to the power rails?
Also one thing that still bothers me is when I put it on the oscilliscope, I didn't see the Arduino clocking the data out after sending the command. I could see the command going out on I2C, but then I expected to see it clocking to read and the altimeter as a slave would send back data.
It now at least works and I can read back data from it.
The only problem is it is reading off by about 1000 feet.
It seems to be following the movement up/down.
The barometric pressure doesn't agree with the national weather service pressure either. i.e. It seems to be reading off valid data that is incorrect.
I'm going to try the other original sensor tonight to see if it works any better.
I've used two different pieces of code written by two different people. Both worked, had similar pressure readings, the same temp readings, and both pressure readings were off from the locally measured barometric pressure.
I also did the measurements with the sensor in a dark environment since it's marked light sensitive. I assume it has some impact on the measurements.
Les.