[New] Added TCA9534OutputConnection from PCF8574OutputConnection
This commit is contained in:
parent
aa9c8197b3
commit
f62cac696b
|
@ -0,0 +1,206 @@
|
|||
package li.strolch.plc.core.hw.i2c;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
|
||||
import static li.strolch.utils.collections.CollectionsHelper.byteStream;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
|
||||
import static li.strolch.utils.helper.StringHelper.toHexString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import com.pi4j.io.i2c.I2CBus;
|
||||
import com.pi4j.io.i2c.I2CDevice;
|
||||
import com.pi4j.io.i2c.I2CFactory;
|
||||
import li.strolch.plc.core.hw.Plc;
|
||||
import li.strolch.plc.core.hw.connections.SimplePlcConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class Multi8BitI2cOutputConnection extends SimplePlcConnection {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(Multi8BitI2cOutputConnection.class);
|
||||
|
||||
protected boolean verbose;
|
||||
protected boolean resetOnConnect;
|
||||
protected int i2cBusNr;
|
||||
protected int nrOfBits;
|
||||
protected boolean inverted;
|
||||
protected boolean reversed;
|
||||
|
||||
protected byte[] addresses;
|
||||
protected I2CDevice[] outputDevices;
|
||||
protected byte[] states;
|
||||
|
||||
protected Map<String, int[]> positionsByAddress;
|
||||
|
||||
public Multi8BitI2cOutputConnection(Plc plc, String id) {
|
||||
super(plc, id);
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public String getDescription() {
|
||||
return "I2C Output " + getName() + " @ " + byteStream(this.addresses).map(b -> "0x" + toHexString(b))
|
||||
.collect(joining(", "));
|
||||
}
|
||||
|
||||
public String getDescription(byte address) {
|
||||
return "I2C Output " + getName() + " @ 0x" + toHexString(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Map<String, Object> parameters) {
|
||||
this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED);
|
||||
|
||||
if (!parameters.containsKey("i2cBus"))
|
||||
throw new IllegalArgumentException("Missing param i2cBus");
|
||||
if (!parameters.containsKey("addresses"))
|
||||
throw new IllegalArgumentException("Missing param addresses");
|
||||
|
||||
this.verbose = parameters.containsKey("verbose") && (Boolean) parameters.get("verbose");
|
||||
this.resetOnConnect = parameters.containsKey("resetOnConnect") && (Boolean) parameters.get("resetOnConnect");
|
||||
this.nrOfBits = parameters.containsKey("nrOfBits") ? ((Integer) parameters.get("nrOfBits")) : 8;
|
||||
this.i2cBusNr = (int) parameters.get("i2cBus");
|
||||
this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted");
|
||||
this.reversed = parameters.containsKey("reversed") && (boolean) parameters.get("reversed");
|
||||
|
||||
logger.info("inverted: " + this.inverted);
|
||||
logger.info("reversed: " + this.reversed);
|
||||
logger.info("nrOfBits: " + this.nrOfBits);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> addressList = (List<Integer>) parameters.get("addresses");
|
||||
this.addresses = new byte[addressList.size()];
|
||||
for (int i = 0; i < addressList.size(); i++) {
|
||||
this.addresses[i] = addressList.get(i).byteValue();
|
||||
}
|
||||
|
||||
Map<String, int[]> positionsByAddress = new HashMap<>();
|
||||
for (int i = 0; i < this.addresses.length; i++) {
|
||||
for (int j = 0; j < this.nrOfBits; j++)
|
||||
positionsByAddress.put(this.id + "." + i + "." + j, new int[] { i, j });
|
||||
}
|
||||
this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress);
|
||||
|
||||
logger.info("Configured " + getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
if (this.simulated) {
|
||||
logger.warn(getName() + ": " + this.id + ": Running SIMULATED, NOT CONNECTING!");
|
||||
return super.connect();
|
||||
}
|
||||
|
||||
if (isConnected()) {
|
||||
logger.warn(getName() + ": " + this.id + ": Already connected");
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.info(getName() + ": " + this.id + ": Connecting...");
|
||||
|
||||
// initialize
|
||||
try {
|
||||
I2CBus i2cBus = I2CFactory.getInstance(this.i2cBusNr);
|
||||
|
||||
this.outputDevices = new I2CDevice[this.addresses.length];
|
||||
this.states = new byte[this.addresses.length];
|
||||
byte[] bytes = this.addresses;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
byte address = bytes[i];
|
||||
I2CDevice i2cDev = i2cBus.getDevice(address);
|
||||
this.outputDevices[i] = i2cDev;
|
||||
}
|
||||
|
||||
if (setup()) {
|
||||
logger.info("Successfully connected " + this.outputDevices.length + " devices as " + getDescription());
|
||||
return super.connect();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} catch (Throwable e) {
|
||||
handleBrokenConnection("Failed to connect to " + getDescription() + ": " + getExceptionMessageWithCauses(e),
|
||||
e);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean setup() throws IOException {
|
||||
boolean ok = true;
|
||||
|
||||
I2CDevice[] devices = this.outputDevices;
|
||||
for (int index = 0; index < devices.length; index++) {
|
||||
byte address = this.addresses[index];
|
||||
I2CDevice outputDevice = devices[index];
|
||||
ok &= setup(address, index, outputDevice);
|
||||
logger.info("Connected " + getDescription(address));
|
||||
}
|
||||
|
||||
if (ok)
|
||||
return ok;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract boolean setup(byte address, int index, I2CDevice outputDevice) throws IOException;
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
if (this.simulated) {
|
||||
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
|
||||
super.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
this.outputDevices = null;
|
||||
this.states = null;
|
||||
|
||||
super.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAddresses() {
|
||||
return new TreeSet<>(this.positionsByAddress.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String address, Object value) {
|
||||
if (this.simulated) {
|
||||
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
|
||||
return;
|
||||
}
|
||||
|
||||
assertConnected();
|
||||
|
||||
int[] pos = this.positionsByAddress.get(address);
|
||||
if (pos == null)
|
||||
throw new IllegalStateException("Address is illegal " + address);
|
||||
|
||||
int device = pos[0];
|
||||
int pin = pos[1];
|
||||
|
||||
I2CDevice outputDevice = this.outputDevices[device];
|
||||
|
||||
boolean high = (boolean) value;
|
||||
|
||||
// see if we need to invert
|
||||
if (this.inverted)
|
||||
high = !high;
|
||||
|
||||
try {
|
||||
synchronized (this) {
|
||||
setPin(device, pin, outputDevice, high);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleBrokenConnection("Failed to write to I2C address: " + address + " at " + getDescription(
|
||||
(byte) outputDevice.getAddress()) + ": " + getExceptionMessageWithCauses(e), e);
|
||||
throw new IllegalStateException("Failed to write to I2C address " + address + " at " + getDescription(
|
||||
(byte) outputDevice.getAddress()), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void setPin(int device, int pin, I2CDevice outputDevice, boolean high) throws IOException;
|
||||
}
|
|
@ -1,198 +1,73 @@
|
|||
package li.strolch.plc.core.hw.i2c;
|
||||
|
||||
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
|
||||
import static li.strolch.utils.helper.ByteHelper.*;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
|
||||
import static li.strolch.utils.helper.StringHelper.toHexString;
|
||||
import static li.strolch.utils.helper.StringHelper.toPrettyHexString;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.pi4j.io.i2c.I2CBus;
|
||||
import com.pi4j.io.i2c.I2CDevice;
|
||||
import com.pi4j.io.i2c.I2CFactory;
|
||||
import li.strolch.plc.core.hw.Plc;
|
||||
import li.strolch.plc.core.hw.connections.SimplePlcConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class PCF8574OutputConnection extends SimplePlcConnection {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PCF8574OutputConnection.class);
|
||||
|
||||
private boolean verbose;
|
||||
private boolean resetOnConnect;
|
||||
private int i2cBusNr;
|
||||
private boolean inverted;
|
||||
|
||||
private byte[] addresses;
|
||||
private I2CDevice[] outputDevices;
|
||||
private byte[] states;
|
||||
|
||||
private Map<String, int[]> positionsByAddress;
|
||||
public class PCF8574OutputConnection extends Multi8BitI2cOutputConnection {
|
||||
|
||||
public PCF8574OutputConnection(Plc plc, String id) {
|
||||
super(plc, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Map<String, Object> parameters) {
|
||||
this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED);
|
||||
|
||||
if (!parameters.containsKey("i2cBus"))
|
||||
throw new IllegalArgumentException("Missing param i2cBus");
|
||||
if (!parameters.containsKey("addresses"))
|
||||
throw new IllegalArgumentException("Missing param addresses");
|
||||
|
||||
this.verbose = parameters.containsKey("verbose") && (Boolean) parameters.get("verbose");
|
||||
this.resetOnConnect = parameters.containsKey("resetOnConnect") && (Boolean) parameters.get("resetOnConnect");
|
||||
this.i2cBusNr = (int) parameters.get("i2cBus");
|
||||
this.inverted = parameters.containsKey("inverted") && (boolean) parameters.get("inverted");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> addressList = (List<Integer>) parameters.get("addresses");
|
||||
this.addresses = new byte[addressList.size()];
|
||||
for (int i = 0; i < addressList.size(); i++) {
|
||||
this.addresses[i] = addressList.get(i).byteValue();
|
||||
}
|
||||
|
||||
Map<String, int[]> positionsByAddress = new HashMap<>();
|
||||
for (int i = 0; i < this.addresses.length; i++) {
|
||||
for (int j = 0; j < 8; j++)
|
||||
positionsByAddress.put(this.id + "." + i + "." + j, new int[] { i, j });
|
||||
}
|
||||
this.positionsByAddress = Collections.unmodifiableMap(positionsByAddress);
|
||||
|
||||
logger.info("Configured PCF8574 Output on I2C addresses 0x " + toPrettyHexString(this.addresses));
|
||||
public String getName() {
|
||||
return "PCF8574";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
if (this.simulated) {
|
||||
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
|
||||
return super.connect();
|
||||
}
|
||||
protected boolean setup() throws IOException {
|
||||
boolean ok = super.setup();
|
||||
|
||||
if (isConnected()) {
|
||||
logger.warn(this.id + ": Already connected");
|
||||
if (ok)
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.info(this.id + ": Connecting...");
|
||||
handleBrokenConnection("Failed to set initial values to " + asBinary((byte) 0xff) + " for " + getDescription(),
|
||||
null);
|
||||
|
||||
// initialize
|
||||
try {
|
||||
I2CBus i2cBus = I2CFactory.getInstance(i2cBusNr);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.outputDevices = new I2CDevice[this.addresses.length];
|
||||
this.states = new byte[this.addresses.length];
|
||||
byte[] bytes = this.addresses;
|
||||
boolean ok = true;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
byte address = bytes[i];
|
||||
@Override
|
||||
protected boolean setup(byte address, int index, I2CDevice i2cDev) throws IOException {
|
||||
boolean ok = true;
|
||||
|
||||
this.outputDevices[i] = i2cBus.getDevice(address);
|
||||
|
||||
if (this.resetOnConnect) {
|
||||
// default is all outputs off, i.e. 1
|
||||
this.states[i] = (byte) 0xff;
|
||||
try {
|
||||
this.outputDevices[i].write(this.states[i]);
|
||||
logger.info("Set initial value to " + asBinary((byte) 0xff) + " for address 0x" + toHexString(
|
||||
address));
|
||||
} catch (Exception e) {
|
||||
ok = false;
|
||||
logger.error("Failed to set initial value to " + asBinary((byte) 0xff) + " on I2C Bus "
|
||||
+ this.i2cBusNr + " and address 0x" + toHexString(address), e);
|
||||
}
|
||||
} else {
|
||||
this.states[i] = (byte) this.outputDevices[i].read();
|
||||
logger.info(
|
||||
"Initial value is " + asBinary(this.states[i]) + " for address 0x" + toHexString(address));
|
||||
}
|
||||
|
||||
logger.info("Connected to I2C Device at address 0x" + toHexString(address) + " on I2C Bus "
|
||||
+ this.i2cBusNr);
|
||||
if (this.resetOnConnect) {
|
||||
// default is all outputs off, i.e. 1
|
||||
this.states[index] = (byte) 0xff;
|
||||
try {
|
||||
i2cDev.write(this.states[index]);
|
||||
logger.info("Set initial value to " + asBinary((byte) 0xff) + " for " + getDescription(address));
|
||||
} catch (Exception e) {
|
||||
ok = false;
|
||||
logger.error(
|
||||
"Failed to set initial value to " + asBinary((byte) 0xff) + " for " + getDescription(address),
|
||||
e);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
return super.connect();
|
||||
|
||||
handleBrokenConnection(
|
||||
"Failed to set initial values to " + asBinary((byte) 0xff) + " on I2C Bus " + this.i2cBusNr
|
||||
+ " and addresses 0x " + toPrettyHexString(this.addresses), null);
|
||||
return false;
|
||||
|
||||
} catch (Throwable e) {
|
||||
handleBrokenConnection(
|
||||
"Failed to connect to I2C Bus " + this.i2cBusNr + " and addresses 0x " + toPrettyHexString(
|
||||
this.addresses) + ": " + getExceptionMessageWithCauses(e), e);
|
||||
|
||||
return false;
|
||||
} else {
|
||||
this.states[index] = (byte) i2cDev.read();
|
||||
logger.info("Initial value is " + asBinary(this.states[index]) + " for " + getDescription(address));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
if (this.simulated) {
|
||||
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
|
||||
super.disconnect();
|
||||
return;
|
||||
}
|
||||
protected void setPin(int device, int pin, I2CDevice outputDevice, boolean high) throws IOException {
|
||||
byte newState;
|
||||
if (high)
|
||||
newState = clearBit(this.states[device], pin);
|
||||
else
|
||||
newState = setBit(this.states[device], pin);
|
||||
|
||||
this.outputDevices = null;
|
||||
this.states = null;
|
||||
if (this.verbose)
|
||||
logger.info("Setting " + getDescription((byte) outputDevice.getAddress()) + " to new state " + asBinary(
|
||||
newState));
|
||||
|
||||
super.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAddresses() {
|
||||
return new TreeSet<>(this.positionsByAddress.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String address, Object value) {
|
||||
if (this.simulated) {
|
||||
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
|
||||
return;
|
||||
}
|
||||
|
||||
assertConnected();
|
||||
|
||||
int[] pos = this.positionsByAddress.get(address);
|
||||
if (pos == null)
|
||||
throw new IllegalStateException("Address is illegal " + address);
|
||||
|
||||
int device = pos[0];
|
||||
int pin = pos[1];
|
||||
|
||||
boolean high = (boolean) value;
|
||||
|
||||
// see if we need to invert
|
||||
if (this.inverted)
|
||||
high = !high;
|
||||
|
||||
try {
|
||||
|
||||
synchronized (this) {
|
||||
byte newState;
|
||||
if (high)
|
||||
newState = clearBit(this.states[device], pin);
|
||||
else
|
||||
newState = setBit(this.states[device], pin);
|
||||
|
||||
if (this.verbose)
|
||||
logger.info("Setting 0x" + toHexString((byte) device) + " to new state " + asBinary(newState));
|
||||
|
||||
this.outputDevices[device].write(newState);
|
||||
this.states[device] = newState;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
handleBrokenConnection(
|
||||
"Failed to write to I2C address: " + address + ": " + getExceptionMessageWithCauses(e), e);
|
||||
throw new IllegalStateException("Failed to write to I2C address " + address, e);
|
||||
}
|
||||
outputDevice.write(newState);
|
||||
this.states[device] = newState;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package li.strolch.plc.core.hw.i2c;
|
||||
|
||||
import static li.strolch.utils.helper.ByteHelper.*;
|
||||
import static li.strolch.utils.helper.StringHelper.toHexString;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.pi4j.io.i2c.I2CDevice;
|
||||
import li.strolch.plc.core.hw.Plc;
|
||||
|
||||
public class TCA9534OutputConnection extends Multi8BitI2cOutputConnection {
|
||||
|
||||
private static final byte TCA9534_REG_ADDR_OUT_PORT = 0x01;
|
||||
private static final byte TCA9534_REG_ADDR_CFG = 0x03;
|
||||
|
||||
public TCA9534OutputConnection(Plc plc, String id) {
|
||||
super(plc, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "TCA9534";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean setup() throws IOException {
|
||||
boolean ok = super.setup();
|
||||
|
||||
if (ok)
|
||||
return true;
|
||||
|
||||
handleBrokenConnection("Failed to configure " + getDescription(), null);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean setup(byte address, int index, I2CDevice i2cDev) throws IOException {
|
||||
boolean ok = true;
|
||||
|
||||
// first read configuration
|
||||
int config = i2cDev.read(TCA9534_REG_ADDR_CFG);
|
||||
if (config < 0)
|
||||
throw new IllegalStateException(
|
||||
"Failed to read configuration from address 0x" + toHexString(TCA9534_REG_ADDR_CFG));
|
||||
|
||||
if (config != 0x00) {
|
||||
logger.warn(getDescription(address) + " is not configured as OUTPUT, setting register 0x" + toHexString(
|
||||
TCA9534_REG_ADDR_CFG) + " to 0x00");
|
||||
i2cDev.write(TCA9534_REG_ADDR_OUT_PORT, (byte) 0x00);
|
||||
i2cDev.write(TCA9534_REG_ADDR_CFG, (byte) 0x00);
|
||||
}
|
||||
|
||||
if (this.resetOnConnect) {
|
||||
|
||||
// default is all outputs off, i.e. 0
|
||||
this.states[index] = (byte) 0x00;
|
||||
try {
|
||||
i2cDev.write(TCA9534_REG_ADDR_OUT_PORT, this.states[index]);
|
||||
logger.info("Set initial value to " + asBinary((byte) 0x00) + " for " + getDescription(address));
|
||||
} catch (Exception e) {
|
||||
ok = false;
|
||||
logger.error(
|
||||
"Failed to set initial value to " + asBinary((byte) 0x00) + " for " + getDescription(address),
|
||||
e);
|
||||
}
|
||||
} else {
|
||||
byte currentState = (byte) i2cDev.read(TCA9534_REG_ADDR_OUT_PORT);
|
||||
|
||||
if (this.reversed)
|
||||
currentState = reverse(currentState);
|
||||
|
||||
this.states[index] = currentState;
|
||||
logger.info("Initial value is " + asBinary(this.states[index]) + " for " + getDescription(address));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setPin(int device, int pin, I2CDevice outputDevice, boolean high) throws IOException {
|
||||
byte newState;
|
||||
if (high)
|
||||
newState = setBit(this.states[device], pin);
|
||||
else
|
||||
newState = clearBit(this.states[device], pin);
|
||||
|
||||
byte writeState = this.reversed ? reverse(newState) : newState;
|
||||
|
||||
if (this.verbose)
|
||||
logger.info("Setting " + getDescription((byte) outputDevice.getAddress()) + " to new state " + asBinary(
|
||||
writeState));
|
||||
|
||||
outputDevice.write(TCA9534_REG_ADDR_OUT_PORT, writeState);
|
||||
this.states[device] = newState;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue