MS5541 depth sensor communication

Hi Rob,
once the code runs well I will make it slim.
In the annex is a graph of a 107 s measurement. I think my problem is in the wiring and not so much in the software, what o you think? I touched the sensor to generate some pressure at the point where the values drop. Even so they are quickly stable again, the compensated values climbs up to fall again...
I checked the formulas twice today, they are ok.
How could I check if the sensor resets?
Thanks

Found a datasheet at: - Loading... -
3,3Volt?

I checked your code with the datasheet especially the formulas for the 6 correction factors.

your code:

  unsigned int c1 = result1 >> 3 & 0x1FFF;
  unsigned int c2 = ((result1 & 0x7) << 10) | ((result2 >> 6) & 0x3FF);
  unsigned int c3 = (result3 >> 6) & 0x3FF;
  unsigned int c5 = ((result2 & 0x3F) << 6) | (result3 & 0x3F);
  unsigned int c4 = (result4 >> 7) & 0x7FF;
  unsigned int c6 = result4 & 0x7F;

If I read the datasheet correctly ( figure Page 12)
There is a small diff in c4 (TCO), it need another mask than in your code, but that cannot cause such a problem.
c4 is used to calculate the compensated pressure.

  long c1 = (result1 >> 3) & 0x1FFF;    //SENST1
  long c2 = ((result1 & 0x0007) << 10) | ((result2 >> 6) & 0x03FF);  // OFFT1
  long c3 = (result3 >> 6) & 0x03FF;  // TCS
  long c4 = (result4 >> 7) & 0x01FF;  // TCO  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  long c5 = (result2 & 0x003F) << 6  | (result 3 & 0x003F); // Tref
  long c6 = result4 & 0x007F;  // TEMPSENS

If I read the formulas correctly the math needs long (32 bits) for intermediate results, so I made the correction factors long so the CPU does the math with enough bits.
(Too few bits could cause overflow => strange results)

Please give it a try..

OMG, it works! Thanks man, thanks a lot!
It seems to have been the "long"-thing, because first I changed the "0x7FF" to "0x1FF" with no effect.
But now: joya! Pressing the sensor increased the value nicely, releasing it made it go back to 1007 mbar immediately.
OK; now I will try to create a library and some loops to make it a little more neat.
Again, thanks a lot!

And show us a new graph !!

If you want feedback on the library don't hesitate to ask (don't have such sensor but written/optimized a few small libs)

Hi,
here is the new graph. You see nicely when I touched and squeezed it and how the temperature rises and slowly falls again.

Hi all,
I wrapped up the code and commented it out. To close the case I will post a sketch to read out all calibration words and one which shows how to include the extracted calibration factors into a sketch to avoid reading these constants all the time while using the sensor in whatever application.
MS5541_read_all.pde

/*
 MS5541 Pressure Sensor calwords readout
 This program will read your MS5441 or compatible pressure sensor every 5 seconds and show you the calibration words, the calibration factors, 
 the raw values and the compensated values of temperature and pressure.
 Once you read out the calibration factors you can define them in the header of any sketch you write for the sensor.
 
Pins:
 MS5541 sensor attached to pins 10 - 13:
 MOSI: pin 11
 MISO: pin 12
 SCK: pin 13
 MCLK: pin 9
 CS is not in use, but might be pin 10
 
 created August 2011
 by SMStrauch based on application note AN510 from www.intersema.ch (http://www.meas-spec.com/downloads/Using_SPI_Protocol_with_Pressure_Sensor_Modules.pdf), 
 and with help of robtillaart and ulrichard. Thanks!
 */

// include library:
#include <SPI.h>

// generate a MCKL signal pin
const int clock = 9;

void resetsensor() //this function keeps the sketch a little shorter
{
  SPI.setDataMode(SPI_MODE0); 
  SPI.transfer(0x15);
  SPI.transfer(0x55);
  SPI.transfer(0x40);
}

void setup() {
  Serial.begin(9600);
  SPI.begin(); //see SPI library details on arduino.cc for details
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV32); //divide 16 MHz to communicate on 500 kHz
  pinMode(clock, OUTPUT);
  delay(100);
}

void loop() 
{
  TCCR1B = (TCCR1B & 0xF8) | 1 ; //generates the MCKL signal
  analogWrite (clock, 128) ; 

  resetsensor();//resets the sensor - caution: afterwards mode = SPI_MODE0!

  //Calibration word 1
  unsigned int result1 = 0;
  unsigned int inbyte1 = 0;
  SPI.transfer(0x1D); //send first byte of command to get calibration word 1
  SPI.transfer(0x50); //send second byte of command to get calibration word 1
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  result1 = SPI.transfer(0x00); //send dummy byte to read first byte of word
  result1 = result1 << 8; //shift returned byte 
  inbyte1 = SPI.transfer(0x00); //send dummy byte to read second byte of word
  result1 = result1 | inbyte1; //combine first and second byte of word
  Serial.print("Calibration word 1 =");
  Serial.println(result1);

  resetsensor();//resets the sensor

  //Calibration word 2; see comments on calibration word 1
  unsigned int result2 = 0;
  byte inbyte2 = 0; 
  SPI.transfer(0x1D);
  SPI.transfer(0x60);
  SPI.setDataMode(SPI_MODE1); 
  result2 = SPI.transfer(0x00);
  result2 = result2 <<8;
  inbyte2 = SPI.transfer(0x00);
  result2 = result2 | inbyte2;
  Serial.print("Calibration word 2 =");
  Serial.println(result2);  

  resetsensor();//resets the sensor

  //Calibration word 3; see comments on calibration word 1
  unsigned int result3 = 0;
  byte inbyte3 = 0;
  SPI.transfer(0x1D);
  SPI.transfer(0x90); 
  SPI.setDataMode(SPI_MODE1); 
  result3 = SPI.transfer(0x00);
  result3 = result3 <<8;
  inbyte3 = SPI.transfer(0x00);
  result3 = result3 | inbyte3;
  Serial.print("Calibration word 3 =");
  Serial.println(result3);  

  resetsensor();//resets the sensor

  //Calibration word 4; see comments on calibration word 1
  unsigned int result4 = 0;
  byte inbyte4 = 0;
  SPI.transfer(0x1D);
  SPI.transfer(0xA0);
  SPI.setDataMode(SPI_MODE1); 
  result4 = SPI.transfer(0x00);
  result4 = result4 <<8;
  inbyte4 = SPI.transfer(0x00);
  result4 = result4 | inbyte4;
  Serial.print("Calibration word 4 =");
  Serial.println(result4);

  //now we do some bitshifting to extract the calibration factors 
  //out of the calibration words; read datasheet AN510 for better understanding
  long c1 = result1 >> 3 & 0x1FFF;
  long c2 = ((result1 & 0x07) << 10) | ((result2 >> 6) & 0x03FF);
  long c3 = (result3 >> 6) & 0x03FF;
  long c4 = (result4 >> 7) & 0x07FF;
  long c5 = ((result2 & 0x003F) << 6) | (result3 & 0x003F);
  long c6 = result4 & 0x007F;

  Serial.println(c1);
  Serial.println(c2);
  Serial.println(c3);
  Serial.println(c4);
  Serial.println(c5);
  Serial.println(c6);

  resetsensor();//resets the sensor

  //Temperature:
  unsigned int tempMSB = 0; //first byte of value
  unsigned int tempLSB = 0; //last byte of value
  unsigned int D2 = 0;
  SPI.transfer(0x0F); //send first byte of command to get temperature value
  SPI.transfer(0x20); //send second byte of command to get temperature value
  delay(35); //wait for conversion end
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  tempMSB = SPI.transfer(0x00); //send dummy byte to read first byte of value
  tempMSB = tempMSB << 8; //shift first byte
  tempLSB = SPI.transfer(0x00); //send dummy byte to read second byte of value
  D2 = tempMSB | tempLSB; //combine first and second byte of value
  Serial.print("Temperature raw =");
  Serial.println(D2); //voilá!

  resetsensor();//resets the sensor

  //Pressure:
  unsigned int presMSB = 0; //first byte of value
  unsigned int presLSB =0; //last byte of value
  unsigned int D1 = 0;
  SPI.transfer(0x0F); //send first byte of command to get pressure value
  SPI.transfer(0x40); //send second byte of command to get pressure value
  delay(35); //wait for conversion end
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  presMSB = SPI.transfer(0x00); //send dummy byte to read first byte of value
  presMSB = presMSB << 8; //shift first byte
  presLSB = SPI.transfer(0x00); //send dummy byte to read second byte of value
  D1 = presMSB | presLSB; //combine first and second byte of value
  Serial.print("Pressure raw =");
  Serial.println(D1);

  //calculation of the real values by means of the calibration factors and the maths
  //in the datasheet. const MUST be long
  const long UT1 = (c5 << 3) + 10000;
  const long dT = D2 - UT1;
  const long TEMP = 200 + ((dT * (c6 + 100)) >> 11);
  const long OFF  = c2 + (((c4 - 250) * dT) >> 12) + 10000;
  const long SENS = (c1/2) + (((c3 + 200) * dT) >> 13) + 3000;
  long PCOMP = (SENS * (D1 - OFF) >> 12) + 1000;
  float TEMPREAL = TEMP/10;

  Serial.print("Real Temperature in °C=");
  Serial.println(TEMPREAL);

  Serial.print("Compensated pressure in mbar =");
  Serial.println(PCOMP);

  //2nd order compensation only for T > 0°C
  const long dT2 = dT - ((dT >> 7 * dT >> 7) >> 3);
  const float TEMPCOMP = (200 + (dT2*(c6+100) >>11))/10;
  Serial.print("2nd order compensated temperature in °C =");
  Serial.println(TEMPCOMP);  

  delay(5000);
}

PS: Un-voided the resetsensor call as of today, Nov 25 2011.

.... and just because it was to much to put it in one post:

MS5541_demo.pde

/*
 MS5541 Pressure Sensor demonstration sketch
 To obtain the calibration factors run the read-calwords-sketch before.
 
 Circuit:
 MS5541 sensor attached to pins 10 - 13:
 MOSI: pin 11
 MISO: pin 12
 SCK: pin 13
 MCLK: pin 9
CS is not in use, but might be pin 10
  
 created August 2011
 by SMStrauch based on application note AN510 from www.intersema.ch (http://www.meas-spec.com/downloads/Using_SPI_Protocol_with_Pressure_Sensor_Modules.pdf), 
 and with help of robtillaart and ulrichard. Thanks!
 */

// include librariy:
#include <SPI.h>

// generate a MCKL signal 
const int clock = 9;

//include the calibration factors according to your individual sensor; they MUST be long for internal calculation of the compensated values
//intersema.ch provides a file with expected values
const long c1 = 2723;
const long c2 = 4648;
const long c3 = 388;
const long c4 = 224;
const long c5 = 2309;
const long c6 = 54;

void resetsensor() //this function keeps the sketch a little shorter
{
  SPI.setDataMode(SPI_MODE0); 
  SPI.transfer(0x15);
  SPI.transfer(0x55);
  SPI.transfer(0x40);
}

void setup() {
  Serial.begin(9600);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV32); //divide 16 MHz to communicate on 500 kHz
  pinMode(clock, OUTPUT); //sets clockpin on output
}

void loop() 
{
  TCCR1B = (TCCR1B & 0xF8) | 1 ; //generates the MCKL signal
  analogWrite (clock, 128) ; 

  void resetsensor(); //resets the sensor, be aware SPI remains in Mode 0 afterwards!

  //Temperature:
  unsigned int tempMSB = 0; //first byte of value
  unsigned int tempLSB = 0; //last byte of value
  unsigned int D2 = 0;
  SPI.transfer(0x0F); //send first byte of command to get temperature value
  SPI.transfer(0x20); //send second byte of command to get temperature value
  delay(35); //wait for conversion end
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  tempMSB = SPI.transfer(0x00); //send dummy byte to read first byte of value
  tempMSB = tempMSB << 8; //shift first byte
  tempLSB = SPI.transfer(0x00); //send dummy byte to read second byte of value
  D2 = tempMSB | tempLSB; //combine first and second byte of value
  Serial.print("Temperature raw =");
  Serial.println(D2); //voilá!

  void resetsensor();//resets the sensor 

  //Pressure:
  unsigned int presMSB = 0; //first byte of value
  unsigned int presLSB =0; //last byte of value
  unsigned int D1 = 0;
  SPI.transfer(0x0F); //send first byte of command to get pressure value
  SPI.transfer(0x40); //send second byte of command to get pressure value
  delay(35); //wait for conversion end
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  presMSB = SPI.transfer(0x00); //send dummy byte to read first byte of value
  presMSB = presMSB << 8; //shift first byte
  presLSB = SPI.transfer(0x00); //send dummy byte to read second byte of value
  D1 = presMSB | presLSB; //combine first and second byte of value
  Serial.print("Pressure raw =");
  Serial.println(D1);

  //calculation of the real values by means of the calibration factors and the maths
  //in the datasheet
  const long UT1 = (c5 << 3) + 10000;
  const long dT = D2 - UT1;
  const long TEMP = 200 + ((dT * (c6 + 100)) >> 11);
  const long OFF  = c2 + (((c4 - 250) * dT) >> 12) + 10000;
  const long SENS = (c1/2) + (((c3 + 200) * dT) >> 13) + 3000;
  long PCOMP = (SENS * (D1 - OFF) >> 12) + 1000;
  float TEMPREAL = TEMP/10;
  //2nd order compensation only for T > 0°C
  const long dT2 = dT - ((dT >> 7 * dT >> 7) >> 3);
  const float TEMPCOMP = (200 + (dT2*(c6+100) >>11))/10;

  Serial.print("Real Temperature [°C]=");
  Serial.println(TEMPREAL);

  Serial.print("Compensated pressure [mbar] =");
  Serial.println(PCOMP);

  Serial.print("2nd order compensated temperature [°C] =");
  Serial.println(TEMPCOMP);  

  delay(5000);
}

Any comments are welcome, and please tell me about your applications with the sensor.

¡Bien hecho :wink:

Might include the link to the datasheet in the code

¡Muchas gracias!
Links are in.

Hi folks,
and on it goes: I have a new riddle to solve!
Since my project requires quite some memory I decided to upgrade and made myself a neat little Sanguino clone board. It works nicely, I downloaded and installed awesome stuff from here Alternate CORE files for Arduino and some Sanguino board data I do not remember from where. It runs both with the Ardu-Duino 644P and Sanguino board settings (but that's probably obvious).
Then I tested sketches I already used on my 2009 as this one... no wait, it is far to long. It is the AnalogLogger example from the SdFat-library which I adapted and added a DS18B20. Unfortunately I only figured out the code formally and adapted it without deep understanding. But it runs like a charm logging 4 analog inputs and digital temperature via SPI onto a µSD-Card. So far so good.
Now comes the riddle: I also adapted the code I displayed in this post but it won't work. There aren't even osci-spikes on the SCK line which I clearly saw running the SdFat-based code. So to me it looks like the sketch doesn't access the SPI-Bus at all, even though I adjusted the PINs to the right ones on the 644 (SS 4, MOSI 5, MISO 6, SCK 7). So - more of a general understanding question - where exactly does the SdFat library access the information about the PIN configuration? And where do I have to give the SPI library that information (I copied and included ArduinoPins.h from the SdFat diractory, but it didn't help)?
Can anybody tell me more about the compiling process, I mean, by selecting a specific board, do I already direct the compiler to a specific file in which the PIN configuration is given or do the respective libraries do that independently of the board I chose; it seems to me a valuable general info and a good point to look for the problem.
Thanks in advance, any comment is apreciated, greetings from Patagonia
Seb

PS: Problem solved with help from Nick. If you modify a .h-file NEVER copy-paste the code but type it. If copy-pasted the changes are visible, appear totally normal but the compiler does not read it properly.

Hey this is great I just bought the sensor and it is pretty small, how did you manage to solder it, any recommendations?

What did you use for the 32.768. khz clock? I bought t a DS32khz/ 14-dip .. http://www.maxim-ic.com/datasheet/index.mvp/id/2940
I will be using an arduino pro mini for the 3.3 volts that the sensor requires.

I will be use this a diving depth logger..

So I will keep you inform.

Thanks for the code I will try it, any more tips?

Hi there,
soldering that little one is a royal pain in the ... soldering hand, I can tell you. End of this week I will try it with a reflow oven, but up to now I managed with no clean flux and a lot of patience. Good luck anyway; if you use SPI for datalogging you will have to put a 74HC125 or so into the DOUT line to avoid interfering of the sensor with the sd-card. And THAT one is a demanding little bugger to solder!
This codeline

  TCCR1B = (TCCR1B & 0xF8) | 1 ; //generates the MCKL signal
  analogWrite (clock, 128) ;

takes care of the signal on pin 9. It is not possible to use any pin but the timer pin, so do not simply switch it. It will allow you to skip the extra chip, so it might be an elegant possibility to keep it all small.
Good luck and share your progress and chalenges, by all means!

Thanks for the info..

did you ever tried to do it without using the SPI? i don really want to add the 74HC125...but i will have to try it.. or maybe storing values in an external memory and then export it through serial port will be another idea..

i will keep you posted..

it was quite a challenge to solder this but and i do not if i broke the sensor for over-heating XD

this is what i go on the serial port when reading the calibration words...

Calibration word 1 =18820
Calibration word 2 =35237
Calibration word 3 =22501
Calibration word 4 =27826
2352
4646
351
217
2405
50
Temperature raw =65535
Pressure raw =65535
Real Temperature in °C=285.00
Compensated pressure in mbar =-209082
2nd order compensated temperature in °C =285.00

some of the values are pretty similar to yours...

but the temperature somehow is not working.. another interesting think is that i am using a 8 MHZ arduino not the 16....

so i wonder if the divider is set correctly for the frequency...

thanks

second try i hooked the 32.768 khz clock i bought directly to the sensor and things got better

first reading is good, second reading is bad...

Temperature raw =30027
Pressure raw =2047
Real Temperature [°C]=31.00
Compensated pressure [mbar] =-12747
2nd order compensated temperature [°C] =31.00
Temperature raw =49151
Pressure raw =2047
Real Temperature [°C]=175.00
Compensated pressure [mbar] =-16794
2nd order compensated temperature [°C] =175.00

i reset and put my finger and temperature increases

buton:
second try i hooked the 32.768 khz clock i bought directly to the sensor and things got better

first reading is good, second reading is bad...

Temperature raw =30027
Pressure raw =2047
Real Temperature [°C]=31.00
Compensated pressure [mbar] =-12747
2nd order compensated temperature [°C] =31.00
Temperature raw =49151
Pressure raw =2047
Real Temperature [°C]=175.00
Compensated pressure [mbar] =-16794
2nd order compensated temperature [°C] =175.00

i reset and put my finger and temperature increases

I solder it again and it is working perfect

thank you man..

the diving community really appreciate your effort

Temperature raw =29659
Pressure raw =14623
Real Temperature [°C]=28.00
Compensated pressure [mbar] =981
2nd order compensated temperature [°C] =28.00

Great, congratulations!
Let me know if you by chance modify the code, because for some reason it only gives X.00 (point zero) temperature values and I would like to have a better resolution.
It is great to contribute to the community after I received so much help and advice, I am very happy to share!

godo:
Great, congratulations!
Let me know if you by chance modify the code, because for some reason it only gives X.00 (point zero) temperature values and I would like to have a better resolution.
It is great to contribute to the community after I received so much help and advice, I am very happy to share!

i think that i got it

change the calculation of the temeperature to double

const double TEMP = 200 + ((dT * (c6 + 100)) >> 11);

double TEMPREAL = TEMP/10;

:slight_smile:

Wow, great, that is a bit of a surprise though, because:

The double implementation on the Arduino is currently exactly the same as the float, with no gain in precision.

But, on the other hand:

Users who borrow code from other sources that includes double variables may wish to examine the code to see if the implied precision is different from that actually achieved on the Arduino.

I will try that asap, thanks!

I realized that having the microsd card and the sensor on the same buss will be too complicated due to the libray SDFAT.. and i dont want to add another IC

so i was thingking about doing some bit banging to get the data...

somehow i got it to work(i think) the reset and pressure conversion sequence works because i am getting data

where i am having problems is on the reading, no reading the correct raw value...how ever if i manually press the sensor with my finger... i start getting numbers...also if i modify the delay on the clk while reading it also changes... :slight_smile:

godo please helpme out if you see something weird.. i almost follow the datasheet and some insights of you spi code..

word start=0b1111010000000000;//start sequence
 unsigned int p1,p2;
long reset=0b10101010101010100000000000000000;//reset sequence
byte counts;//for debug
int dout= 2;//pin for data out MOSI
int din=3;//pin for data in MISO
int clk=4;//data clock
boolean data=0;


void setup() {                
  // initialize the digital pin as an outputand input
  
  Serial.begin(9600);
  pinMode(dout, OUTPUT);     
    pinMode(din, INPUT);  
   pinMode(clk,OUTPUT); 
  
}

void loop() {
  pressure();   //main function
  delay(3000);              // wait for a second
}

void pressure()
{
  Serial.println("start Reset");
  //---start REset sequence...
        for(int i=31;i>10;i--)
        {
          
          boolean x;
          
          x=bitRead(reset,i);
        
         
          digitalWrite(dout,x);
           digitalWrite(clk,HIGH); 
            digitalWrite(clk,LOW); 
        }
    
  //--------Start Conversion sequence
    for(int i=15; i>5;i--)    
    {
               
                       boolean x;
                       x=bitRead(start,i);
                       digitalWrite(dout,x);                
                       //--DRIVE CLOCK
                       digitalWrite(clk,HIGH); 
                       digitalWrite(clk,LOW);   
                                
                                       
                                
                                
   }
                
          
          
   //wait unti conversion
    // i keep runing the clock and wait until the Din(miso) detects that it went to 0 and the data is ready
    
     data=  digitalRead(din);
    while(data==1)
    
    {
      
      digitalWrite(clk,HIGH);   
      digitalWrite(clk,LOW); 
      data=digitalRead(din);
      
      
    }
   //------------ 
 
 
 //---start reading 
    //Serial.println("Read raw pressure");
      
        for(int i=16; i>=0;i--)
            {
                 digitalWrite(clk,HIGH);    // drive the clock down      
                 delay(10);        
                 digitalWrite(clk,LOW);
                 delay(10);
                 data= digitalRead(din);// i read on low cycle de input(output of the sensor)
                  if(data==1)
                  {
                    
                    bitWrite(p1,i,1);// write the value and i index the int to the right..
              
                    }
                 else
                 {
                bitWrite(p1,i,0);
              
                   
                 }
                 
              counts++;
            }
      
      
      
      
    
    
    //testing to see if shifting will help also
  Serial.println(p1);
  p2=p1>>1;
  
  Serial.println(p2);
  Serial.println(counts);//test to se how many times went into for cycle
  counts=0;
  
}