diff --git a/build/build.xml b/build/build.xml
index 839ec6f169..1c70cf6fe9 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -45,6 +45,7 @@
+
@@ -70,11 +71,11 @@
value="../../processing-docs/content/examples" />
-
-
+
+
-
+
@@ -798,11 +799,13 @@
+
+
-
-
diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java
index 73e519871a..ecfbb1715c 100644
--- a/core/src/processing/opengl/PGL.java
+++ b/core/src/processing/opengl/PGL.java
@@ -1287,9 +1287,9 @@ protected PGL initTex2DShader() {
PGL ppgl = primaryPGL ? this : graphics.getPrimaryPGL();
if (!ppgl.loadedTex2DShader || ppgl.tex2DShaderContext != ppgl.glContext) {
- String[] preprocVertSrc = preprocessVertexSource(texVertShaderSource, getGLSLVersion());
+ String[] preprocVertSrc = preprocessVertexSource(texVertShaderSource, getGLSLVersion(), getGLSLVersionSuffix());
String vertSource = PApplet.join(preprocVertSrc, "\n");
- String[] preprocFragSrc = preprocessFragmentSource(tex2DFragShaderSource, getGLSLVersion());
+ String[] preprocFragSrc = preprocessFragmentSource(tex2DFragShaderSource, getGLSLVersion(), getGLSLVersionSuffix());
String fragSource = PApplet.join(preprocFragSrc, "\n");
ppgl.tex2DVertShader = createShader(VERTEX_SHADER, vertSource);
ppgl.tex2DFragShader = createShader(FRAGMENT_SHADER, fragSource);
@@ -1419,9 +1419,9 @@ protected PGL initTexRectShader() {
PGL ppgl = primaryPGL ? this : graphics.getPrimaryPGL();
if (!ppgl.loadedTexRectShader || ppgl.texRectShaderContext != ppgl.glContext) {
- String[] preprocVertSrc = preprocessVertexSource(texVertShaderSource, getGLSLVersion());
+ String[] preprocVertSrc = preprocessVertexSource(texVertShaderSource, getGLSLVersion(), getGLSLVersionSuffix());
String vertSource = PApplet.join(preprocVertSrc, "\n");
- String[] preprocFragSrc = preprocessFragmentSource(texRectFragShaderSource, getGLSLVersion());
+ String[] preprocFragSrc = preprocessFragmentSource(texRectFragShaderSource, getGLSLVersion(), getGLSLVersionSuffix());
String fragSource = PApplet.join(preprocFragSrc, "\n");
ppgl.texRectVertShader = createShader(VERTEX_SHADER, vertSource);
ppgl.texRectFragShader = createShader(FRAGMENT_SHADER, fragSource);
@@ -1848,6 +1848,7 @@ protected static int qualityToSamples(int quality) {
abstract protected int getGLSLVersion();
+ abstract protected String getGLSLVersionSuffix();
protected String[] loadVertexShader(String filename) {
@@ -1880,28 +1881,29 @@ protected String[] loadVertexShader(URL url) {
}
- protected String[] loadVertexShader(String filename, int version) {
+ protected String[] loadVertexShader(String filename, int version, String versionSuffix) {
return loadVertexShader(filename);
}
- protected String[] loadFragmentShader(String filename, int version) {
+ protected String[] loadFragmentShader(String filename, int version, String versionSuffix) {
return loadFragmentShader(filename);
}
- protected String[] loadFragmentShader(URL url, int version) {
+ protected String[] loadFragmentShader(URL url, int version, String versionSuffix) {
return loadFragmentShader(url);
}
- protected String[] loadVertexShader(URL url, int version) {
+ protected String[] loadVertexShader(URL url, int version, String versionSuffix) {
return loadVertexShader(url);
}
protected static String[] preprocessFragmentSource(String[] fragSrc0,
- int version) {
+ int version,
+ String versionSuffix) {
if (containsVersionDirective(fragSrc0)) {
// The user knows what she or he is doing
return fragSrc0;
@@ -1915,7 +1917,7 @@ protected static String[] preprocessFragmentSource(String[] fragSrc0,
int offset = 1;
fragSrc = preprocessShaderSource(fragSrc0, search, replace, offset);
- fragSrc[0] = "#version " + version;
+ fragSrc[0] = "#version " + version + versionSuffix;
} else {
// We need to replace 'texture' uniform by 'texMap' uniform and
// 'textureXXX()' functions by 'texture()' functions. Order of these
@@ -1932,15 +1934,20 @@ protected static String[] preprocessFragmentSource(String[] fragSrc0,
int offset = 2;
fragSrc = preprocessShaderSource(fragSrc0, search, replace, offset);
- fragSrc[0] = "#version " + version;
- fragSrc[1] = "out vec4 _fragColor;";
+ fragSrc[0] = "#version " + version + versionSuffix;
+ if (" es".equals(versionSuffix)) {
+ fragSrc[1] = "out mediump vec4 _fragColor;";
+ } else {
+ fragSrc[1] = "out vec4 _fragColor;";
+ }
}
return fragSrc;
}
protected static String[] preprocessVertexSource(String[] vertSrc0,
- int version) {
+ int version,
+ String versionSuffix) {
if (containsVersionDirective(vertSrc0)) {
// The user knows what she or he is doing
return vertSrc0;
@@ -1954,7 +1961,7 @@ protected static String[] preprocessVertexSource(String[] vertSrc0,
int offset = 1;
vertSrc = preprocessShaderSource(vertSrc0, search, replace, offset);
- vertSrc[0] = "#version " + version;
+ vertSrc[0] = "#version " + version + versionSuffix;
} else {
// We need to replace 'texture' uniform by 'texMap' uniform and
// 'textureXXX()' functions by 'texture()' functions. Order of these
@@ -1971,7 +1978,7 @@ protected static String[] preprocessVertexSource(String[] vertSrc0,
int offset = 1;
vertSrc = preprocessShaderSource(vertSrc0, search, replace, offset);
- vertSrc[0] = "#version " + version;
+ vertSrc[0] = "#version " + version + versionSuffix;
}
return vertSrc;
@@ -2225,7 +2232,7 @@ protected boolean hasPackedDepthStencilSupport() {
protected boolean hasAnisoSamplingSupport() {
int major = getGLVersion()[0];
- if (major < 3) {
+ if (isES() || major < 3) {
String ext = getString(EXTENSIONS);
return -1 < ext.indexOf("_texture_filter_anisotropic");
} else {
diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java
index f2e64514af..6b0f42c635 100644
--- a/core/src/processing/opengl/PGraphicsOpenGL.java
+++ b/core/src/processing/opengl/PGraphicsOpenGL.java
@@ -1561,13 +1561,14 @@ protected void restoreGL() {
}
pgl.depthFunc(PGL.LEQUAL);
- if (OPENGL_RENDERER.equals("VideoCore IV HW")) {
- // Broadcom's VC IV driver is unhappy with either of these
- // ignore for now
+ if (pgl.isES()) {
+ // neither GL_MULTISAMPLE nor GL_POLYGON_SMOOTH are part of GLES2 or GLES3
} else if (smooth < 1) {
pgl.disable(PGL.MULTISAMPLE);
} else if (1 <= smooth) {
pgl.enable(PGL.MULTISAMPLE);
+ }
+ if (!pgl.isES()) {
pgl.disable(PGL.POLYGON_SMOOTH);
}
@@ -6775,16 +6776,14 @@ protected void setGLSettings() {
// quality = temp;
// }
}
- if (OPENGL_RENDERER.equals("VideoCore IV HW")) {
- // Broadcom's VC IV driver is unhappy with either of these
- // ignore for now
+ if (pgl.isES()) {
+ // neither GL_MULTISAMPLE nor GL_POLYGON_SMOOTH are part of GLES2 or GLES3
} else if (smooth < 1) {
pgl.disable(PGL.MULTISAMPLE);
} else if (1 <= smooth) {
pgl.enable(PGL.MULTISAMPLE);
}
- // work around runtime exceptions in Broadcom's VC IV driver
- if (false == OPENGL_RENDERER.equals("VideoCore IV HW")) {
+ if (!pgl.isES()) {
pgl.disable(PGL.POLYGON_SMOOTH);
}
@@ -6895,8 +6894,12 @@ protected void getGLParameters() {
// overwrite the default shaders with vendor specific versions
// if needed
- if (OPENGL_RENDERER.equals("VideoCore IV HW") || // Broadcom's binary driver for Raspberry Pi
- OPENGL_RENDERER.contains("VC4")) { // Mesa driver for same hardware
+ if (OPENGL_RENDERER.equals("VideoCore IV HW")) { // Broadcom's binary driver for Raspberry Pi
+ defLightShaderVertURL =
+ PGraphicsOpenGL.class.getResource("/processing/opengl/shaders/LightVert-brcm.glsl");
+ defTexlightShaderVertURL =
+ PGraphicsOpenGL.class.getResource("/processing/opengl/shaders/TexLightVert-brcm.glsl");
+ } else if (OPENGL_RENDERER.contains("VC4")) { // Mesa driver for same hardware
defLightShaderVertURL =
PGraphicsOpenGL.class.getResource("/processing/opengl/shaders/LightVert-vc4.glsl");
defTexlightShaderVertURL =
diff --git a/core/src/processing/opengl/PJOGL.java b/core/src/processing/opengl/PJOGL.java
index 58f7618d9d..31b5669f73 100644
--- a/core/src/processing/opengl/PJOGL.java
+++ b/core/src/processing/opengl/PJOGL.java
@@ -509,50 +509,59 @@ protected int getGLSLVersion() {
return vn.getMajor() * 100 + vn.getMinor();
}
+ protected String getGLSLVersionSuffix() {
+ VersionNumber vn = context.getGLSLVersionNumber();
+ if (context.isGLESProfile() && 1 < vn.getMajor()) {
+ return " es";
+ } else {
+ return "";
+ }
+ }
+
@Override
protected String[] loadVertexShader(String filename) {
- return loadVertexShader(filename, getGLSLVersion());
+ return loadVertexShader(filename, getGLSLVersion(), getGLSLVersionSuffix());
}
@Override
protected String[] loadFragmentShader(String filename) {
- return loadFragmentShader(filename, getGLSLVersion());
+ return loadFragmentShader(filename, getGLSLVersion(), getGLSLVersionSuffix());
}
@Override
protected String[] loadVertexShader(URL url) {
- return loadVertexShader(url, getGLSLVersion());
+ return loadVertexShader(url, getGLSLVersion(), getGLSLVersionSuffix());
}
@Override
protected String[] loadFragmentShader(URL url) {
- return loadFragmentShader(url, getGLSLVersion());
+ return loadFragmentShader(url, getGLSLVersion(), getGLSLVersionSuffix());
}
@Override
- protected String[] loadFragmentShader(String filename, int version) {
+ protected String[] loadFragmentShader(String filename, int version, String versionSuffix) {
String[] fragSrc0 = sketch.loadStrings(filename);
- return preprocessFragmentSource(fragSrc0, version);
+ return preprocessFragmentSource(fragSrc0, version, versionSuffix);
}
@Override
- protected String[] loadVertexShader(String filename, int version) {
+ protected String[] loadVertexShader(String filename, int version, String versionSuffix) {
String[] vertSrc0 = sketch.loadStrings(filename);
- return preprocessVertexSource(vertSrc0, version);
+ return preprocessVertexSource(vertSrc0, version, versionSuffix);
}
@Override
- protected String[] loadFragmentShader(URL url, int version) {
+ protected String[] loadFragmentShader(URL url, int version, String versionSuffix) {
try {
String[] fragSrc0 = PApplet.loadStrings(url.openStream());
- return preprocessFragmentSource(fragSrc0, version);
+ return preprocessFragmentSource(fragSrc0, version, versionSuffix);
} catch (IOException e) {
PGraphics.showException("Cannot load fragment shader " + url.getFile());
}
@@ -561,10 +570,10 @@ protected String[] loadFragmentShader(URL url, int version) {
@Override
- protected String[] loadVertexShader(URL url, int version) {
+ protected String[] loadVertexShader(URL url, int version, String versionSuffix) {
try {
String[] vertSrc0 = PApplet.loadStrings(url.openStream());
- return preprocessVertexSource(vertSrc0, version);
+ return preprocessVertexSource(vertSrc0, version, versionSuffix);
} catch (IOException e) {
PGraphics.showException("Cannot load vertex shader " + url.getFile());
}
@@ -1926,6 +1935,8 @@ public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX
gl2x.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
} else if (gl3 != null) {
gl3.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ } else if (gl3es3 != null) {
+ gl3es3.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
} else {
throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glBlitFramebuffer()"));
}
@@ -1937,6 +1948,8 @@ public void renderbufferStorageMultisample(int target, int samples, int format,
gl2x.glRenderbufferStorageMultisample(target, samples, format, width, height);
} else if (gl3 != null) {
gl3.glRenderbufferStorageMultisample(target, samples, format, width, height);
+ } else if (gl3es3 != null) {
+ gl3es3.glRenderbufferStorageMultisample(target, samples, format, width, height);
} else {
throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glRenderbufferStorageMultisample()"));
}
@@ -1948,6 +1961,8 @@ public void readBuffer(int buf) {
gl2x.glReadBuffer(buf);
} else if (gl3 != null) {
gl3.glReadBuffer(buf);
+ } else if (gl3es3 != null) {
+ gl3es3.glReadBuffer(buf);
} else {
throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glReadBuffer()"));
}
@@ -1959,6 +1974,11 @@ public void drawBuffer(int buf) {
gl2x.glDrawBuffer(buf);
} else if (gl3 != null) {
gl3.glDrawBuffer(buf);
+ } else if (gl3es3 != null) {
+ IntBuffer intBuffer = IntBuffer.allocate(1);
+ intBuffer.put(buf);
+ intBuffer.rewind();
+ gl3es3.glDrawBuffers(1, intBuffer);
} else {
throw new RuntimeException(String.format(MISSING_GLFUNC_ERROR, "glDrawBuffer()"));
}
diff --git a/core/src/processing/opengl/shaders/LightVert-brcm.glsl b/core/src/processing/opengl/shaders/LightVert-brcm.glsl
new file mode 100644
index 0000000000..b96caa4b3b
--- /dev/null
+++ b/core/src/processing/opengl/shaders/LightVert-brcm.glsl
@@ -0,0 +1,154 @@
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-15 The Processing Foundation
+ Copyright (c) 2004-12 Ben Fry and Casey Reas
+ Copyright (c) 2001-04 Massachusetts Institute of Technology
+
+ 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, version 2.1.
+
+ 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
+*/
+
+uniform mat4 modelviewMatrix;
+uniform mat4 transformMatrix;
+uniform mat3 normalMatrix;
+
+uniform int lightCount;
+uniform vec4 lightPosition[8];
+uniform vec3 lightNormal[8];
+uniform vec3 lightAmbient[8];
+uniform vec3 lightDiffuse[8];
+uniform vec3 lightSpecular[8];
+uniform vec3 lightFalloff[8];
+uniform vec2 lightSpot[8];
+
+attribute vec4 position;
+attribute vec4 color;
+attribute vec3 normal;
+
+attribute vec4 ambient;
+attribute vec4 specular;
+attribute vec4 emissive;
+attribute float shininess;
+
+varying vec4 vertColor;
+varying vec4 backVertColor;
+
+const float zero_float = 0.0;
+const float one_float = 1.0;
+const vec3 zero_vec3 = vec3(0.0);
+const vec3 minus_one_vec3 = vec3(0.0-1.0);
+
+float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) {
+ vec3 lpv = lightPos - vertPos;
+ vec3 dist = vec3(one_float);
+ dist.z = dot(lpv, lpv);
+ dist.y = sqrt(dist.z);
+ return one_float / dot(dist, coeff);
+}
+
+float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) {
+ vec3 lpv = normalize(lightPos - vertPos);
+ vec3 nln = minus_one_vec3 * lightNorm;
+ float spotCos = dot(nln, lpv);
+ return spotCos <= minCos ? zero_float : pow(spotCos, spotExp);
+}
+
+float lambertFactor(vec3 lightDir, vec3 vecNormal) {
+ return max(zero_float, dot(lightDir, vecNormal));
+}
+
+float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) {
+ vec3 np = normalize(vertPos);
+ vec3 ldp = normalize(lightDir - np);
+ return pow(max(zero_float, dot(ldp, vecNormal)), shine);
+}
+
+void main() {
+ // Vertex in clip coordinates
+ gl_Position = transformMatrix * position;
+
+ // Vertex in eye coordinates
+ vec3 ecVertex = vec3(modelviewMatrix * position);
+
+ // Normal vector in eye coordinates
+ vec3 ecNormal = normalize(normalMatrix * normal);
+ vec3 ecNormalInv = ecNormal * minus_one_vec3;
+
+ // Light calculations
+ vec3 totalAmbient = vec3(0, 0, 0);
+
+ vec3 totalFrontDiffuse = vec3(0, 0, 0);
+ vec3 totalFrontSpecular = vec3(0, 0, 0);
+
+ vec3 totalBackDiffuse = vec3(0, 0, 0);
+ vec3 totalBackSpecular = vec3(0, 0, 0);
+
+ // prevent register allocation failure by limiting ourselves to
+ // two lights for now
+ for (int i = 0; i < 2; i++) {
+ if (lightCount == i) break;
+
+ vec3 lightPos = lightPosition[i].xyz;
+ bool isDir = lightPosition[i].w < one_float;
+ float spotCos = lightSpot[i].x;
+ float spotExp = lightSpot[i].y;
+
+ vec3 lightDir;
+ float falloff;
+ float spotf;
+
+ if (isDir) {
+ falloff = one_float;
+ lightDir = minus_one_vec3 * lightNormal[i];
+ } else {
+ falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]);
+ lightDir = normalize(lightPos - ecVertex);
+ }
+
+ spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i],
+ spotCos, spotExp)
+ : one_float;
+
+ if (any(greaterThan(lightAmbient[i], zero_vec3))) {
+ totalAmbient += lightAmbient[i] * falloff;
+ }
+
+ if (any(greaterThan(lightDiffuse[i], zero_vec3))) {
+ totalFrontDiffuse += lightDiffuse[i] * falloff * spotf *
+ lambertFactor(lightDir, ecNormal);
+ totalBackDiffuse += lightDiffuse[i] * falloff * spotf *
+ lambertFactor(lightDir, ecNormalInv);
+ }
+
+ if (any(greaterThan(lightSpecular[i], zero_vec3))) {
+ totalFrontSpecular += lightSpecular[i] * falloff * spotf *
+ blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess);
+ totalBackSpecular += lightSpecular[i] * falloff * spotf *
+ blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess);
+ }
+ }
+
+ // Calculating final color as result of all lights (plus emissive term).
+ // Transparency is determined exclusively by the diffuse component.
+ vertColor = vec4(totalAmbient, 0) * ambient +
+ vec4(totalFrontDiffuse, 1) * color +
+ vec4(totalFrontSpecular, 0) * specular +
+ vec4(emissive.rgb, 0);
+
+ backVertColor = vec4(totalAmbient, 0) * ambient +
+ vec4(totalBackDiffuse, 1) * color +
+ vec4(totalBackSpecular, 0) * specular +
+ vec4(emissive.rgb, 0);
+}
\ No newline at end of file
diff --git a/core/src/processing/opengl/shaders/LightVert-vc4.glsl b/core/src/processing/opengl/shaders/LightVert-vc4.glsl
index b96caa4b3b..ba79726767 100644
--- a/core/src/processing/opengl/shaders/LightVert-vc4.glsl
+++ b/core/src/processing/opengl/shaders/LightVert-vc4.glsl
@@ -96,8 +96,8 @@ void main() {
vec3 totalBackSpecular = vec3(0, 0, 0);
// prevent register allocation failure by limiting ourselves to
- // two lights for now
- for (int i = 0; i < 2; i++) {
+ // four lights for now
+ for (int i = 0; i < 4; i++) {
if (lightCount == i) break;
vec3 lightPos = lightPosition[i].xyz;
diff --git a/core/src/processing/opengl/shaders/TexLightVert-brcm.glsl b/core/src/processing/opengl/shaders/TexLightVert-brcm.glsl
new file mode 100644
index 0000000000..51e88ab056
--- /dev/null
+++ b/core/src/processing/opengl/shaders/TexLightVert-brcm.glsl
@@ -0,0 +1,160 @@
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2012-15 The Processing Foundation
+ Copyright (c) 2004-12 Ben Fry and Casey Reas
+ Copyright (c) 2001-04 Massachusetts Institute of Technology
+
+ 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, version 2.1.
+
+ 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
+*/
+
+uniform mat4 modelviewMatrix;
+uniform mat4 transformMatrix;
+uniform mat3 normalMatrix;
+uniform mat4 texMatrix;
+
+uniform int lightCount;
+uniform vec4 lightPosition[8];
+uniform vec3 lightNormal[8];
+uniform vec3 lightAmbient[8];
+uniform vec3 lightDiffuse[8];
+uniform vec3 lightSpecular[8];
+uniform vec3 lightFalloff[8];
+uniform vec2 lightSpot[8];
+
+attribute vec4 position;
+attribute vec4 color;
+attribute vec3 normal;
+attribute vec2 texCoord;
+
+attribute vec4 ambient;
+attribute vec4 specular;
+attribute vec4 emissive;
+attribute float shininess;
+
+varying vec4 vertColor;
+varying vec4 backVertColor;
+varying vec4 vertTexCoord;
+
+const float zero_float = 0.0;
+const float one_float = 1.0;
+const vec3 zero_vec3 = vec3(0.0);
+const vec3 minus_one_vec3 = vec3(0.0-1.0);
+
+float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) {
+ vec3 lpv = lightPos - vertPos;
+ vec3 dist = vec3(one_float);
+ dist.z = dot(lpv, lpv);
+ dist.y = sqrt(dist.z);
+ return one_float / dot(dist, coeff);
+}
+
+float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) {
+ vec3 lpv = normalize(lightPos - vertPos);
+ vec3 nln = minus_one_vec3 * lightNorm;
+ float spotCos = dot(nln, lpv);
+ return spotCos <= minCos ? zero_float : pow(spotCos, spotExp);
+}
+
+float lambertFactor(vec3 lightDir, vec3 vecNormal) {
+ return max(zero_float, dot(lightDir, vecNormal));
+}
+
+float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) {
+ vec3 np = normalize(vertPos);
+ vec3 ldp = normalize(lightDir - np);
+ return pow(max(zero_float, dot(ldp, vecNormal)), shine);
+}
+
+void main() {
+ // Vertex in clip coordinates
+ gl_Position = transformMatrix * position;
+
+ // Vertex in eye coordinates
+ vec3 ecVertex = vec3(modelviewMatrix * position);
+
+ // Normal vector in eye coordinates
+ vec3 ecNormal = normalize(normalMatrix * normal);
+ vec3 ecNormalInv = ecNormal * minus_one_vec3;
+
+ // Light calculations
+ vec3 totalAmbient = vec3(0, 0, 0);
+
+ vec3 totalFrontDiffuse = vec3(0, 0, 0);
+ vec3 totalFrontSpecular = vec3(0, 0, 0);
+
+ vec3 totalBackDiffuse = vec3(0, 0, 0);
+ vec3 totalBackSpecular = vec3(0, 0, 0);
+
+ // prevent register allocation failure by limiting ourselves to
+ // two lights for now
+ for (int i = 0; i < 2; i++) {
+ if (lightCount == i) break;
+
+ vec3 lightPos = lightPosition[i].xyz;
+ bool isDir = lightPosition[i].w < one_float;
+ float spotCos = lightSpot[i].x;
+ float spotExp = lightSpot[i].y;
+
+ vec3 lightDir;
+ float falloff;
+ float spotf;
+
+ if (isDir) {
+ falloff = one_float;
+ lightDir = minus_one_vec3 * lightNormal[i];
+ } else {
+ falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]);
+ lightDir = normalize(lightPos - ecVertex);
+ }
+
+ spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i],
+ spotCos, spotExp)
+ : one_float;
+
+ if (any(greaterThan(lightAmbient[i], zero_vec3))) {
+ totalAmbient += lightAmbient[i] * falloff;
+ }
+
+ if (any(greaterThan(lightDiffuse[i], zero_vec3))) {
+ totalFrontDiffuse += lightDiffuse[i] * falloff * spotf *
+ lambertFactor(lightDir, ecNormal);
+ totalBackDiffuse += lightDiffuse[i] * falloff * spotf *
+ lambertFactor(lightDir, ecNormalInv);
+ }
+
+ if (any(greaterThan(lightSpecular[i], zero_vec3))) {
+ totalFrontSpecular += lightSpecular[i] * falloff * spotf *
+ blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess);
+ totalBackSpecular += lightSpecular[i] * falloff * spotf *
+ blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess);
+ }
+ }
+
+ // Calculating final color as result of all lights (plus emissive term).
+ // Transparency is determined exclusively by the diffuse component.
+ vertColor = vec4(totalAmbient, 0) * ambient +
+ vec4(totalFrontDiffuse, 1) * color +
+ vec4(totalFrontSpecular, 0) * specular +
+ vec4(emissive.rgb, 0);
+
+ backVertColor = vec4(totalAmbient, 0) * ambient +
+ vec4(totalBackDiffuse, 1) * color +
+ vec4(totalBackSpecular, 0) * specular +
+ vec4(emissive.rgb, 0);
+
+ // Calculating texture coordinates, with r and q set both to one
+ vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);
+}
diff --git a/core/src/processing/opengl/shaders/TexLightVert-vc4.glsl b/core/src/processing/opengl/shaders/TexLightVert-vc4.glsl
index 51e88ab056..f54277e812 100644
--- a/core/src/processing/opengl/shaders/TexLightVert-vc4.glsl
+++ b/core/src/processing/opengl/shaders/TexLightVert-vc4.glsl
@@ -99,8 +99,8 @@ void main() {
vec3 totalBackSpecular = vec3(0, 0, 0);
// prevent register allocation failure by limiting ourselves to
- // two lights for now
- for (int i = 0; i < 2; i++) {
+ // four lights for now
+ for (int i = 0; i < 4; i++) {
if (lightCount == i) break;
vec3 lightPos = lightPosition[i].xyz;
diff --git a/java/application/launch4j/bin/ld-linux-armv6hf b/java/application/launch4j/bin/ld-linux-armv6hf
new file mode 100755
index 0000000000..032cd917a8
Binary files /dev/null and b/java/application/launch4j/bin/ld-linux-armv6hf differ
diff --git a/java/application/launch4j/bin/windres-linux-armv6hf b/java/application/launch4j/bin/windres-linux-armv6hf
new file mode 100755
index 0000000000..41b2223800
Binary files /dev/null and b/java/application/launch4j/bin/windres-linux-armv6hf differ
diff --git a/java/libraries/io/examples/SPIAnalogDigitalOOP/SPIAnalogDigitalOOP.pde b/java/libraries/io/examples/AnalogDigital_SPI_MCP3001/AnalogDigital_SPI_MCP3001.pde
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigitalOOP/SPIAnalogDigitalOOP.pde
rename to java/libraries/io/examples/AnalogDigital_SPI_MCP3001/AnalogDigital_SPI_MCP3001.pde
diff --git a/java/libraries/io/examples/SPIAnalogDigitalOOP/MCP3001.pde b/java/libraries/io/examples/AnalogDigital_SPI_MCP3001/MCP3001.pde
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigitalOOP/MCP3001.pde
rename to java/libraries/io/examples/AnalogDigital_SPI_MCP3001/MCP3001.pde
diff --git a/java/libraries/io/examples/SPIAnalogDigital/setup.png b/java/libraries/io/examples/AnalogDigital_SPI_MCP3001/setup.png
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigital/setup.png
rename to java/libraries/io/examples/AnalogDigital_SPI_MCP3001/setup.png
diff --git a/java/libraries/io/examples/SPIAnalogDigitalOOP8/SPIAnalogDigitalOOP8.pde b/java/libraries/io/examples/AnalogDigital_SPI_MCP3008/AnalogDigital_SPI_MCP3008.pde
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigitalOOP8/SPIAnalogDigitalOOP8.pde
rename to java/libraries/io/examples/AnalogDigital_SPI_MCP3008/AnalogDigital_SPI_MCP3008.pde
diff --git a/java/libraries/io/examples/SPIAnalogDigitalOOP8/MCP3008.pde b/java/libraries/io/examples/AnalogDigital_SPI_MCP3008/MCP3008.pde
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigitalOOP8/MCP3008.pde
rename to java/libraries/io/examples/AnalogDigital_SPI_MCP3008/MCP3008.pde
diff --git a/java/libraries/io/examples/SPIAnalogDigitalOOP8/setup.png b/java/libraries/io/examples/AnalogDigital_SPI_MCP3008/setup.png
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigitalOOP8/setup.png
rename to java/libraries/io/examples/AnalogDigital_SPI_MCP3008/setup.png
diff --git a/java/libraries/io/examples/Compass_I2C_HMC6352/Compass_I2C_HMC6352.pde b/java/libraries/io/examples/Compass_I2C_HMC6352/Compass_I2C_HMC6352.pde
new file mode 100644
index 0000000000..921fe47cb4
--- /dev/null
+++ b/java/libraries/io/examples/Compass_I2C_HMC6352/Compass_I2C_HMC6352.pde
@@ -0,0 +1,19 @@
+import processing.io.*;
+HMC6352 compass;
+
+// see setup.png in the sketch folder for wiring details
+
+void setup() {
+ // the module's I2C address can be changed by modifying values in its EEPROM
+ // 0x21 is however the default address
+
+ //printArray(I2C.list());
+ compass = new HMC6352("i2c-1", 0x21);
+}
+
+void draw() {
+ background(255);
+ float deg = compass.heading();
+ println(deg + " degrees");
+ line(width/2, height/2, width/2+sin(radians(deg))*width/2, height/2-cos(radians(deg))*height/2);
+}
diff --git a/java/libraries/io/examples/Compass_I2C_HMC6352/HMC6352.pde b/java/libraries/io/examples/Compass_I2C_HMC6352/HMC6352.pde
new file mode 100644
index 0000000000..ff925ac3a5
--- /dev/null
+++ b/java/libraries/io/examples/Compass_I2C_HMC6352/HMC6352.pde
@@ -0,0 +1,38 @@
+import processing.io.I2C;
+
+// HMC6352 is a digital compass using I2C
+// datasheet: https://www.sparkfun.com/datasheets/Components/HMC6352.pdf
+
+class HMC6352 extends I2C {
+ int address;
+
+ HMC6352(String dev, int address) {
+ super(dev);
+ this.address = address;
+ setHeadingMode();
+ }
+
+ void setHeadingMode() {
+ beginTransmission(address);
+ // command byte for writing to EEPROM
+ write(0x77);
+ // address of the output data control byte
+ write(0x4e);
+ // give us the plain heading
+ write(0x00);
+ endTransmission();
+ }
+
+ float heading() {
+ beginTransmission(address);
+ // command byte for reading the data
+ write(0x41);
+ byte[] in = read(2);
+ endTransmission();
+ // put bytes together to tenth of degrees
+ // & 0xff makes sure the byte is not interpreted as a negative value
+ int deg = (in[0] & 0xff) << 8 | (in[1] & 0xff);
+ // return degrees
+ return deg / 10.0;
+ }
+}
diff --git a/java/libraries/io/examples/I2CCompass/setup.png b/java/libraries/io/examples/Compass_I2C_HMC6352/setup.png
similarity index 100%
rename from java/libraries/io/examples/I2CCompass/setup.png
rename to java/libraries/io/examples/Compass_I2C_HMC6352/setup.png
diff --git a/java/libraries/io/examples/I2CDigitalAnalogOOP/I2CDigitalAnalogOOP.pde b/java/libraries/io/examples/DigitalAnalog_I2C_MCP4725/DigitalAnalog_I2C_MCP4725.pde
similarity index 100%
rename from java/libraries/io/examples/I2CDigitalAnalogOOP/I2CDigitalAnalogOOP.pde
rename to java/libraries/io/examples/DigitalAnalog_I2C_MCP4725/DigitalAnalog_I2C_MCP4725.pde
diff --git a/java/libraries/io/examples/I2CDigitalAnalogOOP/MCP4725.pde b/java/libraries/io/examples/DigitalAnalog_I2C_MCP4725/MCP4725.pde
similarity index 100%
rename from java/libraries/io/examples/I2CDigitalAnalogOOP/MCP4725.pde
rename to java/libraries/io/examples/DigitalAnalog_I2C_MCP4725/MCP4725.pde
diff --git a/java/libraries/io/examples/I2CScreen/I2CScreen.pde b/java/libraries/io/examples/Display_I2C_SSD1306/Display_I2C_SSD1306.pde
similarity index 100%
rename from java/libraries/io/examples/I2CScreen/I2CScreen.pde
rename to java/libraries/io/examples/Display_I2C_SSD1306/Display_I2C_SSD1306.pde
diff --git a/java/libraries/io/examples/I2CScreen/SSD1306.pde b/java/libraries/io/examples/Display_I2C_SSD1306/SSD1306.pde
similarity index 100%
rename from java/libraries/io/examples/I2CScreen/SSD1306.pde
rename to java/libraries/io/examples/Display_I2C_SSD1306/SSD1306.pde
diff --git a/java/libraries/io/examples/Environment_I2C_BME280/BME280.pde b/java/libraries/io/examples/Environment_I2C_BME280/BME280.pde
new file mode 100644
index 0000000000..572e6d7013
--- /dev/null
+++ b/java/libraries/io/examples/Environment_I2C_BME280/BME280.pde
@@ -0,0 +1,407 @@
+import processing.io.I2C;
+
+// BME280 is an integrated environmental sensor
+// It can measure temperature, pressure and humidity
+// datasheet: https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf
+// code contributed by @OlivierLD
+
+public class BME280 extends I2C {
+
+ public final static int BME280_I2CADDR = 0x77; // this is the default I2C address
+ public final static int DEFAULT_ADDR = BME280_I2CADDR;
+
+ // Operating Modes
+ public final static int BME280_OSAMPLE_1 = 1;
+ public final static int BME280_OSAMPLE_2 = 2;
+ public final static int BME280_OSAMPLE_4 = 3;
+ public final static int BME280_OSAMPLE_8 = 4;
+ public final static int BME280_OSAMPLE_16 = 5;
+
+ // BME280 Registers
+ public final static int BME280_REGISTER_DIG_T1 = 0x88; // Trimming parameter registers
+ public final static int BME280_REGISTER_DIG_T2 = 0x8A;
+ public final static int BME280_REGISTER_DIG_T3 = 0x8C;
+
+ public final static int BME280_REGISTER_DIG_P1 = 0x8E;
+ public final static int BME280_REGISTER_DIG_P2 = 0x90;
+ public final static int BME280_REGISTER_DIG_P3 = 0x92;
+ public final static int BME280_REGISTER_DIG_P4 = 0x94;
+ public final static int BME280_REGISTER_DIG_P5 = 0x96;
+ public final static int BME280_REGISTER_DIG_P6 = 0x98;
+ public final static int BME280_REGISTER_DIG_P7 = 0x9A;
+ public final static int BME280_REGISTER_DIG_P8 = 0x9C;
+ public final static int BME280_REGISTER_DIG_P9 = 0x9E;
+
+ public final static int BME280_REGISTER_DIG_H1 = 0xA1;
+ public final static int BME280_REGISTER_DIG_H2 = 0xE1;
+ public final static int BME280_REGISTER_DIG_H3 = 0xE3;
+ public final static int BME280_REGISTER_DIG_H4 = 0xE4;
+ public final static int BME280_REGISTER_DIG_H5 = 0xE5;
+ public final static int BME280_REGISTER_DIG_H6 = 0xE6;
+ public final static int BME280_REGISTER_DIG_H7 = 0xE7;
+
+ public final static int BME280_REGISTER_CHIPID = 0xD0;
+ public final static int BME280_REGISTER_VERSION = 0xD1;
+ public final static int BME280_REGISTER_SOFTRESET = 0xE0;
+
+ public final static int BME280_REGISTER_CONTROL_HUM = 0xF2;
+ public final static int BME280_REGISTER_CONTROL = 0xF4;
+ public final static int BME280_REGISTER_CONFIG = 0xF5;
+ public final static int BME280_REGISTER_PRESSURE_DATA = 0xF7;
+ public final static int BME280_REGISTER_TEMP_DATA = 0xFA;
+ public final static int BME280_REGISTER_HUMIDITY_DATA = 0xFD;
+
+ private int dig_T1 = 0;
+ private int dig_T2 = 0;
+ private int dig_T3 = 0;
+
+ private int dig_P1 = 0;
+ private int dig_P2 = 0;
+ private int dig_P3 = 0;
+ private int dig_P4 = 0;
+ private int dig_P5 = 0;
+ private int dig_P6 = 0;
+ private int dig_P7 = 0;
+ private int dig_P8 = 0;
+ private int dig_P9 = 0;
+
+ private int dig_H1 = 0;
+ private int dig_H2 = 0;
+ private int dig_H3 = 0;
+ private int dig_H4 = 0;
+ private int dig_H5 = 0;
+ private int dig_H6 = 0;
+
+ private float tFine = 0.0f;
+
+ private int address;
+ private int mode = BME280_OSAMPLE_8;
+ private float standardSeaLevelPressure = 101325.0f; // in Pa (1013.25 hPa)
+
+ protected float temp = 0.0f; // most recent sensor readings, set by update()
+ protected float press = 0.0f;
+ protected float hum = 0.0f;
+
+
+ public BME280(String dev) {
+ this(dev, DEFAULT_ADDR);
+ }
+
+ public BME280(String dev, int address) {
+ super(dev);
+ this.address = address;
+
+ // Soft reset
+ command(BME280_REGISTER_SOFTRESET, (byte)0xB6);
+ // Wait for the chip to wake up
+ delay(300);
+
+ try {
+ readCalibrationData();
+ // showCalibrationData();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ command(BME280_REGISTER_CONTROL, (byte)0x3F);
+ tFine = 0.0f;
+ }
+
+
+ /**
+ * Read and update all sensors values
+ */
+ public void update() {
+ // The order used to read the data is important!
+ // 1.temperature, 2.pressure (analog to altitude), 3.humidity.
+
+ try {
+ temp = readTemperature();
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ ex.printStackTrace();
+ }
+
+ try {
+ press = readPressure();
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ ex.printStackTrace();
+ }
+
+ try {
+ hum = readHumidity();
+ } catch (Exception ex) {
+ System.err.println(ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns the temperature in degrees celsius
+ */
+ public float temperature() {
+ return temp;
+ }
+
+ /**
+ * Returns the pressure in Pa
+ */
+ public float pressure() {
+ return press;
+ }
+
+ /**
+ * Returns the altitude in meters
+ * @param pressure as returned by pressure()
+ */
+ public float altitude(float pressure) {
+ double altitude = 0.0;
+ if (standardSeaLevelPressure != 0) {
+ altitude = 44330.0 * (1.0 - Math.pow(pressure / standardSeaLevelPressure, 0.1903));
+ }
+ return (float)altitude;
+ }
+
+ /**
+ * Returns the altitude in meters
+ * @param pressure as returned by pressure() in Pa
+ * @param temperature as returned by temperature() in Celcius
+ */
+ public float altitude(float pressure, float temperature) {
+ double altitude = 0.0;
+ if (standardSeaLevelPressure != 0) {
+ altitude = ((Math.pow(standardSeaLevelPressure / pressure, 1 / 5.257) - 1) * (temperature + 273.25)) / 0.0065;
+ }
+ return (float)altitude;
+ }
+
+ /**
+ * Returns the humidity in percent
+ */
+ public float humidity() {
+ return hum;
+ }
+
+ /**
+ * Set the standard sea level pressure used for calculating altitude()
+ * Defaults to 101325 Pa (1013.25 hPa)
+ */
+ public void setStandardSeaLevelPressure(float pressure) {
+ standardSeaLevelPressure = pressure;
+ }
+
+
+ protected float readTemperature() {
+ // Returns the compensated temperature in degrees celcius
+ float UT = readRawTemp();
+ float var1 = 0.0f;
+ float var2 = 0.0f;
+ float temp = 0.0f;
+
+ // Read raw temp before aligning it with the calibration values
+ var1 = (UT / 16384.0f - dig_T1 / 1024.0f) * (float) dig_T2;
+ var2 = ((UT / 131072.0f - dig_T1 / 8192.0f) * (UT / 131072.0f - dig_T1 / 8192.0f)) * (float) dig_T3;
+ tFine = (int) (var1 + var2);
+ temp = (var1 + var2) / 5120.0f;
+ // println("DBG: Calibrated temperature = " + temp + " C");
+ return temp;
+ }
+
+ protected float readPressure() {
+ // Returns the compensated pressure in Pascal
+ int adc = readRawPressure();
+ // println("ADC:" + adc + ", tFine:" + tFine);
+ float var1 = (tFine / 2.0f) - 64000.0f;
+ float var2 = var1 * var1 * (dig_P6 / 32768.0f);
+ var2 = var2 + var1 * dig_P5 * 2.0f;
+ var2 = (var2 / 4.0f) + (dig_P4 * 65536.0f);
+ var1 = (dig_P3 * var1 * var1 / 524288.0f + dig_P2 * var1) / 524288.0f;
+ var1 = (1.0f + var1 / 32768.0f) * dig_P1;
+ if (var1 == 0f) {
+ return 0.0f;
+ }
+ float p = 1048576.0f - adc;
+ p = ((p - var2 / 4096.0f) * 6250.0f) / var1;
+ var1 = dig_P9 * p * p / 2147483648.0f;
+ var2 = p * dig_P8 / 32768.0f;
+ p = p + (var1 + var2 + dig_P7) / 16.0f;
+ // println("DBG: Pressure = " + p + " Pa");
+ return p;
+ }
+
+ protected float readHumidity() {
+ // Returns the compensated humidity in percent
+ int adc = readRawHumidity();
+ float h = tFine - 76800.0f;
+ h = (adc - (dig_H4 * 64.0f + dig_H5 / 16384.8f * h)) *
+ (dig_H2 / 65536.0f * (1.0f + dig_H6 / 67108864.0f * h * (1.0f + dig_H3 / 67108864.0f * h)));
+ h = h * (1.0f - dig_H1 * h / 524288.0f);
+ if (h > 100) {
+ h = 100;
+ } else if (h < 0) {
+ h = 0;
+ }
+ // println("DBG: Humidity = " + h);
+ return h;
+ }
+
+
+ private void readCalibrationData() {
+ // Reads the calibration data from the IC
+ dig_T1 = readU16LE(BME280_REGISTER_DIG_T1);
+ dig_T2 = readS16LE(BME280_REGISTER_DIG_T2);
+ dig_T3 = readS16LE(BME280_REGISTER_DIG_T3);
+
+ dig_P1 = readU16LE(BME280_REGISTER_DIG_P1);
+ dig_P2 = readS16LE(BME280_REGISTER_DIG_P2);
+ dig_P3 = readS16LE(BME280_REGISTER_DIG_P3);
+ dig_P4 = readS16LE(BME280_REGISTER_DIG_P4);
+ dig_P5 = readS16LE(BME280_REGISTER_DIG_P5);
+ dig_P6 = readS16LE(BME280_REGISTER_DIG_P6);
+ dig_P7 = readS16LE(BME280_REGISTER_DIG_P7);
+ dig_P8 = readS16LE(BME280_REGISTER_DIG_P8);
+ dig_P9 = readS16LE(BME280_REGISTER_DIG_P9);
+
+ dig_H1 = readU8(BME280_REGISTER_DIG_H1);
+ dig_H2 = readS16LE(BME280_REGISTER_DIG_H2);
+ dig_H3 = readU8(BME280_REGISTER_DIG_H3);
+ dig_H6 = readS8(BME280_REGISTER_DIG_H7);
+
+ int h4 = readS8(BME280_REGISTER_DIG_H4);
+ h4 = (h4 << 24) >> 20;
+ dig_H4 = h4 | (readU8(BME280_REGISTER_DIG_H5) & 0x0F);
+
+ int h5 = readS8(BME280_REGISTER_DIG_H6);
+ h5 = (h5 << 24) >> 20;
+ dig_H5 = h5 | (readU8(BME280_REGISTER_DIG_H5) >> 4 & 0x0F);
+ }
+
+ private String displayRegister(int reg) {
+ return String.format("0x%s (%d)", lpad(Integer.toHexString(reg & 0xFFFF).toUpperCase(), 4, "0"), reg);
+ }
+
+ private void showCalibrationData() {
+ // Displays the calibration values for debugging purposes
+ println("======================");
+ println("DBG: T1 = " + displayRegister(dig_T1));
+ println("DBG: T2 = " + displayRegister(dig_T2));
+ println("DBG: T3 = " + displayRegister(dig_T3));
+ println("----------------------");
+ println("DBG: P1 = " + displayRegister(dig_P1));
+ println("DBG: P2 = " + displayRegister(dig_P2));
+ println("DBG: P3 = " + displayRegister(dig_P3));
+ println("DBG: P4 = " + displayRegister(dig_P4));
+ println("DBG: P5 = " + displayRegister(dig_P5));
+ println("DBG: P6 = " + displayRegister(dig_P6));
+ println("DBG: P7 = " + displayRegister(dig_P7));
+ println("DBG: P8 = " + displayRegister(dig_P8));
+ println("DBG: P9 = " + displayRegister(dig_P9));
+ println("----------------------");
+ println("DBG: H1 = " + displayRegister(dig_H1));
+ println("DBG: H2 = " + displayRegister(dig_H2));
+ println("DBG: H3 = " + displayRegister(dig_H3));
+ println("DBG: H4 = " + displayRegister(dig_H4));
+ println("DBG: H5 = " + displayRegister(dig_H5));
+ println("DBG: H6 = " + displayRegister(dig_H6));
+ println("======================");
+ }
+
+ private void command(int reg, byte val) {
+ super.beginTransmission(address);
+ super.write(reg);
+ super.write(val);
+ super.endTransmission();
+ }
+
+ private int readRawTemp() {
+ // Returns the raw (uncompensated) temperature
+ int meas = mode;
+ // println(String.format("readRawTemp: 1 - meas=%d", meas));
+ command(BME280_REGISTER_CONTROL_HUM, (byte) meas); // HUM ?
+ meas = mode << 5 | mode << 2 | 1;
+ // println(String.format("readRawTemp: 2 - meas=%d", meas));
+ command(BME280_REGISTER_CONTROL, (byte) meas);
+
+ double sleepTime = 0.00125 + 0.0023 * (1 << mode);
+ sleepTime = sleepTime + 0.0023 * (1 << mode) + 0.000575;
+ sleepTime = sleepTime + 0.0023 * (1 << mode) + 0.000575;
+ delay((int)Math.round(sleepTime * 1000));
+ int msb = readU8(BME280_REGISTER_TEMP_DATA);
+ int lsb = readU8(BME280_REGISTER_TEMP_DATA + 1);
+ int xlsb = readU8(BME280_REGISTER_TEMP_DATA + 2);
+ int raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4;
+ // println("DBG: Raw Temp: " + (raw & 0xFFFF) + ", " + raw + String.format(", msb: 0x%04X lsb: 0x%04X xlsb: 0x%04X", msb, lsb, xlsb));
+ return raw;
+ }
+
+ private int readRawPressure() {
+ // Returns the raw (uncompensated) pressure
+ int msb = readU8(BME280_REGISTER_PRESSURE_DATA);
+ int lsb = readU8(BME280_REGISTER_PRESSURE_DATA + 1);
+ int xlsb = readU8(BME280_REGISTER_PRESSURE_DATA + 2);
+ int raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4;
+ // println("DBG: Raw Press: " + (raw & 0xFFFF) + ", " + raw + String.format(", msb: 0x%04X lsb: 0x%04X xlsb: 0x%04X", msb, lsb, xlsb));
+ return raw;
+ }
+
+ private int readRawHumidity() {
+ // Returns the raw (uncompensated) humidity
+ int msb = readU8(BME280_REGISTER_HUMIDITY_DATA);
+ int lsb = readU8(BME280_REGISTER_HUMIDITY_DATA + 1);
+ int raw = (msb << 8) | lsb;
+ return raw;
+ }
+
+ private int readU16LE(int register) {
+ super.beginTransmission(address);
+ super.write((byte)register);
+ byte[] ba = super.read(2);
+ super.endTransmission();
+ return ((ba[1] & 0xFF) << 8) + (ba[0] & 0xFF); // Little Endian
+ }
+
+ private int readS16LE(int register) {
+ super.beginTransmission(address);
+ super.write((byte)register);
+ byte[] ba = super.read(2);
+ super.endTransmission();
+
+ int lo = ba[0] & 0xFF;
+ int hi = ba[1] & 0xFF;
+ if (hi > 127)
+ hi -= 256;
+ return (hi << 8) + lo; // Little Endian
+ }
+
+ private int readU8(int register) {
+ super.beginTransmission(address);
+ super.write(register);
+ byte[] ba = super.read(1);
+ super.endTransmission();
+ return (int)(ba[0] & 0xFF);
+ }
+
+ private int readS8(int register) {
+ int val = readU8(register);
+ if (val > 127)
+ val -= 256;
+ return val;
+ }
+
+ private String rpad(String s, int len, String pad) {
+ String str = s;
+ while (str.length() < len) {
+ str += pad;
+ }
+ return str;
+ }
+
+ private String lpad(String s, int len, String pad) {
+ String str = s;
+ while (str.length() < len) {
+ str = pad + str;
+ }
+ return str;
+ }
+}
diff --git a/java/libraries/io/examples/Environment_I2C_BME280/Environment_I2C_BME280.pde b/java/libraries/io/examples/Environment_I2C_BME280/Environment_I2C_BME280.pde
new file mode 100644
index 0000000000..a5ec80602c
--- /dev/null
+++ b/java/libraries/io/examples/Environment_I2C_BME280/Environment_I2C_BME280.pde
@@ -0,0 +1,29 @@
+import processing.io.*;
+BME280 bme280;
+
+// see setup.png in the sketch folder for wiring details
+
+void setup() {
+ size(720, 320);
+ textSize(72);
+
+ //printArray(I2C.list());
+ bme280 = new BME280("i2c-1", 0x77);
+}
+
+void draw() {
+ background(0);
+ stroke(255);
+
+ bme280.update();
+ float temp = bme280.temperature();
+ float hum = bme280.humidity();
+ float press = bme280.pressure();
+ text(String.format("Temp: %.02f\272C", temp), 10, 75);
+ text(String.format("Hum: %.02f %%", hum), 10, 150);
+ text(String.format("Press: %.02f hPa", press / 100f), 10, 225);
+
+ // pressure can be used to calculate the altitude like so
+ float alt = bme280.altitude(press, temp);
+ text(String.format("Alt: %.02f m", alt), 10, 300);
+}
diff --git a/java/libraries/io/examples/Environment_I2C_BME280/setup.png b/java/libraries/io/examples/Environment_I2C_BME280/setup.png
new file mode 100644
index 0000000000..cb37a3a60e
Binary files /dev/null and b/java/libraries/io/examples/Environment_I2C_BME280/setup.png differ
diff --git a/java/libraries/io/examples/I2CCompass/I2CCompass.pde b/java/libraries/io/examples/I2CCompass/I2CCompass.pde
deleted file mode 100644
index 079a7bcc61..0000000000
--- a/java/libraries/io/examples/I2CCompass/I2CCompass.pde
+++ /dev/null
@@ -1,43 +0,0 @@
-import processing.io.*;
-I2C i2c;
-
-// HMC6352 is a digital compass module using I2C
-// datasheet: https://www.sparkfun.com/datasheets/Components/HMC6352.pdf
-// see setup.png in the sketch folder for wiring details
-
-void setup() {
- //printArray(I2C.list());
- i2c = new I2C(I2C.list()[0]);
- setHeadingMode();
-}
-
-void draw() {
- background(255);
- float deg = getHeading();
- println(deg + " degrees");
- line(width/2, height/2, width/2+sin(radians(deg))*width/2, height/2-cos(radians(deg))*height/2);
-}
-
-void setHeadingMode() {
- i2c.beginTransmission(0x21);
- // command byte for writing to EEPROM
- i2c.write(0x77);
- // address of the output data control byte
- i2c.write(0x4e);
- // give us the plain heading
- i2c.write(0x00);
- i2c.endTransmission();
-}
-
-float getHeading() {
- i2c.beginTransmission(0x21);
- // command byte for reading the data
- i2c.write(0x41);
- byte[] in = i2c.read(2);
- i2c.endTransmission();
- // put bytes together to tenth of degrees
- // & 0xff makes sure the byte is not interpreted as a negative value
- int deg = (in[0] & 0xff) << 8 | (in[1] & 0xff);
- // return degrees
- return deg / 10.0;
-}
diff --git a/java/libraries/io/examples/Light_I2C_TSL2561/Light_I2C_TSL2561.pde b/java/libraries/io/examples/Light_I2C_TSL2561/Light_I2C_TSL2561.pde
new file mode 100644
index 0000000000..c6c9a28ec7
--- /dev/null
+++ b/java/libraries/io/examples/Light_I2C_TSL2561/Light_I2C_TSL2561.pde
@@ -0,0 +1,27 @@
+import processing.io.*;
+TSL2561 sensor;
+
+// see setup.png in the sketch folder for wiring details
+
+// this variable will contain the measured brightness
+// Lux (lx) is the unit of illuminance
+float lux;
+
+void setup() {
+ size(700, 100);
+ textSize(72);
+ //printArray(I2C.list());
+ sensor = new TSL2561("i2c-1", 0x39);
+}
+
+void draw() {
+ background(0);
+ stroke(255);
+ lux = sensor.lux();
+ text(String.format("Light: %.02f Lux", lux), 10, 75);
+}
+
+void dispose() {
+ // turn the sensor off
+ sensor.stop();
+}
diff --git a/java/libraries/io/examples/Light_I2C_TSL2561/TSL2561.pde b/java/libraries/io/examples/Light_I2C_TSL2561/TSL2561.pde
new file mode 100644
index 0000000000..775b9d02ca
--- /dev/null
+++ b/java/libraries/io/examples/Light_I2C_TSL2561/TSL2561.pde
@@ -0,0 +1,187 @@
+import processing.io.I2C;
+
+// TSL2561 is light sensor using I2C
+// datasheet: https://cdn-shop.adafruit.com/datasheets/TSL2561.pdf
+// code contributed by @OlivierLD
+
+public class TSL2561 extends I2C {
+
+ public final static int TSL2561_ADDRESS = 0x39;
+
+ public final static int TSL2561_ADDRESS_LOW = 0x29;
+ public final static int TSL2561_ADDRESS_FLOAT = 0x39;
+ public final static int TSL2561_ADDRESS_HIGH = 0x49;
+
+ public final static int TSL2561_COMMAND_BIT = 0x80;
+ public final static int TSL2561_WORD_BIT = 0x20;
+ public final static int TSL2561_CONTROL_POWERON = 0x03;
+ public final static int TSL2561_CONTROL_POWEROFF = 0x00;
+
+ public final static int TSL2561_REGISTER_CONTROL = 0x00;
+ public final static int TSL2561_REGISTER_TIMING = 0x01;
+ public final static int TSL2561_REGISTER_CHAN0_LOW = 0x0C;
+ public final static int TSL2561_REGISTER_CHAN0_HIGH = 0x0D;
+ public final static int TSL2561_REGISTER_CHAN1_LOW = 0x0E;
+ public final static int TSL2561_REGISTER_CHAN1_HIGH = 0x0F;
+ public final static int TSL2561_REGISTER_ID = 0x0A;
+
+ public final static int TSL2561_GAIN_1X = 0x00;
+ public final static int TSL2561_GAIN_16X = 0x10;
+
+ public final static int TSL2561_INTEGRATIONTIME_13MS = 0x00; // rather 13.7ms
+ public final static int TSL2561_INTEGRATIONTIME_101MS = 0x01;
+ public final static int TSL2561_INTEGRATIONTIME_402MS = 0x02;
+
+ public final static double TSL2561_LUX_K1C = 0.130; // (0x0043) // 0.130 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B1C = 0.0315; // (0x0204) // 0.0315 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M1C = 0.0262; // (0x01ad) // 0.0262 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K2C = 0.260; // (0x0085) // 0.260 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B2C = 0.0337; // (0x0228) // 0.0337 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M2C = 0.0430; // (0x02c1) // 0.0430 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K3C = 0.390; // (0x00c8) // 0.390 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B3C = 0.0363; // (0x0253) // 0.0363 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M3C = 0.0529; // (0x0363) // 0.0529 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K4C = 0.520; // (0x010a) // 0.520 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B4C = 0.0392; // (0x0282) // 0.0392 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M4C = 0.0605; // (0x03df) // 0.0605 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K5C = 0.65; // (0x014d) // 0.65 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B5C = 0.0229; // (0x0177) // 0.0229 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M5C = 0.0291; // (0x01dd) // 0.0291 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K6C = 0.80; // (0x019a) // 0.80 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B6C = 0.0157; // (0x0101) // 0.0157 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M6C = 0.0180; // (0x0127) // 0.0180 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K7C = 1.3; // (0x029a) // 1.3 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B7C = 0.00338; // (0x0037) // 0.00338 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M7C = 0.00260; // (0x002b) // 0.00260 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_K8C = 1.3; // (0x029a) // 1.3 * 2^RATIO_SCALE
+ public final static double TSL2561_LUX_B8C = 0.000; // (0x0000) // 0.000 * 2^LUX_SCALE
+ public final static double TSL2561_LUX_M8C = 0.000; // (0x0000) // 0.000 * 2^LUX_SCALE
+
+ private int gain = TSL2561_GAIN_1X;
+ private int integration = TSL2561_INTEGRATIONTIME_402MS;
+ private int pause = 800;
+
+ private int address;
+
+
+ public TSL2561(String dev) {
+ this(dev, TSL2561_ADDRESS);
+ }
+
+ public TSL2561(String dev, int address) {
+ super(dev);
+ this.address = address;
+ start();
+ }
+
+ public void start() {
+ command(TSL2561_COMMAND_BIT, (byte) TSL2561_CONTROL_POWERON);
+ }
+
+ public void stop() {
+ command(TSL2561_COMMAND_BIT, (byte) TSL2561_CONTROL_POWEROFF);
+ }
+
+ public void setGain() {
+ setGain(TSL2561_GAIN_1X);
+ }
+
+ public void setGain(int gain) {
+ setGain(gain, TSL2561_INTEGRATIONTIME_402MS);
+ }
+
+ public void setGain(int gain, int integration) {
+ if (gain != TSL2561_GAIN_1X && gain != TSL2561_GAIN_16X) {
+ throw new IllegalArgumentException("Invalid gain value");
+ }
+ if (gain != this.gain || integration != this.integration) {
+ command(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, (byte) (gain | integration));
+ //println("Setting low gain");
+ this.gain = gain;
+ this.integration = integration;
+ delay(pause); // pause for integration (pause must be bigger than integration time)
+ }
+ }
+
+ /**
+ * Read visible+IR diode from the I2C device
+ */
+ public int readFull() {
+ int reg = TSL2561_COMMAND_BIT | TSL2561_REGISTER_CHAN0_LOW;
+ return readU16(reg);
+ }
+
+ /**
+ * Read IR only diode from the I2C device
+ */
+ public int readIR() {
+ int reg = TSL2561_COMMAND_BIT | TSL2561_REGISTER_CHAN1_LOW;
+ return readU16(reg);
+ }
+
+ /**
+ * Device lux range 0.1 - 40,000+
+ * see https://learn.adafruit.com/tsl2561/overview
+ */
+ public float lux() {
+ int ambient = this.readFull();
+ int ir = this.readIR();
+
+ //println("IR Result: " + ir);
+ //println("Ambient Result: " + ambient);
+
+ if (ambient >= 0xffff || ir >= 0xffff) {
+ throw new RuntimeException("Gain too high, values exceed range");
+ }
+ double ratio = (ir / (float) ambient);
+
+ /*
+ * For the values below, see https://github.com/adafruit/_TSL2561/blob/master/_TSL2561_U.h
+ */
+ float lux = 0.0f;
+ if ((ratio >= 0) && (ratio <= TSL2561_LUX_K4C)) {
+ lux = (float)((TSL2561_LUX_B1C * ambient) - (0.0593 * ambient * (Math.pow(ratio, 1.4))));
+ } else if (ratio <= TSL2561_LUX_K5C) {
+ lux = (float)((TSL2561_LUX_B5C * ambient) - (TSL2561_LUX_M5C * ir));
+ } else if (ratio <= TSL2561_LUX_K6C) {
+ lux = (float)((TSL2561_LUX_B6C * ambient) - (TSL2561_LUX_M6C * ir));
+ } else if (ratio <= TSL2561_LUX_K7C) {
+ lux = (float)((TSL2561_LUX_B7C * ambient) - (TSL2561_LUX_M7C * ir));
+ } else if (ratio > TSL2561_LUX_K8C) {
+ lux = 0.0f;
+ }
+ return lux;
+ }
+
+
+ private void command(int register, byte value) {
+ beginTransmission(address);
+ write(register);
+ write(value);
+ endTransmission();
+ }
+
+ private int readU8(int register) {
+ beginTransmission(this.address);
+ write(register);
+ byte[] ba = read(1);
+ endTransmission();
+ return (int)(ba[0] & 0xFF);
+ }
+
+ private int readU16(int register) {
+ int lo = readU8(register);
+ int hi = readU8(register + 1);
+ int result = (hi << 8) + lo; // Big Endian
+ //println("(U16) I2C: Device " + toHex(TSL2561_ADDRESS) + " returned " + toHex(result) + " from reg " + toHex(register));
+ return result;
+ }
+
+ private String toHex(int i) {
+ String s = Integer.toString(i, 16).toUpperCase();
+ while (s.length() % 2 != 0) {
+ s = "0" + s;
+ }
+ return "0x" + s;
+ }
+}
diff --git a/java/libraries/io/examples/Light_I2C_TSL2561/setup.png b/java/libraries/io/examples/Light_I2C_TSL2561/setup.png
new file mode 100644
index 0000000000..a0b33fefbe
Binary files /dev/null and b/java/libraries/io/examples/Light_I2C_TSL2561/setup.png differ
diff --git a/java/libraries/io/examples/Servo_I2C_PCA9685/PCA9685.pde b/java/libraries/io/examples/Servo_I2C_PCA9685/PCA9685.pde
new file mode 100644
index 0000000000..67ea815a4f
--- /dev/null
+++ b/java/libraries/io/examples/Servo_I2C_PCA9685/PCA9685.pde
@@ -0,0 +1,148 @@
+import processing.io.I2C;
+
+// PCA9685 is a 16-channel servo/PWM driver
+// datasheet: https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf
+// code contributed by @OlivierLD
+
+public class PCA9685 extends I2C {
+ public final static int PCA9685_ADDRESS = 0x40;
+
+ // registers used
+ public final static int MODE1 = 0x00;
+ public final static int PRESCALE = 0xFE;
+ public final static int LED0_ON_L = 0x06;
+ public final static int LED0_ON_H = 0x07;
+ public final static int LED0_OFF_L = 0x08;
+ public final static int LED0_OFF_H = 0x09;
+
+ private int address;
+ private int freq = 200; // 200 Hz default frequency (after power-up)
+ private boolean hasFreqSet = false; // whether a different frequency has been set
+ private int minPulses[] = new int[16];
+ private int maxPulses[] = new int[16];
+
+
+ public PCA9685(String dev) {
+ this(dev, PCA9685_ADDRESS);
+ }
+ public PCA9685(String dev, int address) {
+ super(dev);
+ this.address = address;
+ // reset device
+ command(MODE1, (byte) 0x00);
+ }
+
+
+ public void attach(int channel) {
+ // same as on Arduino
+ attach(channel, 544, 2400);
+ }
+
+ public void attach(int channel, int minPulse, int maxPulse) {
+ if (channel < 0 || 15 < channel) {
+ throw new IllegalArgumentException("Channel must be between 0 and 15");
+ }
+ minPulses[channel] = minPulse;
+ maxPulses[channel] = maxPulse;
+
+ // set the PWM frequency to be the same as on Arduino
+ if (!hasFreqSet) {
+ frequency(50);
+ }
+ }
+
+ public void write(int channel, float angle) {
+ if (channel < 0 || 15 < channel) {
+ throw new IllegalArgumentException("Channel must be between 0 and 15");
+ }
+ if (angle < 0 || 180 < angle) {
+ throw new IllegalArgumentException("Angle must be between 0 and 180");
+ }
+ int us = (int)(minPulses[channel] + (angle/180.0) * (maxPulses[channel]-minPulses[channel]));
+
+ double pulseLength = 1000000; // 1s = 1,000,000 us per pulse
+ pulseLength /= freq; // 40..1000 Hz
+ pulseLength /= 4096; // 12 bits of resolution
+ int pulse = us;
+ pulse /= pulseLength;
+ // println(pulseLength + " us per bit, pulse:" + pulse);
+ pwm(channel, 0, pulse);
+ }
+
+ public boolean attached(int channel) {
+ if (channel < 0 || 15 < channel) {
+ return false;
+ }
+ return (maxPulses[channel] != 0) ? true : false;
+ }
+
+ public void detach(int channel) {
+ pwm(channel, 0, 0);
+ minPulses[channel] = 0;
+ maxPulses[channel] = 0;
+ }
+
+
+ /**
+ * @param freq 40..1000 Hz
+ */
+ public void frequency(int freq) {
+ this.freq = freq;
+ float preScaleVal = 25000000.0f; // 25MHz
+ preScaleVal /= 4096.0; // 4096: 12-bit
+ preScaleVal /= freq;
+ preScaleVal -= 1.0;
+ // println("Setting PWM frequency to " + freq + " Hz");
+ // println("Estimated pre-scale: " + preScaleVal);
+ double preScale = Math.floor(preScaleVal + 0.5);
+ // println("Final pre-scale: " + preScale);
+ byte oldmode = (byte) readU8(MODE1);
+ byte newmode = (byte) ((oldmode & 0x7F) | 0x10); // sleep
+ command(MODE1, newmode); // go to sleep
+ command(PRESCALE, (byte) (Math.floor(preScale)));
+ command(MODE1, oldmode);
+ delay(5);
+ command(MODE1, (byte) (oldmode | 0x80));
+ hasFreqSet = true;
+ }
+
+ /**
+ * @param channel 0..15
+ * @param on cycle offset to turn output on (0..4095)
+ * @param off cycle offset to turn output off again (0..4095)
+ */
+ public void pwm(int channel, int on, int off) {
+ if (channel < 0 || 15 < channel) {
+ throw new IllegalArgumentException("Channel must be between 0 and 15");
+ }
+ if (on < 0 || 4095 < on) {
+ throw new IllegalArgumentException("On must be between 0 and 4095");
+ }
+ if (off < 0 || 4095 < off) {
+ throw new IllegalArgumentException("Off must be between 0 and 4095");
+ }
+ if (off < on) {
+ throw new IllegalArgumentException("Off must be greater than On");
+ }
+ command(LED0_ON_L + 4 * channel, (byte) (on & 0xFF));
+ command(LED0_ON_H + 4 * channel, (byte) (on >> 8));
+ command(LED0_OFF_L + 4 * channel, (byte) (off & 0xFF));
+ command(LED0_OFF_H + 4 * channel, (byte) (off >> 8));
+ }
+
+
+ private void command(int register, byte value) {
+ beginTransmission(address);
+ write(register);
+ write(value);
+ endTransmission();
+ }
+
+ private byte readU8(int register) {
+ beginTransmission(address);
+ write(register);
+ byte[] ba = read(1);
+ endTransmission();
+ return (byte)(ba[0] & 0xFF);
+ }
+}
diff --git a/java/libraries/io/examples/Servo_I2C_PCA9685/Servo_I2C_PCA9685.pde b/java/libraries/io/examples/Servo_I2C_PCA9685/Servo_I2C_PCA9685.pde
new file mode 100644
index 0000000000..ede995bc5e
--- /dev/null
+++ b/java/libraries/io/examples/Servo_I2C_PCA9685/Servo_I2C_PCA9685.pde
@@ -0,0 +1,41 @@
+import processing.io.*;
+PCA9685 servos;
+
+// see setup.png in the sketch folder for wiring details
+
+void setup() {
+ size(400, 300);
+ //printArray(I2C.list());
+ servos = new PCA9685("i2c-1", 0x40);
+
+ // different servo motors will vary in the pulse width they expect
+ // the lines below set the pulse width for 0 degrees to 544 microseconds (μs)
+ // and the pulse width for 180 degrees to 2400 microseconds
+ // these values match the defaults of the Servo library on Arduino
+ // but you might need to modify this for your particular servo still
+ servos.attach(0, 544, 2400);
+ servos.attach(1, 544, 2400);
+}
+
+void draw() {
+ background(0);
+ stroke(255);
+ strokeWeight(3);
+
+ // we don't go right to the edge to prevent
+ // making the servo unhappy
+ float angle = 90 + sin(frameCount / 100.0)*85;
+ servos.write(0, angle);
+ float y = map(angle, 0, 180, 0, height);
+ line(0, y, width/2, y);
+
+ angle = 90 + cos(frameCount / 100.0)*85;
+ servos.write(1, 90 + cos(frameCount / 100.0)*85);
+ y = map(angle, 0, 180, 0, height);
+ line(width/2, y, width, y);
+}
+
+void dispose() {
+ servos.detach(0);
+ servos.detach(1);
+}
diff --git a/java/libraries/io/examples/Servo_I2C_PCA9685/setup.png b/java/libraries/io/examples/Servo_I2C_PCA9685/setup.png
new file mode 100644
index 0000000000..77b0b74bf0
Binary files /dev/null and b/java/libraries/io/examples/Servo_I2C_PCA9685/setup.png differ
diff --git a/java/libraries/io/examples/I2CDigitalAnalog/I2CDigitalAnalog.pde b/java/libraries/io/examples/SimpleI2C/SimpleI2C.pde
similarity index 86%
rename from java/libraries/io/examples/I2CDigitalAnalog/I2CDigitalAnalog.pde
rename to java/libraries/io/examples/SimpleI2C/SimpleI2C.pde
index 66f034f61e..75a5bacccf 100644
--- a/java/libraries/io/examples/I2CDigitalAnalog/I2CDigitalAnalog.pde
+++ b/java/libraries/io/examples/SimpleI2C/SimpleI2C.pde
@@ -4,6 +4,9 @@ I2C i2c;
// MCP4725 is a Digital-to-Analog converter using I2C
// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/22039d.pdf
+// also see DigitalAnalog_I2C_MCP4725 for how to write the
+// same sketch in an object-oriented way
+
void setup() {
//printArray(I2C.list());
i2c = new I2C(I2C.list()[0]);
diff --git a/java/libraries/io/examples/SimpleInput/SimpleInput.pde b/java/libraries/io/examples/SimpleInput/SimpleInput.pde
index d7a420ce12..5b6f5687a6 100644
--- a/java/libraries/io/examples/SimpleInput/SimpleInput.pde
+++ b/java/libraries/io/examples/SimpleInput/SimpleInput.pde
@@ -5,14 +5,18 @@ import processing.io.*;
// see setup.png in the sketch folder for wiring details
void setup() {
- GPIO.pinMode(4, GPIO.INPUT);
+ // INPUT_PULLUP enables the built-in pull-up resistor for this pin
+ // left alone, the pin will read as HIGH
+ // connected to ground (via e.g. a button or switch) it will read LOW
+ GPIO.pinMode(4, GPIO.INPUT_PULLUP);
}
void draw() {
- // sense the input pin
- if (GPIO.digitalRead(4) == GPIO.HIGH) {
+ if (GPIO.digitalRead(4) == GPIO.LOW) {
+ // button is pressed
fill(255);
} else {
+ // button is not pressed
fill(204);
}
stroke(255);
diff --git a/java/libraries/io/examples/SPIAnalogDigital/SPIAnalogDigital.pde b/java/libraries/io/examples/SimpleSPI/SimpleSPI.pde
similarity index 86%
rename from java/libraries/io/examples/SPIAnalogDigital/SPIAnalogDigital.pde
rename to java/libraries/io/examples/SimpleSPI/SimpleSPI.pde
index bb1311adaf..a1b4e2b08d 100644
--- a/java/libraries/io/examples/SPIAnalogDigital/SPIAnalogDigital.pde
+++ b/java/libraries/io/examples/SimpleSPI/SimpleSPI.pde
@@ -5,6 +5,9 @@ SPI spi;
// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf
// see setup.png in the sketch folder for wiring details
+// also see AnalogDigital_SPI_MCP3001 for how to write the
+// same sketch in an object-oriented way
+
void setup() {
//printArray(SPI.list());
spi = new SPI(SPI.list()[0]);
diff --git a/java/libraries/io/examples/SPIAnalogDigitalOOP/setup.png b/java/libraries/io/examples/SimpleSPI/setup.png
similarity index 100%
rename from java/libraries/io/examples/SPIAnalogDigitalOOP/setup.png
rename to java/libraries/io/examples/SimpleSPI/setup.png
diff --git a/java/libraries/io/examples/ServoSweep/ServoSweep.pde b/java/libraries/io/examples/SoftwareServoSweep/SoftwareServoSweep.pde
similarity index 100%
rename from java/libraries/io/examples/ServoSweep/ServoSweep.pde
rename to java/libraries/io/examples/SoftwareServoSweep/SoftwareServoSweep.pde
diff --git a/java/libraries/io/examples/ServoSweep/setup.png b/java/libraries/io/examples/SoftwareServoSweep/setup.png
similarity index 100%
rename from java/libraries/io/examples/ServoSweep/setup.png
rename to java/libraries/io/examples/SoftwareServoSweep/setup.png
diff --git a/java/libraries/io/examples/ServoSweep/setup_better.png b/java/libraries/io/examples/SoftwareServoSweep/setup_better.png
similarity index 100%
rename from java/libraries/io/examples/ServoSweep/setup_better.png
rename to java/libraries/io/examples/SoftwareServoSweep/setup_better.png
diff --git a/java/libraries/io/examples/Touch_I2C_MPR121/MPR121.pde b/java/libraries/io/examples/Touch_I2C_MPR121/MPR121.pde
new file mode 100644
index 0000000000..0ef023820b
--- /dev/null
+++ b/java/libraries/io/examples/Touch_I2C_MPR121/MPR121.pde
@@ -0,0 +1,112 @@
+import processing.io.I2C;
+
+// MPR121 is a capacitive-touch sensor controller with 12 channels
+// datasheet: https://www.nxp.com/docs/en/data-sheet/MPR121.pdf
+
+class MPR121 extends I2C {
+ int address;
+ int touched;
+
+ // registers used (there are more)
+ static final int EFD0LB = 0x04; // ELE0 Electrode Filtered Data LSB
+ static final int E0TTH = 0x41; // ELE0 Touch Threshold
+ static final int E0RTH = 0x42; // ELE0 Release Threshold
+ static final int E0BV = 0x1e; // ELE0 Baseline Value
+ static final int MHDR = 0x2b; // MHD Rising
+ static final int NHDR = 0x2c; // NHD Amount Rising
+ static final int NCLR = 0x2d; // NCL Rising
+ static final int MHDF = 0x2f; // MHD Falling
+ static final int NHDF = 0x30; // NHD Amount Falling
+ static final int NCLF = 0x31; // NCL Falling
+ static final int CDT = 0x5d; // Filter/Global CDT Configuration
+ static final int ECR = 0x5e; // Electrode Configuration
+ static final int SRST = 0x80; // Soft Reset
+
+ // there can be more than one device connected to the bus
+ // as long as they have different addresses
+ // possible addresses: 0x5a (default) - 0x5d
+ MPR121(String dev, int address) {
+ super(dev);
+ this.address = address;
+ reset();
+ }
+
+ void update() {
+ beginTransmission(address);
+ write(0x00);
+ byte[] in = read(2);
+ // & 0xff makes sure the byte is not interpreted as a negative value
+ touched = (in[1] & 0xff) << 8 | (in[0] & 0xff);
+ }
+
+ boolean touched(int channel) {
+ if (channel < 0 || 11 < channel) {
+ return false;
+ }
+ if ((touched & (1 << channel)) != 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void threshold(int touch, int release) {
+ for (int i=0; i < 12; i++) {
+ threshold(touch, release, i);
+ }
+ }
+
+ void threshold(int touch, int release, int channel) {
+ if (channel < 0 || 11 < channel) {
+ return;
+ }
+ touch = constrain(touch, 0, 255);
+ release = constrain(release, 0, 255);
+ writeRegister(E0TTH + 2*channel, touch);
+ writeRegister(E0RTH + 2*channel, release);
+ }
+
+ int analogRead(int channel) {
+ if (channel < 0 || 11 < channel) {
+ return 0;
+ }
+ beginTransmission(address);
+ write(EFD0LB + 2*channel);
+ byte[] in = read(2);
+ return (in[1] & 0xff) << 8 | (in[0] & 0xff);
+ }
+
+ int analogReadBaseline(int channel) {
+ if (channel < 0 || 11 < channel) {
+ return 0;
+ }
+ beginTransmission(address);
+ write(E0BV + channel);
+ byte[] in = read(1);
+ return (in[0] & 0xff) << 2;
+ }
+
+ void reset() {
+ writeRegister(SRST, 0x63);
+ delay(1);
+ threshold(12, 6);
+ // set baseline filtering control registers (see p. 12)
+ writeRegister(MHDR, 0x01);
+ writeRegister(NHDR, 0x01);
+ writeRegister(NCLR, 0x0e);
+ writeRegister(MHDF, 0x01);
+ writeRegister(NHDF, 0x05);
+ writeRegister(NCLF, 0x01);
+ // change sample interval to 1ms period from default 16ms
+ writeRegister(CDT, 0x20);
+ // start sampling
+ writeRegister(ECR, 0x8f);
+ }
+
+ void writeRegister(int register, int value) {
+ beginTransmission(address);
+ write(register);
+ write(value);
+ endTransmission();
+ }
+}
diff --git a/java/libraries/io/examples/Touch_I2C_MPR121/Touch_I2C_MPR121.pde b/java/libraries/io/examples/Touch_I2C_MPR121/Touch_I2C_MPR121.pde
new file mode 100644
index 0000000000..58f69ad1c1
--- /dev/null
+++ b/java/libraries/io/examples/Touch_I2C_MPR121/Touch_I2C_MPR121.pde
@@ -0,0 +1,26 @@
+import processing.io.*;
+MPR121 touch;
+
+// see setup.png in the sketch folder for wiring details
+
+void setup() {
+ size(600, 200);
+ //printArray(I2C.list());
+ touch = new MPR121("i2c-1", 0x5a);
+}
+
+void draw() {
+ background(204);
+ noStroke();
+
+ touch.update();
+
+ for (int i=0; i < 12; i++) {
+ if (touch.touched(i)) {
+ fill(255, 0, 0);
+ } else {
+ fill(255, 255, 255);
+ }
+ ellipse((width/12) * (i+0.5), height/2, 20, 20);
+ }
+}
diff --git a/java/libraries/io/examples/Touch_I2C_MPR121/setup.png b/java/libraries/io/examples/Touch_I2C_MPR121/setup.png
new file mode 100644
index 0000000000..65bb7a04b2
Binary files /dev/null and b/java/libraries/io/examples/Touch_I2C_MPR121/setup.png differ
diff --git a/java/libraries/io/library/linux-arm64/libprocessing-io.so b/java/libraries/io/library/linux-arm64/libprocessing-io.so
index d180991a48..9659c68af3 100755
Binary files a/java/libraries/io/library/linux-arm64/libprocessing-io.so and b/java/libraries/io/library/linux-arm64/libprocessing-io.so differ
diff --git a/java/libraries/io/library/linux-armv6hf/libprocessing-io.so b/java/libraries/io/library/linux-armv6hf/libprocessing-io.so
index 94ed162d63..9d272c4d86 100755
Binary files a/java/libraries/io/library/linux-armv6hf/libprocessing-io.so and b/java/libraries/io/library/linux-armv6hf/libprocessing-io.so differ
diff --git a/java/libraries/io/library/linux32/libprocessing-io.so b/java/libraries/io/library/linux32/libprocessing-io.so
index 32a231170c..acc2bd6032 100755
Binary files a/java/libraries/io/library/linux32/libprocessing-io.so and b/java/libraries/io/library/linux32/libprocessing-io.so differ
diff --git a/java/libraries/io/library/linux64/libprocessing-io.so b/java/libraries/io/library/linux64/libprocessing-io.so
index 2979766a83..4c2d715886 100755
Binary files a/java/libraries/io/library/linux64/libprocessing-io.so and b/java/libraries/io/library/linux64/libprocessing-io.so differ
diff --git a/java/libraries/io/src/native/iface.h b/java/libraries/io/src/native/iface.h
index 32e235e1e1..74decc1852 100644
--- a/java/libraries/io/src/native/iface.h
+++ b/java/libraries/io/src/native/iface.h
@@ -47,6 +47,30 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile
(JNIEnv *, jclass, jstring, jbyteArray);
+/*
+ * Class: processing_io_NativeInterface
+ * Method: raspbianGpioMemRead
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead
+ (JNIEnv *, jclass, jint);
+
+/*
+ * Class: processing_io_NativeInterface
+ * Method: raspbianGpioMemWrite
+ * Signature: (III)I
+ */
+JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemWrite
+ (JNIEnv *, jclass, jint, jint, jint);
+
+/*
+ * Class: processing_io_NativeInterface
+ * Method: raspbianGpioMemWrite
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias
+ (JNIEnv *, jclass, jint, jint);
+
/*
* Class: processing_io_NativeInterface
* Method: pollDevice
diff --git a/java/libraries/io/src/native/impl.c b/java/libraries/io/src/native/impl.c
index 0f9402ab92..522aa310fa 100644
--- a/java/libraries/io/src/native/impl.c
+++ b/java/libraries/io/src/native/impl.c
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -120,6 +121,143 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile
}
+JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead
+ (JNIEnv *env, jclass cls, jint offset)
+{
+ // validate offset
+ if (4096 <= offset) {
+ return -EINVAL;
+ }
+
+ int file = open("/dev/gpiomem", O_RDWR|O_SYNC);
+ if (file < 0) {
+ return -errno;
+ }
+
+ uint32_t *mem = mmap(NULL, 4096, PROT_READ, MAP_SHARED, file, 0);
+ if (mem == MAP_FAILED) {
+ close(file);
+ return -errno;
+ }
+
+ uint32_t value = mem[offset];
+
+ munmap(mem, 4096);
+ close(file);
+ return value;
+}
+
+
+JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemWrite
+ (JNIEnv *env, jclass cls, jint offset, jint mask, jint value)
+{
+ // validate offset
+ if (4096 <= offset) {
+ return -EINVAL;
+ }
+
+ int file = open("/dev/gpiomem", O_RDWR|O_SYNC);
+ if (file < 0) {
+ return -errno;
+ }
+
+ uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0);
+ if (mem == MAP_FAILED) {
+ close(file);
+ return -errno;
+ }
+
+ mem[offset] = (mem[offset] & ~mask) | (value & mask);
+
+ munmap(mem, 4096);
+ close(file);
+ return 1; // number of bytes written
+}
+
+
+#define BCM2835_GPPUD_OFFSET (0x94 >> 2)
+#define BCM2835_GPPUDCLK0_OFFSET (0x98 >> 2)
+#define BCM2835_GPPUDCLK1_OFFSET (0x9c >> 2)
+
+JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias
+ (JNIEnv *env, jclass cls, jint gpio, jint mode)
+{
+ int ret = 0; // success
+
+ int file = open("/dev/gpiomem", O_RDWR|O_SYNC);
+ if (file < 0) {
+ return -errno;
+ }
+
+ uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0);
+ if (mem == MAP_FAILED) {
+ close(file);
+ return -errno;
+ }
+
+ // validate arguments
+ if (gpio < 0 || 53 < gpio) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ // see BCM2835 datasheet, p. 101
+ uint32_t pud;
+ if (mode == 0) {
+ pud = 0; // floating
+ } else if (mode == 2) {
+ pud = 2; // pull-up
+ } else if (mode == 3) {
+ pud = 1; // pull-down
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * From the BCM2835 datasheet, p. 101:
+ *
+ * The following sequence of events is required:
+ * 1. Write to GPPUD to set the required control signal (i.e. Pull-up or
+ * Pull-Down or neither to remove the current Pull-up/down)
+ * 2. Wait 150 cycles – this provides the required set-up time for the
+ * control signal
+ * 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads
+ * you wish to modify – NOTE only the pads which receive a clock will
+ * be modified, all others will retain their previous state.
+ * 4. Wait 150 cycles – this provides the required hold time for the
+ * control signal
+ * 5. Write to GPPUD to remove the control signal
+ * 6. Write to GPPUDCLK0/1 to remove the clock
+ */
+
+ // python-gpiozero uses a delay of 214 ns, so we do the same
+ struct timespec wait;
+ wait.tv_sec = 0;
+ wait.tv_nsec = 214;
+
+ mem[BCM2835_GPPUD_OFFSET] = pud;
+ nanosleep(&wait, NULL);
+ if (gpio < 32) {
+ mem[BCM2835_GPPUDCLK0_OFFSET] = 1 << gpio;
+ } else {
+ mem[BCM2835_GPPUDCLK1_OFFSET] = 1 << (gpio-32);
+ }
+ nanosleep(&wait, NULL);
+ mem[BCM2835_GPPUD_OFFSET] = 0;
+ if (gpio < 32) {
+ mem[BCM2835_GPPUDCLK0_OFFSET] = 0;
+ } else {
+ mem[BCM2835_GPPUDCLK1_OFFSET] = 0;
+ }
+
+out:
+ munmap(mem, 4096);
+ close(file);
+ return ret;
+}
+
+
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_pollDevice
(JNIEnv *env, jclass cls, jstring _fn, jint timeout)
{
@@ -166,29 +304,36 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c
jbyte *out, *in;
packets.msgs = msgs;
-
- msgs[0].addr = slave;
- msgs[0].flags = 0;
- msgs[0].len = (*env)->GetArrayLength(env, _out);
- out = (*env)->GetByteArrayElements(env, _out, NULL);
- msgs[0].buf = out;
+ packets.nmsgs = 0;
+
+ if (_out != NULL) {
+ msgs[packets.nmsgs].addr = slave;
+ msgs[packets.nmsgs].flags = 0;
+ msgs[packets.nmsgs].len = (*env)->GetArrayLength(env, _out);
+ out = (*env)->GetByteArrayElements(env, _out, NULL);
+ msgs[packets.nmsgs].buf = out;
+ packets.nmsgs++;
+ }
if (_in != NULL) {
+ msgs[packets.nmsgs].addr = slave;
+ msgs[packets.nmsgs].flags = I2C_M_RD; // I2C_M_RECV_LEN is not supported
+ msgs[packets.nmsgs].len = (*env)->GetArrayLength(env, _in);
in = (*env)->GetByteArrayElements(env, _in, NULL);
- msgs[1].addr = slave;
- msgs[1].flags = I2C_M_RD; // I2C_M_RECV_LEN is not supported
- msgs[1].len = (*env)->GetArrayLength(env, _in);
- msgs[1].buf = in;
- packets.nmsgs = 2;
- } else {
- packets.nmsgs = 1;
+ msgs[packets.nmsgs].buf = in;
+ packets.nmsgs++;
}
+ // set the timeout to 100ms - this helps slow devices such as the
+ // Arduino Uno to keep up
+ ioctl(handle, I2C_TIMEOUT, 10);
int ret = ioctl(handle, I2C_RDWR, &packets);
if (ret < 0) {
ret = -errno;
}
- (*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
+ if (_out != NULL) {
+ (*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
+ }
if (_in != NULL) {
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
}
diff --git a/java/libraries/io/src/processing/io/GPIO.java b/java/libraries/io/src/processing/io/GPIO.java
index b4e0d476bf..711aafd808 100644
--- a/java/libraries/io/src/processing/io/GPIO.java
+++ b/java/libraries/io/src/processing/io/GPIO.java
@@ -328,7 +328,7 @@ public static void noInterrupts() {
/**
* Configures a pin to act either as input or output
* @param pin GPIO pin
- * @param mode GPIO.INPUT or GPIO.OUTPUT
+ * @param mode GPIO.INPUT, GPIO.INPUT_PULLUP, GPIO.INPUT_PULLDOWN, or GPIO.OUTPUT
* @see digitalRead
* @see digitalWrite
* @see releasePin
@@ -356,19 +356,15 @@ public static void pinMode(int pin, int mode) {
}
}
- // delay to give udev a chance to change the file permissions behind our back
- // there should really be a cleaner way for this
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
-
// set direction and default level for outputs
fn = String.format("/sys/class/gpio/gpio%d/direction", pin);
String out;
if (mode == INPUT) {
out = "in";
+
+ // attempt to disable any pre-set pullups on the Raspberry Pi
+ NativeInterface.raspbianGpioMemSetPinBias(pin, mode);
+
} else if (mode == OUTPUT) {
if (values.get(pin)) {
out = "high";
@@ -376,13 +372,32 @@ public static void pinMode(int pin, int mode) {
out = "low";
}
} else if (mode == INPUT_PULLUP || mode == INPUT_PULLDOWN) {
+ out = "in";
+
+ // attempt to set pullups on the Raspberry Pi
+ ret = NativeInterface.raspbianGpioMemSetPinBias(pin, mode);
+ if (ret == -2) { // NOENT
+ System.err.println("Setting pullup or pulldown resistors is currently only supported on the Raspberry Pi running Raspbian. Continuing without.");
+ } else if (ret < 0) {
+ System.err.println("Error setting pullup or pulldown resistors: " + NativeInterface.getError(ret) + ". Continuing without.");
+ }
// currently this can't be done in a non-platform-specific way, see
// http://lists.infradead.org/pipermail/linux-rpi-kernel/2015-August/002146.html
- throw new RuntimeException("Not yet implemented");
+
} else {
throw new IllegalArgumentException("Unknown mode");
}
- ret = NativeInterface.writeFile(fn, out);
+
+ // we need to give udev some time to change the file permissions behind our back
+ // retry for 500ms when writing to the file fails with -EACCES
+ long start = System.currentTimeMillis();
+ do {
+ ret = NativeInterface.writeFile(fn, out);
+ if (ret == -13) {
+ Thread.yield();
+ }
+ } while (ret == -13 && System.currentTimeMillis()-start < 500);
+
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
diff --git a/java/libraries/io/src/processing/io/I2C.java b/java/libraries/io/src/processing/io/I2C.java
index 1d0bfac31b..2e5dedb4e5 100644
--- a/java/libraries/io/src/processing/io/I2C.java
+++ b/java/libraries/io/src/processing/io/I2C.java
@@ -125,7 +125,7 @@ public void endTransmission() {
transmitting = false;
out = null;
if (ret < 0) {
- if (ret == -5) { // EIO
+ if (ret == -5 | ret == -121) { // EIO | EREMOTEIO
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));
@@ -185,7 +185,7 @@ public byte[] read(int len) {
transmitting = false;
out = null;
if (ret < 0) {
- if (ret == -5) { // EIO
+ if (ret == -5 | ret == -121) { // EIO | EREMOTEIO
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));
diff --git a/java/libraries/io/src/processing/io/NativeInterface.java b/java/libraries/io/src/processing/io/NativeInterface.java
index 3b1b3e0c32..a05a71ecd5 100644
--- a/java/libraries/io/src/processing/io/NativeInterface.java
+++ b/java/libraries/io/src/processing/io/NativeInterface.java
@@ -62,6 +62,9 @@ public static int writeFile(String fn, String out) {
}
/* GPIO */
+ public static native int raspbianGpioMemRead(int offset);
+ public static native int raspbianGpioMemWrite(int offset, int mask, int value);
+ public static native int raspbianGpioMemSetPinBias(int gpio, int mode);
public static native int pollDevice(String fn, int timeout);
/* I2C */
public static native int transferI2c(int handle, int slave, byte[] out, byte[] in);
diff --git a/java/libraries/io/src/processing/io/PWM.java b/java/libraries/io/src/processing/io/PWM.java
index 2a8fe19f22..c33c9107cb 100644
--- a/java/libraries/io/src/processing/io/PWM.java
+++ b/java/libraries/io/src/processing/io/PWM.java
@@ -206,7 +206,6 @@ public void set(int period, float duty) {
/**
* Enables the PWM output with a preset period of 1 kHz
- * @param duty duty cycle, 0.0 (always off) to 1.0 (always on)
* @webref
*/
public void set(float duty) {
diff --git a/java/libraries/serial/library/jssc.jar b/java/libraries/serial/library/jssc.jar
index 93269cfabe..bf5640670c 100644
Binary files a/java/libraries/serial/library/jssc.jar and b/java/libraries/serial/library/jssc.jar differ
diff --git a/todo.txt b/todo.txt
old mode 100755
new mode 100644