IO: Simulate on all unsupported platforms

Instead of throwing an exception early on, this makes it possible to test all other parts of sketches that make light use of the IO library. A warning is printed once after startup for those environments.

This was tested on OS X against all examples that come with the IO library. Note: x86 and x64 are technically supported platforms. If a user on those prefers to use simulation instead, it is necessary to call NativeInterface.alwaysSimulate() before any other IO library function.
This commit is contained in:
gohai
2016-05-05 19:33:37 +02:00
parent ef1490566b
commit fe2082dcce
6 changed files with 121 additions and 4 deletions
@@ -177,6 +177,10 @@ public class GPIO {
public static int digitalRead(int pin) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
return LOW;
}
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
byte in[] = new byte[2];
int ret = NativeInterface.readFile(fn, in);
@@ -222,6 +226,10 @@ public class GPIO {
throw new IllegalArgumentException("Illegal value");
}
if (NativeInterface.isSimulated()) {
return;
}
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
int ret = NativeInterface.writeFile(fn, out);
if (ret < 0) {
@@ -278,6 +286,10 @@ public class GPIO {
throw new IllegalArgumentException("Unknown mode");
}
if (NativeInterface.isSimulated()) {
return;
}
String fn = String.format("/sys/class/gpio/gpio%d/edge", pin);
int ret = NativeInterface.writeFile(fn, out);
if (ret < 0) {
@@ -325,6 +337,10 @@ public class GPIO {
public static void pinMode(int pin, int mode) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
return;
}
// export pin through sysfs
String fn = "/sys/class/gpio/export";
int ret = NativeInterface.writeFile(fn, Integer.toString(pin));
@@ -409,6 +425,10 @@ public class GPIO {
public static void releasePin(int pin) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
return;
}
String fn = "/sys/class/gpio/unexport";
int ret = NativeInterface.writeFile(fn, Integer.toString(pin));
if (ret < 0) {
@@ -451,6 +471,14 @@ public class GPIO {
protected static boolean waitForInterrupt(int pin, int timeout) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
// pretend the interrupt happens after 200ms
try {
Thread.sleep(200);
} catch (InterruptedException e) {}
return true;
}
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
int ret = NativeInterface.pollDevice(fn, timeout);
if (ret < 0) {
@@ -50,6 +50,11 @@ public class I2C {
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));
@@ -81,6 +86,10 @@ public class I2C {
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
NativeInterface.closeDevice(handle);
handle = 0;
}
@@ -107,6 +116,10 @@ public class I2C {
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;
@@ -126,6 +139,11 @@ public class I2C {
* @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();
@@ -159,6 +177,10 @@ public class I2C {
byte[] in = new byte[len];
if (NativeInterface.isSimulated()) {
return in;
}
int ret = NativeInterface.transferI2c(handle, slave, out, in);
transmitting = false;
out = null;
+19 -1
View File
@@ -53,6 +53,10 @@ public class LED {
NativeInterface.loadLibrary();
this.dev = dev;
if (NativeInterface.isSimulated()) {
return;
}
// read maximum brightness
try {
Path path = Paths.get("/sys/class/leds/" + dev + "/max_brightness");
@@ -105,11 +109,16 @@ public class LED {
* @webref
*/
public void brightness(float bright) {
String fn = "/sys/class/leds/" + dev + "/brightness";
if (bright < 0.0 || 1.0 < bright) {
System.err.println("Brightness must be between 0.0 and 1.0.");
throw new IllegalArgumentException("Illegal argument");
}
if (NativeInterface.isSimulated()) {
return;
}
String fn = "/sys/class/leds/" + dev + "/brightness";
int ret = NativeInterface.writeFile(fn, Integer.toString((int)(bright * maxBrightness)));
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
@@ -122,6 +131,10 @@ public class LED {
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
// restore previous settings
String fn = "/sys/class/leds/" + dev + "/brightness";
int ret = NativeInterface.writeFile(fn, Integer.toString(prevBrightness));
@@ -143,6 +156,11 @@ public class LED {
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
// as on the Raspberry Pi
return new String[]{ "led0", "led1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/sys/class/leds");
File[] files = dir.listFiles();
@@ -26,17 +26,28 @@ package processing.io;
public class NativeInterface {
protected static boolean loaded = false;
protected static boolean alwaysSimulate = false;
public static void loadLibrary() {
if (!loaded) {
if (!"Linux".equals(System.getProperty("os.name"))) {
throw new RuntimeException("The Processing I/O library is only supported on Linux");
if (isSimulated()) {
System.err.println("The Processing I/O library is not supported on this platform. Instead of values from actual hardware ports, your sketch will only receive stand-in values that allow you to test the remainder of its functionality.");
} else {
System.loadLibrary("processing-io");
}
System.loadLibrary("processing-io");
loaded = true;
}
}
public static void alwaysSimulate() {
alwaysSimulate = true;
}
public static boolean isSimulated() {
return alwaysSimulate ||
!"Linux".equals(System.getProperty("os.name"));
}
public static native int openDevice(String fn);
public static native String getError(int errno);
@@ -57,6 +57,10 @@ public class PWM {
chip = channel.substring(0, pos);
this.channel = Integer.parseInt(channel.substring(pos+4));
if (NativeInterface.isSimulated()) {
return;
}
// export channel through sysfs
String fn = "/sys/class/pwm/" + chip + "/export";
int ret = NativeInterface.writeFile(fn, Integer.toString(this.channel));
@@ -89,6 +93,10 @@ public class PWM {
* @webref
*/
public void clear() {
if (NativeInterface.isSimulated()) {
return;
}
String fn = String.format("/sys/class/pwm/%s/pwm%d/enable", chip, channel);
int ret = NativeInterface.writeFile(fn, "0");
if (ret < 0) {
@@ -102,6 +110,10 @@ public class PWM {
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
// XXX: implicit clear()?
// XXX: also check GPIO
@@ -124,6 +136,10 @@ public class PWM {
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
return new String[]{ "pwmchip0/pwm0", "pwmchip0/pwm1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/sys/class/pwm");
File[] chips = dir.listFiles();
@@ -155,6 +171,10 @@ public class PWM {
* @webref
*/
public void set(int period, float duty) {
if (NativeInterface.isSimulated()) {
return;
}
// set period
String fn = fn = String.format("/sys/class/pwm/%s/pwm%d/period", chip, channel);
// convert to nanoseconds
@@ -78,6 +78,11 @@ public class SPI {
public SPI(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));
@@ -90,6 +95,10 @@ public class SPI {
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
NativeInterface.closeDevice(handle);
handle = 0;
}
@@ -110,6 +119,11 @@ public class SPI {
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
// as on the Raspberry Pi
return new String[]{ "spidev0.0", "spidev0.1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/dev");
File[] files = dir.listFiles();
@@ -148,6 +162,10 @@ public class SPI {
* @webref
*/
public byte[] transfer(byte[] out) {
if (NativeInterface.isSimulated()) {
return new byte[out.length];
}
// track the current setting per device across multiple instances
String curSettings = maxSpeed + "-" + dataOrder + "-" + mode;
if (!curSettings.equals(settings.get(dev))) {