/* -*- 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); } }