/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.io.NativeInterface;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @webref
*/
public class I2C {
protected String dev;
protected int handle;
protected int slave;
protected byte[] out;
protected boolean transmitting;
/**
* Opens an I2C interface as master
* @param dev interface name
* @see list
* @webref
*/
public I2C(String dev) {
NativeInterface.loadLibrary();
this.dev = dev;
if (NativeInterface.isSimulated()) {
return;
}
handle = NativeInterface.openDevice("/dev/" + dev);
if (handle < 0) {
throw new RuntimeException(NativeInterface.getError(handle));
}
}
/**
* Begins a transmission to an attached device
* @see write
* @see read
* @see endTransmission
* @webref
*/
public void beginTransmission(int slave) {
// addresses 120 (0x78) to 127 are additionally reserved
if (0x78 <= slave) {
System.err.println("beginTransmission expects a 7 bit address, try shifting one bit to the right");
throw new IllegalArgumentException("Illegal address");
}
this.slave = slave;
transmitting = true;
out = null;
}
/**
* Closes the I2C device
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
NativeInterface.closeDevice(handle);
handle = 0;
}
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Ends the current transmissions
* @see beginTransmission
* @see write
* @webref
*/
public void endTransmission() {
if (!transmitting) {
// silently ignore this case
return;
}
if (NativeInterface.isSimulated()) {
return;
}
// implement these flags if needed: https://github.com/raspberrypi/linux/blob/rpi-patches/Documentation/i2c/i2c-protocol
int ret = NativeInterface.transferI2c(handle, slave, out, null);
transmitting = false;
out = null;
if (ret < 0) {
if (ret == -5) { // EIO
System.err.println("The device did not respond. Check the cabling and whether you are using the correct address.");
}
throw new RuntimeException(NativeInterface.getError(ret));
}
}
/**
* Lists all available I2C interfaces
* @return String array
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
// as on the Raspberry Pi
return new String[]{ "i2c-1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/dev");
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().startsWith("i2c-")) {
devs.add(file.getName());
}
}
}
// listFiles() does not guarantee ordering
String[] tmp = devs.toArray(new String[devs.size()]);
Arrays.sort(tmp);
return tmp;
}
/**
* Reads bytes from the attached device
* @param len number of bytes to read
* @return bytes read from device
* @see beginTransmission
* @see write
* @see endTransmission
* @webref
*/
public byte[] read(int len) {
if (!transmitting) {
throw new RuntimeException("beginTransmisson has not been called");
}
byte[] in = new byte[len];
if (NativeInterface.isSimulated()) {
return in;
}
int ret = NativeInterface.transferI2c(handle, slave, out, in);
transmitting = false;
out = null;
if (ret < 0) {
if (ret == -5) { // EIO
System.err.println("The device did not respond. Check the cabling and whether you are using the correct address.");
}
throw new RuntimeException(NativeInterface.getError(ret));
}
return in;
}
/**
* Adds bytes to be written to the device
* @param out bytes to be written
* @see beginTransmission
* @see read
* @see endTransmission
* @webref
*/
public void write(byte[] out) {
if (!transmitting) {
throw new RuntimeException("beginTransmisson has not been called");
}
if (this.out == null) {
this.out = out;
} else {
byte[] tmp = new byte[this.out.length + out.length];
System.arraycopy(this.out, 0, tmp, 0, this.out.length);
System.arraycopy(out, 0, tmp, this.out.length, out.length);
this.out = tmp;
}
}
/**
* Adds bytes to be written to the attached device
* @param out string to be written
* @see beginTransmission
* @see read
* @see endTransmission
*/
public void write(String out) {
write(out.getBytes());
}
/**
* Adds a byte to be written to the attached device
* @param out single byte to be written, e.g. numeric literal (0 to 255, or -128 to 127)
* @see beginTransmission
* @see read
* @see endTransmission
*/
public void write(int out) {
if (out < -128 || 255 < out) {
System.err.println("The write function can only operate on a single byte at a time. Call it with a value from 0 to 255, or -128 to 127.");
throw new RuntimeException("Argument does not fit into a single byte");
}
byte[] tmp = new byte[1];
tmp[0] = (byte)out;
write(tmp);
}
/**
* Adds a byte to be written to the attached device
* @param out single byte to be written
* @see beginTransmission
* @see read
* @see endTransmission
*/
public void write(byte out) {
// cast to (unsigned) int
write(out & 0xff);
}
}