Skip to content

Commit fe2082d

Browse files
committed
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.
1 parent ef14905 commit fe2082d

File tree

6 files changed

+121
-4
lines changed

6 files changed

+121
-4
lines changed

java/libraries/io/src/processing/io/GPIO.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ protected static void checkValidPin(int pin) {
177177
public static int digitalRead(int pin) {
178178
checkValidPin(pin);
179179

180+
if (NativeInterface.isSimulated()) {
181+
return LOW;
182+
}
183+
180184
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
181185
byte in[] = new byte[2];
182186
int ret = NativeInterface.readFile(fn, in);
@@ -222,6 +226,10 @@ public static void digitalWrite(int pin, int value) {
222226
throw new IllegalArgumentException("Illegal value");
223227
}
224228

229+
if (NativeInterface.isSimulated()) {
230+
return;
231+
}
232+
225233
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
226234
int ret = NativeInterface.writeFile(fn, out);
227235
if (ret < 0) {
@@ -278,6 +286,10 @@ protected static void enableInterrupt(int pin, int mode) {
278286
throw new IllegalArgumentException("Unknown mode");
279287
}
280288

289+
if (NativeInterface.isSimulated()) {
290+
return;
291+
}
292+
281293
String fn = String.format("/sys/class/gpio/gpio%d/edge", pin);
282294
int ret = NativeInterface.writeFile(fn, out);
283295
if (ret < 0) {
@@ -325,6 +337,10 @@ public static void noInterrupts() {
325337
public static void pinMode(int pin, int mode) {
326338
checkValidPin(pin);
327339

340+
if (NativeInterface.isSimulated()) {
341+
return;
342+
}
343+
328344
// export pin through sysfs
329345
String fn = "/sys/class/gpio/export";
330346
int ret = NativeInterface.writeFile(fn, Integer.toString(pin));
@@ -409,6 +425,10 @@ public static void releaseInterrupt(int pin) {
409425
public static void releasePin(int pin) {
410426
checkValidPin(pin);
411427

428+
if (NativeInterface.isSimulated()) {
429+
return;
430+
}
431+
412432
String fn = "/sys/class/gpio/unexport";
413433
int ret = NativeInterface.writeFile(fn, Integer.toString(pin));
414434
if (ret < 0) {
@@ -451,6 +471,14 @@ public static boolean waitForInterrupt(int pin, int mode, int timeout) {
451471
protected static boolean waitForInterrupt(int pin, int timeout) {
452472
checkValidPin(pin);
453473

474+
if (NativeInterface.isSimulated()) {
475+
// pretend the interrupt happens after 200ms
476+
try {
477+
Thread.sleep(200);
478+
} catch (InterruptedException e) {}
479+
return true;
480+
}
481+
454482
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
455483
int ret = NativeInterface.pollDevice(fn, timeout);
456484
if (ret < 0) {

java/libraries/io/src/processing/io/I2C.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public class I2C {
5050
public I2C(String dev) {
5151
NativeInterface.loadLibrary();
5252
this.dev = dev;
53+
54+
if (NativeInterface.isSimulated()) {
55+
return;
56+
}
57+
5358
handle = NativeInterface.openDevice("/dev/" + dev);
5459
if (handle < 0) {
5560
throw new RuntimeException(NativeInterface.getError(handle));
@@ -81,6 +86,10 @@ public void beginTransmission(int slave) {
8186
* @webref
8287
*/
8388
public void close() {
89+
if (NativeInterface.isSimulated()) {
90+
return;
91+
}
92+
8493
NativeInterface.closeDevice(handle);
8594
handle = 0;
8695
}
@@ -107,6 +116,10 @@ public void endTransmission() {
107116
return;
108117
}
109118

119+
if (NativeInterface.isSimulated()) {
120+
return;
121+
}
122+
110123
// implement these flags if needed: https://github.com/raspberrypi/linux/blob/rpi-patches/Documentation/i2c/i2c-protocol
111124
int ret = NativeInterface.transferI2c(handle, slave, out, null);
112125
transmitting = false;
@@ -126,6 +139,11 @@ public void endTransmission() {
126139
* @webref
127140
*/
128141
public static String[] list() {
142+
if (NativeInterface.isSimulated()) {
143+
// as on the Raspberry Pi
144+
return new String[]{ "i2c-1" };
145+
}
146+
129147
ArrayList<String> devs = new ArrayList<String>();
130148
File dir = new File("/dev");
131149
File[] files = dir.listFiles();
@@ -159,6 +177,10 @@ public byte[] read(int len) {
159177

160178
byte[] in = new byte[len];
161179

180+
if (NativeInterface.isSimulated()) {
181+
return in;
182+
}
183+
162184
int ret = NativeInterface.transferI2c(handle, slave, out, in);
163185
transmitting = false;
164186
out = null;

java/libraries/io/src/processing/io/LED.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public LED(String dev) {
5353
NativeInterface.loadLibrary();
5454
this.dev = dev;
5555

56+
if (NativeInterface.isSimulated()) {
57+
return;
58+
}
59+
5660
// read maximum brightness
5761
try {
5862
Path path = Paths.get("/sys/class/leds/" + dev + "/max_brightness");
@@ -105,11 +109,16 @@ public LED(String dev) {
105109
* @webref
106110
*/
107111
public void brightness(float bright) {
108-
String fn = "/sys/class/leds/" + dev + "/brightness";
109112
if (bright < 0.0 || 1.0 < bright) {
110113
System.err.println("Brightness must be between 0.0 and 1.0.");
111114
throw new IllegalArgumentException("Illegal argument");
112115
}
116+
117+
if (NativeInterface.isSimulated()) {
118+
return;
119+
}
120+
121+
String fn = "/sys/class/leds/" + dev + "/brightness";
113122
int ret = NativeInterface.writeFile(fn, Integer.toString((int)(bright * maxBrightness)));
114123
if (ret < 0) {
115124
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
@@ -122,6 +131,10 @@ public void brightness(float bright) {
122131
* @webref
123132
*/
124133
public void close() {
134+
if (NativeInterface.isSimulated()) {
135+
return;
136+
}
137+
125138
// restore previous settings
126139
String fn = "/sys/class/leds/" + dev + "/brightness";
127140
int ret = NativeInterface.writeFile(fn, Integer.toString(prevBrightness));
@@ -143,6 +156,11 @@ public void close() {
143156
* @webref
144157
*/
145158
public static String[] list() {
159+
if (NativeInterface.isSimulated()) {
160+
// as on the Raspberry Pi
161+
return new String[]{ "led0", "led1" };
162+
}
163+
146164
ArrayList<String> devs = new ArrayList<String>();
147165
File dir = new File("/sys/class/leds");
148166
File[] files = dir.listFiles();

java/libraries/io/src/processing/io/NativeInterface.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,28 @@
2626
public class NativeInterface {
2727

2828
protected static boolean loaded = false;
29+
protected static boolean alwaysSimulate = false;
2930

3031
public static void loadLibrary() {
3132
if (!loaded) {
32-
if (!"Linux".equals(System.getProperty("os.name"))) {
33-
throw new RuntimeException("The Processing I/O library is only supported on Linux");
33+
if (isSimulated()) {
34+
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.");
35+
} else {
36+
System.loadLibrary("processing-io");
3437
}
35-
System.loadLibrary("processing-io");
3638
loaded = true;
3739
}
3840
}
3941

42+
public static void alwaysSimulate() {
43+
alwaysSimulate = true;
44+
}
45+
46+
public static boolean isSimulated() {
47+
return alwaysSimulate ||
48+
!"Linux".equals(System.getProperty("os.name"));
49+
}
50+
4051

4152
public static native int openDevice(String fn);
4253
public static native String getError(int errno);

java/libraries/io/src/processing/io/PWM.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public PWM(String channel) {
5757
chip = channel.substring(0, pos);
5858
this.channel = Integer.parseInt(channel.substring(pos+4));
5959

60+
if (NativeInterface.isSimulated()) {
61+
return;
62+
}
63+
6064
// export channel through sysfs
6165
String fn = "/sys/class/pwm/" + chip + "/export";
6266
int ret = NativeInterface.writeFile(fn, Integer.toString(this.channel));
@@ -89,6 +93,10 @@ public PWM(String channel) {
8993
* @webref
9094
*/
9195
public void clear() {
96+
if (NativeInterface.isSimulated()) {
97+
return;
98+
}
99+
92100
String fn = String.format("/sys/class/pwm/%s/pwm%d/enable", chip, channel);
93101
int ret = NativeInterface.writeFile(fn, "0");
94102
if (ret < 0) {
@@ -102,6 +110,10 @@ public void clear() {
102110
* @webref
103111
*/
104112
public void close() {
113+
if (NativeInterface.isSimulated()) {
114+
return;
115+
}
116+
105117
// XXX: implicit clear()?
106118
// XXX: also check GPIO
107119

@@ -124,6 +136,10 @@ public void close() {
124136
* @webref
125137
*/
126138
public static String[] list() {
139+
if (NativeInterface.isSimulated()) {
140+
return new String[]{ "pwmchip0/pwm0", "pwmchip0/pwm1" };
141+
}
142+
127143
ArrayList<String> devs = new ArrayList<String>();
128144
File dir = new File("/sys/class/pwm");
129145
File[] chips = dir.listFiles();
@@ -155,6 +171,10 @@ public static String[] list() {
155171
* @webref
156172
*/
157173
public void set(int period, float duty) {
174+
if (NativeInterface.isSimulated()) {
175+
return;
176+
}
177+
158178
// set period
159179
String fn = fn = String.format("/sys/class/pwm/%s/pwm%d/period", chip, channel);
160180
// convert to nanoseconds

java/libraries/io/src/processing/io/SPI.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ public class SPI {
7878
public SPI(String dev) {
7979
NativeInterface.loadLibrary();
8080
this.dev = dev;
81+
82+
if (NativeInterface.isSimulated()) {
83+
return;
84+
}
85+
8186
handle = NativeInterface.openDevice("/dev/" + dev);
8287
if (handle < 0) {
8388
throw new RuntimeException(NativeInterface.getError(handle));
@@ -90,6 +95,10 @@ public SPI(String dev) {
9095
* @webref
9196
*/
9297
public void close() {
98+
if (NativeInterface.isSimulated()) {
99+
return;
100+
}
101+
93102
NativeInterface.closeDevice(handle);
94103
handle = 0;
95104
}
@@ -110,6 +119,11 @@ protected void finalize() throws Throwable {
110119
* @webref
111120
*/
112121
public static String[] list() {
122+
if (NativeInterface.isSimulated()) {
123+
// as on the Raspberry Pi
124+
return new String[]{ "spidev0.0", "spidev0.1" };
125+
}
126+
113127
ArrayList<String> devs = new ArrayList<String>();
114128
File dir = new File("/dev");
115129
File[] files = dir.listFiles();
@@ -148,6 +162,10 @@ public void settings(int maxSpeed, int dataOrder, int mode) {
148162
* @webref
149163
*/
150164
public byte[] transfer(byte[] out) {
165+
if (NativeInterface.isSimulated()) {
166+
return new byte[out.length];
167+
}
168+
151169
// track the current setting per device across multiple instances
152170
String curSettings = maxSpeed + "-" + dataOrder + "-" + mode;
153171
if (!curSettings.equals(settings.get(dev))) {

0 commit comments

Comments
 (0)