change folder structure and control files to newest concept

This commit is contained in:
Daniel Stock
2018-09-20 16:38:35 +02:00
parent 224fd33794
commit e25c30f942
2747 changed files with 21 additions and 80 deletions
@@ -0,0 +1,74 @@
<?xml version="1.0"?>
<project name="Processing Hardware I/O Library" default="build">
<target name="clean" description="Clean the build directories">
<delete dir="bin" />
<delete file="library/io.jar" />
</target>
<target name="compile" description="Compile sources">
<condition property="core-built">
<available file="../../../core/library/core.jar" />
</condition>
<fail unless="core-built" message="Please build the core library first and make sure it sits in ../../../core/library/core.jar" />
<mkdir dir="bin" />
<javac source="1.8"
target="1.8"
srcdir="src" destdir="bin"
encoding="UTF-8"
includeAntRuntime="false"
classpath="../../../core/library/core.jar"
nowarn="true"
compiler="org.eclipse.jdt.core.JDTCompilerAdapter">
<compilerclasspath path="../../mode/org.eclipse.jdt.core.jar;
../../mode/jdtCompilerAdapter.jar" />
</javac>
</target>
<target name="build" depends="compile" description="Build I/O library">
<jar basedir="bin" destfile="library/io.jar" />
</target>
<target name="dist" depends="build" description="Package standalone library">
<!-- set revision number as library version -->
<loadfile srcfile="../../../todo.txt" property="revision">
<filterchain>
<headfilter lines="1"/>
<tokenfilter>
<stringtokenizer suppressdelims="true"/>
<!-- grab the thing from the first line that's 4 digits -->
<containsregex pattern="(\d\d\d\d)" />
</tokenfilter>
</filterchain>
</loadfile>
<replaceregexp file="library.properties" match="version = .*" replace="version = ${revision}" flags="g" />
<replaceregexp file="library.properties" match="prettyVersion = .*" replace="prettyVersion = ${revision}" flags="g" />
<get src="http://download.processing.org/reference.zip"
dest="reference.zip"
usetimestamp="true" />
<mkdir dir="reference" />
<unzip dest="."
src="reference.zip"
overwrite="true">
<patternset>
<include name="reference/css/**" />
<include name="reference/img/**" />
<include name="reference/javascript/**" />
<include name="reference/libraries/io/**" />
</patternset>
</unzip>
<delete file="reference.zip" />
<echo file="reference/index.html" message="&lt;html&gt;&lt;head&gt;&lt;meta http-equiv='refresh' content='0; url=libraries/io/index.html'&gt;&lt;/head&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;" />
<zip destfile="../io.zip">
<zipfileset dir="." prefix="io">
<exclude name="bin/**"/>
</zipfileset>
</zip>
<copy file="library.properties"
toFile="../io.txt"/>
</target>
</project>
@@ -0,0 +1,107 @@
import processing.io.I2C;
// ADS1015 and ADS1115 are Analog-to-Digital converters using I2C
// they have four channels and 12 and 16 bits of resolution respectively
// datasheets: http://www.ti.com/lit/ds/symlink/ads1015.pdf
// http://www.ti.com/lit/ds/symlink/ads1115.pdf
class ADS1015 extends ADS1X15 {
ADS1015(String dev, int address) {
super(dev, address);
bitShift = 4;
conversionDelay = 1;
}
// returns a number between -1.0 and 1.0
float analogRead(int channel) {
return readSingleEnded(channel) / 2047.0;
}
}
class ADS1115 extends ADS1X15 {
ADS1115(String dev, int address) {
super(dev, address);
bitShift = 0;
conversionDelay = 8;
}
// returns a number between -1.0 and 1.0
float analogRead(int channel) {
return readSingleEnded(channel) / 32767.0;
}
}
class ADS1X15 extends I2C {
int address;
int bitShift; // bits to shift the result to the right
int conversionDelay; // in ms
int channel; // last channel used
int range; // see below
// possible voltage ranges
static final int INTERNAL_6V144 = 0; // +/- 6.144V
static final int INTERNAL_4V096 = 1; // +/- 4.096V (library default)
static final int INTERNAL_2V048 = 2; // +/- 2.048V
static final int INTERNAL_1V024 = 3; // +/- 1.024V
static final int INTERNAL_0V512 = 4; // +/- 0.512V
static final int INTERNAL_0V256 = 5; // +/- 0.256V
ADS1X15(String dev, int address) {
super(dev);
this.address = address;
this.channel = -1;
this.range = INTERNAL_4V096;
}
// be careful not to make the input voltage exceed VCC + 0.3V
// this is regardless of the selected input range
void analogReference(int type) {
if (type < 0 || 7 < type) {
throw new RuntimeException("Invalid range setting");
}
range = type;
}
int readSingleEnded(int channel) {
if (channel < 0 || 3 < channel) {
System.err.println("The channel needs to be from 0 to 3");
throw new IllegalArgumentException("Unexpected channel");
}
if (channel != this.channel) {
int config = 0x0183; // start with the default value from datasheet
config &= ~0x100; // enable continuous readings
config |= (range << 9); // set selected range (gain)
config |= (1 << 14) | (channel << 12); // set single-ended and channel
config |= (1 << 15); // start a single conversion
writeRegister(0x01, config); // write to the configuration register at 0x01
// when the channel switched we need to wait for the upcoming
// conversion to finish
delay(conversionDelay);
// save the channel so that we don't need to do the same for
// subsequent reads from the same channel
this.channel = channel;
}
return readS16(0x00) >> bitShift; // read from the conversion register at 0x00
// the ADS1015 will have its 12-bit result in the upper bits, shift those right by four
}
protected void writeRegister(int register, int value) {
beginTransmission(address);
write(register);
write(value >> 8);
write(value & 0xFF);
endTransmission();
}
protected int readS16(int register) {
beginTransmission(address);
write(register);
byte[] in = read(2);
return (in[0] << 8) | in[1];
}
}
@@ -0,0 +1,38 @@
import processing.io.*;
ADS1015 adc;
// or, alternatively:
// ADS1115 adc;
// see setup.png in the sketch folder for wiring details
void setup() {
//printArray(I2C.list());
adc = new ADS1015("i2c-1", 0x48);
//adc = new ADS1115("i2c-1", 0x48);
// this sets the measuring range to +/- 4.096 Volts
// other ranges supported by this chip:
// INTERNAL_6V144, INTERNAL_2V048, INTERNAL_1V024,
// INTERNAL_0V512, INTERNAL_0V256
adc.analogReference(ADS1X15.INTERNAL_4V096);
// Important: do not attempt to measure voltages higher than
// the supply voltage (VCC) + 0.3V, meaning that 3.6V is the
// absolut maximum voltage on the Raspberry Pi. This is
// irrespective of the analogReference() setting above.
}
void draw() {
// this will return a number between 0 and 1
// (as long as your voltage is positive)
float measured = adc.analogRead(0);
// multiply with the selected range to get the absolut voltage
float volts = measured * 4.096;
println("Analog Input 0 is " + volts + "V");
background(255);
fill(measured * 255);
ellipse(width/2, height/2, width * 0.75, width * 0.75);
}
@@ -0,0 +1,20 @@
import processing.io.*;
MCP3001 adc;
// see setup.png in the sketch folder for wiring details
void setup() {
//printArray(SPI.list());
adc = new MCP3001(SPI.list()[0]);
}
void draw() {
// this will return a number between 0 and 1
float measured = adc.analogRead();
// multiply with the supply voltage to get an absolute value
float volts = 3.3 * measured;
println("Analog Input is " + volts + "V");
background(measured * 255);
}
@@ -0,0 +1,23 @@
import processing.io.SPI;
// MCP3001 is a Analog-to-Digital converter using SPI
// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf
class MCP3001 extends SPI {
MCP3001(String dev) {
super(dev);
settings(500000, SPI.MSBFIRST, SPI.MODE0);
}
// returns a number between 0.0 and 1.0
float analogRead() {
// dummy write, actual values don't matter
byte[] out = { 0, 0 };
byte[] in = transfer(out);
// some input bit shifting according to the datasheet p. 16
int val = ((in[0] & 0x1f) << 5) | ((in[1] & 0xf8) >> 3);
// val is between 0 and 1023
return val/1023.0;
}
}
@@ -0,0 +1,22 @@
import processing.io.*;
MCP3008 adc;
// see setup.png in the sketch folder for wiring details
void setup() {
//printArray(SPI.list());
adc = new MCP3008(SPI.list()[0]);
}
void draw() {
// this will return a number between 0 and 1
float measured = adc.analogRead(0);
// multiply with the supply voltage to get an absolute value
float volts = 3.3 * measured;
println("Analog Input 0 is " + volts + "V");
background(255);
fill(measured * 255);
ellipse(width/2, height/2, width * 0.75, width * 0.75);
}
@@ -0,0 +1,28 @@
import processing.io.SPI;
// MCP3008 is a Analog-to-Digital converter using SPI
// other than the MCP3001, this has 8 input channels
// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf
class MCP3008 extends SPI {
MCP3008(String dev) {
super(dev);
settings(500000, SPI.MSBFIRST, SPI.MODE0);
}
// returns a number between 0.0 and 1.0
float analogRead(int channel) {
if (channel < 0 || 7 < channel) {
System.err.println("The channel needs to be from 0 to 7");
throw new IllegalArgumentException("Unexpected channel");
}
byte[] out = { 0, 0, 0 };
// encode the channel number in the first byte
out[0] = (byte)(0x18 | channel);
byte[] in = transfer(out);
int val = ((in[1] & 0x03) << 8) | (in[2] & 0xff);
// val is between 0 and 1023
return val/1023.0;
}
}
@@ -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);
}
@@ -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;
}
}
@@ -0,0 +1,12 @@
import processing.io.*;
MCP4725 dac;
void setup() {
//printArray(I2C.list());
dac = new MCP4725(I2C.list()[0], 0x60);
}
void draw() {
background(map(mouseX, 0, width, 0, 255));
dac.setAnalog(map(mouseX, 0, width, 0.0, 1.0));
}
@@ -0,0 +1,27 @@
import processing.io.I2C;
// MCP4725 is a Digital-to-Analog converter using I2C
// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/22039d.pdf
class MCP4725 extends I2C {
int address;
// there can be more than one device connected to the bus
// as long as they have different addresses
MCP4725(String dev, int address) {
super(dev);
this.address = address;
}
// outputs voltages from 0V to the supply voltage
// (works with 3.3V and 5V)
void setAnalog(float fac) {
fac = constrain(fac, 0.0, 1.0);
// convert to 12 bit value
int val = int(4095 * fac);
beginTransmission(address);
write(val >> 8);
write(val & 255);
endTransmission();
}
}
@@ -0,0 +1,22 @@
import processing.io.*;
// 0.96" 128x64 OLED display ("SKU 346540")
SSD1306 oled;
void setup() {
size(128, 64);
// the display can be set to one of these two addresses: 0x3c (default) or 0x3d
// (they might be listed as 0x7a and 0x7b on the circuit board)
// you might need to use a different interface on other SBCs
oled = new SSD1306("i2c-1", 0x3c);
}
void draw() {
background(0);
stroke(255);
line(0, 0, 127, 63);
line(0, 63, 127, 0);
oled.sendImage(get());
}
@@ -0,0 +1,118 @@
import processing.io.I2C;
// SSD1306 is a small, inexpensive 128x64 pixels monochrome OLED display
// available online as "0.96" 128x64 OLED display", SKU 346540
// or from Adafruit
// datasheet: https://www.adafruit.com/datasheets/SSD1306.pdf
class SSD1306 extends I2C {
int address;
// there can be more than one device connected to the bus
// as long as they have different addresses
SSD1306(String dev, int address) {
super(dev);
this.address = address;
init();
}
protected void init() {
writeCommand(0xae); // turn display off
writeCommand(0xa8, 0x3f); // set multiplex ratio to the highest setting
writeCommand(0x8d, 0x14); // enable charge pump
writeCommand(0x20, 0x00); // set memory addressing mode to horizontal
writeCommand(0xd5, 0x80); // set display clock divide ratio & oscillator frequency to default
writeCommand(0xd3, 0x00); // no display offset
writeCommand(0x40 | 0x00); // set default display start line
// use the following two lines to flip the display
writeCommand(0xa0 | 0x01); // set segment re-map
writeCommand(0xc8); // set COM output scan direction
writeCommand(0xda, 0x12); // set COM pins hardware configuration
writeCommand(0xd9, 0xf1); // set pre-charge period to 241x DCLK
writeCommand(0xdB, 0x40); // set VCOMH deselect level
writeCommand(0xa4); // display RAM content (not all-on)
writeCommand(0xa6); // set normal (not-inverted) display
// set this since we don't have access to the OLED's reset pins (?)
writeCommand(0x21, 0, 127); // set column address
writeCommand(0x22, 0, 7); // set page address
writeCommand(0x81, 0xcf); // set contrast
writeCommand(0x2e); // deactivate scroll
writeCommand(0xaf); // turn display on
}
void invert(boolean inverted) {
if (inverted) {
writeCommand(0xa7);
} else {
writeCommand(0xa6);
}
}
void sendImage(PImage img) {
sendImage(img, 0, 0);
}
void sendImage(PImage img, int startX, int startY) {
byte[] frame = new byte[1024];
img.loadPixels();
for (int y=startY; y < height && y-startY < 64; y++) {
for (int x=startX; x < width && x-startX < 128; x++) {
if (128 <= brightness(img.pixels[y*img.width+x])) {
// this isn't the normal (scanline) mapping, but 8 pixels below each other at a time
// white pixels have their bit turned on
frame[x + (y/8)*128] |= (1 << (y % 8));
}
}
}
sendFramebuffer(frame);
}
void sendFramebuffer(byte[] buf) {
if (buf.length != 1024) {
System.err.println("The framebuffer should be 1024 bytes long, with one bit per pixel");
throw new IllegalArgumentException("Unexpected buffer size");
}
writeCommand(0x00 | 0x0); // set start address
writeCommand(0x10 | 0x0); // set higher column start address
writeCommand(0x40 | 0x0); // set start line
// send the frame buffer as 16 byte long packets
for (int i=0; i < buf.length/16; i++) {
super.beginTransmission(address);
super.write(0x40); // indicates data write
for (int j=0; j < 16; j++) {
super.write(buf[i*16+j]);
}
super.endTransmission();
}
}
protected void writeCommand(int arg1) {
super.beginTransmission(address);
super.write(0x00); // indicates command write
super.write(arg1);
super.endTransmission();
}
protected void writeCommand(int arg1, int arg2) {
super.beginTransmission(address);
super.write(0x00);
super.write(arg1);
super.write(arg2);
super.endTransmission();
}
protected void writeCommand(int arg1, int arg2, int arg3) {
super.beginTransmission(address);
super.write(0x00);
super.write(arg1);
super.write(arg2);
super.write(arg3);
super.endTransmission();
}
}
@@ -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;
}
}
@@ -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);
}
@@ -0,0 +1,25 @@
import processing.io.*;
color bgcolor = 0;
// GPIO numbers refer to different phyiscal pins on various boards
// On the Raspberry Pi GPIO 4 is physical pin 7 on the header
// see setup.png in the sketch folder for wiring details
void setup() {
GPIO.pinMode(4, GPIO.INPUT);
GPIO.attachInterrupt(4, this, "pinEvent", GPIO.RISING);
}
void draw() {
background(bgcolor);
}
// this function will be called whenever GPIO 4 is brought from LOW to HIGH
void pinEvent(int pin) {
println("Received interrupt");
if (bgcolor == 0) {
bgcolor = color(255);
} else {
bgcolor = color(0);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

@@ -0,0 +1,39 @@
import processing.io.*;
LED leds[];
// the Raspberry Pi has two build-in LEDs we can control
// led0 (green) and led1 (red)
void setup() {
String available[] = LED.list();
print("Available: ");
println(available);
// create an object for each LED and store it in an array
leds = new LED[available.length];
for (int i=0; i < available.length; i++) {
leds[i] = new LED(available[i]);
}
frameRate(1);
}
void draw() {
// make the LEDs count in binary
for (int i=0; i < leds.length; i++) {
if ((frameCount & (1 << i)) != 0) {
leds[i].brightness(1.0);
} else {
leds[i].brightness(0.0);
}
}
println(frameCount);
}
void keyPressed() {
// cleanup
for (int i=0; i < leds.length; i++) {
leds[i].close();
}
exit();
}
@@ -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();
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
@@ -0,0 +1,30 @@
import processing.io.*;
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]);
}
void draw() {
background(map(mouseX, 0, width, 0, 255));
setAnalog(map(mouseX, 0, width, 0.0, 1.0));
}
// outputs voltages from 0V to the supply voltage
// (works with 3.3V and 5V)
void setAnalog(float fac) {
fac = constrain(fac, 0.0, 1.0);
// convert to 12 bit value
int val = int(4095 * fac);
i2c.beginTransmission(0x60);
i2c.write(val >> 8);
i2c.write(val & 255);
i2c.endTransmission();
}
@@ -0,0 +1,24 @@
import processing.io.*;
// GPIO numbers refer to different phyiscal pins on various boards
// On the Raspberry Pi GPIO 4 is physical pin 7 on the header
// see setup.png in the sketch folder for wiring details
void setup() {
// 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() {
if (GPIO.digitalRead(4) == GPIO.LOW) {
// button is pressed
fill(255);
} else {
// button is not pressed
fill(204);
}
stroke(255);
ellipse(width/2, height/2, width*0.75, height*0.75);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

@@ -0,0 +1,25 @@
import processing.io.*;
boolean ledOn = false;
// GPIO numbers refer to different phyiscal pins on various boards
// On the Raspberry Pi GPIO 4 is physical pin 7 on the header
// see setup.png in the sketch folder for wiring details
void setup() {
GPIO.pinMode(4, GPIO.OUTPUT);
frameRate(0.5);
}
void draw() {
// make the LED blink
ledOn = !ledOn;
if (ledOn) {
GPIO.digitalWrite(4, GPIO.LOW);
fill(204);
} else {
GPIO.digitalWrite(4, GPIO.HIGH);
fill(255);
}
stroke(255);
ellipse(width/2, height/2, width*0.75, height*0.75);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

@@ -0,0 +1,56 @@
import processing.io.*;
// using a capacitor that gets charged and discharged, while
// measuring the time it takes, is an inexpensive way to
// read the value of an (analog) resistive sensor, such as
// a photocell
// kudos to ladyada for the original tutorial
// see setup.png in the sketch folder for wiring details
int max = 0;
int min = 9999;
void setup() {
}
void draw() {
int val = sensorRead(4);
println(val);
// track largest and smallest reading, to get a sense
// how we compare
if (max < val) {
max = val;
}
if (val < min) {
min = val;
}
// convert current reading into a number between 0.0 and 1.0
float frac = map(val, min, max, 0.0, 1.0);
background(255 * frac);
}
int sensorRead(int pin) {
// discharge the capacitor
GPIO.pinMode(pin, GPIO.OUTPUT);
GPIO.digitalWrite(pin, GPIO.LOW);
delay(100);
// now the capacitor should be empty
// measure the time takes to fill it
// up to ~ 1.4V again
GPIO.pinMode(pin, GPIO.INPUT);
int start = millis();
while (GPIO.digitalRead(pin) == GPIO.LOW) {
// wait
}
// return the time elapsed
// this will vary based on the value of the
// resistive sensor (lower resistance will
// make the capacitor charge faster)
return millis() - start;
}
@@ -0,0 +1,25 @@
import processing.io.*;
SPI spi;
// MCP3001 is a Analog-to-Digital converter using 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]);
spi.settings(500000, SPI.MSBFIRST, SPI.MODE0);
}
void draw() {
// dummy write, actual values don't matter
byte[] out = { 0, 0 };
byte[] in = spi.transfer(out);
// some input bit shifting according to the datasheet p. 16
int val = ((in[0] & 0x1f) << 5) | ((in[1] & 0xf8) >> 3);
// val is between 0 and 1023
background(map(val, 0, 1023, 0, 255));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

@@ -0,0 +1,34 @@
import processing.io.*;
// see setup.png in the sketch folder for wiring details
// for more reliable operation it is recommended to power
// the servo from an external power source, see setup_better.png
SoftwareServo servo1;
SoftwareServo servo2;
void setup() {
size(400, 300);
servo1 = new SoftwareServo(this);
servo1.attach(17);
servo2 = new SoftwareServo(this);
servo2.attach(4);
}
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;
servo1.write(angle);
float y = map(angle, 0, 180, 0, height);
line(0, y, width/2, y);
angle = 90 + cos(frameCount / 100.0)*85;
servo2.write(90 + cos(frameCount / 100.0)*85);
y = map(angle, 0, 180, 0, height);
line(width/2, y, width, y);
}
@@ -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();
}
}
@@ -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);
}
}
@@ -0,0 +1,9 @@
name = Hardware I/O
authors = The Processing Foundation
url = http://processing.org/reference/libraries/io/index.html
categories = Hardware
sentence = Access peripherals on the Raspberry Pi and other Linux-based computers.
paragraph = For other platforms, this is solely provided in order to build and export sketches that require processing.io.
version = 1
prettyVersion = 1
minRevision = 247
@@ -0,0 +1 @@
name = Hardware I/O for Raspberry Pi and other Linux-based computers
@@ -0,0 +1,20 @@
TARGET := libprocessing-io.so
OBJS := impl.o
CC := gcc
# prefix with -m32 to compile for linux32
CFLAGS := -std=gnu99 -fPIC -g -ffast-math
CFLAGS += -I$(shell dirname $(shell realpath $(shell which javac)))/../include
CFLAGS += -I$(shell dirname $(shell realpath $(shell which javac)))/../include/linux
LDFLAGS := -shared
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
iface.h:
javah -classpath .. -o iface.h processing.io.NativeInterface
clean:
rm -f $(TARGET) $(OBJS)
.PHONY: iface.h clean
@@ -0,0 +1,133 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class processing_io_NativeInterface */
#ifndef _Included_processing_io_NativeInterface
#define _Included_processing_io_NativeInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: processing_io_NativeInterface
* Method: openDevice
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_openDevice
(JNIEnv *, jclass, jstring);
/*
* Class: processing_io_NativeInterface
* Method: getError
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_processing_io_NativeInterface_getError
(JNIEnv *, jclass, jint);
/*
* Class: processing_io_NativeInterface
* Method: closeDevice
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_closeDevice
(JNIEnv *, jclass, jint);
/*
* Class: processing_io_NativeInterface
* Method: readFile
* Signature: (Ljava/lang/String;[B)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile
(JNIEnv *, jclass, jstring, jbyteArray);
/*
* Class: processing_io_NativeInterface
* Method: writeFile
* Signature: (Ljava/lang/String;[B)I
*/
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
* Signature: (Ljava/lang/String;I)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_pollDevice
(JNIEnv *, jclass, jstring, jint);
/*
* Class: processing_io_NativeInterface
* Method: transferI2c
* Signature: (II[B[B)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c
(JNIEnv *, jclass, jint, jint, jbyteArray, jbyteArray);
/*
* Class: processing_io_NativeInterface
* Method: servoStartThread
* Signature: (III)J
*/
JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread
(JNIEnv *, jclass, jint, jint, jint);
/*
* Class: processing_io_NativeInterface
* Method: servoUpdateThread
* Signature: (JII)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread
(JNIEnv *, jclass, jlong, jint, jint);
/*
* Class: processing_io_NativeInterface
* Method: servoStopThread
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread
(JNIEnv *, jclass, jlong);
/*
* Class: processing_io_NativeInterface
* Method: setSpiSettings
* Signature: (IIII)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_setSpiSettings
(JNIEnv *, jclass, jint, jint, jint, jint);
/*
* Class: processing_io_NativeInterface
* Method: transferSpi
* Signature: (I[B[B)I
*/
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferSpi
(JNIEnv *, jclass, jint, jbyteArray, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,489 @@
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/spi/spidev.h>
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <time.h>
#include <unistd.h>
#include "iface.h"
static const int servo_pulse_oversleep = 35; // amount of uS to account for when sleeping
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_openDevice
(JNIEnv *env, jclass cls, jstring _fn)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_RDWR);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
} else {
return file;
}
}
JNIEXPORT jstring JNICALL Java_processing_io_NativeInterface_getError
(JNIEnv *env, jclass cls, jint _errno)
{
char *msg = strerror(abs(_errno));
if (msg) {
return (*env)->NewStringUTF(env, msg);
} else {
return NULL;
}
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_closeDevice
(JNIEnv *env, jclass cls, jint handle)
{
if (close(handle) < 0) {
return -errno;
} else {
return 0;
}
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile
(JNIEnv *env, jclass cls, jstring _fn, jbyteArray _in)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_RDONLY);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
}
jbyte *in = (*env)->GetByteArrayElements(env, _in, NULL);
int len = read(file, in, (*env)->GetArrayLength(env, _in));
if (len < 0) {
len = -errno;
}
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
close(file);
return len;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile
(JNIEnv *env, jclass cls, jstring _fn, jbyteArray _out)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_WRONLY);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
}
jbyte *out = (*env)->GetByteArrayElements(env, _out, JNI_FALSE);
int len = write(file, out, (*env)->GetArrayLength(env, _out));
if (len < 0) {
len = -errno;
}
(*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
close(file);
return len;
}
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)
{
const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE);
int file = open(fn, O_RDONLY|O_NONBLOCK);
(*env)->ReleaseStringUTFChars(env, _fn, fn);
if (file < 0) {
return -errno;
}
// dummy read
char tmp;
while (0 < read(file, &tmp, 1));
struct pollfd fds[1];
memset(fds, 0, sizeof(fds));
fds[0].fd = file;
fds[0].events = POLLPRI|POLLERR;
// and wait
int ret = poll(fds, 1, timeout);
close(file);
if (ret < 0) {
return -errno;
} else if (ret == 0) {
// timeout
return 0;
} else if (fds[0].revents & POLLPRI) {
// interrupt
return 1;
} else {
// POLLERR?
return -ENOMSG;
}
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c
(JNIEnv *env, jclass cls, jint handle, jint slave, jbyteArray _out, jbyteArray _in)
{
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg msgs[2];
jbyte *out, *in;
packets.msgs = msgs;
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[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;
}
if (_out != NULL) {
(*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
}
if (_in != NULL) {
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
}
return ret;
}
typedef struct {
int fd;
pthread_t thread;
int pulse;
int period;
} SERVO_STATE_T;
static void* servoThread(void *ptr) {
SERVO_STATE_T *state = (SERVO_STATE_T*)ptr;
struct timespec on, off;
on.tv_sec = 0;
off.tv_sec = 0;
do {
write(state->fd, "1", 1);
on.tv_nsec = state->pulse * 1000;
nanosleep(&on, NULL);
write(state->fd, "0", 1);
off.tv_nsec = (state->period - state->pulse) * 1000;
nanosleep(&off, NULL);
} while (1);
}
JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread
(JNIEnv *env, jclass cls, jint gpio, jint pulse, jint period)
{
char path[26 + 19 + 1];
int fd;
pthread_t thread;
// setup struct holding our state
SERVO_STATE_T *state = malloc(sizeof(SERVO_STATE_T));
if (!state) {
return -ENOMEM;
}
memset(state, 0, sizeof(*state));
state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0;
// we're obviously also oversleeping in the general period case
// but other than the pulse, this doesn't seem to be crucial with servos
state->period = period;
// open gpio
sprintf(path, "/sys/class/gpio/gpio%d/value", gpio);
state->fd = open(path, O_WRONLY);
if (state->fd < 0) {
free(state);
return -errno;
}
// start thread
int ret = pthread_create(&state->thread, NULL, servoThread, state);
if (ret != 0) {
free(state);
return -ret;
}
// set scheduling policy and priority
struct sched_param param;
param.sched_priority = 75;
ret = pthread_setschedparam(state->thread, SCHED_FIFO, &param);
if (ret != 0) {
fprintf(stderr, "Error setting thread policy: %s\n", strerror(ret));
}
return (intptr_t)state;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread
(JNIEnv *env, jclass cls, jlong handle, jint pulse, jint period)
{
SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle;
state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0;
state->period = period;
return 0;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread
(JNIEnv *env, jclass cls, jlong handle)
{
SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle;
// signal thread to stop
pthread_cancel(state->thread);
pthread_join(state->thread, NULL);
close(state->fd);
free(state);
return 0;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_setSpiSettings
(JNIEnv *env, jclass cls, jint handle, jint _maxSpeed, jint dataOrder, jint mode)
{
uint8_t tmp;
uint32_t maxSpeed;
tmp = (uint8_t)mode;
int ret = ioctl(handle, SPI_IOC_WR_MODE, &tmp);
if (ret < 0) {
return ret;
}
tmp = (uint8_t)dataOrder;
ret = ioctl(handle, SPI_IOC_WR_LSB_FIRST, &tmp);
if (ret < 0) {
return ret;
}
maxSpeed = (uint32_t)_maxSpeed;
ret = ioctl(handle, SPI_IOC_WR_MAX_SPEED_HZ, &maxSpeed);
if (ret < 0) {
return ret;
}
return 0;
}
JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferSpi
(JNIEnv *env, jclass cls, jint handle, jbyteArray _out, jbyteArray _in)
{
jbyte* out = (*env)->GetByteArrayElements(env, _out, NULL);
jbyte* in = (*env)->GetByteArrayElements(env, _in, NULL);
struct spi_ioc_transfer xfer = {
.tx_buf = (unsigned long)out,
.rx_buf = (unsigned long)in,
.len = MIN((*env)->GetArrayLength(env, _out), (*env)->GetArrayLength(env, _in)),
};
int ret = ioctl(handle, SPI_IOC_MESSAGE(1), &xfer);
(*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, _in, in, 0);
return ret;
}
@@ -0,0 +1,529 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.core.*;
import processing.io.NativeInterface;
import java.lang.reflect.Method;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
/**
* @webref
*/
public class GPIO {
// those constants are generally the same as in Arduino.h
public static final int INPUT = 0;
public static final int OUTPUT = 1;
public static final int INPUT_PULLUP = 2;
public static final int INPUT_PULLDOWN = 3;
public static final int LOW = 0;
public static final int HIGH = 1;
public static final int NONE = 0;
/**
* trigger when level changes
*/
public static final int CHANGE = 1;
/**
* trigger when level changes from high to low
*/
public static final int FALLING = 2;
/**
* trigger when level changes from low to high
*/
public static final int RISING = 3;
protected static Map<Integer, Thread> irqThreads = new HashMap<Integer, Thread>();
protected static boolean serveInterrupts = true;
protected static BitSet values = new BitSet();
static {
NativeInterface.loadLibrary();
}
public static void analogWrite(int pin, int value) {
// currently this can't be done in a non-platform-specific way
// the best way forward would be implementing a generic, "soft"
// PWM in the kernel that uses high resolution timers, similiar
// to the patch Bill Gatliff posted, which unfortunately didn't
// get picked up, see
// https://dev.openwrt.org/browser/trunk/target/linux/generic/files/drivers/pwm/gpio-pwm.c?rev=35328
// additionally, there currently doesn't seem to be a way to link
// a PWM channel back to the GPIO pin it is associated with
// alternatively, this could be implemented in user-space to some
// degree
// see http://stackoverflow.com/a/13371570/3030124
// see http://raspberrypi.stackexchange.com/a/304
throw new RuntimeException("Not yet implemented");
}
/**
* Calls a function when the value of an input pin changes
* @param pin GPIO pin
* @param parent typically use "this"
* @param method name of sketch method to call
* @param mode when to call: GPIO.CHANGE, GPIO.FALLING or GPIO.RISING
* @see noInterrupts
* @see interrupts
* @see releaseInterrupt
* @webref
*/
public static void attachInterrupt(int pin, PApplet parent, String method, int mode) {
if (irqThreads.containsKey(pin)) {
throw new RuntimeException("You must call releaseInterrupt before attaching another interrupt on the same pin");
}
enableInterrupt(pin, mode);
final int irqPin = pin;
final PApplet irqObject = parent;
final Method irqMethod;
try {
irqMethod = parent.getClass().getMethod(method, int.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Method " + method + " does not exist");
}
// it might be worth checking how Java threads compare to pthreads in terms
// of latency
Thread t = new Thread(new Runnable() {
public void run() {
boolean gotInterrupt = false;
try {
do {
try {
if (waitForInterrupt(irqPin, 100)) {
gotInterrupt = true;
}
if (gotInterrupt && serveInterrupts) {
irqMethod.invoke(irqObject, irqPin);
gotInterrupt = false;
}
// if we received an interrupt while interrupts were disabled
// we still deliver it the next time interrupts get enabled
// not sure if everyone agrees with this logic though
} catch (RuntimeException e) {
// make sure we're not busy spinning on error
Thread.sleep(100);
}
} while (!Thread.currentThread().isInterrupted());
} catch (Exception e) {
// terminate the thread on any unexpected exception that might occur
System.err.println("Terminating interrupt handling for pin " + irqPin + " after catching: " + e.getMessage());
}
}
}, "GPIO" + pin + " IRQ");
t.setPriority(Thread.MAX_PRIORITY);
t.start();
irqThreads.put(pin, t);
}
/**
* Checks if the GPIO pin number can be valid
*
* Board-specific classes, such as RPI, assign -1 to pins that carry power,
* ground and the like.
* @param pin GPIO pin
*/
protected static void checkValidPin(int pin) {
if (pin < 0) {
throw new RuntimeException("Operation not supported on this pin");
}
}
/**
* Returns the value of an input pin
* @param pin GPIO pin
* @return GPIO.HIGH (1) or GPIO.LOW (0)
* @see pinMode
* @see digitalWrite
* @webref
*/
public static int digitalRead(int pin) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
return LOW;
}
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
byte in[] = new byte[2];
int ret = NativeInterface.readFile(fn, in);
if (ret < 0) {
throw new RuntimeException(NativeInterface.getError(ret));
} else if (1 <= ret && in[0] == '0') {
return LOW;
} else if (1 <= ret && in[0] == '1') {
return HIGH;
} else {
System.err.print("Read " + ret + " bytes");
if (0 < ret) {
System.err.format(", first byte is 0x%02x" + in[0]);
}
System.err.println();
throw new RuntimeException("Unexpected value");
}
}
/**
* Sets an output pin to be either high or low
* @param pin GPIO pin
* @param value GPIO.HIGH (1) or GPIO.LOW (0)
* @see pinMode
* @see digitalRead
* @webref
*/
public static void digitalWrite(int pin, int value) {
checkValidPin(pin);
String out;
if (value == LOW) {
// values are also stored in a bitmap to make it possible to set a
// default level per pin before enabling the output
values.clear(pin);
out = "0";
} else if (value == HIGH) {
values.set(pin);
out = "1";
} else {
System.err.println("Only GPIO.LOW and GPIO.HIGH, 0 and 1, or true and false, can be used.");
throw new IllegalArgumentException("Illegal value");
}
if (NativeInterface.isSimulated()) {
return;
}
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
int ret = NativeInterface.writeFile(fn, out);
if (ret < 0) {
if (ret != -2) { // ENOENT, pin might not yet be exported
throw new RuntimeException(NativeInterface.getError(ret));
}
}
}
/**
* @param value true or false
*/
public static void digitalWrite(int pin, boolean value) {
if (value) {
digitalWrite(pin, HIGH);
} else {
digitalWrite(pin, LOW);
}
}
/**
* Disables an interrupt for an input pin
* @param pin GPIO pin
* @see enableInterrupt
* @see waitForInterrupt
*/
protected static void disableInterrupt(int pin) {
enableInterrupt(pin, NONE);
}
/**
* Enables an interrupt for an input pin
* @param pin GPIO pin
* @param mode what to wait for: GPIO.CHANGE, GPIO.FALLING or GPIO.RISING
* @see waitForInterrupt
* @see disableInterrupt
*/
protected static void enableInterrupt(int pin, int mode) {
checkValidPin(pin);
String out;
if (mode == NONE) {
out = "none";
} else if (mode == CHANGE) {
out = "both";
} else if (mode == FALLING) {
out = "falling";
} else if (mode == RISING) {
out = "rising";
} else {
throw new IllegalArgumentException("Unknown mode");
}
if (NativeInterface.isSimulated()) {
return;
}
String fn = String.format("/sys/class/gpio/gpio%d/edge", pin);
int ret = NativeInterface.writeFile(fn, out);
if (ret < 0) {
if (ret == -2) { // ENOENT
System.err.println("Make sure your called pinMode on the input pin");
}
throw new RuntimeException(NativeInterface.getError(ret));
}
}
/**
* Allows interrupts to happen
* @see attachInterrupt
* @see noInterrupts
* @see releaseInterrupt
* @webref
*/
public static void interrupts() {
serveInterrupts = true;
}
/**
* Prevents interrupts from happpening
* @see attachInterrupt
* @see interrupts
* @see releaseInterrupt
* @webref
*/
public static void noInterrupts() {
serveInterrupts = false;
}
/**
* Configures a pin to act either as input or output
* @param pin GPIO pin
* @param mode GPIO.INPUT, GPIO.INPUT_PULLUP, GPIO.INPUT_PULLDOWN, or GPIO.OUTPUT
* @see digitalRead
* @see digitalWrite
* @see releasePin
* @webref
*/
public static void pinMode(int pin, int mode) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
return;
}
// export pin through sysfs
String fn = "/sys/class/gpio/export";
int ret = NativeInterface.writeFile(fn, Integer.toString(pin));
if (ret < 0) {
if (ret == -2) { // ENOENT
System.err.println("Make sure your kernel is compiled with GPIO_SYSFS enabled");
}
if (ret == -22) { // EINVAL
System.err.println("GPIO pin " + pin + " does not seem to be available on your platform");
}
if (ret != -16) { // EBUSY, returned when the pin is already exported
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
}
// 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";
} else {
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
} else {
throw new IllegalArgumentException("Unknown mode");
}
// 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));
}
}
/**
* Stops listening for interrupts on an input pin
* @param pin GPIO pin
* @see attachInterrupt
* @see noInterrupts
* @see interrupts
* @webref
*/
public static void releaseInterrupt(int pin) {
Thread t = irqThreads.get(pin);
if (t == null) {
return;
}
t.interrupt();
try {
t.join();
} catch (InterruptedException e) {
System.err.println("Error joining thread in releaseInterrupt: " + e.getMessage());
}
t = null;
irqThreads.remove(pin);
disableInterrupt(pin);
}
/**
* Gives ownership of a pin back to the operating system
* @param pin GPIO pin
* @see pinMode
* @webref
*/
public static void releasePin(int pin) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
return;
}
String fn = "/sys/class/gpio/unexport";
int ret = NativeInterface.writeFile(fn, Integer.toString(pin));
if (ret < 0) {
if (ret == -2) { // ENOENT
System.err.println("Make sure your kernel is compiled with GPIO_SYSFS enabled");
}
// EINVAL is returned when trying to unexport pins that weren't exported to begin with, ignore this case
if (ret != -22) {
throw new RuntimeException(NativeInterface.getError(ret));
}
}
}
/**
* Waits for the value of an input pin to change
* @param pin GPIO pin
* @param mode what to wait for: GPIO.CHANGE, GPIO.FALLING or GPIO.RISING
* @webref
*/
public static void waitFor(int pin, int mode) {
waitFor(pin, mode, -1);
}
/**
* Waits for the value of an input pin to change
*
* This function will throw a RuntimeException in case of a timeout.
* @param timeout don't wait more than timeout milliseconds
* @webref
*/
public static void waitFor(int pin, int mode, int timeout) {
enableInterrupt(pin, mode);
if (waitForInterrupt(pin, timeout) == false) {
throw new RuntimeException("Timeout occurred");
}
}
public static boolean waitForInterrupt(int pin, int mode, int timeout) {
throw new RuntimeException("The waitForInterrupt function has been renamed to waitFor. Please update your sketch accordingly.");
}
/**
* Waits for the value of an input pin to change
*
* Make sure to setup the interrupt with enableInterrupt() before calling
* this function. A timeout value of -1 waits indefinitely.
* @param pin GPIO pin
* @param timeout don't wait more than timeout milliseconds
* @return true if the interrupt occured, false if the timeout occured
* @see enableInterrupt
* @see disableInterrupt
*/
protected static boolean waitForInterrupt(int pin, int timeout) {
checkValidPin(pin);
if (NativeInterface.isSimulated()) {
// pretend the interrupt happens after 200ms
try {
Thread.sleep(200);
} catch (InterruptedException e) {}
return true;
}
String fn = String.format("/sys/class/gpio/gpio%d/value", pin);
int ret = NativeInterface.pollDevice(fn, timeout);
if (ret < 0) {
if (ret == -2) { // ENOENT
System.err.println("Make sure your called pinMode on the input pin");
}
throw new RuntimeException(NativeInterface.getError(ret));
} else if (ret == 0) {
// timeout
return false;
} else {
// interrupt
return true;
}
}
}
@@ -0,0 +1,262 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.io.NativeInterface;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @webref
*/
public class I2C {
protected String dev;
protected int handle;
protected int slave;
protected byte[] out;
protected boolean transmitting;
/**
* Opens an I2C interface as master
* @param dev interface name
* @see list
* @webref
*/
public I2C(String dev) {
NativeInterface.loadLibrary();
this.dev = dev;
if (NativeInterface.isSimulated()) {
return;
}
handle = NativeInterface.openDevice("/dev/" + dev);
if (handle < 0) {
throw new RuntimeException(NativeInterface.getError(handle));
}
}
/**
* Begins a transmission to an attached device
* @see write
* @see read
* @see endTransmission
* @webref
*/
public void beginTransmission(int slave) {
// addresses 120 (0x78) to 127 are additionally reserved
if (0x78 <= slave) {
System.err.println("beginTransmission expects a 7 bit address, try shifting one bit to the right");
throw new IllegalArgumentException("Illegal address");
}
this.slave = slave;
transmitting = true;
out = null;
}
/**
* Closes the I2C device
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
NativeInterface.closeDevice(handle);
handle = 0;
}
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Ends the current transmissions
* @see beginTransmission
* @see write
* @webref
*/
public void endTransmission() {
if (!transmitting) {
// silently ignore this case
return;
}
if (NativeInterface.isSimulated()) {
return;
}
// implement these flags if needed: https://github.com/raspberrypi/linux/blob/rpi-patches/Documentation/i2c/i2c-protocol
int ret = NativeInterface.transferI2c(handle, slave, out, null);
transmitting = false;
out = null;
if (ret < 0) {
if (ret == -5 | 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));
}
}
/**
* Lists all available I2C interfaces
* @return String array
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
// as on the Raspberry Pi
return new String[]{ "i2c-1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/dev");
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().startsWith("i2c-")) {
devs.add(file.getName());
}
}
}
// listFiles() does not guarantee ordering
String[] tmp = devs.toArray(new String[devs.size()]);
Arrays.sort(tmp);
return tmp;
}
/**
* Reads bytes from the attached device
* @param len number of bytes to read
* @return bytes read from device
* @see beginTransmission
* @see write
* @see endTransmission
* @webref
*/
public byte[] read(int len) {
if (!transmitting) {
throw new RuntimeException("beginTransmisson has not been called");
}
byte[] in = new byte[len];
if (NativeInterface.isSimulated()) {
return in;
}
int ret = NativeInterface.transferI2c(handle, slave, out, in);
transmitting = false;
out = null;
if (ret < 0) {
if (ret == -5 | 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));
}
return in;
}
/**
* Adds bytes to be written to the device
* @param out bytes to be written
* @see beginTransmission
* @see read
* @see endTransmission
* @webref
*/
public void write(byte[] out) {
if (!transmitting) {
throw new RuntimeException("beginTransmisson has not been called");
}
if (this.out == null) {
this.out = out;
} else {
byte[] tmp = new byte[this.out.length + out.length];
System.arraycopy(this.out, 0, tmp, 0, this.out.length);
System.arraycopy(out, 0, tmp, this.out.length, out.length);
this.out = tmp;
}
}
/**
* Adds bytes to be written to the attached device
* @param out string to be written
* @see beginTransmission
* @see read
* @see endTransmission
*/
public void write(String out) {
write(out.getBytes());
}
/**
* Adds a byte to be written to the attached device
* @param out single byte to be written, e.g. numeric literal (0 to 255, or -128 to 127)
* @see beginTransmission
* @see read
* @see endTransmission
*/
public void write(int out) {
if (out < -128 || 255 < out) {
System.err.println("The write function can only operate on a single byte at a time. Call it with a value from 0 to 255, or -128 to 127.");
throw new RuntimeException("Argument does not fit into a single byte");
}
byte[] tmp = new byte[1];
tmp[0] = (byte)out;
write(tmp);
}
/**
* Adds a byte to be written to the attached device
* @param out single byte to be written
* @see beginTransmission
* @see read
* @see endTransmission
*/
public void write(byte out) {
// cast to (unsigned) int
write(out & 0xff);
}
}
@@ -0,0 +1,177 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.io.NativeInterface;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @webref
*/
public class LED {
protected String dev;
protected int maxBrightness;
protected int prevBrightness;
protected String prevTrigger;
/**
* Opens a LED device
* @param dev device name
* @see list
* @webref
*/
public LED(String dev) {
NativeInterface.loadLibrary();
this.dev = dev;
if (NativeInterface.isSimulated()) {
return;
}
// read maximum brightness
try {
Path path = Paths.get("/sys/class/leds/" + dev + "/max_brightness");
String tmp = new String(Files.readAllBytes(path));
maxBrightness = Integer.parseInt(tmp.trim());
} catch (Exception e) {
System.err.println(e.getMessage());
throw new RuntimeException("Unable to read maximum brightness");
}
// read current trigger setting to be able to restore it later
try {
Path path = Paths.get("/sys/class/leds/" + dev + "/trigger");
String tmp = new String(Files.readAllBytes(path));
int start = tmp.indexOf('[');
int end = tmp.indexOf(']', start);
if (start != -1 && end != -1) {
prevTrigger = tmp.substring(start+1, end);
}
} catch (Exception e) {
System.err.println(e.getMessage());
throw new RuntimeException("Unable to read trigger setting");
}
// read current brightness to be able to restore it later
try {
Path path = Paths.get("/sys/class/leds/" + dev + "/brightness");
String tmp = new String(Files.readAllBytes(path));
prevBrightness = Integer.parseInt(tmp.trim());
} catch (Exception e) {
System.err.println(e.getMessage());
throw new RuntimeException("Unable to read current brightness");
}
// disable trigger
String fn = "/sys/class/leds/" + dev + "/trigger";
int ret = NativeInterface.writeFile(fn, "none");
if (ret < 0) {
if (ret == -13) { // EACCES
System.err.println("You might need to install a custom udev rule to allow regular users to modify /sys/class/leds/*.");
}
throw new RuntimeException(NativeInterface.getError(ret));
}
}
/**
* Sets the brightness
* @param bright 0.0 (off) to 1.0 (maximum)
* @webref
*/
public void brightness(float bright) {
if (bright < 0.0 || 1.0 < bright) {
System.err.println("Brightness must be between 0.0 and 1.0.");
throw new IllegalArgumentException("Illegal argument");
}
if (NativeInterface.isSimulated()) {
return;
}
String fn = "/sys/class/leds/" + dev + "/brightness";
int ret = NativeInterface.writeFile(fn, Integer.toString((int)(bright * maxBrightness)));
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
}
/**
* Restores the previous state
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
// restore previous settings
String fn = "/sys/class/leds/" + dev + "/brightness";
int ret = NativeInterface.writeFile(fn, Integer.toString(prevBrightness));
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
fn = "/sys/class/leds/" + dev + "/trigger";
ret = NativeInterface.writeFile(fn, prevTrigger);
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
}
/**
* Lists all available LED devices
* @return String array
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
// as on the Raspberry Pi
return new String[]{ "led0", "led1" };
}
ArrayList<String> devs = new ArrayList<>();
File dir = new File("/sys/class/leds");
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
devs.add(file.getName());
}
}
// listFiles() does not guarantee ordering
String[] tmp = devs.toArray(new String[devs.size()]);
Arrays.sort(tmp);
return tmp;
}
}
@@ -0,0 +1,78 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
public class NativeInterface {
protected static boolean loaded = false;
protected static boolean alwaysSimulate = false;
public static void loadLibrary() {
if (!loaded) {
if (isSimulated()) {
System.err.println("The Processing I/O library is not supported on this platform. Instead of values from actual hardware ports, your sketch will only receive stand-in values that allow you to test the remainder of its functionality.");
} else {
System.loadLibrary("processing-io");
}
loaded = true;
}
}
public static void alwaysSimulate() {
alwaysSimulate = true;
}
public static boolean isSimulated() {
return alwaysSimulate ||
!"Linux".equals(System.getProperty("os.name"));
}
public static native int openDevice(String fn);
public static native String getError(int errno);
public static native int closeDevice(int handle);
// the following two functions were done in native code to get access to the
// specifc error number (errno) that might occur
public static native int readFile(String fn, byte[] in);
public static native int writeFile(String fn, byte[] out);
public static int writeFile(String fn, String out) {
return writeFile(fn, out.getBytes());
}
/* 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);
/* SoftwareServo */
public static native long servoStartThread(int gpio, int pulse, int period);
public static native int servoUpdateThread(long handle, int pulse, int period);
public static native int servoStopThread(long handle);
/* SPI */
public static native int setSpiSettings(int handle, int maxSpeed, int dataOrder, int mode);
public static native int transferSpi(int handle, byte[] out, byte[] in);
}
@@ -0,0 +1,214 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.io.NativeInterface;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @webref
*/
public class PWM {
int channel;
String chip;
/**
* Opens a PWM channel
* @param channel PWM channel
* @see list
* @webref
*/
public PWM(String channel) {
NativeInterface.loadLibrary();
int pos = channel.indexOf("/pwm");
if (pos == -1) {
throw new IllegalArgumentException("Unsupported channel");
}
chip = channel.substring(0, pos);
this.channel = Integer.parseInt(channel.substring(pos+4));
if (NativeInterface.isSimulated()) {
return;
}
// export channel through sysfs
String fn = "/sys/class/pwm/" + chip + "/export";
int ret = NativeInterface.writeFile(fn, Integer.toString(this.channel));
if (ret < 0) {
if (ret == -2) { // ENOENT
System.err.println("Make sure your kernel is compiled with PWM_SYSFS enabled and you have the necessary PWM driver for your platform");
}
// XXX: check
if (ret == -22) { // EINVAL
System.err.println("PWM channel " + channel + " does not seem to be available on your platform");
}
// XXX: check
if (ret != -16) { // EBUSY, returned when the pin is already exported
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
}
// 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();
}
}
/**
* Disables the PWM output
* @webref
*/
public void clear() {
if (NativeInterface.isSimulated()) {
return;
}
String fn = String.format("/sys/class/pwm/%s/pwm%d/enable", chip, channel);
int ret = NativeInterface.writeFile(fn, "0");
if (ret < 0) {
throw new RuntimeException(NativeInterface.getError(ret));
}
}
/**
* Gives ownership of a channel back to the operating system
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
// XXX: implicit clear()?
// XXX: also check GPIO
String fn = "/sys/class/pwm/" + chip + "/unexport";
int ret = NativeInterface.writeFile(fn, Integer.toString(channel));
if (ret < 0) {
if (ret == -2) { // ENOENT
System.err.println("Make sure your kernel is compiled with PWM_SYSFS enabled and you have the necessary PWM driver for your platform");
}
// XXX: check
// EINVAL is also returned when trying to unexport pins that weren't exported to begin with
throw new RuntimeException(NativeInterface.getError(ret));
}
}
/**
* Lists all available PWM channels
* @return String array
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
return new String[]{ "pwmchip0/pwm0", "pwmchip0/pwm1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/sys/class/pwm");
File[] chips = dir.listFiles();
if (chips != null) {
for (File chip : chips) {
// get the number of supported channels
try {
Path path = Paths.get("/sys/class/pwm/" + chip.getName() + "/npwm");
String tmp = new String(Files.readAllBytes(path));
int npwm = Integer.parseInt(tmp.trim());
for (int i=0; i < npwm; i++) {
devs.add(chip.getName() + "/pwm" + i);
}
} catch (Exception e) {
}
}
}
// listFiles() does not guarantee ordering
String[] tmp = devs.toArray(new String[devs.size()]);
Arrays.sort(tmp);
return tmp;
}
/**
* Enables the PWM output
* @param period cycle period in Hz
* @param duty duty cycle, 0.0 (always off) to 1.0 (always on)
* @webref
*/
public void set(int period, float duty) {
if (NativeInterface.isSimulated()) {
return;
}
// set period
String fn = fn = String.format("/sys/class/pwm/%s/pwm%d/period", chip, channel);
// convert to nanoseconds
int ret = NativeInterface.writeFile(fn, String.format("%d", (int)(1000000000 / period)));
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
// set duty cycle
fn = fn = String.format("/sys/class/pwm/%s/pwm%d/duty_cycle", chip, channel);
if (duty < 0.0 || 1.0 < duty) {
System.err.println("Duty cycle must be between 0.0 and 1.0.");
throw new IllegalArgumentException("Illegal argument");
}
// convert to nanoseconds
ret = NativeInterface.writeFile(fn, String.format("%d", (int)((1000000000 * duty) / period)));
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
// enable output
fn = String.format("/sys/class/pwm/%s/pwm%d/enable", chip, channel);
ret = NativeInterface.writeFile(fn, "1");
if (ret < 0) {
throw new RuntimeException(fn + ": " + NativeInterface.getError(ret));
}
}
/**
* Enables the PWM output with a preset period of 1 kHz
* @webref
*/
public void set(float duty) {
set(1000, duty);
}
}
@@ -0,0 +1,226 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider as part of GSoC 2015
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.io.NativeInterface;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @webref
*/
public class SPI {
/**
* CPOL=0, CPHA=0, most common
*/
public static final int MODE0 = 0;
/**
* CPOL=0, CPHA=1
*/
public static final int MODE1 = 1;
/**
* CPOL=1, CPHA=0
*/
public static final int MODE2 = 2;
/**
* CPOL=1, CPHA=1
*/
public static final int MODE3 = 3;
/**
* most significant bit first, most common
*/
public static final int MSBFIRST = 0;
/**
* least significant bit first
*/
public static final int LSBFIRST = 1;
protected int dataOrder = 0;
protected String dev;
protected int handle;
protected int maxSpeed = 500000;
protected int mode = 0;
protected static Map<String, String> settings = new HashMap<String, String>();
/**
* Opens an SPI interface as master
* @param dev device name
* @see list
* @webref
*/
public SPI(String dev) {
NativeInterface.loadLibrary();
this.dev = dev;
if (NativeInterface.isSimulated()) {
return;
}
handle = NativeInterface.openDevice("/dev/" + dev);
if (handle < 0) {
throw new RuntimeException(NativeInterface.getError(handle));
}
}
/**
* Closes the SPI interface
* @webref
*/
public void close() {
if (NativeInterface.isSimulated()) {
return;
}
NativeInterface.closeDevice(handle);
handle = 0;
}
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Lists all available SPI interfaces
* @return String array
* @webref
*/
public static String[] list() {
if (NativeInterface.isSimulated()) {
// as on the Raspberry Pi
return new String[]{ "spidev0.0", "spidev0.1" };
}
ArrayList<String> devs = new ArrayList<String>();
File dir = new File("/dev");
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().startsWith("spidev")) {
devs.add(file.getName());
}
}
}
// listFiles() does not guarantee ordering
String[] tmp = devs.toArray(new String[devs.size()]);
Arrays.sort(tmp);
return tmp;
}
/**
* Configures the SPI interface
* @param maxSpeed maximum transmission rate in Hz, 500000 (500 kHz) is a resonable default
* @param dataOrder whether data is send with the first- or least-significant bit first (SPI.MSBFIRST or SPI.LSBFIRST, the former is more common)
* @param mode <a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase">SPI.MODE0 to SPI.MODE3</a>
* @webref
*/
public void settings(int maxSpeed, int dataOrder, int mode) {
this.maxSpeed = maxSpeed;
this.dataOrder = dataOrder;
this.mode = mode;
}
/**
* Transfers data over the SPI bus
* @param out bytes to send
* @return bytes read in (array is the same length as out)
* @webref
*/
public byte[] transfer(byte[] out) {
if (NativeInterface.isSimulated()) {
return new byte[out.length];
}
// track the current setting per device across multiple instances
String curSettings = maxSpeed + "-" + dataOrder + "-" + mode;
if (!curSettings.equals(settings.get(dev))) {
int ret = NativeInterface.setSpiSettings(handle, maxSpeed, dataOrder, mode);
if (ret < 0) {
System.err.println(NativeInterface.getError(handle));
throw new RuntimeException("Error updating device configuration");
}
settings.put(dev, curSettings);
}
byte[] in = new byte[out.length];
int transferred = NativeInterface.transferSpi(handle, out, in);
if (transferred < 0) {
throw new RuntimeException(NativeInterface.getError(transferred));
} else if (transferred < out.length) {
throw new RuntimeException("Fewer bytes transferred than requested: " + transferred);
}
return in;
}
/**
* Transfers data over the SPI bus
* @param out string to send
* @return bytes read in (array is the same length as out)
*/
public byte[] transfer(String out) {
return transfer(out.getBytes());
}
/**
* Transfers data over the SPI bus
* @param out single byte to send, e.g. numeric literal (0 to 255, or -128 to 127)
* @return bytes read in (array is the same length as out)
*/
public byte[] transfer(int out) {
if (out < -128 || 255 < out) {
System.err.println("The transfer function can only operate on a single byte at a time. Call it with a value from 0 to 255, or -128 to 127.");
throw new RuntimeException("Argument does not fit into a single byte");
}
byte[] tmp = new byte[1];
tmp[0] = (byte)out;
return transfer(tmp);
}
/**
* Transfers data over the SPI bus
* @param out single byte to send
* @return bytes read in (array is the same length as out)
*/
public byte[] transfer(byte out) {
// cast to (unsigned) int
return transfer(out & 0xff);
}
}
@@ -0,0 +1,162 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Copyright (c) The Processing Foundation 2015
Hardware I/O library developed by Gottfried Haider
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.io;
import processing.core.*;
/**
* @webref
*/
public class SoftwareServo {
public static final int DEFAULT_MIN_PULSE = 544;
public static final int DEFAULT_MAX_PULSE = 2400;
protected int pin = -1; // gpio number (-1 .. not attached)
protected long handle = -1; // native thread id (<0 .. not started)
protected int period = 20000; // 20 ms (50 Hz)
protected int minPulse = 0; // minimum pulse width in microseconds
protected int maxPulse = 0; // maximum pulse width in microseconds
protected int pulse = 0; // current pulse in microseconds
/**
* Opens a servo motor
* @param parent typically use "this"
* @webref
*/
public SoftwareServo(PApplet parent) {
NativeInterface.loadLibrary();
}
/**
* Closes a servo motor
* @webref
*/
public void close() {
detach();
}
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Attaches a servo motor to a GPIO pin
* @param pin GPIO pin
* @webref
*/
public void attach(int pin) {
detach();
this.pin = pin;
this.minPulse = DEFAULT_MIN_PULSE;
this.maxPulse = DEFAULT_MAX_PULSE;
}
/**
* Attaches a servo motor to a GPIO pin using custom pulse widths
* @param minPulse minimum pulse width in microseconds (default: 544, same as on Arduino)
* @param maxPulse maximum pulse width in microseconds (default: 2400, same as on Arduino)
* @webref
*/
public void attach(int pin, int minPulse, int maxPulse) {
detach();
this.pin = pin;
this.minPulse = minPulse;
this.maxPulse = maxPulse;
}
/**
* Moves a servo motor to a given orientation
* @param angle angle in degrees (controls speed and direction on continuous-rotation servos)
* @webref
*/
public void write(float angle) {
if (attached() == false) {
System.err.println("You need to call attach(pin) before write(angle).");
throw new RuntimeException("Servo is not attached");
}
if (angle < 0 || 180 < angle) {
System.err.println("Only degree values between 0 and 180 can be used.");
throw new IllegalArgumentException("Illegal value");
}
pulse = (int)(minPulse + (angle/180.0) * (maxPulse-minPulse));
if (handle < 0) {
// start a new thread
GPIO.pinMode(pin, GPIO.OUTPUT);
if (NativeInterface.isSimulated()) {
return;
}
handle = NativeInterface.servoStartThread(pin, pulse, period);
if (handle < 0) {
throw new RuntimeException(NativeInterface.getError((int)handle));
}
} else {
// thread already running
int ret = NativeInterface.servoUpdateThread(handle, pulse, period);
if (ret < 0) {
throw new RuntimeException(NativeInterface.getError(ret));
}
}
}
/**
* Returns whether a servo motor is attached to a pin
* @return true if attached, false is not
* @webref
*/
public boolean attached() {
return (pin != -1);
}
/**
* Detatches a servo motor from a GPIO pin
* @webref
*/
public void detach() {
if (0 <= handle) {
// stop thread
int ret = NativeInterface.servoStopThread(handle);
GPIO.pinMode(pin, GPIO.INPUT);
handle = -1;
pin = -1;
if (ret < 0) {
throw new RuntimeException(NativeInterface.getError(ret));
}
}
}
}