diff --git a/IODevice.h b/IODevice.h
index 5155aaf..99394b4 100644
--- a/IODevice.h
+++ b/IODevice.h
@@ -1,4 +1,5 @@
/*
+ * © 2023, Paul Antoine, Discord user @ADUBOURG
* © 2021, Neil McKechnie. All rights reserved.
*
* This file is part of DCC++EX API
@@ -410,6 +411,7 @@ private:
#include "IO_MCP23008.h"
#include "IO_MCP23017.h"
#include "IO_PCF8574.h"
+#include "IO_PCF8575.h"
#include "IO_duinoNodes.h"
#include "IO_EXIOExpander.h"
diff --git a/IO_PCF8575.h b/IO_PCF8575.h
new file mode 100644
index 0000000..1b271ec
--- /dev/null
+++ b/IO_PCF8575.h
@@ -0,0 +1,106 @@
+/*
+ * © 2023, Paul Antoine, and Discord user @ADUBOURG
+ * © 2021, Neil McKechnie. All rights reserved.
+ *
+ * This file is part of DCC++EX API
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * It is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with CommandStation. If not, see .
+ */
+
+/*
+ * The PCF8575 is a simple device; it only has one register. The device
+ * input/output mode and pullup are configured through this, and the
+ * output state is written and the input state read through it too.
+ *
+ * This is accomplished by having a weak resistor in series with the output,
+ * and a read-back of the other end of the resistor. As an output, the
+ * pin state is set to 1 or 0, and the output voltage goes to +5V or 0V
+ * (through the weak resistor).
+ *
+ * In order to use the pin as an input, the output is written as
+ * a '1' in order to pull up the resistor. Therefore the input will be
+ * 1 unless the pin is pulled down externally, in which case it will be 0.
+ *
+ * As a consequence of this approach, it is not possible to use the device for
+ * inputs without pullups.
+ */
+
+#ifndef IO_PCF8575_H
+#define IO_PCF8575_H
+
+#include "IO_GPIOBase.h"
+#include "FSH.h"
+
+class PCF8575 : public GPIOBase {
+public:
+ static void create(VPIN firstVpin, uint8_t nPins, uint8_t I2CAddress, int interruptPin=-1) {
+ if (checkNoOverlap(firstVpin, nPins, I2CAddress)) new PCF8575(firstVpin, min(nPins,(uint8_t)16), I2CAddress, interruptPin);
+ }
+
+private:
+ PCF8575(VPIN firstVpin, uint8_t nPins, uint8_t I2CAddress, int interruptPin=-1)
+ : GPIOBase((FSH *)F("PCF8575"), firstVpin, nPins, I2CAddress, interruptPin)
+ {
+ requestBlock.setReadParams(_I2CAddress, inputBuffer, sizeof(inputBuffer));
+ }
+
+ // The pin state is '1' if the pin is an input or if it is an output set to 1. Zero otherwise.
+ void _writeGpioPort() override {
+ I2CManager.write(_I2CAddress, 2, _portOutputState | ~_portMode, (_portOutputState | ~_portMode)>>8);
+ }
+
+ // The PCF8575 handles inputs by applying a weak pull-up when output is driven to '1'.
+ // Therefore, writing '1' in _writePortModes is enough to set the module to input mode
+ // and enable pull-up.
+ void _writePullups() override { }
+
+ // The pin state is '1' if the pin is an input or if it is an output set to 1. Zero otherwise.
+ void _writePortModes() override {
+ I2CManager.write(_I2CAddress, 2, _portOutputState | ~_portMode, (_portOutputState | ~_portMode)>>8);
+ }
+
+ // In immediate mode, _readGpioPort reads the device GPIO port and updates _portInputState accordingly.
+ // When not in immediate mode, it initiates a request using the request block and returns.
+ // When the request completes, _processCompletion finishes the operation.
+ void _readGpioPort(bool immediate) override {
+ if (immediate) {
+ uint8_t buffer[2];
+ I2CManager.read(_I2CAddress, buffer, 2);
+ _portInputState = ((uint16_t)buffer[1]<<8) | buffer[0];
+ } else {
+ requestBlock.wait(); // Wait for preceding operation to complete
+ // Issue new request to read GPIO register
+ I2CManager.queueRequest(&requestBlock);
+ }
+ }
+
+ // This function is invoked when an I/O operation on the requestBlock completes.
+ void _processCompletion(uint8_t status) override {
+ if (status == I2C_STATUS_OK)
+ _portInputState = ((uint16_t)inputBuffer[1]<<8) | inputBuffer[0];
+ else
+ _portInputState = 0xffff;
+ }
+
+ // Set up device ports
+ void _setupDevice() override {
+ _writePortModes();
+ _writeGpioPort();
+ _writePullups();
+ }
+
+ uint8_t inputBuffer[2];
+};
+
+#endif
\ No newline at end of file
diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt
index 5470f76..1c5e701 100644
--- a/myHal.cpp_example.txt
+++ b/myHal.cpp_example.txt
@@ -88,6 +88,21 @@ void halSetup() {
//PCF8574::create(200, 8, 0x23, 40);
+ //=======================================================================
+ // The following directive defines a PCF8575 16-port I2C GPIO Extender module.
+ //=======================================================================
+ // The parameters are:
+ // First Vpin=200
+ // Number of VPINs=16 (numbered 200-215)
+ // I2C address of module=0x23
+
+ //PCF8575::create(200, 16, 0x23);
+
+
+ // Alternative form using INT pin (see above)
+
+ //PCF8575::create(200, 16, 0x23, 40);
+
//=======================================================================
// The following directive defines an HCSR04 ultrasonic ranging module.
//=======================================================================