package org.myrobotlab.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.myrobotlab.framework.Service;
import org.myrobotlab.framework.ServiceType;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.Logging;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.service.Arduino.I2CDeviceMap;
import org.myrobotlab.service.interfaces.DeviceControl;
import org.myrobotlab.service.interfaces.DeviceController;
import org.myrobotlab.service.interfaces.I2CControl;
import org.myrobotlab.service.interfaces.I2CController;
import org.myrobotlab.service.interfaces.ServiceInterface;
import org.slf4j.Logger;
/**
*
* I2CMux - This is the MyRobotLab Service that can be used if you have several
* i2c devices that share the same address. Create one I2CMux for each of the
* i2c buses that you want to use. It can be used with tca9548a and possibly
* other devices.
*
*
* @author Mats Onnerby
*
* More Info : https://www.adafruit.com/product/2717
*
*/
public class I2cMux extends Service implements I2CControl, I2CController {
private static final long serialVersionUID = 1L;
public final static Logger log = LoggerFactory.getLogger(I2cMux.class.getCanonicalName());
transient I2CController controller;
public List<String> controllers = new ArrayList<String>();
public String controllerName;
public List<String> deviceAddressList = Arrays.asList("0x70", "0x71", "0x72", "0x73", "0x74", "0x75", "0x76", "0x77");
public String deviceAddress = "0x70";
public List<String> deviceBusList = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8");
public String deviceBus = "1";
private boolean isAttached = false;
private int lastBusAddress = -1;
transient HashMap<String, I2CDeviceMap> i2cDevices = new HashMap<String, I2CDeviceMap>();
public static void main(String[] args) {
LoggingFactory.getInstance().configure();
LoggingFactory.getInstance().setLevel(Level.DEBUG);
try {
I2cMux i2cMux = (I2cMux) Runtime.start("i2cMux", "I2CMux");
Runtime.start("gui", "GUIService");
} catch (Exception e) {
Logging.logError(e);
}
}
public I2cMux(String n) {
super(n);
refreshControllers();
subscribe(Runtime.getInstance().getName(), "registered", this.getName(), "onRegistered");
}
public void onRegistered(ServiceInterface s) {
refreshControllers();
broadcastState();
}
public void refreshControllers() {
controllers = Runtime.getServiceNamesFromInterface(I2CController.class);
controllers.remove(this.getName());
broadcastState();
}
public void setDeviceBus(String deviceBus) {
this.deviceBus = deviceBus;
broadcastState();
}
public void setDeviceAddress(String deviceAddress) {
this.deviceAddress = deviceAddress;
broadcastState();
}
@Override
public void createI2cDevice(I2CControl control, int busAddress, int deviceAddress) {
// Create a new i2c device in case it doesn't already exists.
String key = String.format("%s.%d", this.deviceBus, deviceAddress);
if (i2cDevices.containsKey(key)) {
// Nothing to do, already exists
} else {
I2CDeviceMap deviceData = new I2CDeviceMap();
deviceData.busAddress = Integer.parseInt(this.deviceBus);
deviceData.deviceAddress = deviceAddress;
deviceData.control = this;
controller.createI2cDevice(this, deviceData.busAddress, deviceAddress);
}
}
@Override
public void releaseI2cDevice(I2CControl control, int busAddress, int deviceAddress) {
// Can't release the device at the lowest level since several devices may
// exist
// with the same i2c address. It can always be reused.
// controller.releaseI2cDevice(this, busAddress, deviceAddress);
}
/**
* This methods sets the i2c Controller that will be used to communicate with
* the i2c device
*/
// @Override
public boolean setController(String controllerName, String deviceBus, String deviceAddress) {
this.controllerName = controllerName;
return setController((I2CController) Runtime.getService(controllerName), deviceBus, deviceAddress);
}
public boolean setController(String controllerName) {
this.controllerName = controllerName;
return setController((I2CController) Runtime.getService(controllerName), this.deviceBus, this.deviceAddress);
}
public boolean setController(I2CController controller) {
return setController(controller, this.deviceBus, this.deviceAddress);
}
/**
* This methods sets the i2c Controller that will be used to communicate with
* the i2c device
*/
public boolean setController(I2CController controller, String deviceBus, String deviceAddress) {
if (controller == null) {
error("setting null as controller");
return false;
}
controllerName = controller.getName();
this.controller = controller;
this.deviceBus = deviceBus;
this.deviceAddress = deviceAddress;
isAttached = true;
log.info(String.format("%s setController %s", getName(), controllerName));
createDevice();
broadcastState();
return true;
}
/**
* This method creates the i2c device
*/
boolean createDevice() {
if (controller != null) {
controller.releaseI2cDevice(this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress));
controller.createI2cDevice(this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress));
}
log.info(String.format("Creating device on bus: %s address %s", deviceBus, deviceAddress));
return true;
}
public void unsetController() {
controller = null;
controllerName = null;
this.deviceBus = null;
this.deviceAddress = null;
isAttached = false;
broadcastState();
}
public I2CController getController() {
return controller;
}
public String getControllerName() {
String controlerName = null;
if (controller != null) {
controlerName = controller.getName();
}
return controlerName;
}
public boolean isAttached() {
return isAttached;
}
public void setMuxBus(int busAddress) {
if (busAddress != lastBusAddress) {
byte bus[] = new byte[1];
bus[0] = (byte) (1 << busAddress);
log.debug(String.format("setMux this.deviceBus %s this.deviceAddress %s bus[0] %s", this.deviceBus, this.deviceAddress, bus[0]));
controller.i2cWrite(this, Integer.parseInt(this.deviceBus), Integer.decode(this.deviceAddress), bus, bus.length);
lastBusAddress = busAddress;
}
}
@Override
public void i2cWrite(I2CControl control, int busAddress, int deviceAddress, byte[] buffer, int size) {
setMuxBus(busAddress);
String key = String.format("%d.%d", busAddress, deviceAddress);
log.debug(String.format("i2cWrite busAddress x%02X deviceAddress x%02X key %s", busAddress, deviceAddress, key));
controller.i2cWrite(this, Integer.parseInt(this.deviceBus), deviceAddress, buffer, size);
}
/**
* TODO Add demuxing. i.e the route back to the caller The i2c will receive
* data that neeeds to be returned syncronous or asycncronus
*/
@Override
public int i2cRead(I2CControl control, int busAddress, int deviceAddress, byte[] buffer, int size) {
setMuxBus(busAddress);
int bytesRead = controller.i2cRead(this, Integer.parseInt(this.deviceBus), deviceAddress, buffer, size);
log.info(String.format("i2cRead. Requested %s bytes, received %s byte", size, bytesRead));
return bytesRead;
}
/**
* TODO Add demuxing. i.e the route back to the caller The i2c will receive
* data that neeeds to be returned syncronous or asycncronus
*/
@Override
public int i2cWriteRead(I2CControl control, int busAddress, int deviceAddress, byte[] writeBuffer, int writeSize, byte[] readBuffer, int readSize) {
setMuxBus(busAddress);
controller.i2cWriteRead(this, Integer.parseInt(this.deviceBus), deviceAddress, writeBuffer, writeSize, readBuffer, readSize);
return readBuffer.length;
}
/**
* This static method returns all the details of the class without it having
* to be constructed. It has description, categories, dependencies, and peer
* definitions.
*
* @return ServiceType - returns all the data
*
*/
static public ServiceType getMetaData() {
ServiceType meta = new ServiceType(I2cMux.class.getCanonicalName());
meta.addDescription("Multiplexer for i2c to be able to use multiple i2c devices");
meta.addCategory("i2c", "control");
meta.setAvailable(true);
meta.setSponsor("Mats");
return meta;
}
@Override
public void setController(DeviceController controller) {
setController(controller);
}
@Override
public void deviceAttach(DeviceControl device, Object... conf) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void deviceDetach(DeviceControl device) {
// TODO Auto-generated method stub
}
}