370 lines
11 KiB
Java
370 lines
11 KiB
Java
package li.strolch.plc.core.hw.i2c;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.text.MessageFormat;
|
|
|
|
import com.pi4j.io.i2c.I2CBus;
|
|
import com.pi4j.io.i2c.I2CDevice;
|
|
import com.pi4j.io.i2c.I2CFactory;
|
|
|
|
/**
|
|
* <p>Compile:</p>
|
|
* <code>javac -cp pi4j-core-1.4-SNAPSHOT.jar:. RSL366OverHorterI2cTest.java</code>
|
|
*
|
|
* <p>Run:</p>
|
|
* <code>java --add-exports java.base/jdk.internal.misc=ALL-UNNAMED -cp pi4j-core-1.4-SNAPSHOT.jar:.
|
|
* RSL366OverHorterI2cTest</code>
|
|
*/
|
|
public class RSL366OverHorterI2cTest {
|
|
|
|
static final byte ADDR_REG_SYS_CODE = 0x00;
|
|
static final byte ADDR_REG_DEV_CODE = 0x01;
|
|
static final byte ADDR_REG_CONF_CODE = 0x02;
|
|
|
|
static final byte LEN_SYS = 6;
|
|
static final byte LEN_DEV = 8;
|
|
static final byte LEN_CONF = 8;
|
|
|
|
static final byte ADDR_INFO_PTR = 0;
|
|
static final byte ADDR_INFO_STATUS = 1;
|
|
static final byte ADDR_INFO_TRANSMITTING = 2;
|
|
static final byte ADDR_INFO_PROTOCOL = 3;
|
|
static final byte ADDR_INFO_REPEATS = 4;
|
|
static final byte ADDR_INFO_VER_MAJOR = 5;
|
|
static final byte ADDR_INFO_VER_MINOR = 6;
|
|
static final byte ADDR_INFO_NR_OF_KNOWN_PROTOCOLS = 7;
|
|
|
|
static final byte TX_STATUS_OFF = 0x0F;
|
|
static final byte TX_STATUS_ACTIVE = (byte) 0xAC;
|
|
|
|
static final byte STATUS_OK = 0x00;
|
|
static final byte STATUS_SYS_TOO_MUCH_DATA = 0x01;
|
|
static final byte STATUS_SYS_MISSING_DATA = 0x02;
|
|
static final byte STATUS_SYS_INVALID_DATA = 0x03;
|
|
static final byte STATUS_SYS_MISSING = 0x04;
|
|
static final byte STATUS_DEV_TOO_MUCH_DATA = 0x05;
|
|
static final byte STATUS_DEV_INVALID_DATA = 0x06;
|
|
static final byte STATUS_PROTO_UNKNOWN = 0x07;
|
|
static final byte STATUS_BAD_PTR = 0x08;
|
|
static final byte STATUS_CONF_TOO_MUCH_DATA = 0x09;
|
|
|
|
static byte[] systemValues = new byte[] { 0, 0, 0, 0 };
|
|
|
|
static byte system;
|
|
static byte device;
|
|
|
|
static I2CDevice dev;
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
System.out.println();
|
|
|
|
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
|
|
|
|
System.out.println("Getting I2C device...");
|
|
I2CBus i2cBus = I2CFactory.getInstance(1);
|
|
dev = i2cBus.getDevice(0x18);
|
|
|
|
byte[] status = configure();
|
|
|
|
String version = status[ADDR_INFO_VER_MAJOR] + "." + status[ADDR_INFO_VER_MINOR];
|
|
System.out.println("Connected to Horter I2C to 433MHz version " + version + " supporting "
|
|
+ status[ADDR_INFO_NR_OF_KNOWN_PROTOCOLS] + " 433MHz protocols");
|
|
|
|
System.out.println();
|
|
readCodes(input);
|
|
System.out.println();
|
|
|
|
boolean run = true;
|
|
while (run) {
|
|
try {
|
|
|
|
System.out.println("Selected: " + system + "." + device);
|
|
System.out.print("Action [o|f|c|e|x]: ");
|
|
String action = input.readLine();
|
|
|
|
switch (action) {
|
|
case "o":
|
|
setState(system, device, true);
|
|
break;
|
|
case "f":
|
|
setState(system, device, false);
|
|
break;
|
|
case "c":
|
|
configure();
|
|
break;
|
|
case "e":
|
|
readCodes(input);
|
|
break;
|
|
case "x":
|
|
run = false;
|
|
break;
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Error: " + getExceptionMessageWithCauses(e, true));
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static byte[] configure() throws IOException, InterruptedException {
|
|
|
|
// configure
|
|
byte protocol = 2;
|
|
byte repeats = 1;
|
|
System.out.println("Configuring...");
|
|
byte[] data = { protocol, repeats };
|
|
System.out.println("=> " + toHexString(ADDR_REG_CONF_CODE) + " " + toHexString(data));
|
|
dev.write(ADDR_REG_CONF_CODE, data);
|
|
Thread.sleep(50L);
|
|
|
|
// validate configuration
|
|
byte[] status = readInfo(true);
|
|
if (status[ADDR_INFO_PROTOCOL] != protocol)
|
|
throw new IllegalStateException("Protocol could not be set to " + protocol);
|
|
if (status[ADDR_INFO_REPEATS] != repeats)
|
|
throw new IllegalStateException("Repeats could not bet set to " + repeats);
|
|
return status;
|
|
}
|
|
|
|
private static void setState(byte system, byte device, boolean state) throws Exception {
|
|
|
|
System.out.println("System: " + system);
|
|
System.out.println("Device: " + device);
|
|
|
|
byte[] status = readInfo(false);
|
|
if (isDeviceTransmitting(status)) {
|
|
Thread.sleep(100L);
|
|
waitForDeviceIdle();
|
|
}
|
|
|
|
configure();
|
|
|
|
// write system code
|
|
System.out.println("Writing system code...");
|
|
System.out.println("=> " + toHexString(ADDR_REG_SYS_CODE) + " " + toHexString(system));
|
|
dev.write(ADDR_REG_SYS_CODE, system);
|
|
Thread.sleep(5L);
|
|
status = readInfo(true);
|
|
if (isSystemCodeInvalid(status))
|
|
throw new IllegalStateException(
|
|
"SystemCode is invalid after sending systemCode: " + parseStatus(status[ADDR_INFO_STATUS]));
|
|
|
|
// write value code
|
|
byte value = state ? (byte) (device + 128) : device;
|
|
System.out.println("Writing value code...");
|
|
System.out.println("=> " + toHexString(ADDR_REG_DEV_CODE) + " " + toHexString(value));
|
|
dev.write(ADDR_REG_DEV_CODE, value);
|
|
Thread.sleep(5L);
|
|
status = readInfo(false);
|
|
if (isDeviceCodeInvalid(status))
|
|
throw new IllegalStateException(
|
|
"DeviceCode is invalid after sending deviceCode: " + parseStatus(status[ADDR_INFO_STATUS]));
|
|
if (!isDeviceTransmitting(status))
|
|
throw new IllegalStateException(
|
|
"Device is not transmitting after sending " + toHexString(system) + "." + toHexString(value)
|
|
+ "...");
|
|
|
|
showInfoRegister(status);
|
|
System.out.println(
|
|
"Successfully sent state change to " + (state ? "on" : "off") + " for device " + system + ", "
|
|
+ device);
|
|
}
|
|
|
|
private static void waitForDeviceIdle() throws Exception {
|
|
byte[] status = readInfo(false);
|
|
|
|
while (isDeviceTransmitting(status)) {
|
|
System.out.println("Device is transmitting, waiting...");
|
|
Thread.sleep(100L);
|
|
|
|
dev.read(ADDR_REG_CONF_CODE, status, 0, status.length);
|
|
}
|
|
|
|
byte errorStatus = status[ADDR_INFO_STATUS];
|
|
if (errorStatus != STATUS_OK)
|
|
throw new IllegalStateException("Device error: " + errorStatus + " " + parseStatus(errorStatus));
|
|
}
|
|
|
|
private static byte[] readInfo(boolean showInfoRegister) throws IOException {
|
|
byte[] status = new byte[LEN_CONF];
|
|
dev.read(ADDR_REG_CONF_CODE, status, 0, status.length);
|
|
System.out.println("<= " + toHexString(ADDR_REG_CONF_CODE) + " " + toHexString(status));
|
|
if (showInfoRegister)
|
|
showInfoRegister(status);
|
|
return status;
|
|
}
|
|
|
|
private static boolean isSystemCodeInvalid(byte[] status) {
|
|
byte error = status[ADDR_INFO_STATUS];
|
|
return error == STATUS_SYS_INVALID_DATA //
|
|
|| error == STATUS_SYS_MISSING //
|
|
|| error == STATUS_SYS_MISSING_DATA //
|
|
|| error == STATUS_SYS_TOO_MUCH_DATA;
|
|
}
|
|
|
|
private static boolean isDeviceCodeInvalid(byte[] status) {
|
|
byte error = status[ADDR_INFO_STATUS];
|
|
return error == STATUS_DEV_INVALID_DATA //
|
|
|| error == STATUS_DEV_TOO_MUCH_DATA;
|
|
}
|
|
|
|
private static void showInfoRegister(byte[] status) {
|
|
System.out.println(" Pointer : " + toHexString(status[ADDR_INFO_PTR]));
|
|
System.out.println(" Status : " + toHexString(status[ADDR_INFO_STATUS]) + " " + parseStatus(
|
|
status[ADDR_INFO_STATUS]));
|
|
System.out.println(" TX : " + toHexString(status[ADDR_INFO_TRANSMITTING]));
|
|
System.out.println(" Protocol : " + toHexString(status[ADDR_INFO_PROTOCOL]));
|
|
System.out.println(" Repeats : " + toHexString(status[ADDR_INFO_REPEATS]));
|
|
System.out.println(
|
|
" Version : " + status[ADDR_INFO_VER_MAJOR] + "." + status[ADDR_INFO_VER_MINOR]);
|
|
System.out.println(" Supported Protocols : " + status[ADDR_INFO_NR_OF_KNOWN_PROTOCOLS]);
|
|
}
|
|
|
|
private static boolean isDeviceTransmitting(byte[] status) {
|
|
return status[ADDR_INFO_TRANSMITTING] == TX_STATUS_ACTIVE;
|
|
}
|
|
|
|
private static String parseStatus(byte status) {
|
|
switch (status) {
|
|
case STATUS_OK:
|
|
return "OK";
|
|
case STATUS_SYS_TOO_MUCH_DATA:
|
|
return "Too much SystemCode data";
|
|
case STATUS_SYS_MISSING_DATA:
|
|
return "SystemCode missing data";
|
|
case STATUS_SYS_INVALID_DATA:
|
|
return "Invalid SystemCode";
|
|
case STATUS_SYS_MISSING:
|
|
return "SystemCode Missing";
|
|
case STATUS_DEV_TOO_MUCH_DATA:
|
|
return "Too much device data";
|
|
case STATUS_DEV_INVALID_DATA:
|
|
return "DeviceCode invalid";
|
|
case STATUS_PROTO_UNKNOWN:
|
|
return "Invalid protocol";
|
|
case STATUS_BAD_PTR:
|
|
return "Bad pointer";
|
|
case STATUS_CONF_TOO_MUCH_DATA:
|
|
return "Too much config data";
|
|
default:
|
|
return "Unknown status " + toHexString(status);
|
|
}
|
|
}
|
|
|
|
private static void readCodes(BufferedReader input) {
|
|
boolean notRead = true;
|
|
while (notRead) {
|
|
try {
|
|
System.out.print("System Code: ");
|
|
String systemCode = input.readLine();
|
|
system = Byte.decode(systemCode);
|
|
if (system < 1 || system > 4)
|
|
throw new IllegalStateException("System must be between 1 and 4 incl.");
|
|
|
|
System.out.print("Device Code: ");
|
|
String deviceCode = input.readLine();
|
|
device = Byte.decode(deviceCode);
|
|
if (device < 1 || device > 4)
|
|
throw new IllegalStateException("Device must be between 1 and 4 incl.");
|
|
|
|
notRead = false;
|
|
|
|
} catch (Exception e) {
|
|
System.err.println("Error: " + getExceptionMessageWithCauses(e, true));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static String toHexString(byte[] raw) throws RuntimeException {
|
|
return toHexString(raw, 0, raw.length);
|
|
}
|
|
|
|
public static String toHexString(byte data) {
|
|
return String.format("%02x", data);
|
|
}
|
|
|
|
public static String toHexString(byte[] raw, int offset, int length) throws RuntimeException {
|
|
try {
|
|
byte[] hex = new byte[2 * length];
|
|
int index = 0;
|
|
|
|
int pos = offset;
|
|
for (int i = 0; i < length; i++) {
|
|
byte b = raw[pos];
|
|
int v = b & 0xFF;
|
|
hex[index++] = HEX_CHAR_TABLE[v >>> 4];
|
|
hex[index++] = HEX_CHAR_TABLE[v & 0xF];
|
|
pos++;
|
|
}
|
|
|
|
return new String(hex, "ASCII"); //$NON-NLS-1$
|
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
String msg = MessageFormat
|
|
.format("Something went wrong while converting to HEX: {0}", e.getMessage()); //$NON-NLS-1$
|
|
throw new RuntimeException(msg, e);
|
|
}
|
|
}
|
|
|
|
private static final byte[] HEX_CHAR_TABLE = { (byte) '0',
|
|
(byte) '1',
|
|
(byte) '2',
|
|
(byte) '3',
|
|
(byte) '4',
|
|
(byte) '5',
|
|
(byte) '6',
|
|
(byte) '7',
|
|
(byte) '8',
|
|
(byte) '9',
|
|
(byte) 'a',
|
|
(byte) 'b',
|
|
(byte) 'c',
|
|
(byte) 'd',
|
|
(byte) 'e',
|
|
(byte) 'f' };
|
|
|
|
public static String asBinary(byte b) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append(((b >>> 7) & 1));
|
|
sb.append(((b >>> 6) & 1));
|
|
sb.append(((b >>> 5) & 1));
|
|
sb.append(((b >>> 4) & 1));
|
|
sb.append(((b >>> 3) & 1));
|
|
sb.append(((b >>> 2) & 1));
|
|
sb.append(((b >>> 1) & 1));
|
|
sb.append(((b >>> 0) & 1));
|
|
|
|
return sb.toString();
|
|
}
|
|
|
|
public static boolean isBitSet(byte data, int position) {
|
|
if (position > 7)
|
|
throw new IllegalStateException("Position " + position + " is not available in a byte!");
|
|
return ((data >> position) & 1) == 1;
|
|
}
|
|
|
|
public static boolean isEmpty(String value) {
|
|
return value == null || value.isEmpty();
|
|
}
|
|
|
|
public static String getExceptionMessage(Throwable t, boolean withClassName) {
|
|
if (withClassName || isEmpty(t.getMessage()))
|
|
return t.getClass().getName() + ": " + t.getMessage();
|
|
return t.getMessage();
|
|
}
|
|
|
|
public static String getExceptionMessageWithCauses(Throwable t, boolean withClassName) {
|
|
if (t.getCause() == null)
|
|
return getExceptionMessage(t, withClassName);
|
|
|
|
String root = getExceptionMessageWithCauses(t.getCause(), withClassName);
|
|
return getExceptionMessage(t, withClassName) + "\n" + root;
|
|
}
|
|
}
|