Skip to content
On this page

Raspberry Pi: Temperatur via I2C

In diesem Versuch werden wir einen Temperatursensor des Typs SHT-40 via I2C Bus auslesen.

Um den Sensor auszulesen verwenden wir die smbus Library in NodeJS und Python. Um zu sehen, ob der Sensor korrekt angeschlossen worden ist verwenden wir die utility i2c-detect von i2c-utils.

Temperatursensor anschliessen und prüfen

Der Temperatursensor hat eine I2C / SMBus Schnittstelle. Der SMBus und die I2C Schnittstelle sind in der Grundfunktion kompatibel, im SMBus Standard sind jedoch noch ein paar Zusatzfeatures definiert.[1]

I2C Signale haben bekanntlich die Bezeichnung SCL (Serial Clock) sowie SDA (Serial Data).

Bevorzugt verwendet man bei den Raspis die zweite I2C Schnittstelle (GPIO2/GPIO3), denn die erste ist zur Identifikation von PiHAT Erweiterungsboards reserviert. Beachten Sie aber, dass die Schnittstellen ab 0 zählen!

  • Schliessen Sie den Temperatursensor am GPIO-Header des Raspi korrekt (!) an, zuerst GND, dann VCC 3.3V (nicht 5V !!!) und dann an die I2C Schnittstellensignale. Die Steckerbelegung der Raspi GPIO Pinleiste finden Sie bekanntlich auf Seite 2 des Versuchs 2 und jene des Temperatursensors im obigen Datenblatts. Als Speisepins verwenden Sie die den SPI Pins nächstgelegenen:
SignalGPIO NrPin NrIC Pin Nr
GND
VCC +3.3V
SCL
SDA

Um zu überprüfen, ob die Schnittstelle verfügbar ist und welche I2C-Devices an dieser erkannt werden, sind die i2c-tools recht nützlich. Installieren Sie dieses Package und inspizieren Sie danach die zweite I2C-Schnittstelle (also jene mit der Nummer 1) mit:

bash
i2cdetect -y 1
  • Oops, offensichtlich ist diese Schnittelle noch nicht verfügbar…

Sie können diese Schnittstelle entweder in der Datei /boot/config.txt über den Parameter dtparam=i2c_arm=on aktiveren oder etwas bequemer, mittels dem bereits bekannten Programm raspi-config über den Punkt Interface Options.

Nun sollte i2cdetect -y 1 den gesamten Adressbereich der Schnittstelle /dev/i2c-1 scannen und anzeigen.

Wenn der Temperatursensor korrekt angeschlossen ist, wird dieser unter der I2C-Adresse Hex erkannt.

  • Der I2C-Bus hat eine 7-bit Adresse. Im Temperatursensor-Datenblatt auf der ersten Seite sind mehrere Adressen aufgeführt. Mit welchem Baustein-Suffix stimmt die obige Adresse überein?

  • Im Kapitel 4.5 des Datenblatts finden Sie Commands, welche Messungen starten, oder die Seriennummer ausgeben. Wie kann die Seriennummer ausgelesen werden?

Interpretation der Daten

Auf den Command 0xFD kommen 6 Byte 63 ce a1 87 e1 fc zurück.

  • Interpretieren Sie die Daten gemäss Datenblatt.
  • Wie errechnet sich die Temperatur in °C und die relative Luftfeuchtigkeit in %?
  • Tipp: Aufbau der Daten wird in der Tabelle 4.5 beschrieben.

NodeJS Programm zum Auslesen des Sensors

  • Erstellen Sie wie im vorherigen Versuch bereits gemacht ein neues Verzeichnis und initialisieren Sie es mit yarn für NodeJS.
  • Nicht vergessen, setzen Sie "type": "module", in package.json.
  • Erstellen Sie ein File sensor.js.

sensor.js:

js
console.log('sensor')

Buffer

Verifizieren Sie, dass alles klappt mit yarn node sensor.js. Sie sollten output sehen.

Das Modul Buffer

Buffer https://nodejs.org/api/buffer.html erlaubt den Umgang mit Byte Arrays und bietet Funktionalität um Byte basierte zu allozieren und interpretieren.

Schauen Sie sich folgende Methoden in der Dokumentation an:

  • alloc
  • readUInt16BE und readUInt16LE
  • writeUint8

Buffer werden wir Einsetzen um Rohdaten via i2c Transfer abzuspeichern und zu senden.

i2c-bus

Das Modul i2c-bus https://github.com/fivdi/i2c-bus#readme erlaubt uns in NodeJS Daten über i2c zu senden und zu empfangen.

  • Fügen Sie dem Projekt die Abhängigkeit via yarn add i2c-bus hinzu.
  • Lesen Sie in der Dokumentation Informationen zu folgenden Funktionen / Attributen
    • openSync
    • i2cWriteSync
    • i2cReadSync

Vorlage

Verwenden Sie für sensor.js folgende Vorlage:

js
import * as i2c from "i2c-bus";

const delay = async (ms) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

(async () => {
  const bus = i2c.openSync(1);
  const DEVICE_ADDR = 0x00; // ?? welche Adresse?

  const buffer = Buffer.alloc(0); // ?? wieviel Byte Platz braucht es hier?
  const wbuffer = Buffer.alloc(1);
  wbuffer.writeUint8(0xfd, 0);

  // mit writeSync den Buffer mit dem Command senden
  await delay(110); // warten
  // mit readSync die Antwort auslesen.

  console.log(buffer); // Buffer loggen

  // temperatur und luftfeuchtigkeit aus buffer extrahieren
  // ...und gemäss formel berechnen
})();
  • Implementieren Sie die fehlenden Zeilen im Programm!
SPOILER: Musterlösung
js
import * as i2c from "i2c-bus";

const delay = async (ms) => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};

(async () => {
  const bus = i2c.openSync(1);
  const DEVICE_ADDR = 0x44;

  const buffer = Buffer.alloc(6);
  const wbuffer = Buffer.alloc(1);
  wbuffer.writeUint8(0xfd, 0);

  bus.i2cWriteSync(DEVICE_ADDR, 1, wbuffer);
  await delay(110);
  bus.i2cReadSync(DEVICE_ADDR, 6, buffer);

  console.log(buffer);

  const temp = buffer.readUInt16BE(0);
  const tC = -45 + (175 * temp) / (Math.pow(2, 16) - 1);
  console.log(tC);

  const rh = buffer.readUInt16BE(3);
  const rhPct = -6 + (125 * rh) / (Math.pow(2, 16) - 1);
  console.log(rhPct);
})();

Web Server mit Express erstellen

Im Teil Web-Applikationen werden wir den serverseitigen Umgang mit Express erlernen. Hier eine kleine Erweiterung, welche uns erlaubt bereits via Web API auf die Sensorwerte zuzugreifen.

Express als Dependency laden und server starten

js
import express from "express";

// ... i2c code

const app = express()
const port = 3000

app.get('/', async (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Sensor app listening on port ${port}`)
})

Der Webserver startet bereits.

  • Greifen Sie auf die Webseite vom Hostsystem her zu via Webbrowser: http://ip-des-raspis:3000. Natürlich muss das Raspi im gleichen Netz sein, wie die Hostmaschine.

Hat das geklappt, dann bauen wir unseren I2C Code noch etwas um. Wir returnen als resultat ein Objekt:

json
{
    "tC": 23.1,
    "rhPct": 52.3
}

Hierzu packen wir den Code in eine Methode readData:

js
import express from "express";
const readData = async () => {
  
  // i2c code

  return {
    rhPct,
    tC,
  };
};


const app = express()
const port = 3000

app.get('/', async (req, res) => {
  const data = readData();
  res.send(data)
})

app.listen(port, () => {
  console.log(`Sensor app listening on port ${port}`)
})
  • Verbinden Sie nochmals zur Webseite. Hat alles geklapp, sehen Sie die Daten bereits als JSON formattiert! Natürlich fehlt jetzt ein hübsches User Interface, dieses können Sie im Teil "Web-Applikationen" nachbauen.

  1. Die Unterschiede liegen im Bereich der zulässigen Datenrate (welche beim SMBus engere Grenzen hat), leicht unterschiedlich definierter logische Schaltschwellen sowie bez. Protokoll beim SMBus einer Re-START Möglichkeit sowie optionaler SMBus Packet Error Checking (PEC). (vgl. http://www.ti.com/lit/an/sloa132/sloa132.pdf) ↩︎