Mit dem Node.js Modul pcf8574 ist es möglich, jeden Pin eines PCF8574/PCF8574A Porterweiterungs-ICs einzeln zu kontrollieren. Es ist eine Eigenentwicklung von mir und kann über den Node.js Paketmanager npm installiert werden.

Der Quellcode ist öffentlich auf GitHub verfügbar. Hier können auch Fehler und Ideen gemeldet werden.

Was kann das Modul pcf8574?

Jeder Pin eine PCF8574/PCF8574A ICs kann einzeln entweder als Eingang oder als Ausgang definiert werden. Änderungen an Eingangs-Pins können über einen Interrupt oder durch aktives Polling erfasst werden.

Der PCF8574/PCF8574A ist ein 8-Bit Porterweiterungs-IC, welcher über den I²C-Bus angesteuert wird. Jeder der 8 Pins kann separat als Eingang oder Ausgang benutzt werden. Weiterhin bietet der IC ein Interrupt-Signal, welches dem I²C-Master (z.B. einem Raspberry Pi) mitteilen kann, dass sich etwas an einem Pin geändert hat. Weitere Informationen zu dem PCF8574/PCF8574A sind im Datenblatt von Texas Instruments zu finden.

Installation

npm install pcf8574

TypeScript-Definitionen sind in dem Paket enthalten. Somit kann es ohne Weiteres direkt mit TypeScript verwendet werden.

Das Modul sollte auf jedem Linux basierten Betriebssystem funktionieren, sofern eine I²C Schnittstelle vorhanden ist.

Zur Verwendung der Interrupt-Erkennung ist ein Raspberry Pi (oder ähnliches) erforderlich.

Beispiele

Beachte bitte, dass das i2c-bus Objekt vorher erstellt und zusammen mit der I²C-Adresse des ICs an den Konstruktor der PCF8574-Klasse übergeben werden muss.

Das folgende Beispiel ist auch zusammen mit einem TypeScript-Beispiel im Repository unter examples zu finden.

// Einbinden des pcf8574 Moduls
const PCF8574 = require('pcf8574').PCF8574;

// Oder importieren nach ES6-Style
// import { PCF8574 } from 'pcf8574';

// Einbinden des i2c-bus Moduls und öffnen des Bus
const i2cBus = require('i2c-bus').openSync(1);

// Die Adresse des PCF8574/PCF8574A festlegen
const addr = 0x38;

// Initialisieren eines neuen PCF8574 mit allen Pins auf high-Level
// Anstelle von 'true' kann auch eine 8-Bit Bitmaske verwendet werden,
// um jeden Pin einzeln festzulegen (z.B. 0b00101010)
const pcf = new PCF8574(i2cBus, addr, true);

// Aktiviere die Interrupt-Erkennung auf BCM-Pin 17 (dies ist GPIO.0)
pcf.enableInterrupt(17);

// Alternativ kann auch beispielsweise ein Intervall verwendet werden,
// um manuell alle 250ms Änderungen abzufragen
// setInterval(pcf.doPoll.bind(pcf), 250);

// Beachte das Fehlende ; am Ende der folgenden Zeilen.
// Die ist eine Promise-Kette!

// Definiere Pin 0 des ICs als invertierten Ausgang mit dem Anfangswert false
pcf.outputPin(0, true, false)

// Dann definiere Pin 1 als invertierten Ausgang mit dem Anfangswert true
.then(() => {
  return pcf.outputPin(1, true, true);
})

// Dann definiere Pin 7 als nicht invertierten Eingang
.then(() => {
  return pcf.inputPin(7, false);
})

// Warte eine Sekunde
.then(() => new Promise((resolve) => {
  setTimeout(resolve, 1000);
}))
 
// Dann schalte Pin 0 ein
.then(() => {
  console.log('turn pin 0 on');
  return pcf.setPin(0, true);
})

// Warte eine Sekunde
.then(() => new Promise((resolve) => {
  setTimeout(resolve, 1000);
}))

// Dann schalte Pin 0 aus
.then(() => {
  console.log('turn pin 0 off');
  return pcf.setPin(0, false);
});

// Fügen einen Event-Listener dem 'input'-Event hinzu
pcf.on('input', (data) => {
  console.log('input', data);

  // Prüfe ob ein Taster, der mit Pin 7 verbunden ist,
  // gedrückt wurde (Signal am Pin geht auf low)
  if(data.pin === 7 && data.value === false){
    // Pin 1 toggeln
    pcf.setPin(1);
  }
});

// Handler zum Aufräumen bei einem SIGINT (ctrl+c) 
process.on('SIGINT', () => {
  pcf.removeAllListeners();
  pcf.disableInterrupt();
});

API

Die API verwendet Events für erkannte Änderungen an Eingängen und Promises für alle asynchronen Aktionen.

Änderungen an Eingängen können auf zwei Wege erkannt werden:

  • Unter Verwendung eines GPIO zur Beachtung des Interrupt-Signal vom PCF8574/PCF8574A. Empfohlen bei Verwendung eines Raspberry Pi oder ähnlichem.
  • Manueller Aufruf der Funktion doPoll() in regelmäßigen Abständen, um aktiv den aktuellen Status des ICs abzufragen. Dies führt zu einer höheren Last auf dem I²C-Bus.

Wenn ein Pin als Eingang definiert ist und eine Änderung an diesem Pin erkannt wird, dann wir ein input-Event ausgelöst. Diesem Event wird ein Objekt mit dem Pin (pin) und dem neuen Wert (value) mitgegeben.

Für jeden Pin kann das invertiert-Flag einzeln gesetzt werden, was einen invertierten Eingang oder Ausgang zur Folge hat. Wenn ein invertierter Eingang ein low-Level aufweist wird dies als true interpretiert und ein high-Level als false. Ein invertierter Ausgang wird ein low-Level aufweisen, wenn er auf true gesetzt wird und ein high-Level bei false.

new PCF8574(i2cBus, address, initialState)

constructor (i2cBus: I2CBus, address: number, initialState: boolean | number);

Konstruktor für eine neue PCF8574-Instanz.

  • i2cBus – Instanz eines geöffneten i2c-bus.
  • address – Die Adresse des PCF8574/PCF8574A ICs.
  • initialState – Der Anfangszustand der Pins des ICs. Es kann eine Bitmaske (z.B. 0b00101010) verwendet werden, um jeden Pin einzeln festzulegen, oder true/false, um alle Pins auf einmal zu definieren.

Beachte bitte, dass das i2c-bus Objekt vorher erstellt und an den Konstruktor der PCF8574-Klasse übergeben werden muss.

Wenn der IC mit einem oder mehreren Eingängen verwendet wird, dann muss eine der folgenden Funktionen aufgerufen werden:

  • enableInterrupt(gpioPin), um die Interrupt-Erkennung über einen GPIO-Pin zu aktivieren, oder
  • doPoll() in regelmäßigen Abständen, um Eingangsänderungen durch manuelles Abfragen zu erkennen.

enableInterrupt(gpioPin)

<code>enableInterrupt (gpioPin: PCF8574.PinNumber): void;

Aktiviert die Interrupt-Erkennung am angegebenen GPIO-Pin. Ein GPIO-Pin kann für mehrere Instanzen der PCF8574-Klasse verwendet werden.

  • gpioPin – BCM-Nummer des Pins, der für Interrupt-Erkennung des PCF8574/PCF8574A ICs genutzt wird.

disableInterrupt()

disableInterrupt (): void;

Deaktiviert die Interrupt-Erkennung. Gibt außerdem dem GPIO-Pin wieder frei.

doPoll()

doPoll (): Promise<void>;

Manuell Änderungen an Eingangs-Pins des PCF8574/PCF8574A abfragen.

Wenn eine Änderung erkannt wurde, dann wird das input-Event ausgelöst. Diesem Event wird ein Objekt mit dem Pin (pin) und dem neuen Wert (value) des Pins mitgegeben.

Diese Funktion muss in regelmäßigen Abständen aufgerufen werden, wenn keine Interrupt-Erkennung verwendet wird. Erfolgt ein neuer Poll bevor der letzte abgeschlossen ist, dann wird das Promise mit einem Fehler zurückgewiesen.

outputPin(pin, inverted, initialValue)

outputPin (pin: PCF8574.PinNumber, inverted: boolean, initialValue?: boolean): Promise<void>;

Definiert einen Pin als Ausgang.

  • pin – Die Nummer des Pins. (0 bis 7)
  • inverted – true wenn der Pin invertiert sein soll.
  • initialValue – (optional) Der Anfangswert des Pins. Wird beim Einrichten des Pins gesetzt.

inputPin(pin, inverted)

inputPin (pin: PCF8574.PinNumber, inverted: boolean): Promise<void>;

Definiert einen Pin als Eingang. Dies markiert einen Pin für die Auswertung von Signaländerungen und setzt einen high-Pegel an diesem Pin.

  • pin – Die Nummer des Pins. (0 bis 7)
  • inverted – true wenn der Pin invertiert sein soll.

Achtung: Der Pin wird immer intern auf einen high-Pegel gesetzt. (Pull-Up)

setPin(pin, value)

setPin (pin: PCF8574.PinNumber, value?: boolean): Promise<void>;

Setzt den Wert eines Ausgangs. Wenn kein neuer Wert angegeben wird, dann wird der Ausgang getoggelt.

  • pin – Die Nummer des Pins. (0 bis 7)
  • value – Der neue Wert für den Pin.

setAllPins(value)

setAllPins (value: boolean): Promise<void>;

Setzt den Wert aller Ausgänge.

  • value – Der neue Wert für alle Ausgangs-Pins.

getPinValue(pin)

getPinValue (pin: PCF8574.PinNumber): boolean;

Gibt den aktuellen Wert eines Pins zurück. Dies ist der zuletzt gespeicherte Zustand des Pins und nicht zwangsläufig der aktuell am IC anliegende Zustand. Um den aktuellen Zustand zu erhalten muss zuerst doPoll() aufgerufen werden, wenn keine Interrupts verwendet werden.

Lizenz

Lizenziert unter der GPL Version 2

Copyright ©2017-2020 Peter Müller