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

15 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
    1. Carsten

      Moin,
      vielen Dank mal vorab die Informationen, die Du hier teilst. Davon inspiriert habe ich mir nun gerade einen Arduino zugelegt. Ich bin also noch sehr neu auf diesem Gebiet, aber ich habe das Program zum laufen bekommen. Meine Frage bezieht sich auf die Verkabelung des TX-Pin. Ich habe da einen TRX3 von weatherdock als Multiplexer. Nun würde ich, um die Daten in mein Bordnetz zu bekommen, denTX-PIN am Arduino mit verbinden und GND am Arduino mit .
      Das einlesen von NMEA-Daten aus dem Bordnetz ist wohl nicht direkt möglich, wenn ich das hier richtig verstanden habe. Du verwendest da einen Wandler (NMEA0183 > UART Wandler). Warum braucht man den? Geht es auch ohne?
      LG
      Carsten

      Antworten
      1. admin Beitragsautor

        UART hat ein 0/5V Spannung und NMEA0183 (RS232) kann eine deutlich höhere Spannung haben, deshalb ist ein Schnittstellenwandler nötig. Ohne kannst Du dir den Arduino Eingang schnell zerstören.

        Antworten
        1. Andre

          Hallo,
          ich möchte hier nochmals nachfragen.
          Ich möchte die Barometerdaten über meinen Shipmodul NMEA Multiplexer auf meinem Plotter anzeigen lassen.
          Habe ich das richtig verstanden, dass ich den TX Pin des Arduino nicht direkt mit dem Multiplexer verbinden darf? Wenn nicht wäre ich dankbar, wenn mir jemand erklären könnte, wie die Anbindung statt dessen erfolgen muss.
          Danke

          Antworten
          1. admin Beitragsautor

            Da solltest Du einen RS232 oder RS422 Schnittstellen Baustein vorschalten. Ansonsten kann es sein dass zu viel Strom aus dem Arduino fließt und er beschädigt wird. Der TTL Pegel von 0V / 5 V könnte vielleicht ausreichen, aber der Strom kann kritisch werden. Das hängt vom Eingangswiderstand des Muliplexers ab und was der Arduino abgeben kann / maximal darf.
            Vom Multiplexer etwas in den Arduino geben geht gar nicht, da muss immer ein Schnittstellenbaustein zwischen.

  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
  7. Ulrich Sprenger

    Hallo,

    der Script funktioniert super, allerdings müsste ich ihn für meinen Plotter die NMEA Sentences von $WIXDR auf $WIMDA umstellen. Da fehlt mir leider das Verständnis, wie man den Datensatz umstellt. Können Sie mir dabei helfen?

    Vielen Dank

    Antworten

Schreibe einen Kommentar zu Ronald Fuss Antworten abbrechen

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

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.