mgr's weblog

uLisp on M5Stack (ESP32):
controlling relays connected to I2C via a PCF8574

October 18, 2021, Lisp
Last edited on October 18, 2021

relay module connected to I2C via a PCF8574; click for a larger version (180 kB).

Looking at the data sheet of the PCF8574 I found that it will be trivially simple to use it to control a relay board without any lower level Arduino library: Just write a second byte in addtion to the address to the I2C bus directly with uLisp's WITH-I2C.

Each bit of the byte describes the state of one of the eight outputs, or rather its inverted state as the PCF8574 has open-drain outputs and thus setting an output to LOW opens a connection to ground (with up to 25 mA), while HIGH disables the relay. (The data sheets actually say they are push-pull outputs but as high-level output the maximum current is just 1 mA which is not much and for this purpuse certainly not enough.)

The whole job can basically done with one or two lines. Here is switching on the forth relay (that is number 3 with zero-based counting):

(with-i2c (str #x20)
  (write-byte (logand #xff (lognot (ash 1 3))) str))

Here is my whole initial library:

#| control a relay module connected to I2C via a PCF8574 module |#
#| written by Max-Gerd Retzlaff <>, 2021 |#
#| the current state of the relay |#
(defvar *relay* 0)
#| address of the PCF8574 module connected to the relay |#
(defvar *relay-address* #x20)
#| show state of relay as binary number |#
(defun show-relay ()
  (format nil "~8,'0b" *relay*))
#| translate *relay* to relay byte as sent to the PCF8574 |#
(defun relay-byte ()
  (logand #xff (lognot *relay*)))
#| actually set real relay via i2c |#
(defun set-relay ()
  (with-i2c (str #x20)
    (write-byte (relay-byte) str)))
#| initialize relay |#
(defvar init-relay set-relay)
#| switch on relay N |#
(defun relay-on (n)
  (setf *relay* (logior *relay* (ash 1 n)))
#| switch off relay N |#
(defun relay-off (n)
  (setf *relay* (logand *relay* (lognot (ash 1 n))))
#| set relay N to STATE |#
(defun relay! (n state)
  ((if state relay-on relay-off) n))
#| query state of relay N |#
(defun relay? (n)
  (= 1 (logand 1 (ash *relay* (- n)))))

Be sure to read the newer data sheets "PCF8574 Remote 8-Bit I/O Expander for I2C Bus" by Texas Instruments, revised in March 2015, or "PCF8574; PCF8574A – Remote 8-bit I/O expander for I2C-bus with interrup" by NXP, revised on 27 May 2013, and not the ancient one by Philips of 2002 that many link to. The new ones are much more detailed and explanatory.

See also "Stand-alone uLisp computer (with code!)", "temperature sensors via one wire", "Curl/Wget for uLisp", time via NTP, lispstring without escaping and more space, flash support, muting of the speaker and backlight control and uLisp on M5Stack (ESP32).

Trackback pings for this entry are listed below. The URL to ping for this entry is:

Select a Theme:

Basilique du Sacré-Cœur de Montmartre (Paris) Parc Floral de Paris Castillo de Santa Barbara (Alicante) About the photos