Appearance
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:
Signal | GPIO Nr | Pin Nr | IC 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"
, inpackage.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
undreadUInt16LE
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.
- Fügen Sie mit
yarn add express
, denexpress
Webserver hinzu. - Studieren Sie das Hello World von
express
: https://expressjs.com/en/starter/hello-world.html
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.
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) ↩︎