Luftdruck und Temperatur als NMEA 0183 Datensatz ausgeben.

Mit den 3 vorherigen Artikeln über den BMP085, der Checksum Berechnung und der Umwandlung von Float in String kann ich nun die Temperatur und Luftdruck Daten als NMEA 0183 Datensatz mit 4800 Baud ausgeben lassen.

Es reicht wenn dieser Datensatz alle 3 Sekunden ausgegeben wird.

Ein Beispiel:
$WIXDR,P,1.0194,B,barometer,C,24.2,C,temperature*52
$:Einleitung NMEA 0183 Datensatz mit 4800 Baud
WIXDR : Wetter Datensatz
1.0194: Luftdruck 1019,4 hPa
24,2: Temperatur Grad Celsius

Hier das fertige Programm

// BMP085 Luftdruck und Temperatur Sensor 
//
// Mit NMEA 0183 Ausgabe
// $WIXDR,P,xxxx.x,B,3,C,xxx.x,C,0,H,xxx.x,P,2,C,099.9,C,1*hh
//
// Matthias Busse 21.5.2014 Version 2.0

#include < Wire.h>

// Einstellungen für den BMP085
#define I2C_ADDRESS 0x77
const unsigned char oversampling_setting = 3; //oversamplig:  0 ungenau (13ms) ... 3 genau (34ms)
const unsigned char pressure_waittime[4] = { 5, 8, 14, 26 };
int ac1, ac2, ac3, b1, b2, mb, mc, md;
unsigned int ac4, ac5, ac6;

int temp = 20, temp_mittel=200;
long druck = 1013, druck_mittel=101300;
float temp_offset=0.0, druck_offset=1.0; // Korrekturwerte
float ftemp, fdruck;
int mitteln=5;
//nmea="$WIXDR,P,1.0213,B,barometer,C,18.5,C,temperature*5Dzz";
char nmea[80];

void setup() {
  Serial.begin(4800);
  Serial.println("BMP085 Temperatur und Luftdruck auslesen.");
  Wire.begin();
  bmp085_get_cal_data();
  bmp085_read_temperature_and_pressure(&temp_mittel,&druck_mittel); // erstmal Mittelwerte lesen
}

void loop() {
  bmp085_read_temperature_and_pressure(&temp, &druck); // dauert ca. 34ms
  temp_mittel = ((temp_mittel * (mitteln-1)) + temp) / mitteln;
  druck_mittel = ((druck_mittel * (mitteln-1)) + druck) / mitteln;
  ftemp=(float)(temp_mittel/10.0)+temp_offset;
  fdruck=((float)druck_mittel/100.0)+druck_offset;
  write_Nmea(fdruck, ftemp);
  delay(3000); // ms
}

void write_Nmea(float dr, float te) {
// Luftdruck und Temperatur als NMEA0183 Datensatz ausgeben
// $WIXDR,P,1.0213,B,barometer,C,18.5,C,temperature*hh
//
// Matthias Busse 5.2014 Version 1.0

String nmea="$WIXDR,P,1.0213,B,barometer,C,18.5,C,temperature*5Dzz"; 
String drs="1.2345", tes="23.4";
int cs;
  
  nmea="$WIXDR,P,"; // Wind Instrument
  drs=float2str(dr/1000.0,4);
  nmea.concat(drs);
  nmea+=",B,barometer,C,";
  tes=float2str(te,1);
  nmea.concat(tes);
  nmea+=",C,temperature*";
  cs=getCheckSum(nmea);
  Serial.print(nmea);
  if(cs < 16) Serial.print("0");
  Serial.println(cs,HEX);
}

String float2str(float f, int n) {
// Float in String umwandeln mit x Kommastellen
// 1234.5678 max. 5 Vorkomma und 0...n Nachkommastellen
// Eingabe 
//   float f: zahl max. 32000,0 und min. 0.00000000001 ? ggf. kleiner
//   int n: Nachkommastellen 0...10 ggf. mehr
// Ausgabe
//   String z.B. "10.04"
// 
// Matthias Busse 20.5.2014 Version 2.0

String s="12345.67890";
int i, k, st=0;
float teiler;
double d,e;
char c;

  s = "";
  d = f;
  for(i=10000; i > 0; i/=10) {
    // 10000er 1000er 100er 10er 1er
    k=(int)d/i; 
    if((k>0) | st) {
      st=1;
      c=k+48; // ASCII
      s.concat(c);
    }
    d = d - (k*i);
  }
  if(st==0) s.concat("0"); // wenigstens 0 ausgeben
  if(n>0) s.concat("."); // Dezimalpunkt
  teiler = 0.1;
  for(i=0; i < n; i++) {
    e = d / teiler; // 0.1er 0.01er 0.001er 0.0001er
    k=(int)e;
    c=k+48; // ASCII
    s.concat(c);
    d = d - (k*teiler);
    teiler = teiler / 10.0;
  }
  return s;
} // float2str

int getCheckSum(String s) {
// Checksum berechnen und als int ausgeben
// wird als HEX benötigt im NMEA Datensatz
// V 1.1: jetzt zwischen $ oder ! und * rechnen
//
// Matthias Busse 19.05.2014 Version 1.1

int i, XOR, c;

  for (XOR = 0, i = 0; i < s.length(); i++) {
    c = (unsigned char)s.charAt(i);
    if (c == '*') break;
    if ((c!='$') && (c!='!')) XOR ^= c;
  }
  return XOR; 
}

void bmp085_read_temperature_and_pressure(int* temp, long* druck) {
int ut= bmp085_read_ut();
long up = bmp085_read_up();
long x1, x2, x3, b3, b5, b6, p;
unsigned long b4, b7;

  x1 = ((long)ut - ac6) * ac5 >> 15; //Temperatur berechnen
  x2 = ((long) mc << 11) / (x1 + md);
  b5 = x1 + x2;
  *temp = (b5 + 8) >> 4;

  b6 = b5 - 4000; //Druck berechnen
  x1 = (b2 * (b6 * b6 >> 12)) >> 11;
  x2 = ac2 * b6 >> 11;
  x3 = x1 + x2;

  if (oversampling_setting == 3) b3 = ((int32_t) ac1 * 4 + x3 + 2) << 1;
  if (oversampling_setting == 2) b3 = ((int32_t) ac1 * 4 + x3 + 2);
  if (oversampling_setting == 1) b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 1;
  if (oversampling_setting == 0) b3 = ((int32_t) ac1 * 4 + x3 + 2) >> 2;

  x1 = ac3 * b6 >> 13;
  x2 = (b1 * (b6 * b6 >> 12)) >> 16;
  x3 = ((x1 + x2) + 2) >> 2;
  b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;
  b7 = ((uint32_t) up - b3) * (50000 >> oversampling_setting);
  p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;

  x1 = (p >> 8) * (p >> 8);
  x1 = (x1 * 3038) >> 16;
  x2 = (-7357 * p) >> 16;
  *druck = p + ((x1 + x2 + 3791) >> 4);
}

unsigned int bmp085_read_ut() {
  write_register(0xf4,0x2e);
  delay(5); //mehr als 4.5 ms
  return read_int_register(0xf6);
}

void bmp085_get_cal_data() {
  ac1 = read_int_register(0xAA);
  ac2 = read_int_register(0xAC);
  ac3 = read_int_register(0xAE);
  ac4 = read_int_register(0xB0);
  ac5 = read_int_register(0xB2);
  ac6 = read_int_register(0xB4);
  b1 = read_int_register(0xB6);
  b2 = read_int_register(0xB8);
  mb = read_int_register(0xBA);
  mc = read_int_register(0xBC);
  md = read_int_register(0xBE);
}

long bmp085_read_up() {
  write_register(0xf4,0x34+(oversampling_setting<<6));
  delay(pressure_waittime[oversampling_setting]);

  unsigned char msb, lsb, xlsb;
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(0xf6);
  Wire.endTransmission();

  Wire.requestFrom(I2C_ADDRESS, 3); 
  while(!Wire.available()) {} // warten
  msb = Wire.read(); 
  while(!Wire.available()) {} // warten
  lsb |= Wire.read(); 
  while(!Wire.available()) {} // warten
  xlsb |= Wire.read(); 
  return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting);
}

void write_register(unsigned char r, unsigned char v) {
  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(r); 
  Wire.write(v); 
  Wire.endTransmission();
}

char read_register(unsigned char r) {
unsigned char v;

  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(r); 
  Wire.endTransmission();

  Wire.requestFrom(I2C_ADDRESS, 1); 
  while(!Wire.available()) {} // warten
  v = Wire.read(); 
  return v;
}

int read_int_register(unsigned char r) {
unsigned char msb, lsb;

  Wire.beginTransmission(I2C_ADDRESS);
  Wire.write(r); 
  Wire.endTransmission();

  Wire.requestFrom(I2C_ADDRESS, 2);
  while(!Wire.available()) {} // warten
  msb = Wire.read(); 
  while(!Wire.available()) {} // warten
  lsb = Wire.read(); 
  return (((int)msb<<8) | ((int)lsb));
}

> Der Luftdruck und Temperatur Sensor BMP085
> NMEA 0183 Checksumme berechnen mit dem Arduino

von Matthias Busse

10 Gedanken zu „Luftdruck und Temperatur als NMEA 0183 Datensatz ausgeben.

  1. richard

    Gutten Tag

    Bitte ich kanst nicht deutsch sprchecn bist English OK? Good day, I have tried your previous code for barometer which reads every 1 and 3 hours and it worked great. I REALLY want to convert that to NMEA strings for a marine chartplotter so really want to get this code (Luftdruck und Temperatur als NMEA 0183 Datensatz ausgeben) to work but when I try and run it get the following error. Please help, vielen dank , Richard (PS. if possible please could you reply to my email Richard at sprs dot co dot za )

    Arduino: 1.6.5 (Windows 8.1), Board: „Arduino/Genuino Uno“

    sketch_feb04a:8: error: #include expects „FILENAME“ or
    sketch_feb04a.ino: In function ‚void setup()‘:
    sketch_feb04a:28: error: ‚Wire‘ was not declared in this scope
    sketch_feb04a.ino: In function ‚long int bmp085_read_up()‘:
    sketch_feb04a:185: error: ‚Wire‘ was not declared in this scope
    sketch_feb04a.ino: In function ‚void write_register(unsigned char, unsigned char)‘:
    sketch_feb04a:200: error: ‚Wire‘ was not declared in this scope
    sketch_feb04a.ino: In function ‚char read_register(unsigned char)‘:
    sketch_feb04a:209: error: ‚Wire‘ was not declared in this scope
    sketch_feb04a.ino: In function ‚int read_int_register(unsigned char)‘:
    sketch_feb04a:222: error: ‚Wire‘ was not declared in this scope
    #include expects „FILENAME“ or

    This report would have more information with
    „Show verbose output during compilation“
    enabled in File > Preferences.

    Antworten
  2. admin Beitragsautor

    Hello Richard,

    at the top of the code there was an error. Now it’s corrected.
    Use
    #include < Wire.h>
    and delete the space between < and Wire. Then it will work. Matthias

    Antworten
    1. admin Beitragsautor

      Hallo,
      Ein Quelltext in Spitzen Klammern < text > und ohne Leerzeichen wird von WordPress einfach gelöscht. Das ist immer wieder ärgerlich. Weiss jemand wie man das Problem beheben kann?

      Matthias

      Antworten
  3. richard

    Good day Matthias

    Thanks for all your help so far. If you have a minute please could you give me some more help. The strings I get out stream beautifully into OpenCPN but the baromtere does not seem to recognise it, is this the correct format

    $WIXDR, P 1.0118,B,barometer,C28.6,C,temperature*5E

    Have you managed to use your set-up with OpenCPN. As I understand it as long as it is formatted as NMEA 0183 then the connection should work.

    Once again thanks for your help, much appreciated, Richard

    Antworten
  4. Ronald Fuss

    Hallo,
    ich möchte die NMEA Datensätze an einem PIN des ARDUINO UNO zur Weiterverarbeitung in einem Navigationsprogramm ausgeben. Darf ich Sie bitten mir in diesem Punkt weiterzuhelfen und ggf. eine Anleitung zuzusenden.

    Besten Dank für Ihre Hilfe.

    Antworten
  5. admin Beitragsautor

    Genau das wird hier über den TX Pin gemacht.
    Der Datensatz wird zusammen gesetzt und Serial.print und 4800 Baud ausgegeben
    Serial.print(nmea);
    Danach wird noch die Checksumme ausgegeben, das gehört zu einem NMEA Datensatz dazu.
    Serial.println(cs,HEX);

    Antworten
  6. Frederik-Matthias Davids

    Hallo läuft das Programm auch auf dem Raspberry Pi?
    Wenn ja, welche Programmsprache muss man nutzen?
    Möchte gerne in OpenCPN den Sensor intigrieren.

    Antworten
    1. admin Beitragsautor

      Mit dem Raspberry habe ich noch nicht gespielt. Das solle sich aber dorthin übersetzen lassen, ist schließlich in C geschrieben. Aber da fragst Du am besten die Spezis in einem Raspberry Forum.

      Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.