diff --git a/.travis.yml b/.travis.yml index 2a9c83061..13d07a3f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ ### # ---------------------------------------------------------------------------------- -# This is a Java program with a minimum requirment of JDK8. +# This is a Java program with a minimum requirement of JDK8. # This build should be performed on a Linux platform. # ---------------------------------------------------------------------------------- language: java diff --git a/TODO b/TODO index ce2bcc0b4..3eab71af6 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ TODO :: MODIFY PROVIDER INTERFACE AND IMPL TP ALLOW FOR I/O CONFIG VALIDATION BEFORE HAVING PROVIDER CREATE INSTANCE TODO :: IMPLEMENT DYNAMIC PROXY AROUND PLATFORM +TODO :: SHOULD Pi4J CONTEXT BE AUTO-CLOSABLE? diff --git a/libraries/pi4j-library-pigpio/pom.xml b/libraries/pi4j-library-pigpio/pom.xml new file mode 100644 index 000000000..a5bc22de0 --- /dev/null +++ b/libraries/pi4j-library-pigpio/pom.xml @@ -0,0 +1,28 @@ + + + + pi4j-library + com.pi4j + 2.0-SNAPSHOT + ../pi4j-library + + 4.0.0 + + + pi4j-library-pigpio + Pi4J :: LIBRARY :: PIGPIO Library + Pi4J wrapper for the PIGPIO library + jar + + + + + com.pi4j + pi4j-test-harness + 2.0-SNAPSHOT + test + + + diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/MainOld.txt b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/MainOld.txt new file mode 100644 index 000000000..3adedc4c2 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/MainOld.txt @@ -0,0 +1,213 @@ +//package com.pi4j.library.pigpio; +///*- +// * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : MainOld.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% +// */ +// +//import com.pi4j.Pi4J; +//import com.pi4j.io.gpio.analog.AnalogChangeListener; +//import com.pi4j.io.gpio.analog.binding.AnalogBindingSync; +// +//public class MainOld { +// +// public MainOld() { +// } +// +// +//// public static byte[] sendCommand(Socket socket, int cmd, int address) throws IOException, InterruptedException { +//// return sendCommand(socket, cmd, address, 0); +//// } +//// public static byte[] sendCommand(Socket socket, int cmd, int address, int value) throws IOException, InterruptedException { +//// +//// byte[] bytes = new byte[16]; +//// ByteBuffer buffer = ByteBuffer.wrap(bytes); +//// buffer.order(ByteOrder.LITTLE_ENDIAN); +//// buffer.putInt(cmd); // CMD (READ==3, WRITE=4) +//// buffer.putInt(address); // GPIO 0 +//// buffer.putInt(value); // +//// //buffer.putInt(0); // +//// //buffer.putInt(0); // +//// //buffer.putInt(0); // +//// +//// System.out.println("[TX] " + Arrays.toString(buffer.array())); +//// socket.getOutputStream().write(buffer.array()); +//// socket.getOutputStream().flush(); +//// +//// Thread.sleep(5); +//// +//// int avail = socket.getInputStream().available(); +//// var rx = socket.getInputStream().readNBytes(avail); +//// +//// System.out.println("BYTES RX : "+ avail); +//// System.out.println(" - [CMD] : " + rx[0] + "," + rx[1]); +//// System.out.println(" - [P1] : " + rx[2] + "," + rx[3]); +//// System.out.println(" - [P2] : " + rx[4] + "," + rx[5]); +//// System.out.println(" - [P3] : " + rx[6] + "," + rx[7]); +//// +//// return rx; +//// } +// +// public static void main(String[] args) throws Exception { +// System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); +// +// // READ 3 gpio 0 0 +// +//// typedef struct +//// { +//// uint32_t cmd; +//// uint32_t p1; +//// uint32_t p2; +//// union +//// { +//// uint32_t p3; +//// uint32_t ext_len; +//// uint32_t res; +//// }; +//// } cmdCmd_t; +// +// PiGpio pigpio = new PiGpio("rpi3bp"); +// +// System.out.println("Raspberry Pi - Hardware Revision : " + pigpio.gpioHardwareRevisionString()); +// System.out.println("Raspberry Pi - PIGPIO Lib Version : " + pigpio.gpioVersion()); +// +// for(int p = 0 ; p <= 31; p++) { +// System.out.println(" GPIO " + p + "; MODE=" + pigpio.gpioGetMode(p) + "; STATE=" + pigpio.gpioRead(p)); +// } +// +// +// //pigpio.digitalState(5); +// +//// pigpio.digitalState(5, DigitalState.HIGH); +//// pigpio.digitalState(5); +//// +//// pigpio.digitalState(5, DigitalState.LOW); +//// pigpio.digitalState(5); +// +// +//// Socket socket = new Socket("rpi3bp", 8888); +// +//// System.out.println("IS CONNECTED: "+ socket.isConnected()); +// +//// sendCommand(socket, 4, 5, 0); +//// sendCommand(socket,3, 5); +// +// +//// byte[] bytes = new byte[16]; +//// ByteBuffer buffer = ByteBuffer.wrap(bytes); +//// buffer.order(ByteOrder.LITTLE_ENDIAN); +//// buffer.putInt(4); // CMD (READ==3, WRITE=4) +//// buffer.putInt(5); // GPIO 0 +//// buffer.putInt(0); // +//// //buffer.putInt(0); // +//// //buffer.putInt(0); // +//// //buffer.putInt(0); // +//// +//// +//// System.out.println("[TX] " + Arrays.toString(buffer.array())); +//// socket.getOutputStream().write(buffer.array()); +//// socket.getOutputStream().flush(); +//// +//// Thread.sleep(2); +//// +//// int avail = socket.getInputStream().available(); +//// System.out.println("BYTES AVAIL : "+ avail); +//// +//// +//// byte[] rx = socket.getInputStream().readNBytes(avail); +//// +//// System.out.println(Arrays.toString(rx)); +//// +//// Thread.sleep(20); +// +//// public String sendMessage(String msg) { +//// out.println(msg); +//// String resp = in.readLine(); +//// return resp; +//// } +// +// +// //while(int b =in.read() +// +// +//// +//// int inputLine; +//// while ((inputLine = in.read()) != -1) { +//// System.out.println(inputLine); +//// } +// +// System.exit(0); +// +// +// // Initialize Pi4J with an auto context +// // An auto context includes AUTO-DETECT BINDINGS enabled +// // which will load all detected Pi4J extension libraries +// // (Platforms and Providers) in the class path +// var pi4j = Pi4J.newAutoContext(); +// +// +//// Serial serial = Serial.instance("/dev/ttyUSB1"); +//// serial.open(); +//// serial.send("TEST DATA"); +//// serial.close(); +// +// var din1 = pi4j.din().create(11); +// var ain1 = pi4j.ain().create(21); +// +// var input = pi4j.ain().create(98); +// var output1 = pi4j.aout().create(99); +// var output2 = pi4j.aout().create(100); +// +// input.addListener((AnalogChangeListener) event -> { +// System.out.print(event); +// }); +// +// +// output1.addListener((AnalogChangeListener) event -> { +// System.out.println(event); +// }); +// output2.addListener((AnalogChangeListener) event -> { +// System.out.println(event); +// }); +// +// input.bind(new AnalogBindingSync(output1, output2)); +// +// +// //((TestAnalogInput)input).test(21).test(22).test(23); +// +// +// output1.value(12); +// output1.setValue(78); +// output1.value(0x01); +// +// +// //AnalogOutput aout1 = AnalogOutput.in +//// DigitalOutput dout1 = DigitalOutput; +// +// // shutdown Pi4J +// pi4j.shutdown(); +// } +//} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio.java new file mode 100644 index 000000000..793e5ca8e --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio.java @@ -0,0 +1,211 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpio.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.impl.PiGpioSocketImpl; + +import java.io.IOException; + +public interface PiGpio extends PiGpio_I2C, PiGpio_GPIO, PiGpio_PWM { + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to a user specified socket hostname/ip address and port. + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @param port TCP port number of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + static PiGpio newSocketInstance(String host, String port) throws IOException { + return PiGpioSocketImpl.newInstance(host, port); + } + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to a user specified socket hostname/ip address and port. + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @param port TCP port number of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + static PiGpio newSocketInstance(String host, int port) throws IOException { + return PiGpioSocketImpl.newInstance(host, port); + } + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to a user specified socket hostname/ip address using the default port (8888). + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + static PiGpio newSocketInstance(String host) throws IOException { + return PiGpioSocketImpl.newInstance(host); + } + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to the local system (127.0.0.1) using the default port (8888). + * + * @throws IOException + */ + static PiGpio newSocketInstance() throws IOException { + return PiGpioSocketImpl.newInstance(); + } + + /** + * Initialises the library. + * + * Returns the pigpio version number if OK, otherwise PI_INIT_FAILED. + * gpioInitialise must be called before using the other library functions with the following exceptions: + * - gpioCfg* + * - gpioVersion + * - gpioHardwareRevision + * + * @return result value + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioInitialise" + */ + long gpioInitialise(); + default long gpioInitialize(){ return gpioInitialise(); } // US spelling variant + default long initialise(){ return gpioInitialise(); } + default long initialize(){ return gpioInitialise(); } + + /** + * Terminates the library. + * + * Returns nothing. + * Call before program exit. + * This function resets the used DMA channels, releases memory, and terminates any running threads. + */ + void gpioTerminate() throws IOException; + default void terminate() throws IOException { gpioTerminate(); } + + /** + * Returns the pigpio library version. + * + * @return pigpio version. + * @throws IOException + * @throws InterruptedException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioVersion" + */ + long gpioVersion() throws IOException; + + /** + * Returns the hardware revision. + * + * If the hardware revision can not be found or is not a valid hexadecimal number the function returns 0. + * The hardware revision is the last few characters on the Revision line of /proc/cpuinfo. + * The revision number can be used to determine the assignment of GPIO to pins (see gpio). + * + * There are at least three types of board. + * - Type 1 boards have hardware revision numbers of 2 and 3. + * - Type 2 boards have hardware revision numbers of 4, 5, 6, and 15. + * - Type 3 boards have hardware revision numbers of 16 or greater. + * + * for "Revision : 0002" the function returns 2. + * for "Revision : 000f" the function returns 15. + * for "Revision : 000g" the function returns 0. + * + * @return hardware revision as raw 32-bit UINT + * @throws IOException + * @seel "http://abyz.me.uk/rpi/pigpio/cif.html#gpioHardwareRevision" + */ + long gpioHardwareRevision() throws IOException; + + + /** + * Returns the hardware revision (as hexadecimal string). + * + * If the hardware revision can not be found or is not a valid hexadecimal number the function returns 0. + * The hardware revision is the last few characters on the Revision line of /proc/cpuinfo. + * The revision number can be used to determine the assignment of GPIO to pins (see gpio). + * + * There are at least three types of board. + * - Type 1 boards have hardware revision numbers of 2 and 3. + * - Type 2 boards have hardware revision numbers of 4, 5, 6, and 15. + * - Type 3 boards have hardware revision numbers of 16 or greater. + * + * for "Revision : 0002" the function returns 2. + * for "Revision : 000f" the function returns 15. + * for "Revision : 000g" the function returns 0. + * + * @return hardware revision in hexadecimal string + * @throws IOException + * @seel "http://abyz.me.uk/rpi/pigpio/cif.html#gpioHardwareRevision" + */ + String gpioHardwareRevisionString() throws IOException; + + /** + * Delays for at least the number of microseconds specified by micros. (between 1 and 1000000 <1 second>) + * (Delays of 100 microseconds or less use busy waits.) + * + * @param micros micros: the number of microseconds to sleep (between 1 and 1000000 <1 second>) + * @return Returns the actual length of the delay in microseconds. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioDelay" + */ + int gpioDelay(int micros) throws IOException; + default int gpioDelayMicroseconds(int micros) throws IOException{ + return gpioDelay(micros); + } + + /** + * Delays for at least the number of milliseconds specified by micros. (between 1 and 60000 <1 minute>) + * + * @param millis millis: the number of milliseconds to sleep (between 1 and 60000 <1 minute>) + * @return Returns the actual length of the delay in milliseconds. + * @see "http://abyz.me.uk/rpi/pigpio/pigs.html#MILS" + */ + int gpioDelayMilliseconds(int millis) throws IOException; + + /** + * Returns the current system tick. + * Tick is the number of microseconds since system boot. + * + * As tick is an unsigned 32 bit quantity it wraps around after 2^32 microseconds, which is + * approximately 1 hour 12 minutes. You don't need to worry about the wrap around as long as you + * take a tick (uint32_t) from another tick, i.e. the following code will always provide the + * correct difference. + * + * Example + * uint32_t startTick, endTick; + * int diffTick; + * startTick = gpioTick(); + * + * // do some processing + * endTick = gpioTick(); + * diffTick = endTick - startTick; + * printf("some processing took %d microseconds", diffTick); + * + * @return Returns the current system tick. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioTick" + */ + long gpioTick() throws IOException; + +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioCmd.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioCmd.java new file mode 100644 index 000000000..6141888c8 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioCmd.java @@ -0,0 +1,173 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioCmd.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +public enum PiGpioCmd { + UNKNOWN(-1), + MODES (0), + MODEG (1), + PUD (2), + READ (3), + WRITE (4), + PWM (5), + PRS (6), + PFS (7), + SERVO (8), + WDOG (9), + BR1 (10), + BR2 (11), + BC1 (12), + BC2 (13), + BS1 (14), + BS2 (15), + TICK (16), + HWVER (17), + NO (18), + NB (19), + NP (20), + NC (21), + PRG (22), + PFG (23), + PRRG (24), + HELP (25), + PIGPV (26), + WVCLR (27), + WVAG (28), + WVAS (29), + WVGO (30), + WVGOR (31), + WVBSY (32), + WVHLT (33), + WVSM (34), + WVSP (35), + WVSC (36), + TRIG (37), + PROC (38), + PROCD (39), + PROCR (40), + PROCS (41), + SLRO (42), + SLR (43), + SLRC (44), + PROCP (45), + MICS (46), + MILS (47), + PARSE (48), + WVCRE (49), + WVDEL (50), + WVTX (51), + WVTXR (52), + WVNEW (53), + I2CO (54), + I2CC (55), + I2CRD (56), + I2CWD (57), + I2CWQ (58), + I2CRS (59), + I2CWS (60), + I2CRB (61), + I2CWB (62), + I2CRW (63), + I2CWW (64), + I2CRK (65), + I2CWK (66), + I2CRI (67), + I2CWI (68), + I2CPC (69), + I2CPK (70), + SPIO (71), + SPIC (72), + SPIR (73), + SPIW (74), + SPIX (75), + SERO (76), + SERC (77), + SERRB (78), + SERWB (79), + SERR (80), + SERW (81), + SERDA (82), + GDC (83), + GPW (84), + HC (85), + HP (86), + CF1 (87), + CF2 (88), + BI2CC (89), + BI2CO (90), + BI2CZ (91), + I2CZ (92), + WVCHA (93), + SLRI (94), + CGI (95), + CSI (96), + FG (97), + FN (98), + NOIB (99), + WVTXM (100), + WVTAT (101), + PADS (102), + PADG (103), + FO (104), + FC (105), + FR (106), + FW (107), + FS (108), + FL (109), + SHELL (110), + BSPIC (111), + BSPIO (112), + BSPIX (113), + BSCX (114), + EVM (115), + EVT (116), + PROCU (117); + + + private int value; + + PiGpioCmd(int value){ + this.value =value; + } + + public int value(){ + return this.value; + } + + public static PiGpioCmd from(Number value){ + for(PiGpioCmd c : PiGpioCmd.values()){ + if(c.value() == value.intValue()) return c; + } + return UNKNOWN; + } + + + +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioConst.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioConst.java new file mode 100644 index 000000000..2a20835a7 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioConst.java @@ -0,0 +1,237 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioConst.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +public interface PiGpioConst { + // -------------------------------------- + // DEFAULT SOCKET CONNECTION PROPERTIES + // -------------------------------------- + int DEFAULT_PORT = 8888; + String DEFAULT_HOST = "127.0.0.1"; + + // ---------------------------------- + // PIGPIO PIN RANGE + // ---------------------------------- + int PI_MIN_GPIO = 0; + int PI_MAX_GPIO = 53; + int PI_MAX_USER_GPIO = 31; + + // ---------------------------------- + // PIGPIO PIN LEVELS + // ---------------------------------- + int PI_OFF = 0; + int PI_ON = 1; + + int PI_CLEAR = 0; + int PI_SET = 1; + + int PI_LOW = 0; + int PI_HIGH = 1; + + // ---------------------------------- + // PIGPIO WATCHDOG TIMEOUT + // ---------------------------------- + int PI_TIMEOUT = 2; + + // ---------------------------------- + // PIGPIO LIBRARY COMMANDS + // ---------------------------------- + int PI_CMD_MODES = 0; + int PI_CMD_MODEG = 1; + int PI_CMD_PUD = 2; + int PI_CMD_READ = 3; + int PI_CMD_WRITE = 4; + int PI_CMD_PWM = 5; + int PI_CMD_PRS = 6; + int PI_CMD_PFS = 7; + int PI_CMD_SERVO = 8; + int PI_CMD_WDOG = 9; + int PI_CMD_BR1 = 10; + int PI_CMD_BR2 = 11; + int PI_CMD_BC1 = 12; + int PI_CMD_BC2 = 13; + int PI_CMD_BS1 = 14; + int PI_CMD_BS2 = 15; + int PI_CMD_TICK = 16; + int PI_CMD_HWVER = 17; + int PI_CMD_NO = 18; + int PI_CMD_NB = 19; + int PI_CMD_NP = 20; + int PI_CMD_NC = 21; + int PI_CMD_PRG = 22; + int PI_CMD_PFG = 23; + int PI_CMD_PRRG = 24; + int PI_CMD_HELP = 25; + int PI_CMD_PIGPV = 26; + int PI_CMD_WVCLR = 27; + int PI_CMD_WVAG = 28; + int PI_CMD_WVAS = 29; + int PI_CMD_WVGO = 30; + int PI_CMD_WVGOR = 31; + int PI_CMD_WVBSY = 32; + int PI_CMD_WVHLT = 33; + int PI_CMD_WVSM = 34; + int PI_CMD_WVSP = 35; + int PI_CMD_WVSC = 36; + int PI_CMD_TRIG = 37; + int PI_CMD_PROC = 38; + int PI_CMD_PROCD = 39; + int PI_CMD_PROCR = 40; + int PI_CMD_PROCS = 41; + int PI_CMD_SLRO = 42; + int PI_CMD_SLR = 43; + int PI_CMD_SLRC = 44; + int PI_CMD_PROCP = 45; + int PI_CMD_MICS = 46; + int PI_CMD_MILS = 47; + int PI_CMD_PARSE = 48; + int PI_CMD_WVCRE = 49; + int PI_CMD_WVDEL = 50; + int PI_CMD_WVTX = 51; + int PI_CMD_WVTXR = 52; + int PI_CMD_WVNEW = 53; + int PI_CMD_I2CO = 54; + int PI_CMD_I2CC = 55; + int PI_CMD_I2CRD = 56; + int PI_CMD_I2CWD = 57; + int PI_CMD_I2CWQ = 58; + int PI_CMD_I2CRS = 59; + int PI_CMD_I2CWS = 60; + int PI_CMD_I2CRB = 61; + int PI_CMD_I2CWB = 62; + int PI_CMD_I2CRW = 63; + int PI_CMD_I2CWW = 64; + int PI_CMD_I2CRK = 65; + int PI_CMD_I2CWK = 66; + int PI_CMD_I2CRI = 67; + int PI_CMD_I2CWI = 68; + int PI_CMD_I2CPC = 69; + int PI_CMD_I2CPK = 70; + int PI_CMD_SPIO = 71; + int PI_CMD_SPIC = 72; + int PI_CMD_SPIR = 73; + int PI_CMD_SPIW = 74; + int PI_CMD_SPIX = 75; + int PI_CMD_SERO = 76; + int PI_CMD_SERC = 77; + int PI_CMD_SERRB = 78; + int PI_CMD_SERWB = 79; + int PI_CMD_SERR = 80; + int PI_CMD_SERW = 81; + int PI_CMD_SERDA = 82; + int PI_CMD_GDC = 83; + int PI_CMD_GPW = 84; + int PI_CMD_HC = 85; + int PI_CMD_HP = 86; + int PI_CMD_CF1 = 87; + int PI_CMD_CF2 = 88; + int PI_CMD_BI2CC = 89; + int PI_CMD_BI2CO = 90; + int PI_CMD_BI2CZ = 91; + int PI_CMD_I2CZ = 92; + int PI_CMD_WVCHA = 93; + int PI_CMD_SLRI = 94; + int PI_CMD_CGI = 95; + int PI_CMD_CSI = 96; + int PI_CMD_FG = 97; + int PI_CMD_FN = 98; + int PI_CMD_NOIB = 99; + int PI_CMD_WVTXM = 100; + int PI_CMD_WVTAT = 101; + int PI_CMD_PADS = 102; + int PI_CMD_PADG = 103; + int PI_CMD_FO = 104; + int PI_CMD_FC = 105; + int PI_CMD_FR = 106; + int PI_CMD_FW = 107; + int PI_CMD_FS = 108; + int PI_CMD_FL = 109; + int PI_CMD_SHELL = 110; + int PI_CMD_BSPIC = 111; + int PI_CMD_BSPIO = 112; + int PI_CMD_BSPIX = 113; + int PI_CMD_BSCX = 114; + int PI_CMD_EVM = 115; + int PI_CMD_EVT = 116; + int PI_CMD_PROCU = 117; + + // ---------------------------------- + // GPIO PIN MODES + // ---------------------------------- + int PI_INPUT = 0; + int PI_OUTPUT = 1; + int PI_ALT0 = 4; + int PI_ALT1 = 5; + int PI_ALT2 = 6; + int PI_ALT3 = 7; + int PI_ALT4 = 3; + int PI_ALT5 = 2; + + // ---------------------------------- + // GPIO PIN PULL OPTIONS + // ---------------------------------- + int PI_PUD_OFF = 0; + int PI_PUD_DOWN = 1; + int PI_PUD_UP = 2; + + // ---------------------------------- + // GPIO PWM + // ---------------------------------- + /* dutycycle: 0-range */ + int PI_DEFAULT_DUTYCYCLE_RANGE = 255; + + /* range: 25-40000 */ + int PI_MIN_DUTYCYCLE_RANGE = 25; + int PI_MAX_DUTYCYCLE_RANGE = 40000; + + // ---------------------------------- + // GPIO SERVO + // ---------------------------------- + + /* pulsewidth: 0, 500-2500 */ + int PI_SERVO_OFF = 0; + int PI_MIN_SERVO_PULSEWIDTH = 500; + int PI_MAX_SERVO_PULSEWIDTH = 250; + + // ---------------------------------- + // GPIO HARDWARE PWM + // ---------------------------------- + int PI_HW_PWM_MIN_FREQ = 1; + int PI_HW_PWM_MAX_FREQ = 125000000; + int PI_HW_PWM_MAX_FREQ_2711 = 187500000; + int PI_HW_PWM_RANGE = 1000000; + + // ---------------------------------- + // DELAYS + // ---------------------------------- + int PI_MAX_MICS_DELAY = 1000000; /* 1 second */ + int PI_MAX_MILS_DELAY = 60000; /* 60 seconds */ + +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioError.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioError.java new file mode 100644 index 000000000..a2bfffc80 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioError.java @@ -0,0 +1,359 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioError.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +public enum PiGpioError { + UNKNOWN(0), + PI_INIT_FAILED (-1), // gpioInitialise failed + PI_BAD_USER_GPIO (-2), // GPIO not 0-31 + PI_BAD_GPIO (-3), // GPIO not 0-53 + PI_BAD_MODE (-4), // mode not 0-7 + PI_BAD_LEVEL (-5), // level not 0-1 + PI_BAD_PUD (-6), // pud not 0-2 + PI_BAD_PULSEWIDTH (-7), // pulsewidth not 0 or 500-2500 + PI_BAD_DUTYCYCLE (-8), // dutycycle outside set range + PI_BAD_TIMER (-9), // timer not 0-9 + PI_BAD_MS (-10), // ms not 10-60000 + PI_BAD_TIMETYPE (-11), // timetype not 0-1 + PI_BAD_SECONDS (-12), // seconds < 0 + PI_BAD_MICROS (-13), // micros not 0-999999 + PI_TIMER_FAILED (-14), // gpioSetTimerFunc failed + PI_BAD_WDOG_TIMEOUT (-15), // timeout not 0-60000 + PI_NO_ALERT_FUNC (-16), // DEPRECATED + PI_BAD_CLK_PERIPH (-17), // clock peripheral not 0-1 + PI_BAD_CLK_SOURCE (-18), // DEPRECATED + PI_BAD_CLK_MICROS (-19), // clock micros not 1, 2, 4, 5, 8, or 10 + PI_BAD_BUF_MILLIS (-20), // buf millis not 100-10000 + PI_BAD_DUTYRANGE (-21), // dutycycle range not 25-40000 + PI_BAD_DUTY_RANGE (-21), // DEPRECATED (use PI_BAD_DUTYRANGE) + PI_BAD_SIGNUM (-22), // signum not 0-63 + PI_BAD_PATHNAME (-23), // can't open pathname + PI_NO_HANDLE (-24), // no handle available + PI_BAD_HANDLE (-25), // unknown handle + PI_BAD_IF_FLAGS (-26), // ifFlags > 4 + PI_BAD_CHANNEL (-27), // DMA channel not 0-15 + PI_BAD_PRIM_CHANNEL (-27), // DMA primary channel not 0-15 + PI_BAD_SOCKET_PORT (-28), // socket port not 1024-32000 + PI_BAD_FIFO_COMMAND (-29), // unrecognized fifo command + PI_BAD_SECO_CHANNEL (-30), // DMA secondary channel not 0-15 + PI_NOT_INITIALISED (-31), // function called before gpioInitialise + PI_INITIALISED (-32), // function called after gpioInitialise + PI_BAD_WAVE_MODE (-33), // waveform mode not 0-3 + PI_BAD_CFG_INTERNAL (-34), // bad parameter in gpioCfgInternals call + PI_BAD_WAVE_BAUD (-35), // baud rate not 50-250K(RX)/50-1M(TX) + PI_TOO_MANY_PULSES (-36), // waveform has too many pulses + PI_TOO_MANY_CHARS (-37), // waveform has too many chars + PI_NOT_SERIAL_GPIO (-38), // no bit bang serial read on GPIO + PI_BAD_SERIAL_STRUC (-39), // bad (null) serial structure parameter + PI_BAD_SERIAL_BUF (-40), // bad (null) serial buf parameter + PI_NOT_PERMITTED (-41), // GPIO operation not permitted + PI_SOME_PERMITTED (-42), // one or more GPIO not permitted + PI_BAD_WVSC_COMMND (-43), // bad WVSC subcommand + PI_BAD_WVSM_COMMND (-44), // bad WVSM subcommand + PI_BAD_WVSP_COMMND (-45), // bad WVSP subcommand + PI_BAD_PULSELEN (-46), // trigger pulse length not 1-100 + PI_BAD_SCRIPT (-47), // invalid script + PI_BAD_SCRIPT_ID (-48), // unknown script id + PI_BAD_SER_OFFSET (-49), // add serial data offset > 30 minutes + PI_GPIO_IN_USE (-50), // GPIO already in use + PI_BAD_SERIAL_COUNT (-51), // must read at least a byte at a time + PI_BAD_PARAM_NUM (-52), // script parameter id not 0-9 + PI_DUP_TAG (-53), // script has duplicate tag + PI_TOO_MANY_TAGS (-54), // script has too many tags + PI_BAD_SCRIPT_CMD (-55), // illegal script command + PI_BAD_VAR_NUM (-56), // script variable id not 0-149 + PI_NO_SCRIPT_ROOM (-57), // no more room for scripts + PI_NO_MEMORY (-58), // can't allocate temporary memory + PI_SOCK_READ_FAILED (-59), // socket read failed + PI_SOCK_WRIT_FAILED (-60), // socket write failed + PI_TOO_MANY_PARAM (-61), // too many script parameters (> 10) + PI_NOT_HALTED (-62), // DEPRECATED + PI_SCRIPT_NOT_READY (-62), // script initialising + PI_BAD_TAG (-63), // script has unresolved tag + PI_BAD_MICS_DELAY (-64), // bad MICS delay (too large) + PI_BAD_MILS_DELAY (-65), // bad MILS delay (too large) + PI_BAD_WAVE_ID (-66), // non existent wave id + PI_TOO_MANY_CBS (-67), // No more CBs for waveform + PI_TOO_MANY_OOL (-68), // No more OOL for waveform + PI_EMPTY_WAVEFORM (-69), // attempt to create an empty waveform + PI_NO_WAVEFORM_ID (-70), // no more waveforms + PI_I2C_OPEN_FAILED (-71), // can't open I2C device + PI_SER_OPEN_FAILED (-72), // can't open serial device + PI_SPI_OPEN_FAILED (-73), // can't open SPI device + PI_BAD_I2C_BUS (-74), // bad I2C bus + PI_BAD_I2C_ADDR (-75), // bad I2C address + PI_BAD_SPI_CHANNEL (-76), // bad SPI channel + PI_BAD_FLAGS (-77), // bad i2c/spi/ser open flags + PI_BAD_SPI_SPEED (-78), // bad SPI speed + PI_BAD_SER_DEVICE (-79), // bad serial device name + PI_BAD_SER_SPEED (-80), // bad serial baud rate + PI_BAD_PARAM (-81), // bad i2c/spi/ser parameter + PI_I2C_WRITE_FAILED (-82), // i2c write failed + PI_I2C_READ_FAILED (-83), // i2c read failed + PI_BAD_SPI_COUNT (-84), // bad SPI count + PI_SER_WRITE_FAILED (-85), // ser write failed + PI_SER_READ_FAILED (-86), // ser read failed + PI_SER_READ_NO_DATA (-87), // ser read no data available + PI_UNKNOWN_COMMAND (-88), // unknown command + PI_SPI_XFER_FAILED (-89), // spi xfer/read/write failed + PI_BAD_POINTER (-90), // bad (NULL) pointer + PI_NO_AUX_SPI (-91), // no auxiliary SPI on Pi A or B + PI_NOT_PWM_GPIO (-92), // GPIO is not in use for PWM + PI_NOT_SERVO_GPIO (-93), // GPIO is not in use for servo pulses + PI_NOT_HCLK_GPIO (-94), // GPIO has no hardware clock + PI_NOT_HPWM_GPIO (-95), // GPIO has no hardware PWM + PI_BAD_HPWM_FREQ (-96), // invalid hardware PWM frequency + PI_BAD_HPWM_DUTY (-97), // hardware PWM dutycycle not 0-1M + PI_BAD_HCLK_FREQ (-98), // invalid hardware clock frequency + PI_BAD_HCLK_PASS (-99), // need password to use hardware clock 1 + PI_HPWM_ILLEGAL (-100), // illegal, PWM in use for main clock + PI_BAD_DATABITS (-101), // serial data bits not 1-32 + PI_BAD_STOPBITS (-102), // serial (half) stop bits not 2-8 + PI_MSG_TOOBIG (-103), // socket/pipe message too big + PI_BAD_MALLOC_MODE (-104), // bad memory allocation mode + PI_TOO_MANY_SEGS (-105), // too many I2C transaction segments + PI_BAD_I2C_SEG (-106), // an I2C transaction segment failed + PI_BAD_SMBUS_CMD (-107), // SMBus command not supported by driver + PI_NOT_I2C_GPIO (-108), // no bit bang I2C in progress on GPIO + PI_BAD_I2C_WLEN (-109), // bad I2C write length + PI_BAD_I2C_RLEN (-110), // bad I2C read length + PI_BAD_I2C_CMD (-111), // bad I2C command + PI_BAD_I2C_BAUD (-112), // bad I2C baud rate, not 50-500k + PI_CHAIN_LOOP_CNT (-113), // bad chain loop count + PI_BAD_CHAIN_LOOP (-114), // empty chain loop + PI_CHAIN_COUNTER (-115), // too many chain counters + PI_BAD_CHAIN_CMD (-116), // bad chain command + PI_BAD_CHAIN_DELAY (-117), // bad chain delay micros + PI_CHAIN_NESTING (-118), // chain counters nested too deeply + PI_CHAIN_TOO_BIG (-119), // chain is too long + PI_DEPRECATED (-120), // deprecated function removed + PI_BAD_SER_INVERT (-121), // bit bang serial invert not 0 or 1 + PI_BAD_EDGE (-122), // bad ISR edge value, not 0-2 + PI_BAD_ISR_INIT (-123), // bad ISR initialisation + PI_BAD_FOREVER (-124), // loop forever must be last command + PI_BAD_FILTER (-125), // bad filter parameter + PI_BAD_PAD (-126), // bad pad number + PI_BAD_STRENGTH (-127), // bad pad drive strength + PI_FIL_OPEN_FAILED (-128), // file open failed + PI_BAD_FILE_MODE (-129), // bad file mode + PI_BAD_FILE_FLAG (-130), // bad file flag + PI_BAD_FILE_READ (-131), // bad file read + PI_BAD_FILE_WRITE (-132), // bad file write + PI_FILE_NOT_ROPEN (-133), // file not open for read + PI_FILE_NOT_WOPEN (-134), // file not open for write + PI_BAD_FILE_SEEK (-135), // bad file seek + PI_NO_FILE_MATCH (-136), // no files match pattern + PI_NO_FILE_ACCESS (-137), // no permission to access file + PI_FILE_IS_A_DIR (-138), // file is a directory + PI_BAD_SHELL_STATUS (-139), // bad shell return status + PI_BAD_SCRIPT_NAME (-140), // bad script name + PI_BAD_SPI_BAUD (-141), // bad SPI baud rate, not 50-500k + PI_NOT_SPI_GPIO (-142), // no bit bang SPI in progress on GPIO + PI_BAD_EVENT_ID (-143), // bad event id + PI_CMD_INTERRUPTED (-144), // Used by Python + PI_NOT_ON_BCM2711 (-145), // not available on BCM2711 + PI_ONLY_ON_BCM2711 (-146), // only available on BCM2711 + + PI_PIGIF_ERR_0 (-2000), + PI_PIGIF_ERR_99 (-2099), + + PI_CUSTOM_ERR_0 (-3000), + PI_CUSTOM_ERR_999 (-3999); + + private int value; + + PiGpioError(int value){ + this.value =value; + } + + public int value(){ + return this.value; + } + + public String message(){ + switch (this){ + case UNKNOWN : return "Unknown"; + case PI_INIT_FAILED : return "pigpio initialisation failed"; + case PI_BAD_USER_GPIO : return "GPIO not 0-31"; + case PI_BAD_GPIO : return "GPIO not 0-53"; + case PI_BAD_MODE : return "mode not 0-7"; + case PI_BAD_LEVEL : return "level not 0-1"; + case PI_BAD_PUD : return "pud not 0-2"; + case PI_BAD_PULSEWIDTH : return "pulsewidth not 0 or 500-2500"; + case PI_BAD_DUTYCYCLE : return "dutycycle not 0-range (default 255)"; + case PI_BAD_TIMER : return "timer not 0-9"; + case PI_BAD_MS : return "ms not 10-60000"; + case PI_BAD_TIMETYPE : return "timetype not 0-1"; + case PI_BAD_SECONDS : return "seconds < 0"; + case PI_BAD_MICROS : return "micros not 0-999999"; + case PI_TIMER_FAILED : return "gpioSetTimerFunc failed"; + case PI_BAD_WDOG_TIMEOUT : return "timeout not 0-60000"; + case PI_NO_ALERT_FUNC : return "DEPRECATED"; + case PI_BAD_CLK_PERIPH : return "clock peripheral not 0-1"; + case PI_BAD_CLK_SOURCE : return "DEPRECATED"; + case PI_BAD_CLK_MICROS : return "clock micros not 1: return 2: return 4: return 5: return 8: return or 10"; + case PI_BAD_BUF_MILLIS : return "buf millis not 100-10000"; + case PI_BAD_DUTYRANGE : return "dutycycle range not 25-40000"; + case PI_BAD_SIGNUM : return "signum not 0-63"; + case PI_BAD_PATHNAME : return "can't open pathname"; + case PI_NO_HANDLE : return "no handle available"; + case PI_BAD_HANDLE : return "unknown handle"; + case PI_BAD_IF_FLAGS : return "ifFlags > 4"; + case PI_BAD_CHANNEL : return "DMA channel not 0-14"; + case PI_BAD_SOCKET_PORT : return "socket port not 1024-30000"; + case PI_BAD_FIFO_COMMAND : return "unknown fifo command"; + case PI_BAD_SECO_CHANNEL : return "DMA secondary channel not 0-14"; + case PI_NOT_INITIALISED : return "function called before gpioInitialise"; + case PI_INITIALISED : return "function called after gpioInitialise"; + case PI_BAD_WAVE_MODE : return "waveform mode not 0-1"; + case PI_BAD_CFG_INTERNAL : return "bad parameter in gpioCfgInternals call"; + case PI_BAD_WAVE_BAUD : return "baud rate not 50-250K(RX)/50-1M(TX)"; + case PI_TOO_MANY_PULSES : return "waveform has too many pulses"; + case PI_TOO_MANY_CHARS : return "waveform has too many chars"; + case PI_NOT_SERIAL_GPIO : return "no bit bang serial read in progress on GPIO"; + case PI_BAD_SERIAL_STRUC : return "bad (null) serial structure parameter"; + case PI_BAD_SERIAL_BUF : return "bad (null) serial buf parameter"; + case PI_NOT_PERMITTED : return "no permission to update GPIO"; + case PI_SOME_PERMITTED : return "no permission to update one or more GPIO"; + case PI_BAD_WVSC_COMMND : return "bad WVSC subcommand"; + case PI_BAD_WVSM_COMMND : return "bad WVSM subcommand"; + case PI_BAD_WVSP_COMMND : return "bad WVSP subcommand"; + case PI_BAD_PULSELEN : return "trigger pulse length not 1-100"; + case PI_BAD_SCRIPT : return "invalid script"; + case PI_BAD_SCRIPT_ID : return "unknown script id"; + case PI_BAD_SER_OFFSET : return "add serial data offset > 30 minute"; + case PI_GPIO_IN_USE : return "GPIO already in use"; + case PI_BAD_SERIAL_COUNT : return "must read at least a byte at a time"; + case PI_BAD_PARAM_NUM : return "script parameter id not 0-9"; + case PI_DUP_TAG : return "script has duplicate tag"; + case PI_TOO_MANY_TAGS : return "script has too many tags"; + case PI_BAD_SCRIPT_CMD : return "illegal script command"; + case PI_BAD_VAR_NUM : return "script variable id not 0-149"; + case PI_NO_SCRIPT_ROOM : return "no more room for scripts"; + case PI_NO_MEMORY : return "can't allocate temporary memory"; + case PI_SOCK_READ_FAILED : return "socket read failed"; + case PI_SOCK_WRIT_FAILED : return "socket write failed"; + case PI_TOO_MANY_PARAM : return "too many script parameters (> 10)"; + case PI_SCRIPT_NOT_READY : return "script initialising"; + case PI_BAD_TAG : return "script has unresolved tag"; + case PI_BAD_MICS_DELAY : return "bad MICS delay (too large)"; + case PI_BAD_MILS_DELAY : return "bad MILS delay (too large)"; + case PI_BAD_WAVE_ID : return "non existent wave id"; + case PI_TOO_MANY_CBS : return "No more CBs for waveform"; + case PI_TOO_MANY_OOL : return "No more OOL for waveform"; + case PI_EMPTY_WAVEFORM : return "attempt to create an empty waveform"; + case PI_NO_WAVEFORM_ID : return "no more waveform ids"; + case PI_I2C_OPEN_FAILED : return "can't open I2C device"; + case PI_SER_OPEN_FAILED : return "can't open serial device"; + case PI_SPI_OPEN_FAILED : return "can't open SPI device"; + case PI_BAD_I2C_BUS : return "bad I2C bus"; + case PI_BAD_I2C_ADDR : return "bad I2C address"; + case PI_BAD_SPI_CHANNEL : return "bad SPI channel"; + case PI_BAD_FLAGS : return "bad i2c/spi/ser open flags"; + case PI_BAD_SPI_SPEED : return "bad SPI speed"; + case PI_BAD_SER_DEVICE : return "bad serial device name"; + case PI_BAD_SER_SPEED : return "bad serial baud rate"; + case PI_BAD_PARAM : return "bad i2c/spi/ser parameter"; + case PI_I2C_WRITE_FAILED : return "I2C write failed"; + case PI_I2C_READ_FAILED : return "I2C read failed"; + case PI_BAD_SPI_COUNT : return "bad SPI count"; + case PI_SER_WRITE_FAILED : return "ser write failed"; + case PI_SER_READ_FAILED : return "ser read failed"; + case PI_SER_READ_NO_DATA : return "ser read no data available"; + case PI_UNKNOWN_COMMAND : return "unknown command"; + case PI_SPI_XFER_FAILED : return "spi xfer/read/write failed"; + case PI_BAD_POINTER : return "bad (NULL) pointer"; + case PI_NO_AUX_SPI : return "no auxiliary SPI on Pi A or B"; + case PI_NOT_PWM_GPIO : return "GPIO is not in use for PWM"; + case PI_NOT_SERVO_GPIO : return "GPIO is not in use for servo pulses"; + case PI_NOT_HCLK_GPIO : return "GPIO has no hardware clock"; + case PI_NOT_HPWM_GPIO : return "GPIO has no hardware PWM"; + case PI_BAD_HPWM_FREQ : return "invalid hardware PWM frequency"; + case PI_BAD_HPWM_DUTY : return "hardware PWM dutycycle not 0-1M"; + case PI_BAD_HCLK_FREQ : return "invalid hardware clock frequency"; + case PI_BAD_HCLK_PASS : return "need password to use hardware clock 1"; + case PI_HPWM_ILLEGAL : return "illegal: return PWM in use for main clock"; + case PI_BAD_DATABITS : return "serial data bits not 1-32"; + case PI_BAD_STOPBITS : return "serial (half) stop bits not 2-8"; + case PI_MSG_TOOBIG : return "socket/pipe message too big"; + case PI_BAD_MALLOC_MODE : return "bad memory allocation mode"; + case PI_TOO_MANY_SEGS : return "too many I2C transaction segments"; + case PI_BAD_I2C_SEG : return "an I2C transaction segment failed"; + case PI_BAD_SMBUS_CMD : return "SMBus command not supported by driver"; + case PI_NOT_I2C_GPIO : return "no bit bang I2C in progress on GPIO"; + case PI_BAD_I2C_WLEN : return "bad I2C write length"; + case PI_BAD_I2C_RLEN : return "bad I2C read length"; + case PI_BAD_I2C_CMD : return "bad I2C command"; + case PI_BAD_I2C_BAUD : return "bad I2C baud rate: return not 50-500k"; + case PI_CHAIN_LOOP_CNT : return "bad chain loop count"; + case PI_BAD_CHAIN_LOOP : return "empty chain loop"; + case PI_CHAIN_COUNTER : return "too many chain counters"; + case PI_BAD_CHAIN_CMD : return "bad chain command"; + case PI_BAD_CHAIN_DELAY : return "bad chain delay micros"; + case PI_CHAIN_NESTING : return "chain counters nested too deeply"; + case PI_CHAIN_TOO_BIG : return "chain is too long"; + case PI_DEPRECATED : return "deprecated function removed"; + case PI_BAD_SER_INVERT : return "bit bang serial invert not 0 or 1"; + case PI_BAD_EDGE : return "bad ISR edge: return not 1: return 1: return or 2"; + case PI_BAD_ISR_INIT : return "bad ISR initialisation"; + case PI_BAD_FOREVER : return "loop forever must be last chain command"; + case PI_BAD_FILTER : return "bad filter parameter"; + case PI_BAD_PAD : return "bad pad number"; + case PI_BAD_STRENGTH : return "bad pad drive strength"; + case PI_FIL_OPEN_FAILED : return "file open failed"; + case PI_BAD_FILE_MODE : return "bad file mode"; + case PI_BAD_FILE_FLAG : return "bad file flag"; + case PI_BAD_FILE_READ : return "bad file read"; + case PI_BAD_FILE_WRITE : return "bad file write"; + case PI_FILE_NOT_ROPEN : return "file not open for read"; + case PI_FILE_NOT_WOPEN : return "file not open for write"; + case PI_BAD_FILE_SEEK : return "bad file seek"; + case PI_NO_FILE_MATCH : return "no files match pattern"; + case PI_NO_FILE_ACCESS : return "no permission to access file"; + case PI_FILE_IS_A_DIR : return "file is a directory"; + case PI_BAD_SHELL_STATUS : return "bad shell return status"; + case PI_BAD_SCRIPT_NAME : return "bad script name"; + case PI_BAD_SPI_BAUD : return "bad SPI baud rate: return not 50-500k"; + case PI_NOT_SPI_GPIO : return "no bit bang SPI in progress on GPIO"; + case PI_BAD_EVENT_ID : return "bad event id"; + case PI_CMD_INTERRUPTED : return "command interrupted: return Python"; + case PI_NOT_ON_BCM2711 : return "not available on BCM2711"; + case PI_ONLY_ON_BCM2711 : return "only available on BCM2711"; + } + return "Not listed"; + } + + public static PiGpioError from(Number value){ + for(PiGpioError c : PiGpioError.values()){ + if(c.value() == value.intValue()) return c; + } + return UNKNOWN; + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioMode.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioMode.java new file mode 100644 index 000000000..93a425d15 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioMode.java @@ -0,0 +1,61 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioMode.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import static com.pi4j.library.pigpio.PiGpioConst.*; + +public enum PiGpioMode { + UNKNOWN(-1), + INPUT (PI_INPUT), + OUTPUT (PI_OUTPUT), + ALT0 (PI_ALT0), + ALT1 (PI_ALT1), + ALT2 (PI_ALT2), + ALT3 (PI_ALT3), + ALT4 (PI_ALT4), + ALT5 (PI_ALT5); + + private int value; + + PiGpioMode(int value){ + this.value =value; + } + + public int value(){ + return this.value; + } + + public static PiGpioMode from(Number value){ + for(PiGpioMode c : PiGpioMode.values()){ + if(c.value() == value.intValue()) return c; + } + return UNKNOWN; + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioPacket.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioPacket.java new file mode 100644 index 000000000..db0e11fd8 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioPacket.java @@ -0,0 +1,247 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioPacket.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.util.StringUtil; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * + * typedef struct + * { + * uint32_t cmd; + * uint32_t p1; + * uint32_t p2; + * union + * { + * uint32_t p3; + * uint32_t ext_len; + * uint32_t res; + * }; + * } cmdCmd_t; + * + * + */ +public class PiGpioPacket { + + private PiGpioCmd cmd; + private int p1 = 0; + private int p2 = 0; + private int p3 = 0; + private byte[] data = new byte[0]; + + public PiGpioPacket(){ + } + + public PiGpioPacket(PiGpioCmd cmd){ + this.cmd(cmd); + } + + public PiGpioPacket(PiGpioCmd cmd, int p1){ + this.cmd(cmd).p1(p1); + } + + public PiGpioPacket(PiGpioCmd cmd, int p1, int p2){ + this.cmd(cmd).p1(p1).p2(p2); + } + + public PiGpioPacket(PiGpioCmd cmd, int p1, int p2, byte[] data){ + this.cmd(cmd).p1(p1).p2(p2).data(data); + } + + public PiGpioCmd cmd(){ + return this.cmd; + } + public PiGpioPacket cmd(PiGpioCmd cmd){ + this.cmd = cmd; + return this; + } + + public int p1(){ + return this.p1; + } + public PiGpioPacket p1(int p1){ + this.p1 = p1; + return this; + } + + public int p2(){ + return this.p2; + } + public PiGpioPacket p2(int p2){ + this.p2 = p2; + return this; + } + + public int p3(){ + return this.p3; + } + protected PiGpioPacket p3(int p3){ + this.p3 = p3; + return this; + } + + // the following methods are actaully returning the "P3" value as P3 + // is a C union and used for multiple purposes depending on context + public int result(){ + return p3(); + } + public boolean success(){ + return p3() >= 0; + } + + public boolean hasData(){ + if(this.data == null) return false; + return this.data.length > 0; + } + public int dataLength(){ + if(this.data == null) return 0; + return this.data.length; + } + + public byte[] data(){ + return this.data; + } + public PiGpioPacket data(byte[] data){ + // check for valid data + if(data != null && data.length > 0) { + this.p3 = data.length; + this.data = Arrays.copyOf(data, data.length); + } + else{ + this.p3 = 0; + this.data = new byte[0]; + } + return this; + } + + public PiGpioPacket data(int value){ + // check for valid value + if(value > 0) { + this.p3 = 4; // 4 bytes length + ByteBuffer buffer = ByteBuffer.allocate(4); + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.putInt(value); + this.data = buffer.array(); + } + else{ + this.p3 = 0; + this.data = new byte[0]; + } + return this; + } + + + public PiGpioPacket data(byte value){ + // check for valid value + if(value > 0) { + this.p3 = 4; // 4 bytes length + this.data = new byte[] { value, 0, 0, 0 }; // little endian + } + else{ + this.p3 = 0; + this.data = new byte[0]; + } + return this; + } + + public String dataToString(){ + if(data == null) return null; + if(data.length == 0) return ""; + return new String(data, StandardCharsets.US_ASCII); + } + + public static PiGpioPacket decode(InputStream stream) throws IOException { + return PiGpioPacket.decode(stream.readNBytes(stream.available())); + } + + public static PiGpioPacket decode(byte[] data) throws IOException { + ByteBuffer rx = ByteBuffer.wrap(data); + rx.order(ByteOrder.LITTLE_ENDIAN); + + // check data length for minimum package size + if(data.length < 16){ + throw new IOException("Insufficient number of data bytes bytes received; COUNT=" + data.length); + } + + // parse packet parameters from raw received bytes + PiGpioCmd cmd = PiGpioCmd.from(rx.getInt()); // CMD <4 bytes :: 0-3> + int p1 = rx.getInt(); // P1 <4 bytes :: 4-7> + int p2 = rx.getInt(); // P2 <4 bytes :: 8-11> + int p3 = rx.getInt(); // P3 <4 bytes :: 12-15> + + // create new packet + PiGpioPacket packet = new PiGpioPacket(cmd, p1, p2) + .p3(p3); // set RAW P3 value + + // apply any extra payload data (if available) + if(rx.hasRemaining()){ + var temp = new byte[rx.remaining()]; + rx.get(temp); + packet.data(temp); + } + return packet; + } + + public static byte[] encode(PiGpioPacket packet){ + // create byte array and byte buffer using LITTLE ENDIAN for the ARM platform + byte[] bytes = new byte[16 + packet.dataLength()]; + ByteBuffer buffer = ByteBuffer.wrap(bytes); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // place packet values into structured data bytes + buffer.putInt(packet.cmd().value()); // CMD + buffer.putInt((packet.p1())); // + buffer.putInt((packet.p2())); // + buffer.putInt((packet.p3())); // + if(packet.hasData()) { + buffer.put(packet.data()); // + } + + // return byte array + return bytes; + } + + @Override + public String toString(){ + if(hasData()) + return String.format("CMD=%s(%d); P1=%d; P2=%d; P3=%d; PAYLOAD=[0x%s]", cmd().name(), cmd().value(), p1(), p2(), p3(), StringUtil.toHexString(data())); + else + return String.format("CMD=%s(%d); P1=%d; P2=%d; P3=%d", cmd().name(), cmd().value(), p1(), p2(), p3()); + } +} + + diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioPud.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioPud.java new file mode 100644 index 000000000..78d985fb1 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioPud.java @@ -0,0 +1,56 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioPud.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import static com.pi4j.library.pigpio.PiGpioConst.*; + +public enum PiGpioPud { + UNKNOWN(-1), + OFF (PI_PUD_OFF), + DOWN (PI_PUD_DOWN), + UP (PI_PUD_UP); + + private int value; + + PiGpioPud(int value){ + this.value =value; + } + + public int value(){ + return this.value; + } + + public static PiGpioPud from(Number value){ + for(PiGpioPud c : PiGpioPud.values()){ + if(c.value() == value.intValue()) return c; + } + return UNKNOWN; + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioState.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioState.java new file mode 100644 index 000000000..30041c39e --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpioState.java @@ -0,0 +1,53 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioState.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +public enum PiGpioState { + UNKNOWN(-1), + LOW (0), + HIGH (1); + + private int value; + + PiGpioState(int value){ + this.value =value; + } + + public int value(){ + return this.value; + } + + public static PiGpioState from(Number value){ + for(PiGpioState c : PiGpioState.values()){ + if(c.value() == value.intValue()) return c; + } + return UNKNOWN; + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_GPIO.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_GPIO.java new file mode 100644 index 000000000..6b69264a7 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_GPIO.java @@ -0,0 +1,85 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpio_GPIO.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.io.IOException; + +public interface PiGpio_GPIO { + + /** + * Sets or clears resistor pull ups or downs on the GPIO. + * + * @param pin gpio pin address + * @param pud pull-up, pull-down, pull-off + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetPullUpDown" + */ + void gpioSetPullUpDown(int pin, PiGpioPud pud) throws IOException; + + /** + * Gets the GPIO mode. + * + * @param pin gpio pin address + * @return pin mode: input, output, etc. + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetMode" + */ + PiGpioMode gpioGetMode(int pin) throws IOException; + + /** + * Sets the GPIO mode, typically input or output. + * + * @param pin gpio pin address + * @param mode pin mode: input, output, etc. + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetMode" + */ + void gpioSetMode(int pin, PiGpioMode mode) throws IOException; + + /** + * Reads the GPIO level, on (HIGH) or off (LOW). + * @param pin gpio pin address + * @return pin state: HIGH or LOW + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioRead" + */ + PiGpioState gpioRead(int pin) throws IOException; + + + /** + * Sets the GPIO level, on (HIGH) or off (LOW). + * + * @param pin gpio pin address + * @param state HIGH or LOW + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioWrite" + */ + void gpioWrite(int pin, PiGpioState state) throws IOException; +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_I2C.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_I2C.java new file mode 100644 index 000000000..6199a375f --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_I2C.java @@ -0,0 +1,160 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpio_I2C.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; + +public interface PiGpio_I2C { + + int i2cOpen(int bus, int device) throws IOException; + void i2cClose(int handle) throws IOException; + void i2cWriteQuick(int handle, boolean bit) throws IOException; + void i2cWriteByte(int handle, byte value) throws IOException; + int i2cReadByte(int handle) throws IOException; + void i2cWriteByteData(int handle, int register, byte value) throws IOException; + void i2cWriteWordData(int handle, int register, int value) throws IOException; + int i2cReadByteData(int handle, int register) throws IOException; + int i2cReadWordData(int handle, int register) throws IOException; + int i2cProcessCall(int handle, int register, int value) throws IOException; + + void i2cWriteBlockData(int handle, int register, byte[] data) throws IOException; + + default void i2cWriteBlockData(int handle, int register, byte[] data, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, data.length); + byte[] temp = Arrays.copyOfRange(data, offset, length); + i2cWriteBlockData(handle, register, temp); + } + default void i2cWriteBlockData(int handle, int register, byte[] data, int length) throws IOException{ + i2cWriteBlockData(handle, register, data, 0 ,length); + } + default void i2cWriteBlockData(int handle, int register, ByteBuffer buffer) throws IOException{ + i2cWriteBlockData(handle, register, buffer.array()); + } + default void i2cWriteBlockData(int handle, int register, ByteBuffer buffer, int offset, int length) throws IOException{ + i2cWriteBlockData(handle, register, buffer.array(), offset, length); + } + default void i2cWriteBlockData(int handle, int register, ByteBuffer buffer, int length) throws IOException{ + i2cWriteBlockData(handle, register, buffer, 0, length); + } + default void i2cWriteBlockData(int handle, int register, String data) throws IOException{ + i2cWriteBlockData(handle, register, data, StandardCharsets.US_ASCII); + } + default void i2cWriteBlockData(int handle, int register, String data, Charset charset) throws IOException{ + i2cWriteBlockData(handle, register, data.getBytes(charset)); + } + + byte[] i2cReadBlockData(int handle, int register) throws IOException; + default String i2cReadBlockDataToString(int handle, int register) throws IOException{ + byte[] rx = i2cReadBlockData(handle, register); + return new String(rx, StandardCharsets.US_ASCII); + } + + byte[] i2cBlockProcessCall(int handle, int register, byte[] data) throws IOException; + default String i2cBlockProcessCallToString(int handle, int register, byte[] data) throws IOException{ + byte[] rx = i2cBlockProcessCall(handle, register, data); + return new String(rx, StandardCharsets.US_ASCII); + } + default byte[] i2cBlockProcessCall(int handle, int register, String data) throws IOException{ + return i2cBlockProcessCall(handle, register, data.getBytes(StandardCharsets.US_ASCII)); + } + default String i2cBlockProcessCallToString(int handle, int register, String data) throws IOException{ + byte[] rx = i2cBlockProcessCall(handle, register, data); + return new String(rx, StandardCharsets.US_ASCII); + } + + byte[] i2cReadI2CBlockData(int handle, int register, int length) throws IOException; + default String i2cReadI2CBlockDataToString(int handle, int register, int length) throws IOException{ + byte[] rx = i2cReadI2CBlockData(handle, register, length); + return new String(rx, StandardCharsets.US_ASCII); + } + + void i2cWriteI2CBlockData(int handle, int register, byte[] data) throws IOException; + + default void i2cWriteI2CBlockData(int handle, int register, byte[] data, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, data.length); + byte[] temp = Arrays.copyOfRange(data, offset, length); + i2cWriteI2CBlockData(handle, register, temp); + } + default void i2cWriteI2CBlockData(int handle, int register, byte[] data, int length) throws IOException{ + i2cWriteI2CBlockData(handle, register, data, 0 ,length); + } + default void i2cWriteI2CBlockData(int handle, int register, ByteBuffer buffer) throws IOException{ + i2cWriteI2CBlockData(handle, register, buffer.array()); + } + default void i2cWriteI2CBlockData(int handle, int register, ByteBuffer buffer, int offset, int length) throws IOException{ + i2cWriteI2CBlockData(handle, register, buffer.array(), offset, length); + } + default void i2cWriteI2CBlockData(int handle, int register, ByteBuffer buffer, int length) throws IOException{ + i2cWriteI2CBlockData(handle, register, buffer, 0, length); + } + default void i2cWriteI2CBlockData(int handle, int register, String data) throws IOException{ + i2cWriteI2CBlockData(handle, register, data, StandardCharsets.US_ASCII); + } + default void i2cWriteI2CBlockData(int handle, int register, String data, Charset charset) throws IOException{ + i2cWriteI2CBlockData(handle, register, data.getBytes(charset)); + } + + byte[] i2cReadDevice(int handle, int length) throws IOException; + default String i2cReadDeviceToString(int handle, int length) throws IOException{ + byte[] rx = i2cReadDevice(handle, length); + return new String(rx, StandardCharsets.US_ASCII); + } + + int i2cWriteDevice(int handle, byte[] data) throws IOException; + default int i2cWriteDevice(int handle, byte[] data, int length) throws IOException{ + return i2cWriteDevice(handle, data, 0, length); + } + default int i2cWriteDevice(int handle, byte[] data, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, data.length); + return i2cWriteDevice(handle, Arrays.copyOfRange(data, offset, length)); + } + + default void i2cWriteDevice(int handle, ByteBuffer buffer) throws IOException{ + i2cWriteDevice(handle, buffer.array()); + } + default void i2cWriteDevice(int handle, ByteBuffer buffer, int length) throws IOException{ + i2cWriteDevice(handle, buffer, 0, length); + } + default void i2cWriteDevice(int handle, ByteBuffer buffer, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + i2cWriteDevice(handle, buffer.array(), offset, length); + } + default void i2cWriteDevice(int handle, String data) throws IOException{ + i2cWriteDevice(handle, data, StandardCharsets.US_ASCII); + } + default void i2cWriteDevice(int handle, String data, Charset charset) throws IOException{ + i2cWriteDevice(handle, data.getBytes(charset)); + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_PWM.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_PWM.java new file mode 100644 index 000000000..3eed228f3 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/PiGpio_PWM.java @@ -0,0 +1,218 @@ +package com.pi4j.library.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpio_PWM.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.io.IOException; + +public interface PiGpio_PWM { + + /** + * Starts PWM on the GPIO, duty-cycle between 0 (off) and range (fully on). Range defaults to 255. + * + * This and the servo functionality use the DMA and PWM or PCM peripherals to control and schedule + * the pulse lengths and duty cycles. + * + * @param pin user_gpio: 0-31 + * @param dutyCycle dutycycle: 0-range + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioPWM" + */ + void gpioPWM(int pin, int dutyCycle) throws IOException; + default void gpioSetPWMdutycycle(int pin, int dutyCycle) throws IOException{ + gpioPWM(pin,dutyCycle); + } + + + /** + * Returns the PWM dutycycle setting for the GPIO. + * + * For normal PWM the dutycycle will be out of the defined range for the GPIO (see gpioGetPWMrange). + * If a hardware clock is active on the GPIO the reported dutycycle will be 500000 (500k) out of 1000000 (1M). + * If hardware PWM is active on the GPIO the reported dutycycle will be out of a 1000000 (1M). + * + * Normal PWM range defaults to 255. + * + * @param pin user_gpio: 0-31 + * @return Returns between 0 (off) and range (fully on) if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMdutycycle" + */ + int gpioGetPWMdutycycle(int pin) throws IOException; + + + /** + * Selects the dutycycle range to be used for the GPIO. Subsequent calls to gpioPWM will use a dutycycle + * between 0 (off) and range (fully on. If PWM is currently active on the GPIO its dutycycle will be + * scaled to reflect the new range. + * + * The real range, the number of steps between fully off and fully on for each frequency, + * is given in the following table. + * + * ------------------------------------------------------- + * #1 #2 #3 #4 #5 #6 #7 #8 #9 + * 25, 50, 100, 125, 200, 250, 400, 500, 625, + * ------------------------------------------------------- + * #10 #11 #12 #13 #14 #15 #16 #17 #18 + * 800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000 + * ------------------------------------------------------- + * + * The real value set by gpioPWM is (dutycycle * real range) / range. + * + * Example + * gpioSetPWMrange(24, 2000); // Now 2000 is fully on + * // 1000 is half on + * // 500 is quarter on, etc. + * @param pin user_gpio: 0-31 + * @param range range: 25-40000 + * @return real range for the given GPIO's frequency if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetPWMrange" + */ + int gpioSetPWMrange(int pin, int range) throws IOException; + + /** + * Returns the duty-cycle range used for the GPIO if OK. + * If a hardware clock or hardware PWM is active on the GPIO the reported range will be 1000000 (1M). + * + * @param pin user_gpio: 0-31 + * @return duty-cycle range + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMrange" + */ + int gpioGetPWMrange(int pin) throws IOException; + + /** + * Returns the real range used for the GPIO if OK. + * If a hardware clock is active on the GPIO the reported real range will be 1000000 (1M). + * If hardware PWM is active on the GPIO the reported real range will be approximately 250M + * divided by the set PWM frequency. + * + * @param pin user_gpio: 0-31 + * @return real range used for the GPIO if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMrealRange" + */ + int gpioGetPWMrealRange(int pin) throws IOException; + + /** + * Sets the frequency in hertz to be used for the GPIO. + * + * If PWM is currently active on the GPIO it will be switched off and then back on at the new frequency. + * Each GPIO can be independently set to one of 18 different PWM frequencies. + * The selectable frequencies depend upon the sample rate which may be 1, 2, 4, 5, 8, or 10 microseconds (default 5). + * + * The frequencies for each sample rate are: + * + * Hertz + * + * 1: 40000 20000 10000 8000 5000 4000 2500 2000 1600 + * 1250 1000 800 500 400 250 200 100 50 + * + * 2: 20000 10000 5000 4000 2500 2000 1250 1000 800 + * 625 500 400 250 200 125 100 50 25 + * + * 4: 10000 5000 2500 2000 1250 1000 625 500 400 + * 313 250 200 125 100 63 50 25 13 + * sample + * rate + * (us) 5: 8000 4000 2000 1600 1000 800 500 400 320 + * 250 200 160 100 80 50 40 20 10 + * + * 8: 5000 2500 1250 1000 625 500 313 250 200 + * 156 125 100 63 50 31 25 13 6 + * + * 10: 4000 2000 1000 800 500 400 250 200 160 + * 125 100 80 50 40 25 20 10 5 + * + * + * Example: + * gpioSetPWMfrequency(23, 0); // Set GPIO23 to lowest frequency. + * gpioSetPWMfrequency(24, 500); // Set GPIO24 to 500Hz. + * gpioSetPWMfrequency(25, 100000); // Set GPIO25 to highest frequency. + * + * @param pin user_gpio: 0-31 + * @param frequency frequency: >=0 + * @return Returns the numerically closest frequency if OK + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetPWMrange" + */ + int gpioSetPWMfrequency(int pin, int frequency) throws IOException; + + + /** + * Returns the frequency (in hertz) used for the GPIO + * + * For normal PWM the frequency will be that defined for the GPIO by gpioSetPWMfrequency. + * If a hardware clock is active on the GPIO the reported frequency will be that set by gpioHardwareClock. + * If hardware PWM is active on the GPIO the reported frequency will be that set by gpioHardwarePWM. + * + * Example: + * f = gpioGetPWMfrequency(23); // Get frequency used for GPIO23. + * + * @param pin user_gpio: 0-31 + * @return Returns the frequency (in hertz) used for the GPIO if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMfrequency" + */ + int gpioGetPWMfrequency(int pin) throws IOException; + + + /** + * Starts hardware PWM on a GPIO at the specified frequency and duty-cycle. + * Frequencies above 30MHz are unlikely to work. + * + * NOTE: Any waveform started by gpioWaveTxSend, or gpioWaveChain will be cancelled. + * + * This function is only valid if the pigpio main clock is PCM. + * The main clock defaults to PCM but may be overridden by a call to gpioCfgClock. + * + * The same PWM channel is available on multiple GPIO. The latest frequency and duty-cycle + * setting will be used by all GPIO which share a PWM channel. + * + * The GPIO must be one of the following. + * + * 12 PWM channel 0 All models but A and B + * 13 PWM channel 1 All models but A and B + * 18 PWM channel 0 All models + * 19 PWM channel 1 All models but A and B + * + * 40 PWM channel 0 Compute module only + * 41 PWM channel 1 Compute module only + * 45 PWM channel 1 Compute module only + * 52 PWM channel 0 Compute module only + * 53 PWM channel 1 Compute module only + * + * + * The actual number of steps between off and fully on is the integral part of + * 250M/PWMfreq (375M/PWMfreq for the BCM2711). + * The actual frequency set is 250M/steps (375M/steps for the BCM2711). + * There will only be a million steps for a frequency of 250 (375 for the BCM2711). Lower + * frequencies will have more steps and higher frequencies will have fewer steps. + * dutyCycle is automatically scaled to take this into account. + * + * @param pin a supported hardware PWM pin + * @param frequency 0 (off) or 1-125M (1-187.5M for the BCM2711) + * @param dutyCycle 0 (off) to 1000000 (1M)(fully on) + */ + void gpioHardwarePWM(int pin, int frequency, int dutyCycle) throws IOException; +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioBase.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioBase.java new file mode 100644 index 000000000..9175ae91d --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioBase.java @@ -0,0 +1,180 @@ +package com.pi4j.library.pigpio.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioBase.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioError; +import com.pi4j.library.pigpio.PiGpioPacket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import static com.pi4j.library.pigpio.PiGpioConst.*; + +public abstract class PiGpioBase implements PiGpio { + + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * -------------------------------------------------------------------------- + * GPIO PINS + * -------------------------------------------------------------------------- + * A Broadcom numbered GPIO, in the range 0-53. + * + * There are 54 General Purpose Input Outputs (GPIO) named GPIO0 through GPIO53. + * + * They are split into two banks. Bank 1 consists of GPIO0 through GPIO31. + * Bank 2 consists of GPIO32 through GPIO53. + * + * All the GPIO which are safe for the user to read and write are in bank 1. + * Not all GPIO in bank 1 are safe though. Type 1 boards have 17 safe GPIO. + * Type 2 boards have 21. Type 3 boards have 26. + * + * See gpioHardwareRevision. + * + * The user GPIO are marked with an X in the following table. + * + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * Type 1 X X - - X - - X X X X X - - X X + * Type 2 - - X X X - - X X X X X - - X X + * Type 3 X X X X X X X X X X X X X X + * + * 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + * Type 1 - X X - - X X X X X - - - - - - + * Type 2 - X X - - - X X X X - X X X X X + * Type 3 X X X X X X X X X X X X - - - - + */ + + protected void validateUserPin(int pin) throws IllegalArgumentException { + validatePin(pin, true); + } + + protected void validatePin(int pin) throws IllegalArgumentException { + validatePin(pin, false); + } + + protected void validatePin(int pin, boolean userPin) throws IllegalArgumentException { + int min = PI_MIN_GPIO; + int max = ((userPin ? PI_MAX_USER_GPIO : PI_MAX_GPIO)); + if(pin < min || pin > max) + throw new IllegalArgumentException("Invalid PIN number: " + pin + "; (supported pins: " + min + "-" + max + ")"); + } + + protected void validateDutyCycle(int dutyCycle) throws IllegalArgumentException{ + int min = 0; + int max = PI_MAX_DUTYCYCLE_RANGE; + if(dutyCycle < min || dutyCycle > max) + throw new IllegalArgumentException("Invalid Duty Cycle: " + dutyCycle + + "; (supported duty-cycle: " + min + " - " + max + ")"); + } + + protected void validateDutyCycleRange(int range) throws IllegalArgumentException{ + int min = PI_MIN_DUTYCYCLE_RANGE; + int max = PI_MAX_DUTYCYCLE_RANGE; + if(range < min || range > max) + throw new IllegalArgumentException("Invalid Duty Cycle Range: " + range + + "; (supported range: " + min + " - " + max + ")"); + } + + protected void validateDelayMicroseconds(int micros){ + int min = 0; + int max = PI_MAX_MICS_DELAY; + if(micros < min || micros > max) + throw new IllegalArgumentException("Invalid microseconds delay: " + micros + + "; (supported range: " + min + " - " + max + ")"); + } + + protected void validateDelayMilliseconds(int millis){ + int min = 0; + int max = PI_MAX_MILS_DELAY; + if(millis < min || millis > max) + throw new IllegalArgumentException("Invalid milliseconds delay: " + millis + + "; (supported range: " + min + " - " + max + ")"); + } + + protected void validateResult(PiGpioPacket result) throws IOException{ + validateResult(result.result()); + } + + protected void validateResult(PiGpioPacket result, boolean throwException) throws IOException{ + validateResult(result.result(), throwException); + } + + protected void validateResult(long value) throws IOException { + validateResult(value, true); + } + + protected void validateResult(long value, boolean throwException) throws IOException { + if(value < 0) { + PiGpioError err = PiGpioError.from(value); + logger.warn("PIGPIO ERROR: " + err.name() + "; " + err.message()); + if(throwException) { + throw new IOException("PIGPIO ERROR: " + err.name() + "; " + err.message()); + } + } + } + + protected void validateI2cHandle(int handle) throws IOException { + // validate I2C handle + if(handle < 0) { + throw new IOException("PIGPIO ERROR: INVALID I2C HANDLE [" + handle + "]; Valid range: >0"); + } + } + + protected void validateI2cRegister(int register) throws IOException { + // validate I2C/SMBus register range + if(register < 0 || register > 255) { + throw new IOException("PIGPIO ERROR: INVALID I2C REGISTER [" + register + "]; Valid range: 0-255"); + } + } + + protected void validateI2cDeviceAddress(int device) throws IOException { + // validate I2C/SMBus device address :: 0-0x7F + if(device < 0 || device > 0x7F) { + throw new IOException("PIGPIO ERROR: INVALID I2C DEVICE ADDRESS [" + device + "]; Valid range: 0-127"); + } + } + + protected void validateI2cBus(int bus) throws IOException { + // validate I2C/SMBus bus number :: >=0 + if(bus < 0) { + throw new IOException("PIGPIO ERROR: INVALID I2C BUS [" + bus + "]; Valid range: >=0"); + } + } + + protected void validateI2cBlockLength(int length) throws IOException { + // validate I2C/SMBus payload data length :: 0-32 + if(length < 0 || length > 32) { + throw new IOException("PIGPIO ERROR: INVALID I2C PAYLOAD DATA LENGTH [" + length + "]; Valid range: 0-32"); + } + } + +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioSocketBase.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioSocketBase.java new file mode 100644 index 000000000..3d63fdafd --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioSocketBase.java @@ -0,0 +1,105 @@ +package com.pi4j.library.pigpio.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioSocketBase.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioCmd; +import com.pi4j.library.pigpio.PiGpioPacket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.Socket; + +import static com.pi4j.library.pigpio.PiGpioConst.DEFAULT_HOST; +import static com.pi4j.library.pigpio.PiGpioConst.DEFAULT_PORT; + +public abstract class PiGpioSocketBase extends PiGpioBase implements PiGpio { + + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + protected Socket socket = null; + protected String host = DEFAULT_HOST; + protected int port = DEFAULT_PORT; + + /** + * ALTERNATE CONSTRUCTOR + * + * Connects to a user specified socket hostname/ip address and port. + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @param port TCP port number of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + protected PiGpioSocketBase(String host, int port) throws IOException { + this.socket = new Socket(host, port); + } + + protected PiGpioPacket sendCommand(PiGpioCmd cmd) throws IOException { + return sendPacket(new PiGpioPacket(cmd)); + } + + protected PiGpioPacket sendCommand(PiGpioCmd cmd, int p1) throws IOException { + return sendPacket(new PiGpioPacket(cmd, p1)); + } + protected PiGpioPacket sendCommand(PiGpioCmd cmd, int p1, int p2) throws IOException { + return sendPacket(new PiGpioPacket(cmd, p1, p2)); + } + protected PiGpioPacket sendPacket(PiGpioPacket tx) throws IOException { + + // get socket streams + var in = socket.getInputStream(); + var out = socket.getOutputStream(); + + // transmit packet + logger.trace("[TX] -> " + tx.toString()); + out.write(PiGpioPacket.encode(tx)); + out.flush(); + + // wait until data has been received (timeout after 500 ms and throw exception) + int millis = 0; + try { + while(in.available() < 16){ + if(millis > 500){ // timeout exception + throw new IOException("Command timed out; no response from host in 500 milliseconds"); + } + millis+=5; + Thread.sleep(5); // ... take a breath .. + } + } catch (InterruptedException e) { + // wrap exception + throw new IOException(e.getMessage(), e); + } + + // read receive packet + PiGpioPacket rx = PiGpioPacket.decode(in); + logger.trace("[RX] <- " + rx.toString()); + return rx; + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioSocketImpl.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioSocketImpl.java new file mode 100644 index 000000000..80998f003 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/impl/PiGpioSocketImpl.java @@ -0,0 +1,814 @@ +package com.pi4j.library.pigpio.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : PiGpioSocketImpl.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import static com.pi4j.library.pigpio.PiGpioCmd.*; +import static com.pi4j.library.pigpio.PiGpioConst.DEFAULT_HOST; +import static com.pi4j.library.pigpio.PiGpioConst.DEFAULT_PORT; + +public class PiGpioSocketImpl extends PiGpioSocketBase implements PiGpio { + + protected Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to a user specified socket hostname/ip address and port. + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @param port TCP port number of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + public static PiGpio newInstance(String host, String port) throws IOException { + return new PiGpioSocketImpl(host, Integer.parseInt(port)); + } + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to a user specified socket hostname/ip address and port. + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @param port TCP port number of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + public static PiGpio newInstance(String host, int port) throws IOException { + return new PiGpioSocketImpl(host, port); + } + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to a user specified socket hostname/ip address using the default port (8888). + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + public static PiGpio newInstance(String host) throws IOException { + return new PiGpioSocketImpl(host, DEFAULT_PORT); + } + + /** + * Creates a PiGpio instance using TCP Socket communication for remote I/O access. + * Connects to the local system (127.0.0.1) using the default port (8888). + * + * @throws IOException + */ + public static PiGpio newInstance() throws IOException { + return new PiGpioSocketImpl(DEFAULT_HOST, DEFAULT_PORT); + } + + /** + * DEFAULT PRIVATE CONSTRUCTOR + * + * Connects to a user specified socket hostname/ip address and port. + * + * @param host hostname or IP address of the RaspberryPi to connect to via TCP/IP socket. + * @param port TCP port number of the RaspberryPi to connect to via TCP/IP socket. + * @throws IOException + */ + private PiGpioSocketImpl(String host, int port) throws IOException { + super(host, port); + } + + /** + * Initialises the library. + * + * Returns the pigpio version number if OK, otherwise PI_INIT_FAILED. + * gpioInitialise must be called before using the other library functions with the following exceptions: + * - gpioCfg* + * - gpioVersion + * - gpioHardwareRevision + * + * @return result value + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioInitialise" + */ + @Override + public long gpioInitialise() { + // TODO :: SETUP NOTIFICATIONS + return 0; + } + + /** + * Terminates the library. + * + * Returns nothing. + * Call before program exit. + * This function resets the used DMA channels, releases memory, and terminates any running threads. + */ + @Override + public void gpioTerminate() throws IOException { + // TODO :: REMOVE ALL NOTIFICATIONS + socket.close(); + } + + /** + * Returns the pigpio library version. + * + * @return pigpio version. + * @throws IOException + * @throws InterruptedException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioVersion" + */ + @Override + public long gpioVersion() throws IOException { + logger.trace("[PIGPIO] -> GET VERSION"); + PiGpioPacket result = sendCommand(PIGPV); + long version = result.result(); + logger.trace("[PIGPIO] <- VERSION: {}", version); + return version; + } + + /** + * Returns the hardware revision. + * + * If the hardware revision can not be found or is not a valid hexadecimal number the function returns 0. + * The hardware revision is the last few characters on the Revision line of /proc/cpuinfo. + * The revision number can be used to determine the assignment of GPIO to pins (see gpio). + * + * There are at least three types of board. + * - Type 1 boards have hardware revision numbers of 2 and 3. + * - Type 2 boards have hardware revision numbers of 4, 5, 6, and 15. + * - Type 3 boards have hardware revision numbers of 16 or greater. + * + * for "Revision : 0002" the function returns 2. + * for "Revision : 000f" the function returns 15. + * for "Revision : 000g" the function returns 0. + * + * @return hardware revision as raw 32-bit UINT + * @throws IOException + * @seel "http://abyz.me.uk/rpi/pigpio/cif.html#gpioHardwareRevision" + */ + @Override + public long gpioHardwareRevision() throws IOException { + logger.trace("[HARDWARE] -> GET REVISION"); + PiGpioPacket result = sendCommand(HWVER); + long revision = result.result(); + logger.trace("[HARDWARE] <- REVISION: {}", revision); + if(revision <= 0) throw new IOException("Hardware revision could not be determined."); + return revision; + } + + /** + * Returns the hardware revision (as hexadecimal string). + * + * If the hardware revision can not be found or is not a valid hexadecimal number the function returns 0. + * The hardware revision is the last few characters on the Revision line of /proc/cpuinfo. + * The revision number can be used to determine the assignment of GPIO to pins (see gpio). + * + * There are at least three types of board. + * - Type 1 boards have hardware revision numbers of 2 and 3. + * - Type 2 boards have hardware revision numbers of 4, 5, 6, and 15. + * - Type 3 boards have hardware revision numbers of 16 or greater. + * + * for "Revision : 0002" the function returns 2. + * for "Revision : 000f" the function returns 15. + * for "Revision : 000g" the function returns 0. + * + * @return hardware revision in hexadecimal string + * @throws IOException + * @seel "http://abyz.me.uk/rpi/pigpio/cif.html#gpioHardwareRevision" + */ + @Override + public String gpioHardwareRevisionString() throws IOException { + logger.trace("[HARDWARE] -> GET REVISION (STRING)"); + PiGpioPacket result = sendCommand(HWVER); + long revision = result.result(); + String revisionString = Integer.toHexString((int)revision); + logger.trace("[HARDWARE] <- REVISION (STRING): {}", revisionString); + return revisionString; + } + + /** + * Sets or clears resistor pull ups or downs on the GPIO. + * + * @param pin gpio pin address + * @param pud pull-up, pull-down, pull-off + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetPullUpDown" + */ + @Override + public void gpioSetPullUpDown(int pin, PiGpioPud pud) throws IOException { + logger.trace("[GPIO::PUD-SET] -> PIN: {}; PUD={}({});", pin, pud.name(), pud.value()); + validatePin(pin); + PiGpioPacket result = sendCommand(MODES, pin, pud.value()); + logger.trace("[GPIO::PUD-SET] <- PIN: {}; PUD={}({}); SUCCESS={}", pud.name(), pud.value(), result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_MODE. + } + + /** + * Gets the GPIO mode. + * + * @param pin + * @return pin mode + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetMode" + */ + @Override + public PiGpioMode gpioGetMode(int pin) throws IOException { + logger.trace("[GPIO::MODE-GET] -> PIN: {};", pin); + validatePin(pin); + PiGpioPacket result = sendCommand(MODEG, pin); + validateResult(result); // Returns the GPIO mode if OK, otherwise PI_BAD_GPIO. + PiGpioMode mode = PiGpioMode.from(result.result()); + logger.trace("[GPIO::MODE-GET] <- PIN: {}; MODE={}({})", pin, mode.name(), mode.value()); + return mode; + } + + /** + * Sets the GPIO mode, typically input or output. + * + * gpio: 0-53 + * mode: 0-7 + * + * @param pin + * @param mode + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetMode" + */ + @Override + public void gpioSetMode(int pin, PiGpioMode mode) throws IOException { + logger.trace("[GPIO::MODE-SET] -> PIN: {}; MODE={}({});", pin, mode.name(), mode.value()); + validatePin(pin); + PiGpioPacket result = sendCommand(MODES, pin, mode.value()); + logger.trace("[GPIO::MODE-SET] <- PIN: {}; MODE={}({}); SUCCESS={}", mode.name(), mode.value(), result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_PUD. + } + + /** + * Reads the GPIO level, on (HIGH) or off (LOW). + * @param pin gpio pin address + * @return + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioRead" + */ + @Override + public PiGpioState gpioRead(int pin) throws IOException { + logger.trace("[GPIO::GET] -> PIN: {}", pin); + validatePin(pin); + PiGpioPacket result = sendCommand(READ, pin); + validateResult(result); // Returns the GPIO level if OK, otherwise PI_BAD_GPIO. + PiGpioState state = PiGpioState.from(result.p3()); // result value stored in P3 + logger.trace("[GPIO::GET] <- PIN: {} is {}({})", pin, state.name(), state.value()); + return state; + } + + /** + * Sets the GPIO level, on (HIGH) or off (LOW). + * + * @param pin gpio pin address + * @param state HIGH or LOW + * @throws IOException + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioWrite" + */ + @Override + public void gpioWrite(int pin, PiGpioState state) throws IOException { + logger.trace("[GPIO::SET] -> PIN: {}; {}({});", pin, state.name(), state.value()); + validatePin(pin); + PiGpioPacket result = sendCommand(WRITE, pin, state.value()); + logger.trace("[GPIO::SET] <- PIN: {}; {}({}); SUCCESS={}", pin, state.name(), state.value(), result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_LEVEL. + } + + /** + * Starts PWM on the GPIO, dutycycle between 0 (off) and range (fully on). Range defaults to 255. + * + * This and the servo functionality use the DMA and PWM or PCM peripherals to control and schedule + * the pulse lengths and duty cycles. + * + * @param pin user_gpio: 0-31 + * @param dutyCycle dutycycle: 0-range + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioPWM" + */ + @Override + public void gpioPWM(int pin, int dutyCycle) throws IOException { + logger.trace("[PWM::SET] -> PIN: {}; DUTY-CYCLE={};", pin, dutyCycle); + validateUserPin(pin); + validateDutyCycle(dutyCycle); + PiGpioPacket result = sendCommand(PWM, pin, dutyCycle); + logger.trace("[PWM::SET] <- PIN: {}; DUTY-CYCLE={}; SUCCESS={}", pin, dutyCycle, result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_GPIO or PI_BAD_LEVEL. + } + + /** + * Returns the PWM dutycycle setting for the GPIO. + * + * For normal PWM the dutycycle will be out of the defined range for the GPIO (see gpioGetPWMrange). + * If a hardware clock is active on the GPIO the reported dutycycle will be 500000 (500k) out of 1000000 (1M). + * If hardware PWM is active on the GPIO the reported dutycycle will be out of a 1000000 (1M). + * + * Normal PWM range defaults to 255. + * + * @param pin user_gpio: 0-31 + * @return Returns between 0 (off) and range (fully on) if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMdutycycle" + */ + @Override + public int gpioGetPWMdutycycle(int pin) throws IOException { + logger.trace("[PWM::GET] -> PIN: {}", pin); + validateUserPin(pin); + PiGpioPacket result = sendCommand(GDC, pin); + var dutyCycle = result.result(); + logger.trace("[PWM::GET] <- PIN: {}; DUTY-CYCLE={}; SUCCESS={}", pin, dutyCycle, result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_NOT_PWM_GPIO. + return dutyCycle; + } + + /** + * Selects the dutycycle range to be used for the GPIO. Subsequent calls to gpioPWM will use a dutycycle + * between 0 (off) and range (fully on. If PWM is currently active on the GPIO its dutycycle will be + * scaled to reflect the new range. + * + * The real range, the number of steps between fully off and fully on for each frequency, + * is given in the following table. + * + * ------------------------------------------------------- + * #1 #2 #3 #4 #5 #6 #7 #8 #9 + * 25, 50, 100, 125, 200, 250, 400, 500, 625, + * ------------------------------------------------------- + * #10 #11 #12 #13 #14 #15 #16 #17 #18 + * 800, 1000, 1250, 2000, 2500, 4000, 5000, 10000, 20000 + * ------------------------------------------------------- + * + * The real value set by gpioPWM is (dutycycle * real range) / range. + * + * Example + * gpioSetPWMrange(24, 2000); // Now 2000 is fully on + * // 1000 is half on + * // 500 is quarter on, etc. + * @param pin user_gpio: 0-31 + * @param range range: 25-40000 + * @return Returns the real range for the given GPIO's frequency if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetPWMrange" + */ + @Override + public int gpioSetPWMrange(int pin, int range) throws IOException { + logger.trace("[PWM-RANGE::SET] -> PIN: {}; RANGE={}", pin, range); + validateUserPin(pin); + //validateDutyCycleRange(range); + PiGpioPacket result = sendCommand(PRS, pin, range); + var readRange = result.result(); + logger.trace("[PWM-RANGE::SET] <- PIN: {}; REAL-RANGE={}; SUCCESS={}", pin, readRange, result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_DUTYRANGE. + return result.result(); + } + + /** + * Returns the duty-cycle range used for the GPIO if OK. + * If a hardware clock or hardware PWM is active on the GPIO the reported range will be 1000000 (1M). + * + * @param pin user_gpio: 0-31 + * @return duty-cycle range + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMrange" + */ + @Override + public int gpioGetPWMrange(int pin) throws IOException { + logger.trace("[PWM-RANGE::GET] -> PIN: {}", pin); + validateUserPin(pin); + PiGpioPacket result = sendCommand(PRG, pin); + var range = result.result(); + logger.trace("[PWM-RANGE::GET] <- PIN: {}; RANGE={}; SUCCESS={}", pin, range, result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_DUTYRANGE. + return range; + } + + /** + * Returns the real range used for the GPIO if OK. + * If a hardware clock is active on the GPIO the reported real range will be 1000000 (1M). + * If hardware PWM is active on the GPIO the reported real range will be approximately 250M + * divided by the set PWM frequency. + * + * @param pin user_gpio: 0-31 + * @return real range used for the GPIO if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMrealRange" + */ + @Override + public int gpioGetPWMrealRange(int pin) throws IOException { + logger.trace("[PWM-REAL-RANGE::GET] -> PIN: {}", pin); + validateUserPin(pin); + PiGpioPacket result = sendCommand(PRRG, pin); + var range = result.result(); + logger.trace("[PWM-REAL-RANGE::GET] <- PIN: {}; RANGE={}; SUCCESS={}", pin, range, result.success()); + validateResult(result); // Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_DUTYRANGE. + return range; + } + + /** + * Sets the frequency in hertz to be used for the GPIO. + * + * If PWM is currently active on the GPIO it will be switched off and then back on at the new frequency. + * Each GPIO can be independently set to one of 18 different PWM frequencies. + * The selectable frequencies depend upon the sample rate which may be 1, 2, 4, 5, 8, or 10 microseconds (default 5). + * + * The frequencies for each sample rate are: + * + * Hertz + * + * 1: 40000 20000 10000 8000 5000 4000 2500 2000 1600 + * 1250 1000 800 500 400 250 200 100 50 + * + * 2: 20000 10000 5000 4000 2500 2000 1250 1000 800 + * 625 500 400 250 200 125 100 50 25 + * + * 4: 10000 5000 2500 2000 1250 1000 625 500 400 + * 313 250 200 125 100 63 50 25 13 + * sample + * rate + * (us) 5: 8000 4000 2000 1600 1000 800 500 400 320 + * 250 200 160 100 80 50 40 20 10 + * + * 8: 5000 2500 1250 1000 625 500 313 250 200 + * 156 125 100 63 50 31 25 13 6 + * + * 10: 4000 2000 1000 800 500 400 250 200 160 + * 125 100 80 50 40 25 20 10 5 + * + * + * Example: + * gpioSetPWMfrequency(23, 0); // Set GPIO23 to lowest frequency. + * gpioSetPWMfrequency(24, 500); // Set GPIO24 to 500Hz. + * gpioSetPWMfrequency(25, 100000); // Set GPIO25 to highest frequency. + * + * @param pin user_gpio: 0-31 + * @param frequency frequency: >=0 + * @return Returns the numerically closest frequency if OK + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioSetPWMrange" + */ + @Override + public int gpioSetPWMfrequency(int pin, int frequency) throws IOException { + logger.trace("[PWM-FREQ::SET] -> PIN: {}; FREQUENCY={}", pin, frequency); + validateUserPin(pin); + // validateFrequency(frequency); TODO :: IMPLEMENT 'validateFrequency()' + PiGpioPacket result = sendCommand(PFS, pin, frequency); + var actualRange = result.result(); + logger.trace("[PWM-FREQ::SET] <- PIN: {}; FREQUENCY={}; SUCCESS={}", pin, frequency, result.success()); + validateResult(result); // Returns the numerically closest frequency if OK, otherwise PI_BAD_USER_GPIO. + return actualRange; + } + + /** + * Returns the frequency (in hertz) used for the GPIO + * + * For normal PWM the frequency will be that defined for the GPIO by gpioSetPWMfrequency. + * If a hardware clock is active on the GPIO the reported frequency will be that set by gpioHardwareClock. + * If hardware PWM is active on the GPIO the reported frequency will be that set by gpioHardwarePWM. + * + * Example: + * f = gpioGetPWMfrequency(23); // Get frequency used for GPIO23. + * + * @param pin user_gpio: 0-31 + * @return Returns the frequency (in hertz) used for the GPIO if OK. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioGetPWMfrequency" + */ + @Override + public int gpioGetPWMfrequency(int pin) throws IOException { + logger.trace("[PWM-FREQ::GET] -> PIN: {}", pin); + validateUserPin(pin); + PiGpioPacket result = sendCommand(PFG, pin); + var frequency = result.result(); + logger.trace("[PWM-FREQ::GET] <- PIN: {}; FREQUENCY={}; SUCCESS={}", pin, frequency, result.success()); + validateResult(result); // Returns the frequency (in hertz) used for the GPIO if OK, otherwise PI_BAD_USER_GPIO. + return frequency; + } + + /** + * Starts hardware PWM on a GPIO at the specified frequency and duty-cycle. + * Frequencies above 30MHz are unlikely to work. + * + * NOTE: Any waveform started by gpioWaveTxSend, or gpioWaveChain will be cancelled. + * + * This function is only valid if the pigpio main clock is PCM. + * The main clock defaults to PCM but may be overridden by a call to gpioCfgClock. + * + * The same PWM channel is available on multiple GPIO. The latest frequency and duty-cycle + * setting will be used by all GPIO which share a PWM channel. + * + * The GPIO must be one of the following. + * + * 12 PWM channel 0 All models but A and B + * 13 PWM channel 1 All models but A and B + * 18 PWM channel 0 All models + * 19 PWM channel 1 All models but A and B + * + * 40 PWM channel 0 Compute module only + * 41 PWM channel 1 Compute module only + * 45 PWM channel 1 Compute module only + * 52 PWM channel 0 Compute module only + * 53 PWM channel 1 Compute module only + * + * + * The actual number of steps between off and fully on is the integral part of + * 250M/PWMfreq (375M/PWMfreq for the BCM2711). + * The actual frequency set is 250M/steps (375M/steps for the BCM2711). + * There will only be a million steps for a frequency of 250 (375 for the BCM2711). Lower + * frequencies will have more steps and higher frequencies will have fewer steps. + * dutyCycle is automatically scaled to take this into account. + * + * @param pin a supported hardware PWM pin + * @param frequency 0 (off) or 1-125M (1-187.5M for the BCM2711) + * @param dutyCycle 0 (off) to 1000000 (1M)(fully on) + * @return Returns 0 if OK, otherwise PI_BAD_GPIO, PI_NOT_HPWM_GPIO, PI_BAD_HPWM_DUTY, PI_BAD_HPWM_FREQ, or PI_HPWM_ILLEGAL. + */ + @Override + public void gpioHardwarePWM(int pin, int frequency, int dutyCycle) throws IOException { + logger.trace("[HW-PWM::SET] -> PIN: {}; FREQUENCY={}; DUTY-CYCLE={}", pin, frequency, dutyCycle); + validateUserPin(pin); + // validateHwPwmFrequency(frequency); TODO :: IMPLEMENT 'validateHwPwmFrequency()' + PiGpioPacket tx = new PiGpioPacket(HP, pin, frequency).data(dutyCycle); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[HW-PWM::SET] <- PIN: {}; SUCCESS={}", pin, rx.success()); + validateResult(rx); // Returns the numerically closest frequency if OK, otherwise PI_BAD_USER_GPIO. + } + + /** + * Delays for at least the number of microseconds specified by micros. + * (Delays of 100 microseconds or less use busy waits.) + * + * @param micros micros: the number of microseconds to sleep + * @return Returns the actual length of the delay in microseconds. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioDelay" + */ + @Override + public int gpioDelay(int micros) throws IOException { + logger.trace("[DELAY] -> MICROS: {}", micros); + validateDelayMicroseconds(micros); + PiGpioPacket result = sendCommand(MICS, (int)micros); + logger.trace("[DELAY] <- MICROS: {}; SUCCESS={}", micros, result.success()); + validateResult(result); // Upon success nothing is returned. On error a negative status code will be returned. + return micros; + } + + /** + * Delays for at least the number of milliseconds specified by micros. (between 1 and 60000 <1 minute>) + * + * @param millis millis: the number of milliseconds to sleep (between 1 and 60000 <1 minute>) + * @return Returns the actual length of the delay in milliseconds. + * @see "http://abyz.me.uk/rpi/pigpio/pigs.html#MILS" + */ + @Override + public int gpioDelayMilliseconds(int millis) throws IOException{ + logger.trace("[DELAY] -> MILLIS: {}", millis); + validateDelayMilliseconds(millis); + PiGpioPacket result = sendCommand(MILS, (int)millis); + logger.trace("[DELAY] <- MILLIS: {}; SUCCESS={}", millis, result.success()); + validateResult(result); // Upon success nothing is returned. On error a negative status code will be returned. + return millis; + } + + /** + * Returns the current system tick. + * Tick is the number of microseconds since system boot. + * + * As tick is an unsigned 32 bit quantity it wraps around after 2^32 microseconds, which is + * approximately 1 hour 12 minutes. You don't need to worry about the wrap around as long as you + * take a tick (uint32_t) from another tick, i.e. the following code will always provide the + * correct difference. + * + * Example + * uint32_t startTick, endTick; + * int diffTick; + * startTick = gpioTick(); + * + * // do some processing + * endTick = gpioTick(); + * diffTick = endTick - startTick; + * printf("some processing took %d microseconds", diffTick); + * + * @return Returns the current system tick. + * @see "http://abyz.me.uk/rpi/pigpio/cif.html#gpioTick" + */ + @Override + public long gpioTick() throws IOException { + logger.trace("[TICK::GET] -> Get current tick"); + PiGpioPacket tx = new PiGpioPacket(TICK); + PiGpioPacket rx = sendPacket(tx); + long tick = Integer.toUnsignedLong(rx.result()); // convert (UInt32) 32-bit unsigned value to long + logger.trace("[TICK::GET] <- TICK: {}; SUCCESS={}", tick, rx.success()); + return tick; + } + + @Override + public int i2cOpen(int bus, int device) throws IOException { + logger.trace("[I2C::OPEN] -> Open I2C Bus [{}] and Device [{}]", bus, device); + validateI2cBus(bus); + validateI2cDeviceAddress(device); + PiGpioPacket tx = new PiGpioPacket(I2CO, bus, device); + PiGpioPacket rx = sendPacket(tx); + int handle = rx.result(); + logger.trace("[I2C::OPEN] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + return handle; + } + + @Override + public void i2cClose(int handle) throws IOException { + logger.trace("[I2C::CLOSE] -> HANDLE={}, Close I2C Bus", handle); + validateI2cHandle(handle); + PiGpioPacket tx = new PiGpioPacket(I2CC, handle); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::CLOSE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public void i2cWriteQuick(int handle, boolean bit) throws IOException { + logger.trace("[I2C::WRITE] -> HANDLE={}; R/W Bit [{}]", handle, bit ? 1 : 0); + validateI2cHandle(handle); + PiGpioPacket tx = new PiGpioPacket(I2CWQ, handle, bit ? 1 : 0); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public void i2cWriteByte(int handle, byte value) throws IOException { + logger.trace("[I2C::WRITE] -> HANDLE={}; Byte [{}]", handle, Byte.toUnsignedInt(value)); + validateI2cHandle(handle); + PiGpioPacket tx = new PiGpioPacket(I2CWS, handle, Byte.toUnsignedInt(value)); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public int i2cReadByte(int handle) throws IOException { + logger.trace("[I2C::READ] -> [{}]; Byte", handle); + validateI2cHandle(handle); + PiGpioPacket tx = new PiGpioPacket(I2CRS, handle); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::READ] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, false); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.result(); + } + + @Override + public void i2cWriteByteData(int handle, int register, byte value) throws IOException { + logger.trace("[I2C::WRITE] -> [{}]; Register [{}]; Byte [{}]", handle ,register, Byte.toUnsignedInt(value)); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CWB, handle, register).data(Byte.toUnsignedInt(value)); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public void i2cWriteWordData(int handle, int register, int value) throws IOException { + logger.trace("[I2C::WRITE] -> [{}]; Register [{}]; Word [{}]", handle ,register, value); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CWW, handle, register).data(value); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public int i2cReadByteData(int handle, int register) throws IOException { + logger.trace("[I2C::READ] -> [{}]; Register [{}]; Byte", handle ,register); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CRB, handle, register); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::READ] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, false); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.result(); + } + + @Override + public int i2cReadWordData(int handle, int register) throws IOException { + logger.trace("[I2C::READ] -> [{}]; Register [{}]; Word", handle ,register); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CRW, handle, register); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::READ] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, false); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.result(); + } + + @Override + public int i2cProcessCall(int handle, int register, int value) throws IOException { + logger.trace("[I2C::W/R] -> [{}]; Register [{}]; Word [{}]", handle ,register, value); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CPC, handle, register).data(value); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::W/R] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, false); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.result(); + } + + @Override + public void i2cWriteBlockData(int handle, int register, byte[] data) throws IOException { + logger.trace("[I2C::WRITE] -> [{}]; Register [{}]; Block [{} bytes]", handle ,register, data.length); + validateI2cHandle(handle); + validateI2cRegister(register); + validateI2cBlockLength(data.length); + PiGpioPacket tx = new PiGpioPacket(I2CWK, handle, register, data); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public byte[] i2cReadBlockData(int handle, int register) throws IOException { + logger.trace("[I2C::READ] -> [{}]; Register [{}]; Block", handle ,register); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CRK, handle, register); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::READ] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, true); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.data(); + } + @Override + public byte[] i2cBlockProcessCall(int handle, int register, byte[] data) throws IOException { + logger.trace("[I2C::W/R] -> [{}]; Register [{}]; Block [{} bytes]", handle ,register, data.length); + validateI2cHandle(handle); + validateI2cRegister(register); + validateI2cBlockLength(data.length); + PiGpioPacket tx = new PiGpioPacket(I2CPK, handle, register, data); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::W/R] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, true); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.data(); + } + + @Override + public byte[] i2cReadI2CBlockData(int handle, int register, int length) throws IOException { + logger.trace("[I2C::READ] -> [{}]; Register [{}]; I2C Block [{} bytes]", handle ,register, length); + validateI2cHandle(handle); + validateI2cRegister(register); + PiGpioPacket tx = new PiGpioPacket(I2CRI, handle, register).data(length); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::READ] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, true); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.data(); + } + + @Override + public void i2cWriteI2CBlockData(int handle, int register, byte[] data) throws IOException { + logger.trace("[I2C::WRITE] -> [{}]; Register [{}]; I2C Block [{} bytes]", handle ,register, data.length); + validateI2cHandle(handle); + validateI2cRegister(register); + validateI2cBlockLength(data.length); + PiGpioPacket tx = new PiGpioPacket(I2CWI, handle, register, data); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + } + + @Override + public byte[] i2cReadDevice(int handle, int length) throws IOException { + logger.trace("[I2C::READ] -> [{}]; I2C Raw Read [{} bytes]", handle, length); + validateI2cHandle(handle); + PiGpioPacket tx = new PiGpioPacket(I2CRD, handle, length); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::READ] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx, false); // Upon success nothing is returned. On error a negative status code will be returned. + return rx.data(); + } + + @Override + public int i2cWriteDevice(int handle, byte[] data) throws IOException { + logger.trace("[I2C::WRITE] -> [{}]; I2C Raw Write [{} bytes]", handle, data.length); + validateI2cHandle(handle); + //validateI2cDataLength(data.length); + PiGpioPacket tx = new PiGpioPacket(I2CWD, handle, 0, data); + PiGpioPacket rx = sendPacket(tx); + logger.trace("[I2C::WRITE] <- HANDLE={}; SUCCESS={}", handle, rx.success()); + validateResult(rx); // Upon success nothing is returned. On error a negative status code will be returned. + return -1; + } +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/util/StringUtil.java b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/util/StringUtil.java new file mode 100644 index 000000000..2aa30b587 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/com/pi4j/library/pigpio/util/StringUtil.java @@ -0,0 +1,310 @@ +package com.pi4j.library.pigpio.util; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : StringUtil.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + + +import java.nio.ByteBuffer; +import java.util.Arrays; + +public class StringUtil { + + public static final String EMPTY = ""; + public static final char DEFAULT_PAD_CHAR = ' '; + + public static boolean isNullOrEmpty(String data, boolean trim){ + if(data == null) + return true; + + // trim if requested + String test = data; + if(trim) + test = data.trim(); + + return (test.length() <= 0); + } + + public static boolean isNullOrEmpty(String data){ + return isNullOrEmpty(data, false); + } + + public static boolean isNotNullOrEmpty(String data){ + return isNotNullOrEmpty(data, false); + } + + public static boolean isNotNullOrEmpty(String data, boolean trim){ + return !(isNullOrEmpty(data, trim)); + } + + public static String setIfNullOrEmpty(String data, String replacement, boolean trim){ + if(isNullOrEmpty(data, trim)) { + return replacement; + } + return data; + } + + public static String setIfNullOrEmpty(String data, String replacement){ + return setIfNullOrEmpty(data, replacement, false); + } + + public static boolean contains(String source, String target) { + return (null != source && null != target && source.contains(target)); + } + + public static boolean contains(String source, String[] targets) { + if (null != source && null != targets) { + for(var target : targets) { + if (source.contains(target)) { + return true; + } + } + } + return false; + } + + public static boolean contains(String[] sources, String target) { + if (null != sources && null != target) { + for (var source : sources) { + if(contains(source, target)) + return true; + } + } + return false; + } + + public static boolean contains(String[] sources, String[] targets) { + if (null != sources && null != targets) { + for (var source : sources) { + if(contains(source, targets)) + return true; + } + } + return false; + } + + public static String create(int length) { + return create(DEFAULT_PAD_CHAR, length); + } + + public static String create(char c, int length) { + StringBuilder sb = new StringBuilder(length); + for(var index = 0; index < length; index++) + sb.append(c); + return sb.toString(); + } + + public static String create(String s, int length) { + StringBuilder sb = new StringBuilder(length * s.length()); + for(var index = 0; index < length; index++) + sb.append(s); + return sb.toString(); + } + + public static String repeat(char c, int length) { + return create(c, length); + } + + public static String repeat(String s, int length) { + return create(s, length); + } + + public static String padLeft(String data, int length) { + return padLeft(data, DEFAULT_PAD_CHAR, length); + } + + public static String padLeft(String data, char pad, int length) { + var sb = new StringBuilder(data.length() + length); + for(var index = 0; index < length; index++) + sb.append(pad); + sb.append(data); + return sb.toString(); + } + + public static String padLeft(String data, String pad, int length) { + var sb = new StringBuilder(data.length() + (length * pad.length())); + for(var index = 0; index < length; index++) + sb.append(pad); + sb.append(data); + return sb.toString(); + } + + public static String padRight(String data, int length) { + return padRight(data, DEFAULT_PAD_CHAR, length); + } + + public static String padRight(String data, char pad, int length) { + var sb = new StringBuilder(data.length() + length); + sb.append(data); + for(var index = 0; index < length; index++) + sb.append(pad); + return sb.toString(); + } + + public static String padRight(String data, String pad, int length) { + var sb = new StringBuilder(data.length() + (length * pad.length())); + sb.append(data); + for(var index = 0; index < length; index++) + sb.append(pad); + return sb.toString(); + } + + public static String pad(String data, int length) { + return pad(data, DEFAULT_PAD_CHAR, length); + } + + public static String pad(String data, char pad, int length) { + return create(pad, length) + data + create(pad, length); + } + + public static String pad(String data, String pad, int length) { + return create(pad, length) + data + create(pad, length); + } + + public static String padCenter(String data, int length) { + return padCenter(data, DEFAULT_PAD_CHAR, length); + } + + public static String padCenter(String data, char pad, int length) { + if(data.length() < length) { + int needed = length - data.length(); + int padNeeded = needed / 2; + StringBuilder result = new StringBuilder(); + result.append(create(pad, padNeeded)); + result.append(data); + result.append(create(pad, padNeeded)); + int remaining = length - result.length(); + result.append(create(pad, remaining)); + return result.toString(); + } + return data; + } + + public static String trimLeft(String data) { + return trimLeft(data, DEFAULT_PAD_CHAR); + } + + public static String trimLeft(String data, char trim) { + for(var index = 0; index < data.length(); index++) + if(!(data.charAt(index) == trim)) + return data.substring(index); + return EMPTY; + } + + public static String trimRight(String data) { + return trimRight(data, DEFAULT_PAD_CHAR); + } + + public static String trimRight(String data, char trim) { + int count = 0; + for(var index = data.length(); index > 0; index--) + if(data.charAt(index-1) == trim) + count++; + else + return data.substring(0, data.length() - count); + return EMPTY; + } + + public static String trim(String data) { + return trim(data, DEFAULT_PAD_CHAR); + } + + public static String trim(String data, char trim) { + var result = trimLeft(data, trim); + return trimRight(result, trim); + } + + public static String center(String text, int length){ + var out = String.format("%"+length+"s%s%"+length+"s", "",text,""); + var mid = (out.length()/2); + var start = mid - (length/2); + var end = start + length; + return out.substring((int) start, (int) end); + } + + public static String concat(String ... data) { + var sb = new StringBuilder(); + for(var d : data){ + sb.append(d); + } + return sb.toString(); + } + + public static void appendHexString(StringBuilder builder, byte byt){ + builder.append(String.format("%02X", byt)); + } + public static String toHexString(byte byt){ + return String.format("%02X", byt); + } + + public static void appendHexString(StringBuilder builder, int byt){ + builder.append(String.format("%02X", (byte)byt)); + } + public static String toHexString(int byt){ + return String.format("%02X", (byte)byt); + } + + public static void appendHexString(StringBuilder builder, byte[] bytes){ + for (byte b : bytes) { + builder.append(String.format("%02X ", b)); + } + } + public static String toHexString(byte[] bytes){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, bytes); + return sb.toString().trim(); + } + + public static void appendHexString(StringBuilder builder, ByteBuffer buffer){ + appendHexString(builder, buffer.array()); + } + public static String toHexString(ByteBuffer buffer){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, buffer); + return sb.toString().trim(); + } + + public static void appendHexString(StringBuilder builder, byte[] bytes, int offset, int length){ + appendHexString(builder, Arrays.copyOfRange(bytes, offset, length)); + } + public static String toHexString(byte[] bytes, int offset, int length){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, bytes, offset, length); + return sb.toString().trim(); + } + + public static void appendHexString(StringBuilder builder, ByteBuffer buffer, int offset, int length){ + appendHexString(builder, buffer.array(), offset, length); + } + public static String toHexString(ByteBuffer buffer, int offset, int length){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, buffer, offset, length); + return sb.toString().trim(); + } + +} diff --git a/libraries/pi4j-library-pigpio/src/main/java/module-info.java b/libraries/pi4j-library-pigpio/src/main/java/module-info.java new file mode 100644 index 000000000..cd32cebaa --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/main/java/module-info.java @@ -0,0 +1,36 @@ +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : module-info.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +module pi4j.library.pigpio { + + // SLF4J + requires slf4j.api; + + // EXPORTS + exports com.pi4j.library.pigpio; +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/Main.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/Main.java new file mode 100644 index 000000000..dede20f2e --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/Main.java @@ -0,0 +1,168 @@ +package com.pi4j.library.pigpio; +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : Main.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.io.IOException; + +public class Main { + + + public static void main(String[] args) throws Exception { + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + + PiGpio pig = PiGpio.newSocketInstance("rpi3bp"); //"10.1.2.222" + + pig.initialize(); + + // set pin ALT0 modes for I2C BUS<1> usage on RPI3B + pig.gpioSetMode(2, PiGpioMode.ALT0); + pig.gpioSetMode(3, PiGpioMode.ALT0); + + + for(int x = 0; x < 100; x++) { + try { + pig.i2cClose(x); + } + catch (IOException e){ + continue; + } + } + + int handle = pig.i2cOpen(1, 0x04); + + + //pig.initialize(); + //pig.gpioSetPWMfrequency(4, 5000); + pig.gpioHardwarePWM(13, 100, 500000); + + //var frequency = pig.gpioGetPWMfrequency(4); + //System.out.println("FREQUENCY: " + frequency); + + //System.in.read(); + +// pig.gpioHardwarePWM(18, 0,0); + + + // SINGLE RAW BYTE +// pig.i2cWriteByte(handle, (byte)0xD); +// byte b = pig.i2cReadByte(handle); +// System.out.println("[BYTE]" + Byte.toUnsignedInt(b)); + + // SINGLE WORD + //pig.i2cWriteWordData(handle, 2, 1024); +// for(int x = 0; x < 50; x++) { +// int word = pig.i2cReadWordData(handle, 0x00); +// System.out.println("[WORD]" + word); +// Thread.sleep(50); +// } + + // DATA BLOCK + //pig.i2cWriteBlockData(handle, 2, "Hello World!"); + //byte[] data = pig.i2cReadBlockData(2 , 0x02); + //byte[] data = pig.i2cBlockProcessCall(handle, 2, "Hello World!"); + //System.out.println("[BLOCK]" + Arrays.toString(data)); + +// pig.i2cWriteI2CBlockData(handle, 2, "Hello World!"); +// byte[] rx = pig.i2cReadI2CBlockData(handle, 2, 32); +// System.out.println("[BLOCK] <" + rx.length + "> " + Arrays.toString(rx)); +// + + int max = 32; + byte[] data = new byte[max]; + for(int i = 0; i < max; i++) { + data[i] = (byte)i; + } + + pig.i2cWriteDevice(handle, data); + + //Thread.sleep(200); + //byte rx[] = pig.i2cReadDevice(handle, data.length); + + //System.out.println("[RAW-READ] <" + rx.length + "> " + rx); + + +// // RAW I2C READ/WRITE +// for(int i = 0; i <= 100; i++) { +// String data = "Hello World! " + i; +// pig.i2cWriteDevice(handle, data); +// //Thread.sleep(200); +// String rx = pig.i2cReadDeviceToString(handle, data.length()); +// System.out.println("[RAW-READ] <" + rx.length() + "> " + rx); +// } + +// +// pig.gpioWrite(4, PiGpioState.LOW); + + // CLOSE + pig.i2cClose(handle); + + + +// pig.gpioSetMode(4, PiGpioMode.OUTPUT); +// +// for(int x = 0; x < 100; x++) { +// pig.gpioWrite(4, PiGpioState.LOW); +// //pig.gpioWrite(3, PiGpioState.LOW); +// pig.gpioWrite(4, PiGpioState.HIGH); +// //pig.gpioWrite(3, PiGpioState.HIGH); +// } + +// pig.gpioPWM(2, 50); +// Thread.sleep(1000);; +// pig.gpioPWM(2, 0); + + +// pig.gpioSetMode(4, PiGpioMode.INPUT); +// pig.gpioSetPullUpDown(4, PiGpioPud.DOWN); +// +// for(int x = 0; x < 100000; x++) { +// var value = pig.gpioRead(4); +// System.out.println(value); +// } +// + + +// pig.initialize(); +// +// System.out.println("---------------------------------------------------------------------"); +// System.out.println("Raspberry Pi - Hardware Revision : " + pig.gpioHardwareRevisionString()); +// System.out.println("Raspberry Pi - PIGPIO Lib Version : " + pig.gpioVersion()); +// System.out.println("---------------------------------------------------------------------"); +// +// pig.gpioSetMode(28, PiGpioMode.OUTPUT); +// pig.gpioWrite(28, PiGpioState.HIGH); +// +// for (int p = 0; p <= 31; p++) { +// System.out.println(" GPIO " + p + "; MODE=" + pig.gpioGetMode(p) + "; STATE=" + pig.gpioRead(p)); +// } + +// pig.gpioTick(); + + } +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/gpio/TestDigitalInputsUsingTestHarness.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/gpio/TestDigitalInputsUsingTestHarness.java new file mode 100644 index 000000000..6be0802c1 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/gpio/TestDigitalInputsUsingTestHarness.java @@ -0,0 +1,271 @@ +package com.pi4j.library.pigpio.test.gpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : TestDigitalInputsUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.library.pigpio.PiGpioPud; +import com.pi4j.library.pigpio.PiGpioState; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPin; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +@DisplayName("PIGPIO Library :: Test Digital Input Pins") +public class TestDigitalInputsUsingTestHarness { + + private static PiGpio pigpio; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestDigitalInputsUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + pigpio = PiGpio.newSocketInstance(System.getProperty("pi4j.pigpio.host", "rpi3bp.savage.lan"), + System.getProperty("pi4j.pigpio.port", "8888")); + + // initialize test harness and PIGPIO instances + pigpio.initialize(); + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestDigitalInputsUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // terminate test harness and PIGPIO instances + pigpio.terminate(); + harness.terminate(); + } + + @Test + @DisplayName("GPIO :: Test Digital Input Pins") + public void testGpioDigitalInputs() throws IOException { + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // iterate over pins and perform test on each + // TODO :: IMPLEMENT CORRECT SET OF TEST PINS + for(int pin = 2; pin <= 19; pin++){ + testDigitalInputPin(pin); + } + } + + @Test + @DisplayName("GPIO :: Test Pull-Up/Down on Digital Pins") + public void testGpioDigitalPullUpDown() throws IOException { + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // iterate over pins and perform test on each + // TODO :: IMPLEMENT CORRECT SET OF TEST PINS + for(int pin = 2; pin <= 19; pin++){ + + // the following inputs are skipped because they always fail; possible + // because they are tied to other things that override the pull-up/down + if(pin == 5) continue; + if(pin == 6) continue; + if(pin == 9) continue; + if(pin == 10) continue; + if(pin == 11) continue; + if(pin == 12) continue; + if(pin == 13) continue; + if(pin == 16) continue; + if(pin == 18) continue; + if(pin == 19) continue; + testDigitalInputPullUpDown(pin); + } + } + + public void testDigitalInputPin(int pin) throws IOException{ + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST SOC DIGITAL INPUT PIN [" + pin + "]"); + System.out.println("----------------------------------------"); + + // configure pin as an output pin on the test harness + TestHarnessPin p = harness.setOutputPin(pin, false); + + // configure input pin on test SoC (RaspberryPi) + pigpio.gpioSetMode(pin, PiGpioMode.INPUT); + + // do not use internal pull down resistors; + // these seem to overpower the HIGH signal from the Arduino + pigpio.gpioSetPullUpDown(pin, PiGpioPud.OFF); + + // get input pin state from SoC (RaspberryPi) + PiGpioState state = pigpio.gpioRead(pin); + + System.out.println(); + System.out.println("TEST INPUT FOR [LOW] STATE "); + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + state); + Assert.assertEquals("INCORRECT PIN VALUE", p.value, state.value()); + + System.out.println(); + System.out.println("TEST INPUT FOR [HIGH] STATE "); + p = harness.setOutputPin(pin, true); // set output pin state on the test harness + state = pigpio.gpioRead(pin); // get input pin state from SoC (RaspberryPi) + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + state); + Assert.assertEquals("INCORRECT PIN VALUE", p.value, state.value()); + + System.out.println(); + System.out.println("TEST INPUT FOR [LOW] STATE "); + p = harness.setOutputPin(pin, false); // set output pin state on the test harness + state = pigpio.gpioRead(pin); // get input pin state from SoC (RaspberryPi) + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + state); + Assert.assertEquals("INCORRECT PIN VALUE", p.value, state.value()); + + // now test the input pins using the PULL UP resistor on the SoC + pigpio.gpioSetPullUpDown(pin, PiGpioPud.UP); + + System.out.println(); + System.out.println("TEST INPUT FOR [HIGH] STATE "); + p = harness.setOutputPin(pin, true); // set output pin state on the test harness + state = pigpio.gpioRead(pin); // get input pin state from SoC (RaspberryPi) + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + state); + Assert.assertEquals("INCORRECT PIN VALUE", p.value, state.value()); + + System.out.println(); + System.out.println("TEST INPUT FOR [LOW] STATE "); + p = harness.setOutputPin(pin, false); // set output pin state on the test harness + state = pigpio.gpioRead(pin); // get input pin state from SoC (RaspberryPi) + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + state); + Assert.assertEquals("INCORRECT PIN VALUE", p.value, state.value()); + + // disable test pin on the test harness + p = harness.disablePin(pin); + System.out.println(); + System.out.println("DISABLE TEST PIN [" + p.pin + "] ON TEST HARNESS <" + p.access + ">"); + } + + public void testDigitalInputPullUpDown(int pin) throws IOException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST SOC DIGITAL INPUT PULL on PIN [" + pin + "]"); + System.out.println("----------------------------------------"); + + // configure pin as an input pin on the test harness + harness.setInputPin(pin, true); + + // configure input pin on test SoC (RaspberryPi) + pigpio.gpioSetMode(pin, PiGpioMode.INPUT); + + + PiGpioPud pud = PiGpioPud.DOWN; + pigpio.gpioSetPullUpDown(pin, pud); // set input pin PUD on Soc (RaspberryPi) + pigpio.gpioDelayMilliseconds(100); + TestHarnessPin p = harness.getPin(pin); // get input state from the test harness + System.out.println(); + System.out.println("TEST PULL FOR [DOWN] STATE"); + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] PULL = " + pud.name()); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + p.value); + Assert.assertEquals("INCORRECT PIN PULL", p.value, 0); + + pud = PiGpioPud.UP; + pigpio.gpioSetPullUpDown(pin, pud); // set input pin PUD on Soc (RaspberryPi) + p = harness.getPin(pin); // get input state from the test harness + System.out.println(); + System.out.println("TEST PULL FOR [DOWN] STATE"); + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] PULL = " + pud.name()); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + p.value); + Assert.assertEquals("INCORRECT PIN PULL", p.value, 1); + + pud = PiGpioPud.DOWN; + pigpio.gpioSetPullUpDown(pin, pud); // set input pin PUD on Soc (RaspberryPi) + pigpio.gpioDelayMilliseconds(100); + p = harness.getPin(pin); // get input state from the test harness + System.out.println(); + System.out.println("TEST PULL FOR [DOWN] STATE"); + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] PULL = " + pud.name()); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + p.value); + Assert.assertEquals("INCORRECT PIN PULL", p.value, 0); + + pud = PiGpioPud.UP; + pigpio.gpioSetPullUpDown(pin, pud); // set input pin PUD on Soc (RaspberryPi) + p = harness.getPin(pin); // get input state from the test harness + System.out.println(); + System.out.println("TEST PULL FOR [DOWN] STATE"); + System.out.println(" (SET) >> TEST PIN [" + p.pin + "] PULL = " + pud.name()); + System.out.println(" (READ) << SOC PIN [" + pin + "] VALUE = " + p.value); + Assert.assertEquals("INCORRECT PIN PULL", p.value, 1); + + // disable test pin on the test harness + p = harness.disablePin(pin); + System.out.println(); + System.out.println("DISABLE TEST PIN [" + p.pin + "] ON TEST HARNESS <" + p.access + ">"); + } +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/gpio/TestDigitalOutputsUsingTestHarness.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/gpio/TestDigitalOutputsUsingTestHarness.java new file mode 100644 index 000000000..08a0b130c --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/gpio/TestDigitalOutputsUsingTestHarness.java @@ -0,0 +1,185 @@ +package com.pi4j.library.pigpio.test.gpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : TestDigitalOutputsUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.library.pigpio.PiGpioPud; +import com.pi4j.library.pigpio.PiGpioState; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPin; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +@DisplayName("PIGPIO Library :: Test Digital Ouput Pins") +public class TestDigitalOutputsUsingTestHarness { + + private static PiGpio pigpio; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestDigitalOutputsUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + pigpio = PiGpio.newSocketInstance(System.getProperty("pi4j.pigpio.host", "rpi3bp.savage.lan"), + System.getProperty("pi4j.pigpio.port", "8888")); + + // initialize test harness and PIGPIO instances + pigpio.initialize(); + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestDigitalOutputsUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // terminate test harness and PIGPIO instances + pigpio.terminate(); + harness.terminate(); + } + + @Test + @DisplayName("GPIO :: Test Digital Output Pins") + public void testGpioDigitalOutputs() throws IOException { + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // iterate over pins and perform test on each + // TODO :: IMPLEMENT CORRECT SET OF TEST PINS + for(int pin = 2; pin <= 12; pin++){ + testDigitalOutputPin(pin); + } + } + + public void testDigitalOutputPin(int pin) throws IOException{ + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST SOC DIGITAL OUTPUT PIN [" + pin + "]"); + System.out.println("----------------------------------------"); + + // configure test pin as an input pin on the test harness; + // use the internal pull-up resistor on the Arduino hardware + harness.setInputPin(pin, true); + + // configure output pin on test SoC (RaspberryPi) + pigpio.gpioSetMode(pin, PiGpioMode.OUTPUT); + + System.out.println(); + PiGpioState state = PiGpioState.LOW; + System.out.println("TEST OUTPUT FOR [" + state.name() + "] STATE"); + pigpio.gpioWrite(pin, state); // set output pin state on SoC (RaspberryPi) + TestHarnessPin p = harness.getPin(pin); // get input pin state from the test harness + System.out.println(" (SET) >> SOC PIN [" + pin + "] VALUE = " + state); + System.out.println(" (READ) << TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + Assert.assertEquals("INCORRECT PIN VALUE", state.value(), p.value); + + System.out.println(); + state = PiGpioState.HIGH; + System.out.println("TEST OUTPUT FOR [" + state.name() + "] STATE"); + pigpio.gpioWrite(pin, state); // set output pin state on SoC (RaspberryPi) + p = harness.getPin(pin); // get input pin state from the test harness + System.out.println(" (SET) >> SOC PIN [" + pin + "] VALUE = " + state); + System.out.println(" (READ) << TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + Assert.assertEquals("INCORRECT PIN VALUE", state.value(), p.value); + + System.out.println(); + state = PiGpioState.LOW; + System.out.println("TEST OUTPUT FOR [" + state.name() + "] STATE"); + pigpio.gpioWrite(pin, state); // set output pin state on SoC (RaspberryPi) + p = harness.getPin(pin); // get input pin state from the test harness + System.out.println(" (SET) >> SOC PIN [" + pin + "] VALUE = " + state); + System.out.println(" (READ) << TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + Assert.assertEquals("INCORRECT PIN VALUE", state.value(), p.value); + + System.out.println(); + state = PiGpioState.HIGH; + System.out.println("TEST OUTPUT FOR [" + state.name() + "] STATE"); + pigpio.gpioWrite(pin, state); // set output pin state on SoC (RaspberryPi) + p = harness.getPin(pin); // get input pin state from the test harness + System.out.println(" (SET) >> SOC PIN [" + pin + "] VALUE = " + state); + System.out.println(" (READ) << TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + Assert.assertEquals("INCORRECT PIN VALUE", state.value(), p.value); + + System.out.println(); + state = PiGpioState.LOW; + System.out.println("TEST OUTPUT FOR [" + state.name() + "] STATE"); + pigpio.gpioWrite(pin, state); // set output pin state on SoC (RaspberryPi) + p = harness.getPin(pin); // get input pin state from the test harness + System.out.println(" (SET) >> SOC PIN [" + pin + "] VALUE = " + state); + System.out.println(" (READ) << TEST PIN [" + p.pin + "] VALUE = " + PiGpioState.from(p.value)); + Assert.assertEquals("INCORRECT PIN VALUE", state.value(), p.value); + + // disable test pin on the test harness + p = harness.disablePin(pin); + System.out.println(); + System.out.println("DISABLE TEST PIN [" + p.pin + "] ON TEST HARNESS <" + p.access + ">"); + + // reset pin on SoC (Raspberry Pi) (defaults as an input pin) + pigpio.gpioSetMode(pin, PiGpioMode.INPUT); + pigpio.gpioSetPullUpDown(pin, PiGpioPud.OFF); + } +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/i2c/TestI2cRawUsingTestHarness.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/i2c/TestI2cRawUsingTestHarness.java new file mode 100644 index 000000000..366d75f21 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/i2c/TestI2cRawUsingTestHarness.java @@ -0,0 +1,189 @@ +package com.pi4j.library.pigpio.test.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : TestI2cRawUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.util.UUID; + +@DisplayName("PIGPIO Library :: Test I2C Raw Communication") +public class TestI2cRawUsingTestHarness { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int I2C_TEST_HARNESS_BUS = 0; + private static int I2C_TEST_HARNESS_DEVICE = 0x04; + + private PiGpio pigpio; + private int handle; + + @BeforeAll + public static void initialize() { + //System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestI2cRawUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + ArduinoTestHarness harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // enable the I2C bus and device on the test harness hardware + // (enable RAW mode data processing) + harness.enableI2C(I2C_TEST_HARNESS_BUS, I2C_TEST_HARNESS_DEVICE, true); + System.out.println(); + System.out.println("ENABLE I2C BUS [" + I2C_BUS + "] ON TEST HARNESS;"); + + // terminate connection to test harness + harness.terminate(); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestI2cRawUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + } + + @BeforeEach + public void beforeEach() throws IOException { + // create test harness and PIGPIO instances + pigpio = PiGpio.newSocketInstance(System.getProperty("pi4j.pigpio.host", "rpi3bp.savage.lan"), + System.getProperty("pi4j.pigpio.port", "8888")); + + // initialize test harness and PIGPIO instances + pigpio.initialize(); + + // set pin ALT0 modes for I2C BUS<1> usage on RPI3B + pigpio.gpioSetMode(2, PiGpioMode.ALT0); + pigpio.gpioSetMode(3, PiGpioMode.ALT0); + + // OPEN I2C + handle = pigpio.i2cOpen(I2C_BUS, I2C_DEVICE); + } + + @AfterEach + public void afterEach() throws IOException { + + // CLOSE I2C + pigpio.i2cClose(handle); + + // terminate test harness and PIGPIO instances + pigpio.terminate(); + } + + @Test + @DisplayName("I2C :: Test SINGLE-BYTE (W/R)") + public void testI2CSingleByteTxRx() throws IOException, InterruptedException { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C SINGLE BYTE RAW READ/WRITE"); + System.out.println("----------------------------------------"); + + // iterate over BYTE range of values, WRITE the byte then immediately + // READ back the byte value and compare to make sure they are the same values. + for(int b = 0; b < 255; b++) { + System.out.println("[TEST WRITE/READ SINGLE BYTE]"); + + // WRITE :: SINGLE RAW BYTE + System.out.println(" (WRITE) >> VALUE = " + b); + pigpio.i2cWriteByte(handle, (byte)b); + Thread.sleep(5); + + // READ :: SINGLE RAW BYTE + byte rx = (byte)pigpio.i2cReadByte(handle); + System.out.println(" (READ) << VALUE = " + Byte.toUnsignedInt(rx)); + + Assert.assertEquals("I2C BYTE VALUE MISMATCH", b, Byte.toUnsignedInt(rx)); + } + } + + @Test + @DisplayName("I2C :: Test MULTI-BYTE (W/R)") + public void testI2CMultiByteTxRx() throws IOException, InterruptedException { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C MULTI-BYTE RAW READ/WRITE"); + System.out.println("----------------------------------------"); + + // iterate over series of test values, WRITE the byte then immediately + // READ back the byte value and compare to make sure they are the same values. + for(int x = 0; x < 100; x++) { + System.out.println("[TEST WRITE/READ MULTI-BYTE]"); + + String value = UUID.randomUUID().toString().substring(0, 8); + + // WRITE :: RAW MULTI-BYTE + System.out.println(" (WRITE) >> VALUE = " + value); + pigpio.i2cWriteDevice(handle, value); + Thread.sleep(5); + + // READ :: RAW MULTI-BYTE + String rx = pigpio.i2cReadDeviceToString(handle, value.length()); + System.out.println(" (READ) << VALUE = " + rx); + Thread.sleep(5); + + Assert.assertEquals("I2C MULTI-BYTE VALUE MISMATCH", value, rx); + } + } +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/i2c/TestI2cUsingTestHarness.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/i2c/TestI2cUsingTestHarness.java new file mode 100644 index 000000000..a9a44234f --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/i2c/TestI2cUsingTestHarness.java @@ -0,0 +1,376 @@ +package com.pi4j.library.pigpio.test.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : TestI2cUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.Random; +import java.util.UUID; + +@DisplayName("PIGPIO Library :: Test I2C Communication") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestI2cUsingTestHarness { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int I2C_TEST_HARNESS_BUS = 0; + private static int I2C_TEST_HARNESS_DEVICE = 0x04; + private static int MAX_REGISTERS = 100; + + private PiGpio pigpio; + private int handle; + + + @BeforeAll + public static void initialize() { + //System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestI2cUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + ArduinoTestHarness harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // enable the I2C bus and device on the test harness hardware + harness.enableI2C(I2C_TEST_HARNESS_BUS, I2C_TEST_HARNESS_DEVICE); + System.out.println(); + System.out.println("ENABLE I2C BUS [" + I2C_BUS + "] ON TEST HARNESS;"); + + harness.terminate(); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestI2cUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + } + + @BeforeEach + public void beforeEach() throws IOException { + // create test harness and PIGPIO instances + pigpio = PiGpio.newSocketInstance(System.getProperty("pi4j.pigpio.host", "rpi3bp.savage.lan"), + System.getProperty("pi4j.pigpio.port", "8888")); + + // initialize test harness and PIGPIO instances + pigpio.initialize(); + + // set pin ALT0 modes for I2C BUS<1> usage on RPI3B + pigpio.gpioSetMode(2, PiGpioMode.ALT0); + pigpio.gpioSetMode(3, PiGpioMode.ALT0); + + // OPEN I2C + handle = pigpio.i2cOpen(I2C_BUS, I2C_DEVICE); + } + + @AfterEach + public void afterEach() throws IOException { + + // CLOSE I2C + pigpio.i2cClose(handle); + + // terminate test harness and PIGPIO instances + pigpio.terminate(); + } + + @Test + @DisplayName("I2C :: Test register: BYTE (W/R)") + @Order(1) + public void testI2CSingleByteTxRx() throws IOException, InterruptedException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE BYTE"); + System.out.println("----------------------------------------"); + + // value cache + byte[] values = new byte[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(5); + System.out.println("[TEST WRITE BYTE] :: REGISTER=" + register); + + Random rand = new Random(); + values[register] = (byte)rand.nextInt(0xFF); // max 8 bits (1 byte) + System.out.println(" (WRITE) >> VALUE = " + Byte.toUnsignedInt(values[register])); + + // WRITE :: SINGLE WORD TO REGISTER + pigpio.i2cWriteByteData(handle, register, values[register]); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(5); + System.out.println("[TEST READ BYTE] :: REGISTER=" + register); + + // READ :: SINGLE RAW WORD + int value = pigpio.i2cReadByteData(handle, register); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + Byte.toUnsignedInt(values[register]) + ")"); + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C WORD VALUE MISMATCH", values[register], (byte)value); + } + } + + + @Test + @DisplayName("I2C :: Test register: WORD (W/R)") + @Order(1) + public void testI2CSingleWordTxRx() throws IOException, InterruptedException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE WORD"); + System.out.println("----------------------------------------"); + + // value cache + int[] values = new int[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(5); + System.out.println("[TEST WRITE WORD] :: REGISTER=" + register); + + Random rand = new Random(); + values[register] = rand.nextInt(0xFFFF); // max 16 bits (2 bytes) + System.out.println(" (WRITE) >> VALUE = " + values[register]); + + // WRITE :: SINGLE WORD TO REGISTER + pigpio.i2cWriteWordData(handle, register, values[register]); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(5); + System.out.println("[TEST READ WORD] :: REGISTER=" + register); + + // READ :: SINGLE RAW WORD + int value = pigpio.i2cReadWordData(handle, register); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[register] + ")"); + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C WORD VALUE MISMATCH", values[register], value); + } + } + + @Test + @DisplayName("I2C :: Test register: WORD process (W/R)") + @Order(2) + public void testI2CProcessWordTxRx() throws IOException, InterruptedException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER PROCESS (W/R) "); + System.out.println("----------------------------------------"); + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + // the process call should immediately return the same values back. + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(10); + System.out.println("[TEST PROCESS WORD] :: REGISTER=" + register); + + Random rand = new Random(); + int generatedValue = rand.nextInt(0xFFFF); // max 16 bits (2 bytes) + System.out.println(" (WRITE) >> VALUE = " + generatedValue); + + // WRITE/READ :: SINGLE WORD TO REGISTER + int returnedValue = pigpio.i2cProcessCall(handle, register, generatedValue); + System.out.println(" (READ) << VALUE = " + returnedValue); + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C WORD VALUE MISMATCH", generatedValue, returnedValue); + } + } + + @Test + @DisplayName("I2C :: Test register: BLOCK (W/R)") + @Order(3) + public void testI2CBlockDataTxRx() throws IOException, InterruptedException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE BLOCK"); + System.out.println("----------------------------------------"); + + // value cache + String[] values = new String[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(10); + System.out.println("[TEST WRITE BLOCK] :: REGISTER=" + register); + values[register] = UUID.randomUUID().toString().substring(0, 30); + System.out.println(" (WRITE) >> VALUE = " + values[register]); + + // WRITE :: BLOCK TO REGISTER + pigpio.i2cWriteBlockData(handle, register, values[register]); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int register = 0; register < 10; register++) { + Thread.sleep(50); + System.out.println("[TEST READ BLOCK] :: REGISTER=" + register); + + + // TODO ::PIGPIO ERROR: PI_BAD_SMBUS_CMD; SMBus command not supported by driver + +// // READ :: BLOCK +// String value = pigpio.i2cReadBlockDataToString(handle, register); +// System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[register] + ")"); +// +// // validate read value match with expected value that was writted to this register +// Assert.assertEquals("I2C BLOCK VALUE MISMATCH", values[register], value); + } + } + + @Test + @DisplayName("I2C :: Test register: BLOCK process (W/R)") + @Order(4) + @Disabled("This test is disabled until we can research the problem with the 'I2CPK' command response.") + public void testI2CProcessBlockTxRx() throws IOException, InterruptedException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER PROCESS (W/R) "); + System.out.println("----------------------------------------"); + + // TODO :: THIS TEST IS NOT WORKING? IS THIS SUPPORTED BY THE I2C HARDWARE/DRIVER? + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + // the process call should immediately return the same values back. + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(10); + System.out.println("[TEST PROCESS BLOCK] :: REGISTER=" + register); + + String generatedValue = UUID.randomUUID().toString().substring(0, 10); + System.out.println(" (WRITE) >> VALUE = " + generatedValue); + + // WRITE/READ :: BLOCK TO REGISTER + String returnedValue = pigpio.i2cBlockProcessCallToString(handle, register, generatedValue); + System.out.println(" (READ) << VALUE = " + returnedValue); + + // validate read value match with expected value that was written to this register + Assert.assertEquals("I2C BLOCK VALUE MISMATCH", generatedValue, returnedValue); + } + } + + @Test + @DisplayName("I2C :: Test register: I2C BLOCK (W/R)") + @Order(5) + public void testI2CBlockI2CDataTxRx() throws IOException, InterruptedException, NoSuchAlgorithmException { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE BLOCK"); + System.out.println("----------------------------------------"); + + // value cache + String[] values = new String[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int register = 0; register < MAX_REGISTERS; register++) { + Thread.sleep(50); + System.out.println("[TEST WRITE BLOCK] :: REGISTER=" + register); + values[register] = UUID.randomUUID().toString().substring(0, 30); + System.out.println(" (WRITE) >> VALUE = " + values[register]); + + // WRITE :: BLOCK TO REGISTER + pigpio.i2cWriteI2CBlockData(handle, register, values[register]); + } + + // READ back the random values from the I2C storage registers on the test harness and compare them. + for(int register = 0; register < MAX_REGISTERS; register++) { + System.out.println("[TEST READ BLOCK] :: REGISTER=" + register + "; LENGTH=" + values[register].length()); + + // READ :: BLOCK + Thread.sleep(50); + String value = pigpio.i2cReadI2CBlockDataToString(handle, register, values[register].length()); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[register] + ")"); + + // attempt #2 + if(!values[register].equals(value)){ + Thread.sleep(100); + value = pigpio.i2cReadI2CBlockDataToString(handle, register, values[register].length()); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[register] + ") "); + } + + // attempt #3 + if(!values[register].equals(value)){ + Thread.sleep(500); + value = pigpio.i2cReadI2CBlockDataToString(handle, register, values[register].length()); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[register] + ") "); + } + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C BLOCK VALUE MISMATCH", values[register], value); + } + } +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/pwm/TestHardwarePwmUsingTestHarness.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/pwm/TestHardwarePwmUsingTestHarness.java new file mode 100644 index 000000000..1a38942a7 --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/pwm/TestHardwarePwmUsingTestHarness.java @@ -0,0 +1,252 @@ +package com.pi4j.library.pigpio.test.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : TestHardwarePwmUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessFrequency; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.util.Arrays; + +@DisplayName("PIGPIO Library :: Test Hardware-Supported PWM Pins") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestHardwarePwmUsingTestHarness { + + private PiGpio pigpio; + private int handle; + private static ArduinoTestHarness harness; + + private static int ALT0_PINS[] = { 12, 13 }; + private static int ALT5_PINS[] = { 18, 19 }; + + @BeforeAll + public static void initialize() { + //System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestHardwarePwmUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestHardwarePwmUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // terminate connection to test harness + harness.terminate(); + } + + @BeforeEach + public void beforeEach() throws IOException { + // create test harness and PIGPIO instances + pigpio = PiGpio.newSocketInstance(System.getProperty("pi4j.pigpio.host", "rpi3bp.savage.lan"), + System.getProperty("pi4j.pigpio.port", "8888")); + + // initialize test harness and PIGPIO instances + pigpio.initialize(); + + // reset I/O + //harness.reset(); + } + + @AfterEach + public void afterEach() throws IOException { + + // terminate test harness and PIGPIO instances + //pigpio.terminate(); + } + + @Test + @Order(0) + @DisplayName("PWM :: Test HW PWM @ 15 Hz") + public void testPwmAt15Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 15); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 15); + } + + @Test + @Order(1) + @DisplayName("PWM :: Test HW PWM @ 50 Hz") + public void testPwmAt50Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 50); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 50); + } + + @Test + @Order(2) + @DisplayName("PWM :: Test HW PWM @ 100 Hz") + public void testPwmAt100Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 100); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 100); + } + + @Test + @Order(3) + @DisplayName("PWM :: Test HW PWM @ 700 Hz") + public void testPwmAt700Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 700); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 700); + } + + @Test + @Order(4) + @DisplayName("PWM :: Test HW PWM @ 1000 Hz (1 KHz)") + public void testPwmAt1000Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 1000); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 1000); + } + + @Test + @Order(5) + @DisplayName("PWM :: Test HW PWM @ 5000 Hz (5 KHz)") + public void testPwmAt5000Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 5000); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 5000); + } + + @Test + @Order(6) + @DisplayName("PWM :: Test HW PWM @ 10000 Hz (10 KHz)") + public void testPwmAt10000Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 10000); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 10000); + } + + @Test + @Order(7) + @DisplayName("PWM :: Test HW PWM @ 100000 Hz (100 KHz)") + public void testPwmAt100000Hertz() throws IOException, InterruptedException { + testPwm(PiGpioMode.ALT0, ALT0_PINS, 100000); + testPwm(PiGpioMode.ALT5, ALT5_PINS, 100000); + } + + public void testPwm(PiGpioMode mode, int[] pins, int frequency) throws IOException, InterruptedException { + testPwm(mode, pins, frequency, 35); // 35% duty-cycle by default + } + public void testPwm(PiGpioMode mode, int[] pins, int frequency, int dutyCycle) throws IOException, InterruptedException { + System.out.println(); + System.out.println("---------------------------------------------------------"); + System.out.println("TEST PWM SIGNALS AT " + frequency + " HZ; PINS=" + Arrays.toString(pins) + "; MODE=" + mode.name()); + System.out.println("---------------------------------------------------------"); + + for(int p : pins) { + + // set pin ALT modes for PWM + pigpio.gpioSetMode(p, mode); + + System.out.println(); + System.out.println("[TEST HARDWARE PWM] :: PIN=" + p + " <" + mode.name() + ">"); + + // hardware PWM duty cycle scaling + dutyCycle = dutyCycle * 10000; + + // write PWM frequency and duty-cycle + pigpio.gpioHardwarePWM(p, frequency, dutyCycle); + System.out.println(" (WRITE) >> PWM FREQUENCY = " + frequency); + System.out.println(" (WRITE) >> PWM DUTY-CYCLE = " + dutyCycle); + + // test once .. + if(measureFrequency(p, frequency) == false){ + Thread.sleep(500); + // test twice .. + if(measureFrequency(p, frequency) == false){ + Thread.sleep(1000); + // test thrice .. + if(measureFrequency(p, frequency) == false){ + // turn off PWM pin + pigpio.gpioHardwarePWM(p, 0, 0); + + // give up and fail + Assert.fail("PWM MEASURED FREQUENCY OUT OF ACCEPTABLE MARGIN OF ERROR"); + } + } + } + + // turn off PWM pin + pigpio.gpioHardwarePWM(p, 0, 0); + } + } + + private boolean measureFrequency(int pin, int frequency) throws IOException { + TestHarnessFrequency measured = harness.getFrequency(pin); + System.out.println(" (TEST) << MEASURED FREQUENCY = " + measured.frequency); + + // we allow a 75% margin of error, the testing harness uses a simple pulse counter to crudely + // measure the PWM signal, its not very accurate but should provide sufficient validation testing + // just to verify the applied PWM signal is close to the expected frequency + // calculate margin of error offset value + long marginOfError = Math.round(frequency * .75); + + // test measured value against HI/LOW offsets to determine acceptable range + if(measured.frequency < frequency-marginOfError) return false; + if(measured.frequency > frequency+marginOfError) return false; + + // success + return true; + } +} diff --git a/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/pwm/TestSoftwarePwmUsingTestHarness.java b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/pwm/TestSoftwarePwmUsingTestHarness.java new file mode 100644 index 000000000..98e29aeee --- /dev/null +++ b/libraries/pi4j-library-pigpio/src/test/java/com/pi4j/library/pigpio/test/pwm/TestSoftwarePwmUsingTestHarness.java @@ -0,0 +1,231 @@ +package com.pi4j.library.pigpio.test.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : TestSoftwarePwmUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessFrequency; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; + +@DisplayName("PIGPIO Library :: Test Software Emulated PWM Pins") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestSoftwarePwmUsingTestHarness { + + private PiGpio pigpio; + private int handle; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + //System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestSoftwarePwmUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestSoftwarePwmUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // terminate connection to test harness + harness.terminate(); + } + + @BeforeEach + public void beforeEach() throws IOException { + // create test harness and PIGPIO instances + pigpio = PiGpio.newSocketInstance(System.getProperty("pi4j.pigpio.host", "rpi3bp.savage.lan"), + System.getProperty("pi4j.pigpio.port", "8888")); + + // initialize test harness and PIGPIO instances + pigpio.initialize(); + + // reset I/O + //harness.reset(); + } + + @AfterEach + public void afterEach() throws IOException { + + // terminate test harness and PIGPIO instances + pigpio.terminate(); + } + + @Test + @Order(1) + @DisplayName("PWM :: Test PWM @ 50 Hz") + public void testPwmAt50Hertz() throws IOException, InterruptedException { + testPwm(50); + } + + @Test + @Order(2) + @DisplayName("PWM :: Test PWM @ 100 Hz") + public void testPwmAt100Hertz() throws IOException, InterruptedException { + testPwm(100); + } + + @Test + @Order(3) + @DisplayName("PWM :: Test PWM @ 700 Hz") + public void testPwmAt700Hertz() throws IOException, InterruptedException { + testPwm(700); + } + + @Test + @Order(4) + @DisplayName("PWM :: Test PWM @ 1000 Hz (1 KHz)") + public void testPwmAt1000Hertz() throws IOException, InterruptedException { + testPwm(1000); + } + + @Test + @Order(5) + @DisplayName("PWM :: Test PWM @ 5000 Hz (5 KHz)") + public void testPwmAt5000Hertz() throws IOException, InterruptedException { + testPwm(5000); + } + + @Test + @Order(6) + @DisplayName("PWM :: Test PWM @ 10000 Hz (10 KHz)") + public void testPwmAt10000Hertz() throws IOException, InterruptedException { + testPwm(10000); + } + + public void testPwm(int frequency) throws IOException, InterruptedException { + testPwm(frequency, 80); // 80% duty-cycle by default + } + public void testPwm(int frequency, int dutyCycle) throws IOException, InterruptedException { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST PWM SIGNALS AT " + frequency + " HZ"); + System.out.println("----------------------------------------"); + + for(int p = 2; p < 20; p++) { + System.out.println(); + System.out.println("[TEST SOFT PWM] :: PIN=" + p); + int actualFrequency = pigpio.gpioSetPWMfrequency(p, frequency); + + // write PWM frequency and duty-cycle + pigpio.gpioPWM(p, dutyCycle); + System.out.println(" (WRITE) >> PWM FREQUENCY = " + frequency + + ((actualFrequency != frequency) ? " (ACTUAL FREQ SET: " + actualFrequency + ")" : "")); + System.out.println(" (WRITE) >> PWM DUTY-CYCLE = " + dutyCycle); + + // read back frequency and duty cycle + int readFrequency = pigpio.gpioGetPWMfrequency(p); + int readDutyCycle = pigpio.gpioGetPWMdutycycle(p); + + System.out.println(" (READ) << PWM FREQUENCY = " + readFrequency + "; (EXPECTED=" + actualFrequency + ")"); + System.out.println(" (READ) << PWM DUTY-CYCLE = " + readDutyCycle + "; (EXPECTED=" + dutyCycle + ")"); + + Assert.assertEquals("PWM FREQUENCY MISMATCH", actualFrequency, readFrequency); + Assert.assertEquals("PWM DUTY-CYCLE MISMATCH", dutyCycle, readDutyCycle); + + + // test once .. + if(measureFrequency(p, actualFrequency) == false){ + // test twice .. + Thread.sleep(1000); + if(measureFrequency(p, actualFrequency) == false){ + // test thrice .. + Thread.sleep(2000); + if(measureFrequency(p, actualFrequency) == false){ + // turn off PWM pin + pigpio.gpioPWM(p, 0);; + + // give up and fail + Assert.fail("PWM MEASURED FREQUENCY OUT OF ACCEPTABLE MARGIN OF ERROR"); + } + } + } + + // turn off PWM pin + pigpio.gpioPWM(p, 0);; + } + } + + private boolean measureFrequency(int pin, int frequency) throws IOException { + TestHarnessFrequency measured = harness.getFrequency(pin); + System.out.println(" (TEST) << MEASURED FREQUENCY = " + measured.frequency); + + // we allow a 60% margin of error, the testing harness uses a simple pulse counter to crudely + // measure the PWM signal, its not very accurate but should provide sufficient validation testing + // just to verify the applied PWM signal is close to the expected frequency + // calculate margin of error offset value + long marginOfError = Math.round(frequency * .60); + + // test measured value against HI/LOW offsets to determine acceptable range + if(measured.frequency < frequency-marginOfError) return false; + if(measured.frequency > frequency+marginOfError) return false; + + // success + return true; + } +} diff --git a/libraries/pi4j-library/pom.xml b/libraries/pi4j-library/pom.xml new file mode 100644 index 000000000..8f1cdd897 --- /dev/null +++ b/libraries/pi4j-library/pom.xml @@ -0,0 +1,125 @@ + + + + pi4j-parent + com.pi4j + 2.0-SNAPSHOT + ../../ + + 4.0.0 + + + pi4j-library + Pi4J :: LIBRARY :: Libraries Parent POM + Pi4J Plugin Parent Maven POM + pom + + + + ../pi4j-library-pigpio + + + + + + + ${project.build.directory} + false + + LICENSE.txt + NOTICE.txt + README.md + + + + src/main/resources + + **/** + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + org.apache.maven.plugins + maven-source-plugin + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + org.codehaus.mojo + license-maven-plugin + + false + + + + append-license-file-headers + + update-file-header + + process-sources + + lgpl_v3 + ${basedir}/../../src/license/template.ftl + true + true + true + + *.sh + + + src/main/native + src/main/java + src/test + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + + + + + + oss-snapshots-repo + Sonatype OSS Maven Repository + https://oss.sonatype.org/content/groups/public + + true + always + + + + + diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/AddPwmPreset.java b/pi4j-api/src/main/java/com/pi4j/annotation/AddPwmPreset.java new file mode 100644 index 000000000..ff693f508 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/AddPwmPreset.java @@ -0,0 +1,39 @@ +package com.pi4j.annotation; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : AddPwmPreset.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.lang.annotation.*; + +@Repeatable(AddPwmPresets.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface AddPwmPreset { + String name(); + int frequency() default -1; + int dutyCycle() default -1; +} diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/AddPwmPresets.java b/pi4j-api/src/main/java/com/pi4j/annotation/AddPwmPresets.java new file mode 100644 index 000000000..da4e1f43d --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/AddPwmPresets.java @@ -0,0 +1,39 @@ +package com.pi4j.annotation; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : AddPwmPresets.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface AddPwmPresets { + AddPwmPreset[] value(); +} diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/DutyCycle.java b/pi4j-api/src/main/java/com/pi4j/annotation/DutyCycle.java new file mode 100644 index 000000000..964fd3f82 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/DutyCycle.java @@ -0,0 +1,41 @@ +package com.pi4j.annotation; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DutyCycle.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface DutyCycle { + int value() default -1; + int percent() default -1; + int range() default -1; +} diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/Frequency.java b/pi4j-api/src/main/java/com/pi4j/annotation/Frequency.java new file mode 100644 index 000000000..3a2bcddbb --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/Frequency.java @@ -0,0 +1,39 @@ +package com.pi4j.annotation; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : Frequency.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Frequency { + int value(); +} diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/I2CAddress.java b/pi4j-api/src/main/java/com/pi4j/annotation/I2CAddress.java new file mode 100644 index 000000000..a7fa0a7ee --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/I2CAddress.java @@ -0,0 +1,40 @@ +package com.pi4j.annotation; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CAddress.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface I2CAddress { + int bus(); + int device(); +} diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/processor/register/I2CRegistrationProcessor.java b/pi4j-api/src/main/java/com/pi4j/annotation/processor/register/I2CRegistrationProcessor.java new file mode 100644 index 000000000..7aa24286c --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/processor/register/I2CRegistrationProcessor.java @@ -0,0 +1,121 @@ +package com.pi4j.annotation.processor.register; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CRegistrationProcessor.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.annotation.*; +import com.pi4j.annotation.exception.AnnotationException; +import com.pi4j.annotation.impl.WithAnnotationProcessor; +import com.pi4j.context.Context; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CProvider; +import com.pi4j.platform.Platform; +import com.pi4j.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; + +public class I2CRegistrationProcessor implements RegisterProcessor { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Override + public boolean isEligible(Context context, Object instance, Register annotation, Field field) throws Exception { + + // make sure this field is of type 'I2C' + if(!I2C.class.isAssignableFrom(field.getType())) + return false; + + // this processor can process this annotated instance + return true; + } + + @Override + public I2C process(Context context, Object instance, Register annotation, Field field) throws Exception { + + // validate that the 'ID' (value) attribute is not empty on this field annotation + if (StringUtil.isNullOrEmpty(annotation.value())) + throw new AnnotationException("Missing required 'value ' attribute"); + + // make sure the field instance is null; we can only register our own dynamically created I/O instances + if(field.get(instance) != null) + throw new AnnotationException("This @Register annotated instance is not null; it must be NULL " + + "to register a new I/O instance. If you just want to access an existing I/O instance, " + + "use the '@Inject(id)' annotation instead."); + + // create I/O config builder + var builder = I2C.newConfigBuilder(); + if (annotation.value() != null) builder.id((annotation).value()); + + // test for required additional annotations + if (!field.isAnnotationPresent(I2CAddress.class)) + throw new AnnotationException("Missing required '@I2CAddress' annotation for this I/O type."); + + // all supported additional annotations for configuring the digital output + I2CAddress address = field.getAnnotation(I2CAddress.class); + builder.bus(address.bus()); + builder.device(address.device()); + + Name name = null; + if (field.isAnnotationPresent(Name.class)) { + name = field.getAnnotation(Name.class); + if (name != null) builder.name(name.value()); + } + + Description description = null; + if (field.isAnnotationPresent(Description.class)) { + description = field.getAnnotation(Description.class); + if (description != null) builder.description(description.value()); + } + + // get designated platform to use to register this IO (if provided) + Platform platform = null; + if (field.isAnnotationPresent(WithPlatform.class)) { + platform = WithAnnotationProcessor.getPlatform(context, field); + } + + // get designated provider to use to register this IO (if provided) + I2CProvider provider = null; + if (field.isAnnotationPresent(WithProvider.class)) { + provider = WithAnnotationProcessor.getProvider(context, platform, field, I2CProvider.class); + } + + // if a provider was found, then create PWM IO instance using that provider + if(provider != null){ + return provider.create(builder.build()); + } + + // if no provider was found, then create PWM IO instance using defaults + else { + if(platform != null) + return platform.provider(I2CProvider.class).create(builder.build()); + else + return context.provider(I2CProvider.class).create(builder.build()); + } + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/annotation/processor/register/PwmRegistrationProcessor.java b/pi4j-api/src/main/java/com/pi4j/annotation/processor/register/PwmRegistrationProcessor.java new file mode 100644 index 000000000..1d9d83f24 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/annotation/processor/register/PwmRegistrationProcessor.java @@ -0,0 +1,173 @@ +package com.pi4j.annotation.processor.register; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : PwmRegistrationProcessor.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.annotation.*; +import com.pi4j.annotation.exception.AnnotationException; +import com.pi4j.annotation.impl.WithAnnotationProcessor; +import com.pi4j.context.Context; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmPreset; +import com.pi4j.io.pwm.PwmPresetBuilder; +import com.pi4j.io.pwm.PwmProvider; +import com.pi4j.platform.Platform; +import com.pi4j.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; + +public class PwmRegistrationProcessor implements RegisterProcessor { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Override + public boolean isEligible(Context context, Object instance, Register annotation, Field field) throws Exception { + + // make sure this field is of type 'Pwm' + if(!Pwm.class.isAssignableFrom(field.getType())) + return false; + + // this processor can process this annotated instance + return true; + } + + @Override + public Pwm process(Context context, Object instance, Register annotation, Field field) throws Exception { + + // validate that the 'ID' (value) attribute is not empty on this field annotation + if (StringUtil.isNullOrEmpty(annotation.value())) + throw new AnnotationException("Missing required 'value ' attribute"); + + // make sure the field instance is null; we can only register our own dynamically created I/O instances + if(field.get(instance) != null) + throw new AnnotationException("This @Register annotated instance is not null; it must be NULL " + + "to register a new I/O instance. If you just want to access an existing I/O instance, " + + "use the '@Inject(id)' annotation instead."); + + // create I/O config builder + var builder = Pwm.newConfigBuilder(); + if (annotation.value() != null) builder.id((annotation).value()); + + // test for required additional annotations + if (!field.isAnnotationPresent(Address.class)) + throw new AnnotationException("Missing required '@Address' annotation for this I/O type."); + + // all supported additional annotations for configuring the digital output + Address address = field.getAnnotation(Address.class); + builder.address(address.value()); + + Name name = null; + if (field.isAnnotationPresent(Name.class)) { + name = field.getAnnotation(Name.class); + if (name != null) builder.name(name.value()); + } + + Description description = null; + if (field.isAnnotationPresent(Description.class)) { + description = field.getAnnotation(Description.class); + if (description != null) builder.description(description.value()); + } + + ShutdownValue shutdownValue = null; + if (field.isAnnotationPresent(ShutdownValue.class)) { + shutdownValue = field.getAnnotation(ShutdownValue.class); + if (shutdownValue != null) builder.shutdown(shutdownValue.value()); + } + + InitialValue initialValue = null; + if (field.isAnnotationPresent(InitialValue.class)) { + initialValue = field.getAnnotation(InitialValue.class); + if (initialValue != null) builder.initial(initialValue.value()); + } + + Frequency frequency = null; + if (field.isAnnotationPresent(Frequency.class)) { + frequency = field.getAnnotation(Frequency.class); + if (frequency != null) builder.frequency(frequency.value()); + } + + DutyCycle dutyCycle = null; + if (field.isAnnotationPresent(DutyCycle.class)) { + dutyCycle = field.getAnnotation(DutyCycle.class); + if (dutyCycle != null){ + + if(dutyCycle.value() >= 0) { + builder.dutyCycle(dutyCycle.value()); + } + if(dutyCycle.range() >= 0) { + builder.range(dutyCycle.range()); + } + if(dutyCycle.percent() >= 0) { + //builder.dutyCycle(dutyCycle.range()); + } + } + } + + AddPwmPresets pwmPresets = null; + if (field.isAnnotationPresent(AddPwmPresets.class)) { + pwmPresets = field.getAnnotation(AddPwmPresets.class); + AddPwmPreset[] presets = pwmPresets.value(); + for(AddPwmPreset preset : presets){ + PwmPresetBuilder presetBuilder = PwmPreset.newBuilder(preset.name()); + if(preset.dutyCycle() >= 0) + presetBuilder.dutyCycle(preset.dutyCycle()); + if(preset.frequency() >= 0) + presetBuilder.frequency(preset.frequency()); + + // add applyPreset to PWM config builder + builder.preset(presetBuilder.build()); + } + } + + // get designated platform to use to register this IO (if provided) + Platform platform = null; + if (field.isAnnotationPresent(WithPlatform.class)) { + platform = WithAnnotationProcessor.getPlatform(context, field); + } + + // get designated provider to use to register this IO (if provided) + PwmProvider provider = null; + if (field.isAnnotationPresent(WithProvider.class)) { + provider = WithAnnotationProcessor.getProvider(context, platform, field, PwmProvider.class); + } + + // if a provider was found, then create PWM IO instance using that provider + if(provider != null){ + return provider.create(builder.build()); + } + + // if no provider was found, then create PWM IO instance using defaults + else { + if(platform != null) + return platform.provider(PwmProvider.class).create(builder.build()); + else + return context.provider(PwmProvider.class).create(builder.build()); + } + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/config/impl/AddressConfigBase.java b/pi4j-api/src/main/java/com/pi4j/config/impl/AddressConfigBase.java index 82248f48b..05cbbb0c1 100644 --- a/pi4j-api/src/main/java/com/pi4j/config/impl/AddressConfigBase.java +++ b/pi4j-api/src/main/java/com/pi4j/config/impl/AddressConfigBase.java @@ -48,6 +48,11 @@ protected AddressConfigBase(){ super(); } + protected AddressConfigBase(Number address){ + super(); + this.address = address.intValue(); + } + /** * PRIVATE CONSTRUCTOR * @param properties diff --git a/pi4j-api/src/main/java/com/pi4j/context/Context.java b/pi4j-api/src/main/java/com/pi4j/context/Context.java index 80710e9fe..314652b87 100644 --- a/pi4j-api/src/main/java/com/pi4j/context/Context.java +++ b/pi4j-api/src/main/java/com/pi4j/context/Context.java @@ -32,13 +32,19 @@ import com.pi4j.common.Descriptor; import com.pi4j.exception.LifecycleException; import com.pi4j.io.IOType; -import com.pi4j.io.gpio.analog.AnalogInputProvider; -import com.pi4j.io.gpio.analog.AnalogOutputProvider; -import com.pi4j.io.gpio.digital.DigitalInputProvider; -import com.pi4j.io.gpio.digital.DigitalOutputProvider; +import com.pi4j.io.gpio.analog.*; +import com.pi4j.io.gpio.digital.*; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CConfig; import com.pi4j.io.i2c.I2CProvider; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmConfig; import com.pi4j.io.pwm.PwmProvider; +import com.pi4j.io.serial.Serial; +import com.pi4j.io.serial.SerialConfig; import com.pi4j.io.serial.SerialProvider; +import com.pi4j.io.spi.Spi; +import com.pi4j.io.spi.SpiConfig; import com.pi4j.io.spi.SpiProvider; import com.pi4j.platform.Platform; import com.pi4j.platform.Platforms; @@ -52,6 +58,7 @@ public interface Context extends Describable { ContextConfig config(); + ContextProperties properties(); Providers providers(); Registry registry(); Platforms platforms(); @@ -159,6 +166,31 @@ default

P platform(){ return platforms().getDefault(); } + default AnalogOutput create(AnalogOutputConfig config) throws Exception{ + return this.aout().create(config); + } + default AnalogInput create(AnalogInputConfig config) throws Exception{ + return this.ain().create(config); + } + default DigitalOutput create(DigitalOutputConfig config) throws Exception{ + return this.dout().create(config); + } + default DigitalInput create(DigitalInputConfig config) throws Exception{ + return this.din().create(config); + } + default Pwm create(PwmConfig config) throws Exception{ + return this.pwm().create(config); + } + default I2C create(I2CConfig config) throws Exception{ + return this.i2c().create(config); + } + default Spi create(SpiConfig config) throws Exception{ + return this.spi().create(config); + } + default Serial create(SerialConfig config) throws Exception{ + return this.serial().create(config); + } + default Descriptor describe() { Descriptor descriptor = Descriptor.create() .category("CONTEXT") @@ -168,6 +200,7 @@ default Descriptor describe() { descriptor.add(registry().describe()); descriptor.add(platforms().describe()); descriptor.add(providers().describe()); + descriptor.add(properties().describe()); return descriptor; } } diff --git a/pi4j-api/src/main/java/com/pi4j/context/ContextBuilder.java b/pi4j-api/src/main/java/com/pi4j/context/ContextBuilder.java index db314a422..2417d7e8f 100644 --- a/pi4j-api/src/main/java/com/pi4j/context/ContextBuilder.java +++ b/pi4j-api/src/main/java/com/pi4j/context/ContextBuilder.java @@ -32,6 +32,13 @@ import com.pi4j.platform.Platform; import com.pi4j.provider.Provider; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Map; +import java.util.Properties; + public interface ContextBuilder extends Builder { static ContextBuilder newInstance(){ @@ -91,4 +98,75 @@ default ContextBuilder setDefaultPlatform(String platformId){ default ContextBuilder setDefaultPlatform(Platform platform){ return defaultPlatform(platform); } + + + ContextBuilder property(String key, String value); + ContextBuilder property(Map.Entry ... value); + + ContextBuilder properties(Map values); + ContextBuilder properties(Map properties, String prefixFilter); + ContextBuilder properties(Properties properties, String prefixFilter); + ContextBuilder properties(InputStream stream, String prefixFilter) throws IOException; + ContextBuilder properties(Reader reader, String prefixFilter) throws IOException; + ContextBuilder properties(File file, String prefixFilter) throws IOException; + + default ContextBuilder properties(Properties properties){ + return properties(properties, null); + } + default ContextBuilder properties(InputStream stream) throws IOException{ + return properties(stream, null); + } + default ContextBuilder properties(Reader reader) throws IOException{ + return properties(reader, null); + } + default ContextBuilder properties(File file) throws IOException{ + return properties(file, null); + } + + default ContextBuilder addProperty(String key, String value){ + return property(key, value); + } + default ContextBuilder addProperty(Map.Entry ... value){ + return property(value); + } + default ContextBuilder addProperties(Properties properties, String prefixFilter){ + return properties(properties, prefixFilter); + } + default ContextBuilder addProperties(Properties properties){ + return properties(properties, null); + } + default ContextBuilder addProperties(Map properties){ + return properties(properties, null); + } + default ContextBuilder addProperties(Map properties, String prefixFilter){ + return properties(properties, prefixFilter); + } + + default ContextBuilder addProperties(InputStream stream) throws IOException{ + return properties(stream, null); + } + default ContextBuilder addProperties(InputStream stream, String prefixFilter) throws IOException{ + return properties(stream, prefixFilter); + } + + default ContextBuilder addProperties(Reader reader) throws IOException{ + return properties(reader, null); + } + default ContextBuilder addProperties(Reader reader, String prefixFilter) throws IOException{ + return properties(reader, prefixFilter); + } + + default ContextBuilder addProperties(File file) throws IOException{ + return properties(file, null); + } + default ContextBuilder addProperties(File file, String prefixFilter) throws IOException{ + return properties(file, prefixFilter); + } + + default ContextBuilder add(Properties properties, String prefixFilter){ + return properties(properties, prefixFilter); + } + default ContextBuilder add(Properties properties){ + return properties(properties, null); + } } diff --git a/pi4j-api/src/main/java/com/pi4j/context/ContextConfig.java b/pi4j-api/src/main/java/com/pi4j/context/ContextConfig.java index aed107449..d264f2550 100644 --- a/pi4j-api/src/main/java/com/pi4j/context/ContextConfig.java +++ b/pi4j-api/src/main/java/com/pi4j/context/ContextConfig.java @@ -31,6 +31,7 @@ import com.pi4j.provider.Provider; import java.util.Collection; +import java.util.Map; public interface ContextConfig { @@ -60,4 +61,9 @@ default Collection getProviders(){ boolean autoDetectProviders(); default boolean getAutoDetectProviders() { return autoDetectProviders(); }; default boolean isAutoDetectProviders() { return autoDetectProviders(); }; + + // ************************************************** + // PROPERTIES + // ************************************************** + Map properties(); } diff --git a/pi4j-api/src/main/java/com/pi4j/context/ContextProperties.java b/pi4j-api/src/main/java/com/pi4j/context/ContextProperties.java new file mode 100644 index 000000000..875415f4c --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/context/ContextProperties.java @@ -0,0 +1,82 @@ +package com.pi4j.context; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : ContextProperties.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.common.Describable; +import com.pi4j.common.Descriptor; +import com.pi4j.util.StringUtil; + +import java.util.Map; + +public interface ContextProperties extends Describable { + + boolean has(String key); + String get(String key); + + Map all(); + int count(); + + default String get(String key, String defaultValue){ + String value = get(key); + if(StringUtil.isNotNullOrEmpty(value)) { + return value; + } + return defaultValue; + } + + default boolean exists(String key){ + return has(key); + } + + default Integer getInteger(String key){ + return getInteger(key, null); + } + + default Integer getInteger(String key, Integer defaultValue){ + if(has(key)) return StringUtil.parseInteger(get(key), defaultValue); + return defaultValue; + } + + default Descriptor describe() { + Descriptor descriptor = Descriptor.create() + .category("PROPERTIES") + .name("Properties") + .quantity(this.count()) + .type(this.getClass()); + + for(Map.Entry e : this.all().entrySet()){ + descriptor.add( + Descriptor.create() + .name(e.getKey().toString()) + .category("PROPERTY") + .description(e.getValue().toString()) + ); + } + return descriptor; + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContext.java b/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContext.java index ad943dce2..b5dd13d0d 100644 --- a/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContext.java +++ b/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContext.java @@ -30,6 +30,7 @@ import com.pi4j.annotation.exception.AnnotationException; import com.pi4j.context.Context; import com.pi4j.context.ContextConfig; +import com.pi4j.context.ContextProperties; import com.pi4j.exception.LifecycleException; import com.pi4j.exception.Pi4JException; import com.pi4j.exception.ShutdownException; @@ -49,6 +50,7 @@ public class DefaultContext implements Context { private Logger logger = LoggerFactory.getLogger(this.getClass()); private Runtime runtime = null; private ContextConfig config = null; + private ContextProperties properties = null; private Providers providers = null; private Platforms platforms = null; private Registry registry = null; @@ -72,6 +74,9 @@ private DefaultContext(ContextConfig config) throws Pi4JException { // create internal runtime state instance (READ-ONLY ACCESS OBJECT) this.runtime = DefaultRuntime.newInstance(this); + // create API accessible properties instance (READ-ONLY ACCESS OBJECT) + this.properties = DefaultContextProperties.newInstance(this.runtime.properties()); + // create API accessible registry instance (READ-ONLY ACCESS OBJECT) this.registry = DefaultRegistry.newInstance(this.runtime.registry()); @@ -90,6 +95,11 @@ private DefaultContext(ContextConfig config) throws Pi4JException { @Override public ContextConfig config() { return this.config; } + @Override + public ContextProperties properties() { + return this.properties; + } + @Override public Providers providers() { return providers; } diff --git a/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java b/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java index 0885d8ffc..97111239e 100644 --- a/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java +++ b/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextBuilder.java @@ -33,13 +33,13 @@ import com.pi4j.exception.Pi4JException; import com.pi4j.platform.Platform; import com.pi4j.provider.Provider; +import com.pi4j.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; public class DefaultContextBuilder implements ContextBuilder { @@ -56,6 +56,9 @@ public class DefaultContextBuilder implements ContextBuilder { protected Collection platforms = Collections.synchronizedList(new ArrayList<>()); protected Collection providers = Collections.synchronizedList(new ArrayList<>()); + // properties + protected Map properties = Collections.synchronizedMap(new HashMap<>()); + /** * Private Constructor */ @@ -116,6 +119,72 @@ public ContextBuilder noAutoDetectProviders() { return this; } + @Override + public ContextBuilder property(String key, String value){ + this.properties.put(key, value); + return this; + } + + @Override + public ContextBuilder property(Map.Entry ... value){ + for(Map.Entry e : value){ + this.properties.put(e.getKey().toString(), e.getValue().toString()); + } + return this; + } + + @Override + public ContextBuilder properties(Properties properties, String prefixFilter){ + // convert java.util.Properties to a Map object + Map entries = properties.keySet().stream() + .collect(Collectors.toMap(k->k.toString(), key->properties.get(key).toString())); + return properties(entries, prefixFilter); + } + + @Override + public ContextBuilder properties(Map properties) { + this.properties.putAll(properties); + return this; + } + + @Override + public ContextBuilder properties(Map properties, String prefixFilter){ + + // if a filter was not provided, then load properties without a filter + if(StringUtil.isNullOrEmpty(prefixFilter)) return properties(properties); + + // sanitize the prefix filter and make sure it includes a "." character at the end + var prefix = (prefixFilter.endsWith(".")) ? prefixFilter : prefixFilter+"."; + + // iterate the properties object and assign any key with the prefix filter to this config + properties.keySet().stream().filter(key -> key.startsWith(prefix)).forEach((key)->{ + this.properties.put(key.substring(prefix.length()), properties.get(key)); + }); + return this; + } + + @Override + public ContextBuilder properties(InputStream stream, String prefixFilter) throws IOException{ + Properties prop = new Properties(); + prop.load(stream); + return properties(prop, prefixFilter); + } + + @Override + public ContextBuilder properties(Reader reader, String prefixFilter) throws IOException{ + Properties prop = new Properties(); + prop.load(reader); + return properties(prop, prefixFilter); + } + + @Override + public ContextBuilder properties(File file, String prefixFilter) throws IOException{ + Properties prop = new Properties(); + prop.load(new FileInputStream(file)); + return properties(prop, prefixFilter); + } + + @Override public ContextConfig toConfig() { // set instance reference @@ -147,6 +216,11 @@ public boolean autoDetectPlatforms() { public boolean autoDetectProviders() { return builder.autoDetectProviders; } + + @Override + public Map properties() { + return Collections.unmodifiableMap(builder.properties); + } }; } diff --git a/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextProperties.java b/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextProperties.java new file mode 100644 index 000000000..b6e6b29cd --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/context/impl/DefaultContextProperties.java @@ -0,0 +1,66 @@ +package com.pi4j.context.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultContextProperties.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.context.ContextProperties; +import com.pi4j.runtime.RuntimeProperties; + +import java.util.Map; + +public class DefaultContextProperties implements ContextProperties { + + private final RuntimeProperties properties; + + public static ContextProperties newInstance(RuntimeProperties properties){ + return new DefaultContextProperties(properties); + } + + private DefaultContextProperties(RuntimeProperties properties){ + this.properties = properties; + } + + @Override + public boolean has(String key) { + return this.properties.has(key); + } + + @Override + public String get(String key) { + return this.properties.get(key); + } + + @Override + public Map all() { + return this.properties.all(); + } + + @Override + public int count() { + return this.properties.count(); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/extension/Extension.java b/pi4j-api/src/main/java/com/pi4j/extension/Extension.java index 78e3e7de6..2b91f9b6a 100644 --- a/pi4j-api/src/main/java/com/pi4j/extension/Extension.java +++ b/pi4j-api/src/main/java/com/pi4j/extension/Extension.java @@ -37,7 +37,7 @@ default Descriptor describe() { return Descriptor.create() .id(this.id()) .name(this.name()) - .category("BINDING") + .category("EXTENSION") .description(this.description()).type(this.getClass()); } } diff --git a/pi4j-api/src/main/java/com/pi4j/extension/Plugin.java b/pi4j-api/src/main/java/com/pi4j/extension/Plugin.java index 3f61b5ad6..6e8c8b94f 100644 --- a/pi4j-api/src/main/java/com/pi4j/extension/Plugin.java +++ b/pi4j-api/src/main/java/com/pi4j/extension/Plugin.java @@ -27,6 +27,14 @@ * #L% */ +import com.pi4j.context.Context; + +import java.io.IOException; + public interface Plugin { - void initialize(PluginService service); + void initialize(PluginService service) throws IOException; + + default void shutdown(Context context) throws IOException{ + // do nothing + } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/IODataReader.java b/pi4j-api/src/main/java/com/pi4j/io/IODataReader.java new file mode 100644 index 000000000..2898640fb --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/IODataReader.java @@ -0,0 +1,147 @@ +package com.pi4j.io; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : IODataReader.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.exception.IOReadException; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; + +/** + * Data Writer Interface for Pi4J Data Communications + * + * @author Robert Savage + * + * Based on previous contributions from: + * Daniel Sendula, + * RasPelikan + */ +public interface IODataReader { + + int read() throws IOException; + int read(ByteBuffer buffer, int offset, int length) throws IOException; + + // ------------------------------------------------------------------------------------------------------------ + + default int read(byte[] buffer, int offset, int length) throws IOException{ + ByteBuffer bb = ByteBuffer.wrap(buffer); + return read(bb, offset, length); + } + default int read(byte[] buffer, int length) throws IOException{ + return read(buffer, 0, length); + } + default int read(byte[] buffer) throws IOException{ + return read(buffer, 0, buffer.length); + } + default int read(ByteBuffer buffer, int length) throws IOException{ + return read(buffer, 0, length); + } + default int read(ByteBuffer buffer) throws IOException{ + return read(buffer, 0, buffer.capacity()); + } + + // ------------------------------------------------------------------------------------------------------------ + + default String readString(int length, Charset charset) throws IOException, IOReadException { + byte[] temp = new byte[length]; + int actual = read(temp, 0, length); + if(actual < 0) throw new IOReadException(actual); + return new String(temp, 0, actual, charset); + } + + default String readString(int length) throws IOException, IOReadException { + return readString(length, StandardCharsets.US_ASCII); + } + + /** + * Read a single byte value (16-bit) from the raw I2C device (not a register). + * + * @return The 16-bit word value + * @throws IOException thrown on write error + */ + default byte readByte() throws IOException{ + int actual = read(); + if(actual < 0) { + System.out.println(actual); + throw new IOException("I2C READ ERROR; " + actual); + } + return (byte)actual; + } + + /** + * Read a single word value (16-bit) from the raw I2C device (not a register). + * + * @return The 16-bit word value + * @throws IOException thrown on write error + */ + default int readWord() throws IOException, IOReadException { + byte[] buffer = new byte[2]; + int actual = read(buffer); + if(actual < 2) throw new IOReadException(actual); + return ((buffer[0] & 0xff) << 8) | (buffer[1] & 0xff); + } + + default ByteBuffer readBuffer(int length) throws IOException, IOReadException { + byte[] temp = new byte[length]; + int actual = read(temp, 0, length); + if(actual < 0) throw new IOReadException(actual); + return ByteBuffer.wrap(temp, 0, actual); + } + + default byte[] readArray(int length) throws IOException, IOReadException { + byte[] temp = new byte[length]; + int actual = read(temp, 0, length); + if(actual < 0) throw new IOReadException(actual); + return Arrays.copyOf(temp, actual); + } + + default InputStream getInputStream(){ + var t = this; + return new InputStream() { + @Override + public int read() throws IOException { + return t.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + Objects.checkFromIndexSize(off, len, b.length); + return t.read(b, off, len); + } + }; + } + + default InputStream in() { + return getInputStream(); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/IODataWriter.java b/pi4j-api/src/main/java/com/pi4j/io/IODataWriter.java new file mode 100644 index 000000000..9e4a2e843 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/IODataWriter.java @@ -0,0 +1,214 @@ +package com.pi4j.io; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : IODataWriter.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * Data Writer Interface for Pi4J Data Communications + * + * @author Robert Savage + * + * Based on previous contributions from: + * Daniel Sendula, + * RasPelikan + */ +public interface IODataWriter { + + /** + * Write a single raw byte value. + * + * @param b byte to be written + * @throws IOException thrown on write error + */ + void write(byte b) throws IOException; + + /** + * Write a buffer of byte values with given offset (starting position) and length in the provided data buffer. + * + * @param buffer byte buffer of data to be written + * @param offset offset in data buffer to start at + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + int write(ByteBuffer buffer, int offset, int length) throws IOException; + + /** + * Write a single raw byte value. + * + * @param b byte to be written + * @throws IOException thrown on write error + */ + default void write(int b) throws IOException{ + write((byte)b); + } + + /** + * Write a single word value (16-bit) to the raw I2C device. + * + * @param word 16-bit word value to be written + * @throws IOException thrown on write error + */ + default void writeWord(int word) throws IOException{ + byte[] buffer = new byte[] { (byte)(word >> 8), (byte)word }; + this.write(buffer); + } + + /** + * Write an array of byte values with given offset (starting position) and length in the provided data array. + * + * @param data data array of bytes to be written + * @param offset offset in data buffer to start at + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(byte[] data, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, data.length); + return write(ByteBuffer.wrap(data), offset, length); + } + + /** + * Write an array of byte values starting with the first byte in the array up to the provided length. + * + * @param data data array of bytes to be written + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(byte[] data, int length) throws IOException{ + return write(data, 0, length); + } + + /** + * Write an array of byte values (all bytes in array). + * + * @param data data array of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(byte[] data) throws IOException{ + return write(data, 0, data.length); + } + + /** + * Write a buffer of byte values starting with the first byte in the array up to the provided length. + * + * @param buffer byte buffer of data to be written + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(ByteBuffer buffer, int length) throws IOException{ + return write(buffer, 0, length); + } + + /** + * Write a buffer of byte values (all bytes in buffer). + * + * @param buffer byte buffer of data to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(ByteBuffer buffer) throws IOException{ + return write(buffer, 0, buffer.capacity()); + } + + /** + * Write a buffer of byte values (all bytes in buffer). + * + * @param stream stream of data to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(InputStream stream) throws IOException{ + return write(stream.readAllBytes()); + } + + /** + * Writes an ASCII data string. + * + * @param data string data (US_ASCII) to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(String data) throws IOException{ + return write(data, StandardCharsets.US_ASCII); + } + + /** + * Writes a data string with specified character set (encoding). + * + * @param data string data (US_ASCII) to be written + * @param charset character set to use for byte encoding + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int write(String data, Charset charset) throws IOException{ + return write(data.getBytes(charset)); + } + + /** + * Get an output stream to write data to + * @return new output stream instance to write to + */ + default OutputStream getOutputStream(){ + var t = this; + return new OutputStream() { + @Override + public void write(int b) throws IOException { + t.write((byte)b);; + } + + @Override + public void write(byte b[]) throws IOException { + t.write(b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + this.write(b, off, len); + } + }; + } + + /** + * Get an output stream to write data to + * @return new output stream instance to write to + */ + default OutputStream out() { + return getOutputStream(); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/exception/IOReadException.java b/pi4j-api/src/main/java/com/pi4j/io/exception/IOReadException.java new file mode 100644 index 000000000..d4da1e7aa --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/exception/IOReadException.java @@ -0,0 +1,56 @@ +package com.pi4j.io.exception; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : IOReadException.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + + +/** + *

+ * This exception is thrown if a platform assignment is attempted when a + * platform instance has already been assigned. + *

+ * + * @see http://www.pi4j.com/ + * @author Robert Savage (http://www.savagehomeautomation.com) + */ +public class IOReadException extends IOException { + + /** + * Default Constructor + */ + public IOReadException(String message){ + super(message); + } + + /** + * Alternate Constructor + */ + public IOReadException(int error){ + super("I/O READ ERROR: " + error); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogInput.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogInput.java index 252335a77..4e5944eec 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogInput.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogInput.java @@ -30,5 +30,8 @@ import com.pi4j.io.Input; public interface AnalogInput extends Analog, Input { + static AnalogInputConfigBuilder newConfigBuilder(){ + return AnalogInputConfigBuilder.newInstance(); + } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogOutput.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogOutput.java index 6edccbba5..ec46e2e9e 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogOutput.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/analog/AnalogOutput.java @@ -32,11 +32,14 @@ import com.pi4j.io.exception.IOIllegalValueException; public interface AnalogOutput extends Analog, Output { - AnalogOutput value(Integer value) throws IOIllegalValueException, IOBoundsException; + static AnalogOutputConfigBuilder newConfigBuilder(){ + return AnalogOutputConfigBuilder.newInstance(); + } + + AnalogOutput value(Integer value) throws IOIllegalValueException, IOBoundsException; AnalogOutput stepUp(); AnalogOutput stepDown(); AnalogOutput step(Integer value) throws IOIllegalValueException, IOBoundsException; - default AnalogOutput setValue(Integer value) throws IOIllegalValueException, IOBoundsException { return value(value); }; } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalBase.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalBase.java index 46e9a3ca5..f715fa3f0 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalBase.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalBase.java @@ -28,6 +28,7 @@ */ import com.pi4j.context.Context; +import com.pi4j.exception.ShutdownException; import com.pi4j.io.gpio.GpioBase; import com.pi4j.io.gpio.digital.binding.DigitalBinding; @@ -91,7 +92,7 @@ protected void dispatch(DigitalChangeEvent event){ } @Override - public DIGITAL_TYPE shutdown(Context context){ + public DIGITAL_TYPE shutdown(Context context) throws ShutdownException { // remove all listeners listeners.clear(); diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInput.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInput.java index 91281113a..4a646a67f 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInput.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInput.java @@ -31,5 +31,10 @@ import com.pi4j.io.Input; public interface DigitalInput extends Digital, Input { + + static DigitalInputConfigBuilder newConfigBuilder(){ + return DigitalInputConfigBuilder.newInstance(); + } + default PullResistance pull() { return config().pull(); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputBase.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputBase.java index ae4f645f1..7ad003796 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputBase.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputBase.java @@ -30,6 +30,5 @@ public abstract class DigitalInputBase extends DigitalBase implements DigitalInput { public DigitalInputBase(DigitalInputProvider provider, DigitalInputConfig config){ super(provider, config); - this.name = (config.name() != null) ? config.name() : "DIN-" + config.address(); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputProvider.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputProvider.java index 1d0640a99..0c85d7271 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputProvider.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalInputProvider.java @@ -29,27 +29,41 @@ public interface DigitalInputProvider extends DigitalProvider { - default T create(Integer address) throws Exception { - var builder = DigitalInputConfigBuilder.newInstance(); - builder.address(address); + default T create(DigitalInputConfigBuilder builder) throws Exception { return (T)create(builder.build()); } + default T create(Integer address) throws Exception { + var config = DigitalInput.newConfigBuilder() + .address(address) + .build(); + return (T)create(config); + } + default T create(Integer address, String id) throws Exception { - var builder = DigitalInputConfigBuilder.newInstance(); - builder.id(id).address(address).id(id); - return (T)create(builder.build()); + var config = DigitalInput.newConfigBuilder() + .address(address) + .id(id) + .build(); + return (T)create(config); } default T create(Integer address, String id, String name) throws Exception { - var builder = DigitalInputConfigBuilder.newInstance(); - builder.id(id).address(address).id(id).name(name); - return (T)create(builder.build()); + var config = DigitalInput.newConfigBuilder() + .address(address) + .id(id) + .name(name) + .build(); + return (T)create(config); } default T create(Integer address, String id, String name, String description) throws Exception { - var builder = DigitalInputConfigBuilder.newInstance(); - builder.id(id).address(address).id(id).name(name).description(description); - return (T)create(builder.build()); + var config = DigitalInput.newConfigBuilder() + .address(address) + .id(id) + .name(name) + .description(description) + .build(); + return (T)create(config); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutput.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutput.java index ae986910c..ab18903eb 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutput.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutput.java @@ -28,6 +28,7 @@ */ import com.pi4j.io.Output; +import com.pi4j.io.exception.IOException; import java.util.concurrent.Callable; import java.util.concurrent.Future; @@ -35,48 +36,52 @@ public interface DigitalOutput extends Digital, Output { - DigitalOutput state(DigitalState state); - DigitalOutput pulse(int interval, TimeUnit unit, DigitalState state, Callable callback); + static DigitalOutputConfigBuilder newConfigBuilder(){ + return DigitalOutputConfigBuilder.newInstance(); + } + + DigitalOutput state(DigitalState state) throws IOException; + DigitalOutput pulse(int interval, TimeUnit unit, DigitalState state, Callable callback) throws IOException; Future pulseAsync(int interval, TimeUnit unit, DigitalState state, Callable callback); DigitalOutput blink(int delay, int duration, TimeUnit unit, DigitalState state, Callable callback); Future blinkAsync(int delay, int duration, TimeUnit unit, DigitalState state, Callable callback); - default DigitalOutput setState(boolean state){ + default DigitalOutput setState(boolean state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput setState(byte state){ + default DigitalOutput setState(byte state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput setState(short state){ + default DigitalOutput setState(short state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput setState(int state){ + default DigitalOutput setState(int state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput setState(long state){ + default DigitalOutput setState(long state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput setState(float state){ + default DigitalOutput setState(float state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput setState(double state){ + default DigitalOutput setState(double state) throws IOException { return this.state(DigitalState.getState(state)); } - default DigitalOutput high(){ + default DigitalOutput high() throws IOException { return this.state(DigitalState.HIGH); } - default DigitalOutput low(){ + default DigitalOutput low() throws IOException { return this.state(DigitalState.LOW); } - default DigitalOutput toggle(){ + default DigitalOutput toggle() throws IOException { return this.state(DigitalState.getInverseState(this.state())); } - default DigitalOutput pulseHigh(int interval, TimeUnit unit){ + default DigitalOutput pulseHigh(int interval, TimeUnit unit) throws IOException { return pulse(interval, unit, DigitalState.HIGH); } - default DigitalOutput pulseLow(int interval, TimeUnit unit){ + default DigitalOutput pulseLow(int interval, TimeUnit unit) throws IOException { return pulse(interval, unit, DigitalState.LOW); } @@ -88,10 +93,10 @@ default Future pulseLowAsync(int interval, TimeUnit unit, Callable call return pulseAsync(interval, unit, DigitalState.LOW, callback); } - default DigitalOutput pulse(int interval, TimeUnit unit){ + default DigitalOutput pulse(int interval, TimeUnit unit) throws IOException { return pulse(interval, unit, DigitalState.HIGH); } - default DigitalOutput pulse(int interval, TimeUnit unit, DigitalState state){ + default DigitalOutput pulse(int interval, TimeUnit unit, DigitalState state) throws IOException { return pulse(interval, unit, state, null); } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputBase.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputBase.java index 696cf78ac..05d5e4a62 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputBase.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputBase.java @@ -28,6 +28,9 @@ */ import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.exception.ShutdownException; +import com.pi4j.io.exception.IOException; import java.util.concurrent.Callable; import java.util.concurrent.Future; @@ -39,16 +42,25 @@ public abstract class DigitalOutputBase extends DigitalBase callback){ + public DigitalOutput pulse(int interval, TimeUnit unit, DigitalState state, Callable callback) throws IOException { int millis = 0; // validate arguments @@ -97,17 +109,20 @@ public DigitalOutput pulse(int interval, TimeUnit unit, DigitalState state, Call @Override public Future pulseAsync(int interval, TimeUnit unit, DigitalState state, Callable callback) { - return null; + // TODO :: IMPLEMENT DIGITAL OUTPUT PULSE ASYNC + throw new UnsupportedOperationException("PULSE ASYNC has not yet been implemented!"); } @Override public DigitalOutput blink(int delay, int duration, TimeUnit unit, DigitalState state, Callable callback) { - return null; + // TODO :: IMPLEMENT DIGITAL OUTPUT BLINK + throw new UnsupportedOperationException("BLINK has not yet been implemented!"); } @Override public Future blinkAsync(int delay, int duration, TimeUnit unit, DigitalState state, Callable callback) { - return null; + // TODO :: IMPLEMENT DIGITAL OUTPUT BLINK ASYNC + throw new UnsupportedOperationException("BLINK ASYNC has not yet been implemented!"); } @Override @@ -116,11 +131,15 @@ public DigitalState state() { } @Override - public DigitalOutput shutdown(Context context){ + public DigitalOutput shutdown(Context context) throws ShutdownException { // set pin state to shutdown state if a shutdown state is configured if(config().shutdownState() != null && config().shutdownState() != DigitalState.UNKNOWN){ - state(config().shutdownState()); + try { + state(config().shutdownState()); + } catch (IOException e) { + throw new ShutdownException(e); + } } - return this; + return super.shutdown(context); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputProvider.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputProvider.java index bae42d0c9..605a94c34 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputProvider.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalOutputProvider.java @@ -28,28 +28,41 @@ */ public interface DigitalOutputProvider extends DigitalProvider { - default T create(Integer address) throws Exception { - var builder = DigitalOutputConfigBuilder.newInstance(); - builder.address(address); + default T create(DigitalOutputConfigBuilder builder) throws Exception { return (T)create(builder.build()); } + default T create(Integer address) throws Exception { + var config = DigitalOutput.newConfigBuilder() + .address(address) + .build(); + return (T)create(config); + } + default T create(Integer address, String id) throws Exception { - var builder = DigitalOutputConfigBuilder.newInstance(); - builder.id(id).address(address).id(id); - return (T)create(builder.build()); + var config = DigitalOutput.newConfigBuilder() + .id(id) + .address(address) + .build(); + return (T)create(config); } default T create(Integer address, String id, String name) throws Exception { - var builder = DigitalOutputConfigBuilder.newInstance(); - builder.id(id).address(address).id(id).name(name); - return (T)create(builder.build()); + var config = DigitalOutput.newConfigBuilder() + .address(address) + .id(id) + .name(name) + .build(); + return (T)create(config); } default T create(Integer address, String id, String name, String description) throws Exception { - var builder = DigitalOutputConfigBuilder.newInstance(); - builder.id(id).address(address).id(id).name(name).description(description); - return (T)create(builder.build()); + var config = DigitalOutput.newConfigBuilder() + .address(address) + .id(id) + .name(name) + .description(description) + .build(); + return (T)create(config); } - } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalState.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalState.java index 80864c6fa..288cb6be3 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalState.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/DigitalState.java @@ -57,6 +57,10 @@ public boolean isLow() { return (this == LOW); } + public Number value() { + return getValue(); + } + public Number getValue() { return value; } @@ -99,6 +103,10 @@ public String toString() { return name; } + public static DigitalState state(Number state) { + return getState(state); + } + public static DigitalState getState(Number state) { for (var item : DigitalState.values()) { if (item.getValue().intValue() == state.intValue()) { @@ -108,6 +116,10 @@ public static DigitalState getState(Number state) { return null; } + public static DigitalState inverseState(DigitalState state) { + return getInverseState(state); + } + public static DigitalState getInverseState(DigitalState state) { return (state == HIGH ? LOW : HIGH); } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingInverseSync.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingInverseSync.java index c40225f72..46263760b 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingInverseSync.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingInverseSync.java @@ -27,10 +27,13 @@ * #L% */ +import com.pi4j.io.exception.IOException; import com.pi4j.io.gpio.digital.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DigitalBindingInverseSync extends DigitalBindingBase implements DigitalBinding { - + private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * Default Constructor * @param target Variable argument list of analog outputs @@ -42,7 +45,11 @@ public DigitalBindingInverseSync(DigitalOutput ... target){ @Override public void process(DigitalChangeEvent event) { targets.forEach((target)->{ - ((DigitalOutput)target).state(DigitalState.getInverseState(event.state())); + try { + ((DigitalOutput)target).state(DigitalState.getInverseState(event.state())); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } }); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingSync.java b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingSync.java index 3a49df6bf..9b708b67b 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingSync.java +++ b/pi4j-api/src/main/java/com/pi4j/io/gpio/digital/binding/DigitalBindingSync.java @@ -27,13 +27,18 @@ * #L% */ +import com.pi4j.io.exception.IOException; import com.pi4j.io.gpio.digital.DigitalChangeEvent; import com.pi4j.io.gpio.digital.DigitalOutput; import com.pi4j.io.gpio.digital.DigitalOutputConfig; import com.pi4j.io.gpio.digital.DigitalOutputProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DigitalBindingSync extends DigitalBindingBase implements DigitalBinding { + private Logger logger = LoggerFactory.getLogger(this.getClass()); + /** * Default Constructor * @param target Variable argument list of analog outputs @@ -45,7 +50,11 @@ public DigitalBindingSync(DigitalOutput ... target){ @Override public void process(DigitalChangeEvent event) { targets.forEach((target)->{ - ((DigitalOutput)target).state(event.state()); + try { + ((DigitalOutput)target).state(event.state()); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } }); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2C.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2C.java index 1e499cc82..d783b6def 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2C.java +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2C.java @@ -27,176 +27,82 @@ * #L% */ -import com.pi4j.context.Context; import com.pi4j.io.IO; -import com.pi4j.io.i2c.impl.I2CFactory; -import com.pi4j.provider.exception.ProviderException; - -import java.io.IOException; +import com.pi4j.io.IODataReader; +import com.pi4j.io.IODataWriter; /** - * This is abstraction of an i2c device. It allows data to be read or written to the device. + * I2C I/O Interface for Pi4J I2C Bus/Device Communications * - * @author Daniel Sendula, refactored by RasPelikan + * @author Robert Savage * + * Based on previous contributions from: + * Daniel Sendula, + * RasPelikan */ -public interface I2C extends IO { - - static final String ID = "I2C"; - - static I2C instance(Context context, String device, int address) throws ProviderException { - return I2CFactory.instance(context, device, address); - } - - static I2C instance(Context context, I2CConfig config) throws ProviderException { - return I2CFactory.instance(context, config); - } - - static I2C instance(Context context, String providerId, String device, int address) throws ProviderException { - return I2CFactory.instance(context, providerId, device, address); - } - - static I2C instance(Context context, String providerId, I2CConfig config) throws ProviderException { - return I2CFactory.instance(context, providerId, config); +public interface I2C extends IO, + IODataWriter, + IODataReader, + I2CRegisterDataReaderWriter, + AutoCloseable { + + static I2CConfigBuilder newConfigBuilder(){ + return I2CConfigBuilder.newInstance(); } - static I2C instance(I2CProvider provider, String device, int address) throws ProviderException { - return I2CFactory.instance(provider, device ,address); - } - - static I2C instance(I2CProvider provider, I2CConfig config) throws ProviderException { - return I2CFactory.instance(provider, config); - } - - /** - * @return The address for which this instance is constructed for. - */ - int getAddress(); - - /** - * This method writes one byte directly to i2c device. - * - * @param b byte to be written - * - * @throws IOException thrown in case byte cannot be written to the i2c device or i2c bus - */ - void write(byte b) throws IOException; - - /** - * This method writes several bytes directly to the i2c device from given buffer at given offset. - * - * @param buffer buffer of data to be written to the i2c device in one go - * @param offset offset in buffer - * @param size number of bytes to be written - * - * @throws IOException thrown in case byte cannot be written to the i2c device or i2c bus - */ - void write(byte[] buffer, int offset, int size) throws IOException; - /** - * This method writes all bytes included in the given buffer directly to the i2c device. - * - * @param buffer buffer of data to be written to the i2c device in one go - * - * @throws IOException thrown in case byte cannot be written to the i2c device or i2c bus + * I2C Device Address + * @return The I2C device address for which this instance is constructed for. */ - void write(byte[] buffer) throws IOException; - - /** - * This method writes one byte to i2c device. - * - * @param address local address in the i2c device - * @param b byte to be written - * - * @throws IOException thrown in case byte cannot be written to the i2c device or i2c bus - */ - void write(int address, byte b) throws IOException; - - /** - * This method writes several bytes to the i2c device from given buffer at given offset. - * - * @param address local address in the i2c device - * @param buffer buffer of data to be written to the i2c device in one go - * @param offset offset in buffer - * @param size number of bytes to be written - * - * @throws IOException thrown in case byte cannot be written to the i2c device or i2c bus - */ - void write(int address, byte[] buffer, int offset, int size) throws IOException; + default int device(){ + return config().device(); + } /** - * This method writes all bytes included in the given buffer directoy to the register address on the i2c device - * - * @param address local address in the i2c device - * @param buffer buffer of data to be written to the i2c device in one go - * - * @throws IOException thrown in case byte cannot be written to the i2c device or i2c bus + * I2C Bus Address + * @return The I2C bus address for which this instance is constructed for. */ - void write(int address, byte[] buffer) throws IOException; + default int bus(){ + return config().bus(); + } /** - * This method reads one byte from the i2c device. - * Result is between 0 and 255 if read operation was successful, else a negative number for an error. - * - * @return byte value read: positive number (or zero) to 255 if read was successful. Negative number if reading failed. - * - * @throws IOException thrown in case byte cannot be read from the i2c device or i2c bus + * I2C Device Communication State is OPEN + * @return The I2C device communication state */ - int read() throws IOException; + boolean isOpen(); /** - * This method reads bytes directly from the i2c device to given buffer at asked offset. - * - * @param buffer buffer of data to be read from the i2c device in one go - * @param offset offset in buffer - * @param size number of bytes to be read - * - * @return number of bytes read - * - * @throws IOException thrown in case byte cannot be read from the i2c device or i2c bus + * I2C Bus Address + * @return The I2C bus address for which this instance is constructed for. */ - int read(byte[] buffer, int offset, int size) throws IOException; + default int getBus(){ + return bus(); + } /** - * This method reads one byte from the i2c device. - * Result is between 0 and 255 if read operation was successful, else a negative number for an error. - * - * @param address local address in the i2c device - * @return byte value read: positive number (or zero) to 255 if read was successful. Negative number if reading failed. - * - * @throws IOException thrown in case byte cannot be read from the i2c device or i2c bus + * I2C Device Address + * @return The I2C device address for which this instance is constructed for. */ - int read(int address) throws IOException; + default int getDevice(){ + return device(); + } /** - * This method reads bytes from the i2c device to given buffer at asked offset. - * - * @param address local address in the i2c device - * @param buffer buffer of data to be read from the i2c device in one go - * @param offset offset in buffer - * @param size number of bytes to be read - * - * @return number of bytes read - * - * @throws IOException thrown in case byte cannot be read from the i2c device or i2c bus + * Get an encapsulated interface for reading and writing to a specific I2C device register + * @param address + * @return */ - int read(int address, byte[] buffer, int offset, int size) throws IOException; + I2CRegister getRegister(int address); /** - * This method writes and reads bytes to/from the i2c device in a single method call - * - * @param writeBuffer buffer of data to be written to the i2c device in one go - * @param writeOffset offset in write buffer - * @param writeSize number of bytes to be written from buffer - * @param readBuffer buffer of data to be read from the i2c device in one go - * @param readOffset offset in read buffer - * @param readSize number of bytes to be read - * - * @return number of bytes read - * - * @throws IOException thrown in case byte cannot be read from the i2c device or i2c bus + * I2C Device Register + * Get an encapsulated interface for reading and writing to a specific I2C device register + * @param address the (16-bit) device register address + * @return an instance of I2CRegister for the provided register address */ - int read(byte[] writeBuffer, int writeOffset, int writeSize, byte[] readBuffer, int readOffset, int readSize) throws IOException; - + default I2CRegister register(int address){ + return getRegister(address); + } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CBase.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CBase.java index 0d6cafd0c..e2241aad9 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CBase.java +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CBase.java @@ -27,11 +27,54 @@ * #L% */ +import com.pi4j.context.Context; +import com.pi4j.exception.ShutdownException; import com.pi4j.io.IOBase; +import com.pi4j.io.i2c.impl.DefaultI2CRegister; + +import java.io.IOException; public abstract class I2CBase extends IOBase implements I2C { + protected boolean isOpen = false; + public I2CBase(I2CProvider provider, I2CConfig config) { super(provider, config); + this.name = config.name(); + this.id = config.id(); + this.description = config.description(); + this.isOpen = true; + } + + @Override + public boolean isOpen() { + return this.isOpen; + } + + @Override + public void close() throws IOException { + this.isOpen = false; + } + + /** + * Get an encapsulated interface for reading and writing to a specific I2C device register + * @param address + * @return + */ + public I2CRegister getRegister(int address){ + return new DefaultI2CRegister(this, address); + } + + @Override + public I2C shutdown(Context context) throws ShutdownException { + // if this I2C device is still open, then we need to close it since we are shutting down + if(this.isOpen()) { + try { + this.close(); + } catch (Exception e) { + throw new ShutdownException(e); + } + } + return (I2C)this; } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfig.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfig.java index 03f399e0c..9127abffe 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfig.java +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfig.java @@ -27,16 +27,24 @@ * #L% */ -import com.pi4j.config.impl.DeviceConfigBase; import com.pi4j.io.IOConfig; -public class I2CConfig extends DeviceConfigBase implements IOConfig { +public interface I2CConfig extends IOConfig { - private I2CConfig(){ - super(); + String BUS_KEY = "bus"; + String DEVICE_KEY = "device"; + + Integer bus(); + default Integer getBus() { + return bus(); + } + + Integer device(); + default Integer getDevice() { + return device(); } - public I2CConfig(String device, int address){ - //super(device, address); + static I2CConfigBuilder newBuilder() { + return I2CConfigBuilder.newInstance(); } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfigBuilder.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfigBuilder.java new file mode 100644 index 000000000..e4403826e --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CConfigBuilder.java @@ -0,0 +1,40 @@ +package com.pi4j.io.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CConfigBuilder.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.config.ConfigBuilder; +import com.pi4j.io.i2c.impl.DefaultI2CConfigBuilder; + +public interface I2CConfigBuilder extends ConfigBuilder { + static I2CConfigBuilder newInstance() { + return DefaultI2CConfigBuilder.newInstance(); + } + + I2CConfigBuilder bus(Integer bus); + I2CConfigBuilder device(Integer device); +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CProvider.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CProvider.java index 1cfa97dd6..b06edb3ca 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CProvider.java +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CProvider.java @@ -30,5 +30,47 @@ import com.pi4j.provider.Provider; public interface I2CProvider extends Provider { - //I2C instance(I2CConfig config) throws Exception; + + default T create(I2CConfigBuilder builder) throws Exception { + return (T)create(builder.build()); + } + + default T create(Integer bus, Integer device) throws Exception { + var config = I2C.newConfigBuilder() + .bus(bus) + .device(device) + .build(); + return (T)create(config); + } + + default T create(Integer bus, Integer device, String id) throws Exception { + var config = I2C.newConfigBuilder() + .bus(bus) + .device(device) + .id(id) + .build(); + return (T)create(config); + } + + default T create(Integer bus, Integer device, String id, String name) throws Exception { + var config = I2C.newConfigBuilder() + .bus(bus) + .device(device) + .id(id) + .name(name) + .build(); + return (T)create(config); + } + + default T create(Integer bus, Integer device, String id, String name, String description) throws Exception { + var config = I2C.newConfigBuilder() + .bus(bus) + .device(device) + .id(id) + .name(name) + .description(description) + .build(); + return (T)create(config); + } + } diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegister.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegister.java new file mode 100644 index 000000000..66c306c77 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegister.java @@ -0,0 +1,60 @@ +package com.pi4j.io.i2c; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CRegister.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.IODataReader; +import com.pi4j.io.IODataWriter; +import com.pi4j.io.exception.IOReadException; + +import java.io.IOException; + +/** + * I2C Device Register. + * This abstraction allows data to be read or written to a specific device register on the I2C bus. + * + * @author Robert Savage + */ +public interface I2CRegister extends IODataWriter, IODataReader { + /** + * @return This I2C device register address + */ + int getAddress(); + default int address(){ + return getAddress(); + } + + /** + * Write a single word value (16-bit) to the I2C device register + * and immediately reads back a 16-bit word value. + * + * @param word 16-bit word value to be written + * @return The 16-bit word value read/returned; or a negative value if error + * @throws IOException thrown on write error + */ + int writeReadWord(int word) throws IOException, IOReadException; +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataReader.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataReader.java new file mode 100644 index 000000000..d4ff983eb --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataReader.java @@ -0,0 +1,112 @@ +package com.pi4j.io.i2c; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CRegisterDataReader.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.exception.IOReadException; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * I2C Register Data Writer Interface for Pi4J Data Communications + * + * @author Robert Savage + * + * Based on previous contributions from: + * Daniel Sendula, + * RasPelikan + */ +public interface I2CRegisterDataReader { + + // ------------------------------------------------------------------------------------------------------------ + + int readRegister(int register) throws IOException; + int readRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException; + + // ------------------------------------------------------------------------------------------------------------ + + default int readRegister(int register, byte[] buffer, int offset, int length) throws IOException{ + ByteBuffer bb = ByteBuffer.wrap(buffer); + return readRegister(register, bb, offset, length); + } + default int readRegister(int register, byte[] buffer, int length) throws IOException{ + return readRegister(register, buffer, 0, length); + } + default int readRegister(int register, byte[] buffer) throws IOException{ + return readRegister(register, buffer, 0, buffer.length); + } + default int readRegister(int register, ByteBuffer buffer, int length) throws IOException{ + return readRegister(register, buffer, 0, length); + } + default int readRegister(int register, ByteBuffer buffer) throws IOException{ + return readRegister(register, buffer, 0, buffer.capacity()); + } + + // ------------------------------------------------------------------------------------------------------------ + + default String readRegisterString(int register, int length) throws IOException, IOReadException { + return readRegisterString(register, length, StandardCharsets.US_ASCII); + } + + default String readRegisterString(int register, int length, Charset charset) throws IOException, IOReadException { + byte[] temp = new byte[length]; + int actual = readRegister(register, temp, 0, length); + if(actual < 0) throw new IOReadException(actual); + return new String(temp, 0, actual, charset); + } + + default byte readRegisterByte(int register) throws IOException, IOReadException { + int actual = readRegister(register); + if(actual < 0) throw new IOReadException(actual); + return (byte)actual; + } + + default int readRegisterWord(int register) throws IOException, IOReadException { + byte[] buffer= new byte[2]; + int actual = readRegister(register, buffer); + if(actual < 2) throw new IOReadException(actual); + return ((buffer[0] & 0xff) << 8) | (buffer[1] & 0xff); + } + + default ByteBuffer readRegisterBuffer(int register, int length) throws IOException, IOReadException { + byte[] temp = new byte[length]; + int actual = readRegister(register, temp, 0, length); + if(actual < 0) throw new IOReadException(actual); + return ByteBuffer.wrap(temp, 0, actual); + } + + default byte[] readRegisterArray(int register, int length) throws IOException, IOReadException { + byte[] temp = new byte[length]; + int actual = readRegister(register, temp, 0, length); + if(actual < 0) throw new IOReadException(actual); + return Arrays.copyOf(temp, actual); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataReaderWriter.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataReaderWriter.java new file mode 100644 index 000000000..83cb9282a --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataReaderWriter.java @@ -0,0 +1,57 @@ +package com.pi4j.io.i2c; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CRegisterDataReaderWriter.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.exception.IOReadException; + +import java.io.IOException; + +/** + * I2C Register Data Writer Interface for Pi4J Data Communications + * + * @author Robert Savage + * + * Based on previous contributions from: + * Daniel Sendula, + * RasPelikan + */ +public interface I2CRegisterDataReaderWriter extends I2CRegisterDataReader, I2CRegisterDataWriter { + /** + * Write a single word value (16-bit) to the I2C device register + * and immediately reads back a 16-bit word value. + * + * @param register the register address to write to + * @param word 16-bit word value to be written + * @return The 16-bit word value read/returned; or a negative value if error + * @throws IOException thrown on write error + */ + default int writeReadRegisterWord(int register, int word) throws IOException, IOReadException { + writeRegisterWord(register, word); + return readRegisterWord(register); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataWriter.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataWriter.java new file mode 100644 index 000000000..65e92ebc4 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/I2CRegisterDataWriter.java @@ -0,0 +1,193 @@ +package com.pi4j.io.i2c; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : I2CRegisterDataWriter.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * I2C Register Data Writer Interface for Pi4J Data Communications + * + * @author Robert Savage + * + * Based on previous contributions from: + * Daniel Sendula, + * RasPelikan + */ +public interface I2CRegisterDataWriter { + + /** + * Write a single raw byte (8-bit) value to the I2C device register. + * + * @param register the register address to write to + * @param b byte to be written + * @throws IOException thrown on write error + */ + void writeRegister(int register, byte b) throws IOException; + + /** + * Write a single raw byte (8-bit) value to the I2C device register. + * + * @param register the register address to write to + * @param b byte to be written; the provided Integer wll be cast to a Byte. + * @throws IOException thrown on write error + */ + default void writeRegister(int register, int b) throws IOException{ + writeRegister(register, (byte)b); + } + + /** + * Write a single word value (16-bit) to the I2C device register. + * + * @param register the register address to write to + * @param word 16-bit word value to be written + * @throws IOException thrown on write error + */ + default void writeRegisterWord(int register, int word) throws IOException{ + byte[] buffer = new byte[] { (byte)(word >> 8), (byte)word }; + this.writeRegister(register, buffer); + } + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param buffer byte buffer of data to be written to the i2c device in one go + * @param offset offset in buffer + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + int writeRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException; + + /** + * Write a array of byte values with given offset (starting position) + * and length in the provided data array to a specific I2C device register. + * + * @param register the register address to write to + * @param data data array of bytes to be written + * @param offset offset in data buffer to start at + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, byte[] data, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, data.length); + return writeRegister(register, ByteBuffer.wrap(data), offset, length); + } + + /** + * Write a array of byte values starting with the first byte in the array up to the provided length. + * + * @param register the register address to write to + * @param data data array of bytes to be written + * @param length number of bytes to be written + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, byte[] data, int length) throws IOException{ + return writeRegister(register, data, 0, length); + } + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param data data to be written to the i2c device in one go + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, byte[] data) throws IOException{ + return writeRegister(register, data, 0, data.length); + } + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param buffer byte buffer of data to be written to the i2c device in one go + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, ByteBuffer buffer, int length) throws IOException{ + return writeRegister(register, buffer, 0, length); + } + + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param buffer byte buffer of data to be written to the i2c device in one go + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, ByteBuffer buffer) throws IOException{ + return writeRegister(register, buffer, 0, buffer.capacity()); + } + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param stream stream of data to be written to the i2c device in one go + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, InputStream stream) throws IOException{ + return writeRegister(register, stream.readAllBytes()); + } + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param data string data to be written to the i2c device in one go + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, String data) throws IOException{ + return writeRegister(register, data.getBytes(StandardCharsets.US_ASCII)); + } + + /** + * This method writes all bytes included in the given buffer directly to the i2c device. + * + * @param register the register address to write to + * @param data string data to be written to the i2c device in one go + * @return The number of bytes written, possibly zero + * @throws IOException thrown on write error + */ + default int writeRegister(int register, String data, Charset charset) throws IOException{ + return writeRegister(register, data.getBytes(charset)); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CConfig.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CConfig.java new file mode 100644 index 000000000..dc36bc42b --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CConfig.java @@ -0,0 +1,92 @@ +package com.pi4j.io.i2c.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultI2CConfig.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.config.ConfigBase; +import com.pi4j.config.exception.ConfigMissingRequiredKeyException; +import com.pi4j.io.gpio.digital.PullResistance; +import com.pi4j.io.i2c.I2CConfig; +import com.pi4j.util.StringUtil; + +import java.util.Map; + +public class DefaultI2CConfig + extends ConfigBase + implements I2CConfig { + + // private configuration properties + protected Integer bus = null; + protected Integer device = null; + + /** + * PRIVATE CONSTRUCTOR + */ + private DefaultI2CConfig(){ + super(); + } + + // private configuration properties + protected PullResistance pullResistance = PullResistance.OFF; + + /** + * PRIVATE CONSTRUCTOR + * @param properties + */ + protected DefaultI2CConfig(Map properties){ + super(properties); + + // define default property values if any are missing (based on the required address value) + this.id = StringUtil.setIfNullOrEmpty(this.id, "I2C-" + this.bus() + "." + this.device(), true); + this.name = StringUtil.setIfNullOrEmpty(this.name, "I2C-" + this.bus() + "." + this.device(), true); + this.description = StringUtil.setIfNullOrEmpty(this.description, "I2C-" + this.bus() + "." + this.device(), true); + + // load (required) BUS property + if(properties.containsKey(BUS_KEY)){ + this.bus = Integer.parseInt(properties.get(BUS_KEY)); + } else { + throw new ConfigMissingRequiredKeyException(BUS_KEY); + } + + // load (required) DEVICE property + if(properties.containsKey(DEVICE_KEY)){ + this.device = Integer.parseInt(properties.get(DEVICE_KEY)); + } else { + throw new ConfigMissingRequiredKeyException(DEVICE_KEY); + } + } + + @Override + public Integer bus() { + return this.bus; + } + + @Override + public Integer device() { + return this.device; + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CConfigBuilder.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CConfigBuilder.java new file mode 100644 index 000000000..4c762d874 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CConfigBuilder.java @@ -0,0 +1,66 @@ +package com.pi4j.io.i2c.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultI2CConfigBuilder.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.config.impl.ConfigBuilderBase; +import com.pi4j.io.i2c.I2CConfig; +import com.pi4j.io.i2c.I2CConfigBuilder; + +public class DefaultI2CConfigBuilder + extends ConfigBuilderBase + implements I2CConfigBuilder { + + /** + * PRIVATE CONSTRUCTOR + */ + protected DefaultI2CConfigBuilder(){ + super(); + } + + public static I2CConfigBuilder newInstance() { + return new DefaultI2CConfigBuilder(); + } + + @Override + public I2CConfig build() { + I2CConfig config = new DefaultI2CConfig(properties); + return config; + } + + @Override + public I2CConfigBuilder bus(Integer bus){ + this.properties.put(I2CConfig.BUS_KEY, bus.toString()); + return this; + } + + @Override + public I2CConfigBuilder device(Integer device){ + this.properties.put(I2CConfig.DEVICE_KEY, device.toString()); + return this; + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CRegister.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CRegister.java new file mode 100644 index 000000000..3a557ad88 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/DefaultI2CRegister.java @@ -0,0 +1,107 @@ +package com.pi4j.io.i2c.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultI2CRegister.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.exception.IOReadException; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CRegister; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Objects; + +public class DefaultI2CRegister implements I2CRegister { + + protected final int address; + protected final I2C i2c; + + public DefaultI2CRegister(I2C i2c, int address){ + this.i2c = i2c; + this.address = address; + } + + @Override + public int getAddress() { + return this.address; + } + + @Override + public void write(byte b) throws IOException { + this.i2c.writeRegister(this.address, b); + } + + /** + * Write a single word value (16-bit) to the I2C device register. + * + * @param word 16-bit word value to be written + * @return The number of bytes written, possibly zero; typically 2 + * @throws IOException thrown on write error + */ + @Override + public void writeWord(int word) throws IOException{ + this.i2c.writeRegisterWord(this.address, word); + } + + @Override + public int readWord() throws IOException, IOReadException { + return this.i2c.readRegisterWord(this.address); + } + + @Override + public int writeReadWord(int word) throws IOException, IOReadException { + return this.i2c.writeReadRegisterWord(this.address, word); + } + + @Override + public int write(ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + return this.i2c.writeRegister(this.address, buffer, offset, length); + } + + @Override + public int write(String data, Charset charset) throws IOException { + return this.i2c.writeRegister(this.address, data, charset); + } + + @Override + public int read() throws IOException { + return this.i2c.readRegister(this.address); + } + + @Override + public int read(ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + return this.i2c.readRegister(this.address, buffer, offset, length); + } + + @Override + public String readString(int length, Charset charset) throws IOException, IOReadException { + return this.i2c.readRegisterString(this.address, length, charset); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/I2CFactory.java b/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/I2CFactory.java deleted file mode 100644 index 2fb728148..000000000 --- a/pi4j-api/src/main/java/com/pi4j/io/i2c/impl/I2CFactory.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.pi4j.io.i2c.impl; - -/* - * #%L - * ********************************************************************** - * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: LIBRARY :: Java Library (API) - * FILENAME : I2CFactory.java - * - * This file is part of the Pi4J project. More information about - * this project can be found here: https://pi4j.com/ - * ********************************************************************** - * %% - * Copyright (C) 2012 - 2019 Pi4J - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import com.pi4j.context.Context; -import com.pi4j.io.i2c.I2C; -import com.pi4j.io.i2c.I2CConfig; -import com.pi4j.io.i2c.I2CProvider; -import com.pi4j.provider.exception.ProviderException; - -/** - * @author Robert Savage (http://www .savagehomeautomation.com) - */ -public class I2CFactory { - - // private constructor - private I2CFactory() { - // forbid object construction - } - - public static I2C instance(Context context, I2CConfig config) throws ProviderException { - // get I2C instance using default io - var provider = context.platform().i2c(); - return instance(provider, config); - } - - public static I2C instance(Context context, String device, int address) throws ProviderException { - return instance(context, new I2CConfig(device, address)); - } - - public static I2C instance(Context context, String providerId, String device, int address) throws ProviderException { - return instance(context, providerId, new I2CConfig(device, address)); - } - - public static I2C instance(Context context, String providerId, I2CConfig config) throws ProviderException { - // if provided, lookup the specified io; else use the default io - if(providerId == null) { - return instance(context, config); - } - else{ - var provider = context.providers().i2c().get(providerId); - return instance(provider, config); - } - } - - public static I2C instance(I2CProvider provider, String device, int address) throws ProviderException { - return instance(provider, new I2CConfig(device, address)); - } - - public static I2C instance(I2CProvider provider, I2CConfig config) throws ProviderException { - try { - // create a I2C instance using the io - return provider.create(config); - } catch(ProviderException pe){ - throw pe; - } catch (Exception e) { - throw new ProviderException(provider, e); - } - } -} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/Pwm.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/Pwm.java index c55ce82dc..f9a7b1881 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/pwm/Pwm.java +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/Pwm.java @@ -27,36 +27,100 @@ * #L% */ -import com.pi4j.context.Context; import com.pi4j.io.IO; -import com.pi4j.io.pwm.impl.PwmFactory; -import com.pi4j.provider.exception.ProviderException; + +import java.io.IOException; +import java.util.Map; public interface Pwm extends IO { - static final String ID = "PWM"; + static PwmConfigBuilder newConfigBuilder(){ + return PwmConfigBuilder.newInstance(); + } + + default int getAddress(){ + return config().address(); + } + default int address(){ + return getAddress(); + } + + boolean isOn(); + Pwm on() throws IOException; + Pwm off() throws IOException; + + default PwmType pwmType(){ + return config().getPwmType(); + } + default PwmType getPwmType(){ + return pwmType(); + } + + default Pwm on(int dutyCycle) throws IOException{ + if(dutyCycle > 0) { + setDutyCycle(dutyCycle); + return on(); + } + else{ + return off(); + } + } - static Pwm instance(Context context, PwmConfig config) throws ProviderException { - return PwmFactory.instance(context, config); + default Pwm on(int dutyCycle, int frequency) throws IOException{ + if(dutyCycle > 0 && frequency > 0) { + setDutyCycle(dutyCycle); + setFrequency(frequency); + return on(); + } + else{ + return off(); + } } - static Pwm instance(Context context, int address) throws ProviderException { - return PwmFactory.instance(context, address); + default boolean isOff(){ + return !isOn(); } - static Pwm instance(Context context, String providerId, int address) throws ProviderException { - return PwmFactory.instance(context, providerId, address); + default float getDutyCyclePercent() throws IOException{ + return getDutyCycle() * 100 / getRange(); } + default float dutyCyclePercent() throws IOException { return getDutyCyclePercent();} + + int getDutyCycle() throws IOException; + default int dutyCycle() throws IOException { return getDutyCycle();} + + int getFrequency() throws IOException; + default int frequency() throws IOException { return getFrequency();} + + void setDutyCycle(int dutyCycle) throws IOException; + default Pwm dutyCycle(int dutyCycle) throws IOException { setDutyCycle(dutyCycle); return this; } - static Pwm instance(Context context, String providerId, PwmConfig config) throws ProviderException { - return PwmFactory.instance(context, providerId, config); + default void setDutyCyclePercent(float percent) throws IOException{ + int dutyCycle = Math.round(this.range() * percent / 100); + setDutyCycle(dutyCycle); } + default Pwm dutyCyclePercent(int percent) throws IOException { setDutyCyclePercent(percent); return this; } - static Pwm instance(PwmProvider provider, int address) throws ProviderException { - return PwmFactory.instance(provider, address); + void setFrequency(int frequency) throws IOException; + default Pwm frequency(int frequency) throws IOException { setFrequency(frequency); return this; } + + int getRange() throws IOException; + default int range() throws IOException { return getRange();} + + void setRange(int range) throws IOException; + default Pwm range(int range) throws IOException { setRange(range); return this; } + + Map getPresets(); + default Map presets(){ + return getPresets(); } - static Pwm instance(PwmProvider provider, PwmConfig config) throws ProviderException { - return PwmFactory.instance(provider, config); + PwmPreset getPreset(String name); + default PwmPreset preset(String name){ + return getPreset(name); } + + PwmPreset deletePreset(String name); + Pwm addPreset(PwmPreset preset); + Pwm applyPreset(String name) throws IOException; } diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmBase.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmBase.java index 4c9a42312..a3ba689e6 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmBase.java +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmBase.java @@ -27,12 +27,142 @@ * #L% */ +import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.exception.ShutdownException; import com.pi4j.io.IOBase; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public abstract class PwmBase extends IOBase implements Pwm { + protected int range = -1; + protected int frequency = -1; + protected int dutyCycle = -1; + protected boolean onState = false; + protected Map presets = Collections.synchronizedMap(new HashMap<>()); + public PwmBase(PwmProvider provider, PwmConfig config) { super(provider, config); - this.name = "PWM-" + config.address(); + this.name = config.name(); + this.id = config.id(); + this.description = config.description(); + for(PwmPreset preset : config.presets()){ + this.presets.put(preset.name().toLowerCase().trim(), preset); + } + } + + @Override + public int getDutyCycle() throws IOException { + return this.dutyCycle; + } + + @Override + public int getFrequency() throws IOException { + return this.frequency; + } + + @Override + public void setDutyCycle(int dutyCycle) throws IOException { + this.dutyCycle = dutyCycle; + } + + @Override + public void setFrequency(int frequency) throws IOException { + this.frequency = frequency; + } + + @Override + public int getRange() throws IOException { + return this.range; + } + + @Override + public void setRange(int range) throws IOException { + this.range = range; + } + + @Override + public boolean isOn() { + return this.onState; + } + + @Override + public Pwm initialize(Context context) throws InitializeException { + // apply an initial value if configured + if(this.config.initialValue() != null){ + try { + this.on(this.config.initialValue()); + } catch (IOException e) { + throw new InitializeException(e); + } + } + return this; + } + + @Override + public Pwm shutdown(Context context) throws ShutdownException { + // apply a shutdown value if configured + if(this.config.shutdownValue() != null){ + try { + if(this.config.shutdownValue() <= 0){ + this.off(); + } else { + this.on(this.config.shutdownValue()); + } + } catch (IOException e) { + throw new ShutdownException(e); + } + } + return this; + } + + @Override + public Map getPresets(){ + return Collections.unmodifiableMap(this.presets); + } + + @Override + public PwmPreset getPreset(String name){ + String key = name.toLowerCase().trim(); + if(presets.containsKey(key)) { + return presets.get(key); + } + return null; + } + + @Override + public PwmPreset deletePreset(String name){ + String key = name.toLowerCase().trim(); + if(presets.containsKey(key)) { + return presets.remove(key); + } + return null; + } + + @Override + public Pwm addPreset(PwmPreset preset){ + String key = preset.name().toLowerCase().trim(); + presets.put(key, preset); + return this; + } + + @Override + public Pwm applyPreset(String name) throws IOException { + String key = name.toLowerCase().trim(); + if(presets.containsKey(key)) { + PwmPreset preset = presets.get(key); + if(preset.dutyCycle() != null) + setDutyCycle(preset.dutyCycle().intValue()); + if(preset.frequency() != null) + setFrequency(preset.frequency().intValue()); + on(); // update PWM signal now + } else{ + throw new IOException("PWM PRESET NOT FOUND: "+ name); + } + return this; } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfig.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfig.java index 674bfaa17..852124289 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfig.java +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfig.java @@ -27,12 +27,64 @@ * #L% */ -import com.pi4j.config.impl.AddressConfigBase; -import com.pi4j.io.IOConfig; +import com.pi4j.config.AddressConfig; +import com.pi4j.io.gpio.GpioConfig; -public class PwmConfig extends AddressConfigBase implements IOConfig { +import java.util.Collection; - public PwmConfig(Number address) { - super(); +public interface PwmConfig extends GpioConfig, AddressConfig { + + String PWM_TYPE_KEY = "pwm-type"; + String FREQUENCY_KEY = "frequency"; + String RANGE_KEY = "range"; + String DUTY_CYCLE_KEY = "duty-cycle"; + String DUTY_CYCLE_PERCENT_KEY = "duty-cycle-percent"; + String SHUTDOWN_VALUE_KEY = "shutdown"; + String INITIAL_VALUE_KEY = "initial"; + String PRESET_KEY = "applyPreset"; + + Integer dutyCycle(); + default Integer getDutyCycle() { + return dutyCycle(); + } + + Integer dutyCyclePercent(); + default Integer getDutyCyclePercent() { + return dutyCyclePercent(); + } + + Integer range(); + default Integer getRange() { + return range(); + } + + Integer frequency(); + default Integer getFrequency() { + return frequency(); + } + + PwmType pwmType(); + default PwmType getPwmType(){ + return pwmType(); + } + + Integer shutdownValue(); + PwmConfig shutdownValue(Integer value); + default Integer getShutdownValue(){ + return shutdownValue(); } + default void setShutdownValue(Integer value){ + this.shutdownValue(value); + } + + Integer initialValue(); + default Integer getInitialValue(){ + return initialValue(); + } + + Collection presets(); + default Collection getPresets(){ + return presets(); + } + } diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfigBuilder.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfigBuilder.java new file mode 100644 index 000000000..b2dd3ad69 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmConfigBuilder.java @@ -0,0 +1,46 @@ +package com.pi4j.io.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : PwmConfigBuilder.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.gpio.GpioConfigBuilder; +import com.pi4j.io.pwm.impl.DefaultPwmConfigBuilder; + +public interface PwmConfigBuilder extends GpioConfigBuilder { + static PwmConfigBuilder newInstance() { + return DefaultPwmConfigBuilder.newInstance(); + } + + PwmConfigBuilder range(Integer range); + PwmConfigBuilder frequency(Integer frequency); + PwmConfigBuilder dutyCycle(Integer dutyCycle); + PwmConfigBuilder dutyCyclePercent(Integer percent); + PwmConfigBuilder pwmType(PwmType pwmType); + PwmConfigBuilder shutdown(Integer value); + PwmConfigBuilder initial(Integer value); + PwmConfigBuilder preset(PwmPreset ... preset); +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmPreset.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmPreset.java new file mode 100644 index 000000000..9131d3fca --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmPreset.java @@ -0,0 +1,52 @@ +package com.pi4j.io.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : PwmPreset.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.pwm.impl.DefaultPwmPresetBuilder; + +public interface PwmPreset { + + static PwmPresetBuilder newBuilder(String name){ + return DefaultPwmPresetBuilder.newInstance(name); + } + + String name(); + default String getName() { + return name(); + } + + Integer dutyCycle(); + default Integer getDutyCycle() { + return dutyCycle(); + } + + Integer frequency(); + default Integer getFrequency() { + return frequency(); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmPresetBuilder.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmPresetBuilder.java new file mode 100644 index 000000000..145c6844b --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmPresetBuilder.java @@ -0,0 +1,39 @@ +package com.pi4j.io.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : PwmPresetBuilder.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.config.Builder; +import com.pi4j.io.pwm.impl.DefaultPwmPresetBuilder; + +public interface PwmPresetBuilder extends Builder { + static PwmPresetBuilder newInstance(String name) { + return DefaultPwmPresetBuilder.newInstance(name); + } + PwmPresetBuilder frequency(Integer frequency); + PwmPresetBuilder dutyCycle(Integer dutyCycle); +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmProvider.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmProvider.java index e3da04e4e..a259d9bb1 100644 --- a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmProvider.java +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmProvider.java @@ -30,5 +30,42 @@ import com.pi4j.provider.Provider; public interface PwmProvider extends Provider { - //Pwm instance(PwmConfig config) throws Exception; + + default T create(PwmConfigBuilder builder) throws Exception { + return (T)create(builder.build()); + } + + default T create(Integer address) throws Exception { + var config = Pwm.newConfigBuilder() + .address(address) + .build(); + return (T)create(config); + } + + default T create(Integer address, String id) throws Exception { + var config = Pwm.newConfigBuilder() + .address(address) + .id(id) + .build(); + return (T)create(config); + } + + default T create(Integer address, String id, String name) throws Exception { + var config = Pwm.newConfigBuilder() + .address(address) + .id(id) + .name(name) + .build(); + return (T)create(config); + } + + default T create(Integer address, String id, String name, String description) throws Exception { + var config = Pwm.newConfigBuilder() + .address(address) + .id(id) + .name(name) + .description(description) + .build(); + return (T)create(config); + } } diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmType.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmType.java new file mode 100644 index 000000000..b318efeb8 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/PwmType.java @@ -0,0 +1,74 @@ +package com.pi4j.io.pwm; + +import java.util.EnumSet; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : PwmType.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +/** + * Digital Pin Pull Resistance Enumerations + * + * @author Robert Savage (http://www.savagehomeautomation.com) + */ +public enum PwmType { + SOFTWARE(0, "software"), + HARDWARE(1, "hardware"); + + private final int value; + private final String name; + + private PwmType(int value, String name) { + this.value = value; + this.name = name; + } + + public int getValue() { + return value; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name.toUpperCase(); + } + + public static EnumSet all() { + return EnumSet.allOf(PwmType.class); + } + + public static PwmType parse(String pull) { + if(pull.equalsIgnoreCase("0")) return PwmType.SOFTWARE; + if(pull.equalsIgnoreCase("1")) return PwmType.HARDWARE; + if(pull.toLowerCase().startsWith("h")) return PwmType.HARDWARE; + if(pull.toLowerCase().startsWith("s")) return PwmType.SOFTWARE; + return PwmType.SOFTWARE; // default + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmConfig.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmConfig.java new file mode 100644 index 000000000..945c1f10b --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmConfig.java @@ -0,0 +1,184 @@ +package com.pi4j.io.pwm.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultPwmConfig.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.config.impl.AddressConfigBase; +import com.pi4j.io.gpio.digital.PullResistance; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmPreset; +import com.pi4j.io.pwm.PwmType; +import com.pi4j.util.StringUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class DefaultPwmConfig + extends AddressConfigBase + implements PwmConfig { + + // private configuration properties + protected Integer dutyCycle = null; + protected Integer dutyCyclePercent = null; + protected Integer range = null; + protected Integer frequency = null; + protected PwmType pwmType = PwmType.SOFTWARE; + protected Integer shutdownValue = null; + protected Integer initialValue = null; + protected List presets = new ArrayList<>(); + + /** + * PRIVATE CONSTRUCTOR + */ + private DefaultPwmConfig(){ + super(); + } + + // private configuration properties + protected PullResistance pullResistance = PullResistance.OFF; + + /** + * PRIVATE CONSTRUCTOR + * @param properties + */ + protected DefaultPwmConfig(Map properties, Collection presets){ + this(properties); + this.presets.addAll(presets); + } + + /** + * PRIVATE CONSTRUCTOR + * @param properties + */ + protected DefaultPwmConfig(Map properties){ + super(properties); + + // define default property values if any are missing (based on the required address value) + this.id = StringUtil.setIfNullOrEmpty(this.id, "PWM-" + this.address, true); + this.name = StringUtil.setIfNullOrEmpty(this.name, "PWM-" + this.address, true); + this.description = StringUtil.setIfNullOrEmpty(this.description, "PWM-" + this.address, true); + + // load optional pwm duty-cycle from properties + if(properties.containsKey(DUTY_CYCLE_KEY)){ + this.dutyCycle = Integer.parseInt(properties.get(DUTY_CYCLE_KEY)); + } + + // load optional pwm duty-cycle from properties + if(properties.containsKey(DUTY_CYCLE_PERCENT_KEY)){ + this.dutyCyclePercent = Integer.parseInt(properties.get(DUTY_CYCLE_PERCENT_KEY)); + } + + // load optional pwm frequency from properties + if(properties.containsKey(FREQUENCY_KEY)){ + this.frequency = Integer.parseInt(properties.get(FREQUENCY_KEY)); + } + + // load optional pwm range from properties + if(properties.containsKey(RANGE_KEY)){ + this.range = Integer.parseInt(properties.get(RANGE_KEY)); + } + + // load optional pwm type from properties + if(properties.containsKey(PWM_TYPE_KEY)){ + this.pwmType = PwmType.parse(properties.get(PWM_TYPE_KEY)); + } + + // load initial value property + if(properties.containsKey(INITIAL_VALUE_KEY)){ + this.initialValue = Integer.parseInt(properties.get(INITIAL_VALUE_KEY)); + } + + // load shutdown value property + if(properties.containsKey(SHUTDOWN_VALUE_KEY)){ + this.shutdownValue = Integer.parseInt(properties.get(SHUTDOWN_VALUE_KEY)); + } + + // bounds checking + if(this.dutyCyclePercent != null && this.dutyCyclePercent > 100) + this.dutyCyclePercent = 100; + + // bounds checking + if(this.dutyCyclePercent != null && this.dutyCyclePercent < 0) + this.dutyCyclePercent = 0; + } + + @Override + public Integer dutyCycle() { + return this.dutyCycle; + } + + @Override + public Integer range() { + return this.range; + } + + @Override + public Integer frequency() { + return this.frequency; + } + + @Override + public PwmType pwmType() { + return this.pwmType; + } + + @Override + public Integer shutdownValue(){ + return this.shutdownValue; + } + + @Override + public Integer dutyCyclePercent(){ + // bounds checking + if(this.dutyCyclePercent != null && this.dutyCyclePercent > 100) + this.dutyCyclePercent = 100; + + // bounds checking + if(this.dutyCyclePercent != null && this.dutyCyclePercent < 0) + this.dutyCyclePercent = 0; + + return this.dutyCyclePercent; + } + + @Override + public PwmConfig shutdownValue(Integer value){ + this.shutdownValue = value; + return this; + } + + @Override + public Integer initialValue() { + return this.initialValue; + } + + @Override + public Collection presets(){ + return this.presets; + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmConfigBuilder.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmConfigBuilder.java new file mode 100644 index 000000000..e2dd120ad --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmConfigBuilder.java @@ -0,0 +1,120 @@ +package com.pi4j.io.pwm.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultPwmConfigBuilder.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.config.impl.AddressConfigBuilderBase; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmConfigBuilder; +import com.pi4j.io.pwm.PwmPreset; +import com.pi4j.io.pwm.PwmType; + +import java.util.ArrayList; +import java.util.List; + +public class DefaultPwmConfigBuilder + extends AddressConfigBuilderBase + implements PwmConfigBuilder { + + protected List presets = new ArrayList<>(); + + /** + * PRIVATE CONSTRUCTOR + */ + protected DefaultPwmConfigBuilder(){ + super(); + } + + public static PwmConfigBuilder newInstance() { + return new DefaultPwmConfigBuilder(); + } + + @Override + public PwmConfigBuilder range(Integer range) { + this.properties.put(PwmConfig.RANGE_KEY, range.toString()); + return this; + } + + @Override + public PwmConfigBuilder frequency(Integer frequency) { + this.properties.put(PwmConfig.FREQUENCY_KEY, frequency.toString()); + return this; + } + + @Override + public PwmConfigBuilder dutyCycle(Integer dutyCycle) { + this.properties.put(PwmConfig.DUTY_CYCLE_KEY, dutyCycle.toString()); + return this; + } + + @Override + public PwmConfigBuilder dutyCyclePercent(Integer percent) { + + // bounds checking + if(percent != null && percent > 100) + percent = 100; + + // bounds checking + if(percent != null && percent < 0) + percent = 0; + + this.properties.put(PwmConfig.DUTY_CYCLE_PERCENT_KEY, percent.toString()); + return this; + } + + @Override + public PwmConfigBuilder pwmType(PwmType pwmType) { + this.properties.put(PwmConfig.PWM_TYPE_KEY, pwmType.toString()); + return this; + } + + @Override + public PwmConfigBuilder shutdown(Integer value) { + this.properties.put(PwmConfig.SHUTDOWN_VALUE_KEY, value.toString()); + return this; + } + + @Override + public PwmConfigBuilder initial(Integer value) { + this.properties.put(PwmConfig.INITIAL_VALUE_KEY, value.toString()); + return this; + } + + @Override + public PwmConfigBuilder preset(PwmPreset ... preset){ + for(PwmPreset p : preset) { + this.presets.add(p); + } + return this; + } + + @Override + public PwmConfig build() { + PwmConfig config = new DefaultPwmConfig(this.properties, this.presets); + return config; + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmPreset.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmPreset.java new file mode 100644 index 000000000..0bc312ea3 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmPreset.java @@ -0,0 +1,64 @@ +package com.pi4j.io.pwm.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultPwmPreset.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.pwm.PwmPreset; + +public class DefaultPwmPreset implements PwmPreset { + + protected final String name; + protected final Integer dutyCycle; + protected final Integer frequency; + + public DefaultPwmPreset(String name, Integer dutyCycle){ + this.name = name.toLowerCase().trim(); + this.dutyCycle = dutyCycle; + this.frequency = null; + } + + public DefaultPwmPreset(String name, Integer dutyCycle, Integer frequency){ + this.name = name.toLowerCase().trim(); + this.dutyCycle = dutyCycle; + this.frequency = frequency; + } + + @Override + public String name() { + return this.name; + } + + @Override + public Integer dutyCycle() { + return this.dutyCycle; + } + + @Override + public Integer frequency() { + return this.frequency; + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmPresetBuilder.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmPresetBuilder.java new file mode 100644 index 000000000..09414fc3f --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/DefaultPwmPresetBuilder.java @@ -0,0 +1,64 @@ +package com.pi4j.io.pwm.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultPwmPresetBuilder.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.io.pwm.PwmPreset; +import com.pi4j.io.pwm.PwmPresetBuilder; + +public class DefaultPwmPresetBuilder implements PwmPresetBuilder{ + protected Integer dutyCycle = null; + protected Integer frequency = null; + protected final String name; + + /** + * PRIVATE CONSTRUCTOR + */ + protected DefaultPwmPresetBuilder(String name){ + super(); this.name = name; + } + + public static PwmPresetBuilder newInstance(String name) { + return new DefaultPwmPresetBuilder(name); + } + + @Override + public PwmPresetBuilder frequency(Integer frequency) { + this.frequency = frequency; + return this; + } + + @Override + public PwmPresetBuilder dutyCycle(Integer dutyCycle) { + this.dutyCycle = dutyCycle; + return this; + } + @Override + public PwmPreset build() { + return new DefaultPwmPreset(this.name, this.dutyCycle, this.frequency); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/PwmFactory.java b/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/PwmFactory.java deleted file mode 100644 index 052bdee07..000000000 --- a/pi4j-api/src/main/java/com/pi4j/io/pwm/impl/PwmFactory.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.pi4j.io.pwm.impl; - -/* - * #%L - * ********************************************************************** - * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: LIBRARY :: Java Library (API) - * FILENAME : PwmFactory.java - * - * This file is part of the Pi4J project. More information about - * this project can be found here: https://pi4j.com/ - * ********************************************************************** - * %% - * Copyright (C) 2012 - 2019 Pi4J - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import com.pi4j.context.Context; -import com.pi4j.io.pwm.Pwm; -import com.pi4j.io.pwm.PwmConfig; -import com.pi4j.io.pwm.PwmProvider; -import com.pi4j.provider.exception.ProviderException; - -/** - * @author Robert Savage (http://www .savagehomeautomation.com) - */ -public class PwmFactory { - - // private constructor - private PwmFactory() { - // forbid object construction - } - - public static Pwm instance(Context context, PwmConfig config) throws ProviderException { - // get I2C instance using default io - var provider = context.platform().pwm(); - return instance(provider, config); - } - - public static Pwm instance(Context context, int address) throws ProviderException { - return instance(context, new PwmConfig(address)); - } - - public static Pwm instance(Context context, String providerId, int address) throws ProviderException { - return instance(context, providerId, new PwmConfig(address)); - } - - public static Pwm instance(Context context, String providerId, PwmConfig config) throws ProviderException { - // if provided, lookup the specified io; else use the default io - if(providerId == null) { - return instance(context, config); - } - else{ - PwmProvider provider = context.providers().pwm().get(providerId); - return instance(provider, config); - } - } - - public static Pwm instance(PwmProvider provider, int address) throws ProviderException { - return instance(provider, new PwmConfig(address)); - } - - public static Pwm instance(PwmProvider provider, PwmConfig config) throws ProviderException { - try { - // create a PWM instance using the io - return provider.create(config); - } catch(ProviderException pe){ - throw pe; - } catch (Exception e) { - throw new ProviderException(provider, e); - } - } -} diff --git a/pi4j-api/src/main/java/com/pi4j/runtime/Runtime.java b/pi4j-api/src/main/java/com/pi4j/runtime/Runtime.java index 70efae21e..083939130 100644 --- a/pi4j-api/src/main/java/com/pi4j/runtime/Runtime.java +++ b/pi4j-api/src/main/java/com/pi4j/runtime/Runtime.java @@ -39,6 +39,7 @@ public interface Runtime { RuntimeRegistry registry(); RuntimeProviders providers(); RuntimePlatforms platforms(); + RuntimeProperties properties(); Context context(); Runtime inject(Object... objects) throws AnnotationException; diff --git a/pi4j-api/src/main/java/com/pi4j/runtime/RuntimeProperties.java b/pi4j-api/src/main/java/com/pi4j/runtime/RuntimeProperties.java new file mode 100644 index 000000000..8bb95ec22 --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/runtime/RuntimeProperties.java @@ -0,0 +1,40 @@ +package com.pi4j.runtime; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : RuntimeProperties.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.context.ContextProperties; + +import java.util.Map; +import java.util.Properties; + +public interface RuntimeProperties extends ContextProperties { + void put(String key, String value); + void put(Properties properties); + void put(Map values); + void put(Map.Entry ... value); +} diff --git a/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java b/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java index df0716878..9ef62c36d 100644 --- a/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java +++ b/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntime.java @@ -46,13 +46,11 @@ import com.pi4j.registry.impl.DefaultRuntimeRegistry; import com.pi4j.registry.impl.RuntimeRegistry; import com.pi4j.runtime.Runtime; +import com.pi4j.runtime.RuntimeProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; -import java.util.HashSet; -import java.util.ServiceLoader; -import java.util.Set; +import java.util.*; public class DefaultRuntime implements Runtime { @@ -62,6 +60,9 @@ public class DefaultRuntime implements Runtime { private RuntimeRegistry registry = null; private RuntimeProviders providers = null; private RuntimePlatforms platforms = null; + private RuntimeProperties properties = null; + private List plugins = new ArrayList<>(); + private boolean isShutdown = false; public static Runtime newInstance(Context context) throws Pi4JException { return new DefaultRuntime(context); @@ -72,12 +73,26 @@ private DefaultRuntime(Context context) throws Pi4JException { // set local references this.context = context; + this.properties = DefaultRuntimeProperties.newInstance(context); this.annotationEngine = DefaultAnnotationEngine.newInstance(context); this.registry = DefaultRuntimeRegistry.newInstance(this); this.providers = DefaultRuntimeProviders.newInstance(this); this.platforms = DefaultRuntimePlatforms.newInstance(this); logger.debug("Pi4J runtime context successfully created & initialized.'"); + + // listen for shutdown to properly clean up + // TODO :: ADD PI4J INTERNAL SHUTDOWN CALLBACKS/EVENTS + java.lang.Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + // shutdown Pi4J + shutdown(); + } + catch (Exception e) { + e.printStackTrace(); + } + })); + } @Override @@ -96,6 +111,11 @@ public RuntimePlatforms platforms() { return this.platforms; } + @Override + public RuntimeProperties properties() { + return this.properties; + } + @Override public Runtime inject(Object... objects) throws AnnotationException { annotationEngine.inject(objects); @@ -104,24 +124,36 @@ public Runtime inject(Object... objects) throws AnnotationException { @Override public Runtime shutdown() throws ShutdownException { - logger.trace("invoked 'shutdown();'"); - try { - // shutdown all providers - this.providers.shutdown(); - - // shutdown platforms - this.platforms.shutdown(); - - // remove all I/O instances - this.registry.shutdown(); + if(!isShutdown) { // re-entrant calls should not perform shutdown again + isShutdown = true; + logger.trace("invoked 'shutdown();'"); + try { + // remove all I/O instances + this.registry.shutdown(); + + // shutdown platforms + this.platforms.shutdown(); + + // shutdown all providers + this.providers.shutdown(); + + // shutdown all plugins + for (Plugin plugin : this.plugins) { + try { + plugin.shutdown(this.context); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + } catch (Exception e) { + logger.error("failed to 'shutdown(); '", e); + throw new ShutdownException(e); + } - } catch (Exception e) { - logger.error("failed to 'shutdown(); '", e); - throw new ShutdownException(e); + logger.debug("Pi4J context/runtime successfully shutdown.'"); + } else{ + logger.debug("Pi4J context/runtime is already shutdown.'"); } - - logger.debug("Pi4J context/runtime successfully shutdown.'"); - return this; } @@ -129,6 +161,9 @@ public Runtime shutdown() throws ShutdownException { public Runtime initialize() throws InitializeException { logger.trace("invoked 'initialize();'"); try { + // clear plugins container + plugins.clear(); + // container sets for providers and platforms to load Set providers = Collections.synchronizedSet(new HashSet<>()); Set platforms = Collections.synchronizedSet(new HashSet<>()); @@ -147,6 +182,9 @@ public Runtime initialize() throws InitializeException { logger.trace("detected plugin: [{}] in classpath; calling 'initialize()'", plugin.getClass().getName()); try { + // add plugin to internal cache + this.plugins.add(plugin); + PluginStore store = new PluginStore(); plugin.initialize(DefaultPluginService.newInstance(this.context(), store)); diff --git a/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntimeProperties.java b/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntimeProperties.java new file mode 100644 index 000000000..c636ef19d --- /dev/null +++ b/pi4j-api/src/main/java/com/pi4j/runtime/impl/DefaultRuntimeProperties.java @@ -0,0 +1,225 @@ +package com.pi4j.runtime.impl; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: Java Library (API) + * FILENAME : DefaultRuntimeProperties.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.context.Context; +import com.pi4j.runtime.RuntimeProperties; +import com.pi4j.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +public class DefaultRuntimeProperties implements RuntimeProperties { + + public static String PI4J_PROPERTIES_FILE_NAME = "pi4j.properties"; + + protected Map properties = Collections.synchronizedMap(new HashMap<>()); + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + // static singleton instance + public static RuntimeProperties newInstance(Context context){ + return new DefaultRuntimeProperties(context); + } + + private DefaultRuntimeProperties(Context context){ + // now lets load optional Pi4J.properties files from the file system + + // first; load any default Pi4J properties defined in the Environment Variables + try{ + // use "pi4j." prefix filter to limit the environment variables that we care about + put(System.getenv(), "pi4j."); + } + catch (Exception e){ + logger.error(e.getMessage(), e); + } + + // first; load system-scoped Pi4J properties file + // /etc/pi4j/pi4j.properties + try { + Path appFile = Paths.get("/etc/pi4j", PI4J_PROPERTIES_FILE_NAME); + if (Files.exists(appFile)) { + Properties p = new Properties(); + p.load(new FileInputStream(appFile.toFile())); + put(p); + } + } + catch (Exception e){ + logger.error(e.getMessage(), e); + } + + // second; load user-scoped Pi4J properties file + // ~/.pi4j.properties + try { + Path appFile = Paths.get(System.getProperty("user.home"), "." + PI4J_PROPERTIES_FILE_NAME); + if (Files.exists(appFile)) { + Properties p = new Properties(); + p.load(new FileInputStream(appFile.toFile())); + put(p); + } + } + catch (Exception e){ + logger.error(e.getMessage(), e); + } + + // third; load an application-scoped Pi4J properties file + // {pwd}/pi4j.properties + try { + Path appFile = Paths.get(System.getProperty("user.dir"), PI4J_PROPERTIES_FILE_NAME); + if (Files.exists(appFile)) { + Properties p = new Properties(); + p.load(new FileInputStream(appFile.toFile())); + put(p); + } + } + catch (Exception e){ + logger.error(e.getMessage(), e); + } + + // fourth; load an application-embedded resource Pi4J properties file + // {app}/{resources}/pi4j.properties + try { + URL resource = getClass().getClassLoader().getResource(PI4J_PROPERTIES_FILE_NAME); + if(resource != null) { + File resourceFile = new File(resource.getFile()); + if (resourceFile != null && resourceFile.exists()) { + Properties p = new Properties(); + p.load(new FileInputStream(resourceFile)); + put(p); + } + } + } + catch (Exception e){ + logger.error(e.getMessage(), e); + } + + // finally; load any default properties defined in the System.properties + try{ + // use "pi4j." prefix filter to limit the system properties that we care about + put(System.getProperties(), "pi4j."); + } + catch (Exception e){ + logger.error(e.getMessage(), e); + } + + + // add all context pre-configured properties to the runtime properties cache + this.put(context.config().properties()); + } + + protected String sanitizeKey(String key){ + return key.trim().toLowerCase(); + } + + @Override + public boolean has(String key) { + return properties.containsKey(sanitizeKey(key)); + } + + @Override + public String get(String key) { + String k = sanitizeKey(key); + + // first, attempt to get property for internal cache + if(properties.containsKey(k)){ + return properties.get(k); + } + return null; + } + + @Override + public void put(String key, String value) { + properties.put(sanitizeKey(key), value); + } + + @Override + public void put(Properties properties) { + properties.forEach((key,value)->{ + this.properties.put(sanitizeKey(key.toString()), value.toString()); + }); + } + + @Override + public void put(Map values) { + values.forEach((key,value)->{ + this.properties.put(sanitizeKey(key), value); + }); + } + + @Override + public void put(Map.Entry... value) { + for(Map.Entry e : value){ + this.properties.put(sanitizeKey(e.getKey().toString()), e.getValue().toString()); + } + } + + @Override + public Map all() { + return Collections.unmodifiableMap(this.properties); + } + + @Override + public int count() { + return this.properties.size(); + } + + + protected void put(Properties properties, String prefixFilter){ + // convert java.util.Properties to a Map object + Map entries = properties.keySet().stream() + .collect(Collectors.toMap(k->k.toString(), key->properties.get(key).toString())); + put(entries, prefixFilter); + } + + protected void put(Map properties, String prefixFilter){ + + // if a filter was not provided, then load properties without a filter + if(StringUtil.isNullOrEmpty(prefixFilter)) { + put(properties); + return; + } + + // sanitize the prefix filter and make sure it includes a "." character at the end + var prefix = (prefixFilter.endsWith(".")) ? prefixFilter : prefixFilter+"."; + + // iterate the properties object and assign any key with the prefix filter to this config + properties.keySet().stream().filter(key -> key.startsWith(prefix)).forEach((key)->{ + put(key.substring(prefix.length()), properties.get(key)); + }); + } +} diff --git a/pi4j-api/src/main/java/com/pi4j/util/StringUtil.java b/pi4j-api/src/main/java/com/pi4j/util/StringUtil.java index b946be59a..325ac3336 100644 --- a/pi4j-api/src/main/java/com/pi4j/util/StringUtil.java +++ b/pi4j-api/src/main/java/com/pi4j/util/StringUtil.java @@ -28,6 +28,9 @@ */ +import java.nio.ByteBuffer; +import java.util.Arrays; + public class StringUtil { public static final String EMPTY = ""; @@ -249,4 +252,75 @@ public static String concat(String ... data) { } return sb.toString(); } + + public static void appendHexString(StringBuilder builder, byte byt){ + builder.append(String.format("%02X", byt)); + } + public static String toHexString(byte byt){ + return String.format("%02X", byt); + } + + public static void appendHexString(StringBuilder builder, int byt){ + builder.append(String.format("%02X", (byte)byt)); + } + public static String toHexString(int byt){ + return String.format("%02X", (byte)byt); + } + + public static void appendHexString(StringBuilder builder, byte[] bytes){ + for (byte b : bytes) { + builder.append(String.format("%02X ", b)); + } + } + public static String toHexString(byte[] bytes){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, bytes); + return sb.toString().trim(); + } + + public static void appendHexString(StringBuilder builder, ByteBuffer buffer){ + appendHexString(builder, buffer.array()); + } + public static String toHexString(ByteBuffer buffer){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, buffer); + return sb.toString().trim(); + } + + public static void appendHexString(StringBuilder builder, byte[] bytes, int offset, int length){ + appendHexString(builder, Arrays.copyOfRange(bytes, offset, length)); + } + public static String toHexString(byte[] bytes, int offset, int length){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, bytes, offset, length); + return sb.toString().trim(); + } + + public static void appendHexString(StringBuilder builder, ByteBuffer buffer, int offset, int length){ + appendHexString(builder, buffer.array(), offset, length); + } + public static String toHexString(ByteBuffer buffer, int offset, int length){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, buffer, offset, length); + return sb.toString().trim(); + } + + public static boolean isNumeric(String str) { + try { + Double.parseDouble(str); + return true; + } catch(NumberFormatException e){ + return false; + } + } + + public static int parseInteger(String str, Integer defaultValue) { + try { + Integer v = Integer.parseInt(str); + return v.intValue(); + } catch(NumberFormatException e){ + return defaultValue; + } + } } + diff --git a/pi4j-api/src/main/java/module-info.java b/pi4j-api/src/main/java/module-info.java index 224ad84e2..5eeef51c4 100644 --- a/pi4j-api/src/main/java/module-info.java +++ b/pi4j-api/src/main/java/module-info.java @@ -80,8 +80,10 @@ DigitalChangeListenerRegistrationProcessor, DigitalInputRegistrationProcessor, DigitalOutputRegistrationProcessor, + I2CRegistrationProcessor, PlatformRegistrationProcessor, ProviderRegistrationProcessor, + PwmRegistrationProcessor, com.pi4j.annotation.processor.event.AnalogChangeEventProcessor, com.pi4j.annotation.processor.event.DigitalChangeEventProcessor; diff --git a/pi4j-api/src/main/resources/META-INF/services/com.pi4j.annotation.Processor b/pi4j-api/src/main/resources/META-INF/services/com.pi4j.annotation.Processor index 179a0abb3..01db0387f 100644 --- a/pi4j-api/src/main/resources/META-INF/services/com.pi4j.annotation.Processor +++ b/pi4j-api/src/main/resources/META-INF/services/com.pi4j.annotation.Processor @@ -20,7 +20,9 @@ com.pi4j.annotation.processor.register.AnalogOutputRegistrationProcessor com.pi4j.annotation.processor.register.DigitalChangeListenerRegistrationProcessor com.pi4j.annotation.processor.register.DigitalInputRegistrationProcessor com.pi4j.annotation.processor.register.DigitalOutputRegistrationProcessor +com.pi4j.annotation.processor.register.I2CRegistrationProcessor com.pi4j.annotation.processor.register.PlatformRegistrationProcessor com.pi4j.annotation.processor.register.ProviderRegistrationProcessor +com.pi4j.annotation.processor.register.PwmRegistrationProcessor com.pi4j.annotation.processor.event.AnalogChangeEventProcessor com.pi4j.annotation.processor.event.DigitalChangeEventProcessor diff --git a/pi4j-example/pom.xml b/pi4j-example/pom.xml index 6727372cc..ea85366b4 100644 --- a/pi4j-example/pom.xml +++ b/pi4j-example/pom.xml @@ -19,15 +19,9 @@ pi4j-api ${project.version} - - junit - junit - test - org.slf4j slf4j-simple - @@ -36,6 +30,11 @@ pi4j-plugin-raspberrypi ${project.version} + + com.pi4j + pi4j-plugin-pigpio + ${project.version} + com.pi4j pi4j-plugin-mock @@ -88,6 +87,4 @@ - - diff --git a/pi4j-example/src/main/java/com/pi4j/example/GettingStartedExample2.java b/pi4j-example/src/main/java/com/pi4j/example/GettingStartedExample2.java index 52e66c808..1348b2f37 100644 --- a/pi4j-example/src/main/java/com/pi4j/example/GettingStartedExample2.java +++ b/pi4j-example/src/main/java/com/pi4j/example/GettingStartedExample2.java @@ -29,7 +29,10 @@ import com.pi4j.Pi4J; import com.pi4j.context.Context; -import com.pi4j.io.gpio.analog.*; +import com.pi4j.io.gpio.analog.AnalogInput; +import com.pi4j.io.gpio.analog.AnalogInputConfig; +import com.pi4j.io.gpio.analog.AnalogInputProvider; +import com.pi4j.io.gpio.analog.AnalogInputProviderBase; import com.pi4j.io.spi.Spi; import com.pi4j.io.spi.SpiConfig; import com.pi4j.io.spi.SpiProviderBase; @@ -84,7 +87,7 @@ public Spi create(SpiConfig config) throws Exception { AnalogInput ain1 = pi4j.ain().create(1, "my-custom-name-1"); // create I/O config - AnalogInputConfig config = AnalogInputConfigBuilder.newInstance() + var config = AnalogInput.newConfigBuilder() .address(2) .name("my-custom-name-2") .build(); diff --git a/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExample.java b/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExample.java index 2e860ab6b..ccc6c255c 100644 --- a/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExample.java +++ b/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExample.java @@ -29,7 +29,7 @@ import com.pi4j.Pi4J; import com.pi4j.io.gpio.analog.AnalogChangeListener; -import com.pi4j.io.gpio.analog.AnalogOutputConfigBuilder; +import com.pi4j.io.gpio.analog.AnalogOutput; import com.pi4j.util.Console; public class AnalogOutputExample { @@ -61,11 +61,13 @@ public static void main(String[] args) throws Exception { // (Platforms and Providers) in the class path var pi4j = Pi4J.newAutoContext(); - AnalogOutputConfigBuilder builder = AnalogOutputConfigBuilder.newInstance(); - builder.id(ANALOG_OUTPUT_PIN_ID) + // create Analog Output configuration + var config = AnalogOutput.newConfigBuilder() + .id(ANALOG_OUTPUT_PIN_ID) .name(ANALOG_OUTPUT_PIN_NAME) - .address(ANALOG_OUTPUT_PIN); - var output = pi4j.aout().create(builder.build()); + .address(ANALOG_OUTPUT_PIN) + .build(); + var output = pi4j.aout().create(config); // create an analog output instance using the default analog output provider //var output = AnalogOutput.create(ANALOG_OUTPUT_PIN_ID, ANALOG_OUTPUT_PIN); diff --git a/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExampleFromPropertiesFile.java b/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExampleFromPropertiesFile.java index 71a582528..983e08099 100644 --- a/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExampleFromPropertiesFile.java +++ b/pi4j-example/src/main/java/com/pi4j/example/gpio/analog/AnalogOutputExampleFromPropertiesFile.java @@ -29,7 +29,7 @@ import com.pi4j.Pi4J; import com.pi4j.io.gpio.analog.AnalogChangeListener; -import com.pi4j.io.gpio.analog.AnalogOutputConfigBuilder; +import com.pi4j.io.gpio.analog.AnalogOutput; import com.pi4j.util.Console; import java.io.IOException; @@ -75,8 +75,10 @@ public static void main(String[] args) throws Exception { var pi4j = Pi4J.newAutoContext(); // build the analog output config using the loaded properties, but include a prefix filter - var builder = AnalogOutputConfigBuilder.newInstance().load(prop, "my-analog-example"); - var output = pi4j.analogOutput().create(builder.build()); + var config = AnalogOutput.newConfigBuilder() + .load(prop, "my-analog-example") + .build(); + var output = pi4j.analogOutput().create(config); // setup a analog output listener to listen for any state changes on the analog output output.addListener((AnalogChangeListener) event -> { diff --git a/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExample.java b/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExample.java index 6c5463b69..1be04fe1a 100644 --- a/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExample.java +++ b/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExample.java @@ -29,7 +29,7 @@ import com.pi4j.Pi4J; import com.pi4j.io.gpio.digital.DigitalChangeListener; -import com.pi4j.io.gpio.digital.DigitalInputConfigBuilder; +import com.pi4j.io.gpio.digital.DigitalInput; import com.pi4j.io.gpio.digital.PullResistance; import com.pi4j.util.Console; @@ -60,11 +60,12 @@ public static void main(String[] args) throws Exception { // create a digital input instance using the default digital input provider // we will use the PULL_DOWN argument to set the pin pull-down resistance on this GPIO pin - DigitalInputConfigBuilder builder = DigitalInputConfigBuilder.newInstance(); - builder.id("my-digital-input") + var config = DigitalInput.newConfigBuilder() + //.id("my-digital-input") .address(DIGITAL_INPUT_PIN) - .pull(PullResistance.PULL_DOWN); - var input = pi4j.din().create(builder.build()); + .pull(PullResistance.PULL_DOWN) + .build(); + var input = pi4j.din().create(config); // setup a digital output listener to listen for any state changes on the digital input input.addListener((DigitalChangeListener) event -> { diff --git a/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExampleFromProperties.java b/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExampleFromProperties.java index a8b4610f5..68f17f9ee 100644 --- a/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExampleFromProperties.java +++ b/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalInputExampleFromProperties.java @@ -29,7 +29,7 @@ import com.pi4j.Pi4J; import com.pi4j.io.gpio.digital.DigitalChangeListener; -import com.pi4j.io.gpio.digital.DigitalInputConfigBuilder; +import com.pi4j.io.gpio.digital.DigitalInput; import com.pi4j.util.Console; import java.util.Properties; @@ -68,8 +68,11 @@ public static void main(String[] args) throws Exception { // create a digital input instance using the default digital input provider // we will use the PULL_DOWN argument to set the pin pull-down resistance on this GPIO pin - var builder = DigitalInputConfigBuilder.newInstance().load(properties); - var input = pi4j.din().create(builder.build()); + var config = DigitalInput.newConfigBuilder() + .load(properties) + .build(); + + var input = pi4j.din().create(config); // setup a digital output listener to listen for any state changes on the digital input input.addListener((DigitalChangeListener) event -> { diff --git a/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalOutputFromProperties.java b/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalOutputFromProperties.java index eb1e2491f..21d308080 100644 --- a/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalOutputFromProperties.java +++ b/pi4j-example/src/main/java/com/pi4j/example/gpio/digital/DigitalOutputFromProperties.java @@ -29,7 +29,7 @@ import com.pi4j.Pi4J; import com.pi4j.io.gpio.digital.DigitalChangeListener; -import com.pi4j.io.gpio.digital.DigitalOutputConfigBuilder; +import com.pi4j.io.gpio.digital.DigitalOutput; import com.pi4j.io.gpio.digital.DigitalState; import com.pi4j.util.Console; @@ -69,8 +69,10 @@ public static void main(String[] args) throws Exception { properties.put("name", "DIGI4"); // create a digital output instance using the default digital output provider - var builder = DigitalOutputConfigBuilder.newInstance().load(properties); - var output = pi4j.dout().create(builder.build()); + var config = DigitalOutput.newConfigBuilder() + .load(properties) + .build(); + var output = pi4j.dout().create(config); // setup a digital output listener to listen for any state changes on the digital output output.addListener((DigitalChangeListener) event -> { diff --git a/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cDeviceExample.java b/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cDeviceExample.java new file mode 100644 index 000000000..dce6607d4 --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cDeviceExample.java @@ -0,0 +1,119 @@ +package com.pi4j.example.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : I2cDeviceExample.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.io.i2c.I2C; +import com.pi4j.util.Console; + +import java.nio.ByteBuffer; + +public class I2cDeviceExample { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + + public static void main(String[] args) throws Exception { + + // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY + // this temporary property is used to tell + // PIGPIO which remote Raspberry Pi to connect to + System.setProperty("pi4j.host", "rpi3bp.savage.lan"); + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "Basic I2C Device Example"); + + // allow for user to exit program using CTRL-C + console.promptForExit(); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + var pi4j = Pi4J.newAutoContext(); + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // use try-with-resources to auto-close I2C when complete + try (var i2c = pi4j.i2c().create(config);) { + + // we will be reading and writing to register address 0x01 + var register = i2c.register(0x01); + + // --> write a single (8-bit) byte value to the I2C device register + register.write(0x0D); + + // <-- read a single (8-bit) byte value from the I2C device register + byte readByte = register.readByte(); + + // --> write a single (16-bit) word value to the I2C device register + register.writeWord(0xFFFF); + + // <-- read a single (16-bit) word value from the I2C device register + int readWord = register.readWord(); + + // --> write an array of data bytes to the I2C device register + register.write(new byte[] { 0,1,2,3,4,5,6,7,8,9 }); + + // <-- read a byte array of specified length from the I2C device register + byte[] readArray = register.readArray(10); + + // --> write a buffer of data bytes to the I2C device register + ByteBuffer buffer = ByteBuffer.allocate(10); + register.write(buffer); + + // <-- read ByteBuffer of specified length from the I2C device register + ByteBuffer readBuffer = register.readBuffer(10); + + // --> write a string of data to the I2C device register + register.write("This is a test"); + + // <-- read string of data of specified length from the I2C device register + String readString = register.readString(14); + } + + + // create a digital input instance using the default digital input provider + // wait (block) for user to exit program using CTRL-C + console.waitForExit(); + + // shutdown Pi4J + console.println("ATTEMPTING TO SHUTDOWN/TERMINATE THIS PROGRAM"); + pi4j.shutdown(); + } +} diff --git a/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cExampleUsingDependencyInjection.java b/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cExampleUsingDependencyInjection.java new file mode 100644 index 000000000..e7345cb24 --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cExampleUsingDependencyInjection.java @@ -0,0 +1,107 @@ +package com.pi4j.example.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : I2cExampleUsingDependencyInjection.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.annotation.I2CAddress; +import com.pi4j.annotation.Name; +import com.pi4j.annotation.Register; +import com.pi4j.annotation.WithProvider; +import com.pi4j.context.Context; +import com.pi4j.io.i2c.I2C; +import com.pi4j.plugin.mock.provider.i2c.MockI2CProvider; +import com.pi4j.util.Console; + +import java.util.concurrent.Callable; + +public class I2cExampleUsingDependencyInjection { + + private static final int I2C_BUS = 1; + private static final int I2C_DEVICE = 0x04; + private static final int I2C_DEVICE_REGISTER = 0x01; + + public static void main(String[] args) throws Exception { + +// // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY +// // this temporary property is used to tell +// // PIGPIO which remote Raspberry Pi to connect to +// System.setProperty("pi4j.host", "rpi3bp.savage.lan"); + + // Pi4J cannot perform dependency injection on static classes + // we will create a container instance to run our example + new RuntimeContainer().call(); + } + + public static class RuntimeContainer implements Callable { + + // create a digital output instance using the default digital output provider + @Register("I2C-TEST-DI") + @Name("My I2C Device") + @I2CAddress(bus=I2C_BUS, device=I2C_DEVICE) + @WithProvider(type= MockI2CProvider.class) + private I2C i2c; + + @Override + public Void call() throws Exception { + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "PWM Example using Dependency Injection"); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + // ... + // Also, inject this class instance into the Pi4J context + // for annotation processing and dependency injection + Context pi4j = Pi4J.newAutoContext().inject(this); + + // we will be reading and writing to register address 0x01 + var register = i2c.register(I2C_DEVICE_REGISTER); + + // --> write a single (8-bit) byte value to the I2C device register + register.write(0x0D); + + // <-- read a single (8-bit) byte value from the I2C device register + byte readByte = register.readByte(); + + // close I2C device :: I2C closure will happen automatically when Pi4J shuts down + //i2c.close(); + + // shutdown Pi4J + pi4j.shutdown(); + + // we are done + return null; + } + } +} diff --git a/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cRawDeviceExample.java b/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cRawDeviceExample.java new file mode 100644 index 000000000..79038740c --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/i2c/I2cRawDeviceExample.java @@ -0,0 +1,108 @@ +package com.pi4j.example.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : I2cRawDeviceExample.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.io.i2c.I2C; +import com.pi4j.util.Console; + +import java.nio.ByteBuffer; + +public class I2cRawDeviceExample { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + + public static void main(String[] args) throws Exception { + + // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY + // this temporary property is used to tell + // PIGPIO which remote Raspberry Pi to connect to + System.setProperty("pi4j.host", "rpi3bp.savage.lan"); + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "Basic I2C Raw Device Example"); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + var pi4j = Pi4J.newAutoContext(); + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // use try-with-resources to auto-close I2C when complete + try (var i2c = pi4j.i2c().create(config);) { + + // --> write a single (8-bit) byte value to the raw I2C device (not to a register) + i2c.write(0x0D); + + // <-- read a single (8-bit) byte value from the raw I2C device (not to a register) + byte readByte = i2c.readByte(); + + // --> write a single (16-bit) word value to the raw I2C device (not to a register) + i2c.writeWord(0xFFFF); + + // <-- read a single (16-bit) word value from the raw I2C device (not to a register) + int readWord = i2c.readWord(); + + // --> write an array of data bytes to the raw I2C device (not to a register) + i2c.write(new byte[] { 0,1,2,3,4,5,6,7,8,9 }); + + // <-- read a byte array of specified length from the raw I2C device (not to a register) + byte[] readArray = i2c.readArray(10); + + // --> write a buffer of data bytes to the raw I2C device (not to a register) + ByteBuffer buffer = ByteBuffer.allocate(10); + i2c.write(buffer); + + // <-- read ByteBuffer of specified length from the raw I2C device (not to a register) + ByteBuffer readBuffer = i2c.readBuffer(10); + + // --> write a string of data to the raw I2C device (not to a register) + i2c.write("This is a test"); + + // <-- read string of data of specified length from the raw I2C device (not to a register) + String readString = i2c.readString(14); + } + + // shutdown Pi4J + console.println("ATTEMPTING TO SHUTDOWN/TERMINATE THIS PROGRAM"); + pi4j.shutdown(); + } +} diff --git a/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingDependencyInjection.java b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingDependencyInjection.java new file mode 100644 index 000000000..503fa90b1 --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingDependencyInjection.java @@ -0,0 +1,151 @@ +package com.pi4j.example.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : PwmExampleUsingDependencyInjection.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.annotation.*; +import com.pi4j.context.Context; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.util.Console; + +import java.util.concurrent.Callable; + +public class PwmExampleUsingDependencyInjection { + + public static void main(String[] args) throws Exception { + + // Pi4J cannot perform dependency injection on static classes + // we will create a container instance to run our example + new RuntimeContainer().call(); + } + + public static class RuntimeContainer implements Callable { + + // create a digital output instance using the default digital output provider + @Register("My PWM Pin") + @Address(2) // pin number 2 + @ShutdownValue(0) + @WithProvider(type=PiGpioPwmProvider.class) + @Frequency(5000) + @DutyCycle(range=1000, percent=50) + @AddPwmPreset(name = "one-quarter", dutyCycle = 250 ) + @AddPwmPreset(name = "three-quarter", dutyCycle = 750 ) + @AddPwmPreset(name = "1KHZ", frequency = 1000) + @AddPwmPreset(name = "10KHZ", frequency = 10000) + private Pwm pwm; + + @Override + public Void call() throws Exception { + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "PWM Example using Dependency Injection"); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + // ... + // Also, inject this class instance into the Pi4J context + // for annotation processing and dependency injection + var contextBuidler = Pi4J.newContextBuilder().autoDetect(); + + // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY + // this temporary property is used to tell + // PIGPIO which remote Raspberry Pi to connect to + contextBuidler.property("host", "rpi3bp.savage.lan"); + + // create Pi4J context + Context pi4j = contextBuidler.build().inject(this); + + pi4j.describe().print(System.out); + + // turn on the PWM pin + pwm.on(); + + console.println("[PWM I/O INSTANCE] : "); + pwm.describe().print(System.out); + console.println(); + console.println(" ... PWM SIGNAL SHOULD BE <--ON-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + console.println(); + console.println(" ... WAITING 5 SECONDS TO APPLY PRESET: 10KHZ"); + + // wait 5 seconds then exit + Thread.sleep(5000); + + // turn off PWM pin + pwm.applyPreset("10KHZ"); + + console.println(); + console.println(" ... PWM SIGNAL SHOULD BE <--10KHZ-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + + console.println(" ... WAITING 5 SECONDS TO TURN PWM SIGNAL OFF"); + + // wait 5 seconds then exit + Thread.sleep(5000); + + // turn off PWM pin + pwm.off(); + + console.println(); + console.println(" ... PWM SIGNAL SHOULD BE <--OFF-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + // shutdown Pi4J + pi4j.shutdown(); + + // we are done + return null; + } + } +} diff --git a/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingHardwarePwm.java b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingHardwarePwm.java new file mode 100644 index 000000000..874c617e0 --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingHardwarePwm.java @@ -0,0 +1,113 @@ +package com.pi4j.example.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : PwmExampleUsingHardwarePwm.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmType; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.util.Console; + +public class PwmExampleUsingHardwarePwm { + + public static int PWM_PIN = 13; // MUST BE A HARDWARE PWM SUPPORTED PIN + + public static void main(String[] args) throws Exception { + + // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY + // this temporary property is used to tell + // PIGPIO which remote Raspberry Pi to connect to + System.setProperty("pi4j.host", "rpi3bp.savage.lan"); + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "PWM Example using Software-Emulated PWM"); + + // allow for user to exit program using CTRL-C + console.promptForExit(); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + var pi4j = Pi4J.newAutoContext(); + + // create PWM instance config + var config = Pwm.newConfigBuilder() + .id("my-pwm-pin") + .name("My Test PWM Pin") + .address(PWM_PIN) + .pwmType(PwmType.HARDWARE) // USE HARDWARE PWM + .frequency(1000) // optionally pre-configure the desired frequency to 1KHz + .shutdown(0) // optionally pre-configure a shutdown duty-cycle value (on terminate) + //.initial(125) // optionally pre-configure an initial duty-cycle value (on startup) + .build(); + + // use try-with-resources to auto-close I2C when complete + var pwm = pi4j.providers().get(PiGpioPwmProvider.class).create(config); + + // + // optionally override pre-configured PWM frequency; set PWM frequency to 1KHz + //pwm.frequency(1000); + + // + // optionally override pre-configured duty-cycle value; + // this is in relation to the previously defined rage value + //pwm.setDutyCycle(500000); // hardware PWM range is 0-1M; this value is 50% of range + + // + // alternatively, you can also just simply set the duty-cycle as a percent value + pwm.setDutyCyclePercent(50); // 50% + + // enable the PWM signal + pwm.on(); + + console.println("[PWM I/O INSTANCE] : "); + pwm.describe().print(System.out); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + // create a digital input instance using the default digital input provider + // wait (block) for user to exit program using CTRL-C + console.waitForExit(); + + // shutdown Pi4J + console.println("ATTEMPTING TO SHUTDOWN/TERMINATE THIS PROGRAM"); + + // shutdown Pi4J + pi4j.shutdown(); + } +} diff --git a/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingSoftwarePwm.java b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingSoftwarePwm.java new file mode 100644 index 000000000..19322a1ae --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmExampleUsingSoftwarePwm.java @@ -0,0 +1,118 @@ +package com.pi4j.example.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : PwmExampleUsingSoftwarePwm.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.util.Console; + +public class PwmExampleUsingSoftwarePwm { + + public static int PWM_PIN = 4; + + public static void main(String[] args) throws Exception { + + // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY + // this temporary property is used to tell + // PIGPIO which remote Raspberry Pi to connect to + System.setProperty("pi4j.host", "rpi3bp.savage.lan"); + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "PWM Example using Software-Emulated PWM"); + + // allow for user to exit program using CTRL-C + console.promptForExit(); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + var pi4j = Pi4J.newAutoContext(); + + // create PWM instance config + var config = Pwm.newConfigBuilder() + .id("my-pwm-pin") + .name("My Test PWM Pin") + .address(PWM_PIN) + .frequency(1000) // optionally pre-configure the desired frequency to 1KHz + .range(255) // optionally pre-configure the desired duty-cycle range (0-255) + .dutyCycle(128) // optionally pre-configure the desired duty-cycle (50%) + .shutdown(0) // optionally pre-configure a shutdown duty-cycle value (on terminate) + //.initial(125) // optionally pre-configure an initial duty-cycle value (on startup) + .build(); + + // use try-with-resources to auto-close I2C when complete + var pwm = pi4j.providers().get(PiGpioPwmProvider.class).create(config); + + // + // optionally override pre-configured PWM frequency; set PWM frequency to 1KHz + //pwm.frequency(1000); + + // + // optionally override pre-configured duty-cycle range (Default is 0-255); + // this function sets the upper limit of the range + //pwm.setRange(255); + + // + // optionally override pre-configured duty-cycle value; + // this is in relation to the previously defined rage value + //pwm.setDutyCycle(128); + + // + // alternatively, you can also just simply set the duty-cycle as a percent value + //pwm.setDutyCyclePercent(50); // 50% + + // enable the PWM signal + pwm.on(); + + console.println("[PWM I/O INSTANCE] : "); + pwm.describe().print(System.out); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + // create a digital input instance using the default digital input provider + // wait (block) for user to exit program using CTRL-C + console.waitForExit(); + + // shutdown Pi4J + console.println("ATTEMPTING TO SHUTDOWN/TERMINATE THIS PROGRAM"); + + // shutdown Pi4J + pi4j.shutdown(); + } +} diff --git a/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmPresetsExample.java b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmPresetsExample.java new file mode 100644 index 000000000..91d105ad1 --- /dev/null +++ b/pi4j-example/src/main/java/com/pi4j/example/pwm/PwmPresetsExample.java @@ -0,0 +1,191 @@ +package com.pi4j.example.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: EXAMPLE :: Sample Code + * FILENAME : PwmPresetsExample.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmPreset; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.util.Console; + +public class PwmPresetsExample { + + public static int PWM_PIN = 4; + + public static void main(String[] args) throws Exception { + + // TODO :: REMOVE TEMPORARY PROPERTIES WHEN NATIVE PIGPIO LIB IS READY + // this temporary property is used to tell + // PIGPIO which remote Raspberry Pi to connect to + System.setProperty("pi4j.host", "rpi3bp.savage.lan"); + + // create Pi4J console wrapper/helper + // (This is a utility class to abstract some of the boilerplate stdin/stdout code) + final var console = new Console(); + + // print program title/header + console.title("<-- The Pi4J Project -->", "PWM Presets Example using Software-Emulated PWM"); + + // Initialize Pi4J with an auto context + // An auto context includes AUTO-DETECT BINDINGS enabled + // which will load all detected Pi4J extension libraries + // (Platforms and Providers) in the class path + var pi4j = Pi4J.newAutoContext(); + + // create PWM instance config + var config = Pwm.newConfigBuilder() + .id("my-pwm-pin") + .name("My Test PWM Pin") + .address(PWM_PIN) + .frequency(1000) // optionally pre-configure the desired frequency to 1KHz + .range(255) // optionally pre-configure the desired duty-cycle range (0-255) + .dutyCycle(128) // optionally pre-configure the desired duty-cycle (50%) + .shutdown(0) // optionally pre-configure a shutdown duty-cycle value (on terminate) + //.initial(125) // optionally pre-configure an initial duty-cycle value (on startup) + .build(); + + // use try-with-resources to auto-close I2C when complete + var pwm = pi4j.providers().get(PiGpioPwmProvider.class).create(config); + + // add PWM presets + pwm.addPreset(PwmPreset.newBuilder("one") + .frequency(5000) + .dutyCycle(128) + .build()); + + pwm.addPreset(PwmPreset.newBuilder("two") + .frequency(80) + .build()); + + pwm.addPreset(PwmPreset.newBuilder("three") + .dutyCycle(180) + .build()); + + + console.println("[PWM I/O INSTANCE] : "); + pwm.describe().print(System.out); + + // + // optionally override pre-configured PWM frequency; set PWM frequency to 1KHz + //pwm.frequency(1000); + + // + // optionally override pre-configured duty-cycle range (Default is 0-255); + // this function sets the upper limit of the range + //pwm.setRange(255); + + // + // optionally override pre-configured duty-cycle value; + // this is in relation to the previously defined rage value + //pwm.setDutyCycle(128); + + // + // alternatively, you can also just simply set the duty-cycle as a percent value + //pwm.setDutyCyclePercent(50); // 50% + + // enable the PWM signal (using the configured settings) + pwm.on(); + + console.println(); + console.println(" ... PWM SIGNAL SHOULD BE <--ON-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + // wait 5 seconds then exit + console.println(); + console.println(" ... WAITING 5 SECONDS ... "); + Thread.sleep(5000); + + // recall applyPreset one + pwm.applyPreset("one"); + + console.println(); + console.println(" ... RECALLING PWM PRESET <--PRESET('one')-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + + // wait 5 seconds then exit + console.println(); + console.println(" ... WAITING 5 SECONDS ... "); + Thread.sleep(5000); + + // recall applyPreset two + pwm.applyPreset("two"); + + console.println(); + console.println(" ... RECALLING PWM PRESET <--PRESET('two')-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + + // wait 5 seconds then exit + console.println(); + console.println(" ... WAITING 5 SECONDS ... "); + Thread.sleep(5000); + + // recall applyPreset two + pwm.applyPreset("three"); + + console.println(); + console.println(" ... RECALLING PWM PRESET <--PRESET('two')-->"); + console.println(); + console.println(" - GPIO PIN : " + pwm.address()); + console.println(" - PWM TYPE : " + pwm.pwmType()); + console.println(" - FREQUENCY : " + pwm.frequency() + " Hz"); + console.println(" - RANGE : 0-" + pwm.range()); + console.println(" - DUTY-CYCLE : " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + console.println(" - IS-ON : " + pwm.isOn()); + + // wait 5 seconds then exit + console.println(); + console.println(" ... WAITING 5 SECONDS ... "); + Thread.sleep(5000); + + // shutdown Pi4J + console.println("ATTEMPTING TO SHUTDOWN/TERMINATE THIS PROGRAM"); + + // shutdown Pi4J + pi4j.shutdown(); + } +} diff --git a/pi4j-example/src/main/java/module-info.java b/pi4j-example/src/main/java/module-info.java index 879bd56a2..55f968e62 100644 --- a/pi4j-example/src/main/java/module-info.java +++ b/pi4j-example/src/main/java/module-info.java @@ -53,8 +53,14 @@ uses com.pi4j.plugin.mock.provider.serial.MockSerial; uses com.pi4j.plugin.mock.provider.serial.MockSerialProvider; + requires pi4j.plugin.pigpio; + requires pi4j.plugin.raspberrypi; + requires pi4j.plugin.linuxfs; + // allow access to classes in the following namespaces for Pi4J annotation processing opens com.pi4j.example.gpio.analog; opens com.pi4j.example.gpio.digital; + opens com.pi4j.example.pwm; + opens com.pi4j.example.i2c; opens com.pi4j.example; } diff --git a/pi4j-example/src/main/resources/pi4j-example.properties b/pi4j-example/src/main/resources/pi4j-example.properties index d9ad442ff..e6c2fd73f 100644 --- a/pi4j-example/src/main/resources/pi4j-example.properties +++ b/pi4j-example/src/main/resources/pi4j-example.properties @@ -4,3 +4,5 @@ my-analog-example.name=My Test Analog Output my-analog-example.description=My Test Analog Output Description my-analog-example.initial=56 my-analog-example.shutdown=2 + +host=rpi3bp.savage.lan diff --git a/pi4j-test-harness/pom.xml b/pi4j-test-harness/pom.xml new file mode 100644 index 000000000..a822c7b09 --- /dev/null +++ b/pi4j-test-harness/pom.xml @@ -0,0 +1,78 @@ + + + + pi4j-parent + com.pi4j + 2.0-SNAPSHOT + + + 4.0.0 + pi4j-test-harness + Pi4J :: TESTING :: Arduino Test Harness + Arduino-based Testing Harness for Pi4J Hardware Integration Testing + + + + + com.fazecast + jSerialComm + 2.5.1 + + + com.google.code.gson + gson + 2.8.5 + + + org.json + json + 20190722 + + + + + + + default-profile + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*.java + + + + + + + + + + test-hardware + + + + org.apache.maven.plugins + maven-surefire-plugin + + ** + + ${pi4j.test.pigpio.host} + ${pi4j.test.pigpio.port} + ${pi4j.test.harness.port} + + + + + + + + diff --git a/pi4j-test-harness/src/main/arduino/.gitignore b/pi4j-test-harness/src/main/arduino/.gitignore new file mode 100644 index 000000000..45c5c9824 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/.gitignore @@ -0,0 +1,70 @@ +# ********************************************************************** +# ORGANIZATION : Pi4J +# PROJECT : Pi4J :: TEST :: Arduino Test Harness +# +# This file is part of the Pi4J project. More information about +# this project can be found here: https://pi4j.com/ +# ********************************************************************** +# +# Copyright (C) 2012 - 2019 Pi4J +# +# This program 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 3 of the +# License, or (at your option) any later version. +# +# This program 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# . +# ********************************************************************** + +# Build files/directories +out +target +gen +build +dist +temp +trash +scratch + +# Log directory +log + +# PlatformIO specific IDE files +.pioenvs +.piolibdeps +.pio + +# Microsoft Visual Studio Code IDE files +.vscode +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json + +# IntelliJ IDE files +.idea +*.iml + +# Eclipse IDE files +.settings +.classpath +.project +.externalToolBuilders +.metadata +maven-eclipse.xml + +# Vim files +*.swp + +# Mac files +.DS_Store + +# Windows files +Thumbs.db + diff --git a/pi4j-test-harness/src/main/arduino/.travis.yml b/pi4j-test-harness/src/main/arduino/.travis.yml new file mode 100644 index 000000000..4170000cb --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/.travis.yml @@ -0,0 +1,93 @@ +# ********************************************************************** +# ORGANIZATION : Pi4J +# PROJECT : Pi4J :: TEST :: Arduino Test Harness +# +# This file is part of the Pi4J project. More information about +# this project can be found here: https://pi4j.com/ +# ********************************************************************** +# +# Copyright (C) 2012 - 2019 Pi4J +# +# This program 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 3 of the +# License, or (at your option) any later version. +# +# This program 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# . +# ********************************************************************** + + +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < https://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < https://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < https://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choose one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to be used as a library with examples. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/pi4j-test-harness/src/main/arduino/VERSION b/pi4j-test-harness/src/main/arduino/VERSION new file mode 100644 index 000000000..74ced3132 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/VERSION @@ -0,0 +1 @@ +1.0.0-ALPHA \ No newline at end of file diff --git a/pi4j-test-harness/src/main/arduino/lib/readme.txt b/pi4j-test-harness/src/main/arduino/lib/readme.txt new file mode 100644 index 000000000..cfa16dfa2 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/lib/readme.txt @@ -0,0 +1,41 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link them to executable files. + +The source code of each library should be placed in separate directories, like +"lib/private_lib/[here are source files]". + +For example, see the structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- readme.txt --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/pi4j-test-harness/src/main/arduino/platformio.ini b/pi4j-test-harness/src/main/arduino/platformio.ini new file mode 100644 index 000000000..6535f6e7d --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/platformio.ini @@ -0,0 +1,77 @@ +# * ********************************************************************** +# * ORGANIZATION : Pi4J +# * PROJECT : Pi4J :: TEST :: Arduino Test Harness +# * +# * This file is part of the Pi4J project. More information about +# * this project can be found here: https://pi4j.com/ +# * ********************************************************************** +# * %% +# * Copyright (C) 2012 - 2019 Pi4J +# * %% +# * This program 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 3 of the +# * License, or (at your option) any later version. +# * +# * This program 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 General Lesser Public License for more details. +# * +# * You should have received a copy of the GNU General Lesser Public +# * License along with this program. If not, see +# * . +# * ********************************************************************** + +[platformio] +env_default = arduino_due +build_dir=build + +; --------------------------------------------------------------- +; COMMON BUILD ENVIRONMENT +; --------------------------------------------------------------- + +; You MUST inject these options into [env:] section +; using ${common_env_data.***} (see below) +[common_env_data] + +platform = atmelsam@3.5.0 +framework = arduino +extra_scripts = + +; Build options +build_flags = + !echo "-DFIRMWARE_VERSION="$(cat VERSION) "-DFIRMWARE_DATE="$(date +\"%Y-%m-%d\") + +lib_deps_builtin = + SPI + Wire +lib_deps_external = + ArduinoSTL + ArduinoJson + https://github.com/ppedro74/Arduino-SerialCommands + https://github.com/paulo-raca/ArduinoBufferedStreams + +; --------------------------------------------------------------- +; ARDUNIO DUE BOARD CONFIG +; --------------------------------------------------------------- +[env:arduino_due] +platform = ${common_env_data.platform} +framework = ${common_env_data.framework} +board = due +extra_scripts = ${common_env_data.extra_scripts} + +; Build options +build_flags = + ${common_env_data.build_flags} + -D DEBUG=0 + +; Library options +lib_deps = + ${common_env_data.lib_deps_builtin} + ${common_env_data.lib_deps_external} + +; Board communication +upload_port = /dev/cu.usbmodem142401 +monitor_port = /dev/cu.usbmodem142401 +monitor_speed = 115200 diff --git a/pi4j-test-harness/src/main/arduino/src/command/Commands.h b/pi4j-test-harness/src/main/arduino/src/command/Commands.h new file mode 100644 index 000000000..4340d56c3 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/Commands.h @@ -0,0 +1,67 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMANDS_H +#define PI4J_COMMANDS_H + +#include "main.h" +#include +#include "CommandsArgumentParser.h" +#include "EchoCommand.h" +#include "FrequencyCommand.h" +#include "I2cCommand.h" +#include "InfoCommand.h" +#include "InvalidCommand.h" +#include "PinCommand.h" +#include "PinsCommand.h" +#include "RebootCommand.h" +#include "ResetCommand.h" + + +/** + * ADD INTERACTIVE COMMAND TO THE SERIAL COMMAND PROCESSOR + */ +void AddInteractiveCommands(SerialCommands& processor){ + + // add default command handler for unrecognized commands + processor.SetDefaultHandler(invalid_command_handler); + + // add Pi4J interactive commands + processor.AddCommand(&EchoCommand); + processor.AddCommand(&FrequencyCommand); + processor.AddCommand(&FrequencyShortCommand); + processor.AddCommand(&RebootCommand); + processor.AddCommand(&ResetCommand); + processor.AddCommand(&I2cCommand); + processor.AddCommand(&InfoCommand); + processor.AddCommand(&InfoCommandKey); + processor.AddCommand(&PinCommand); + processor.AddCommand(&PinsCommand); + processor.AddCommand(&PinShortCommand); +} + +#endif // PI4J_COMMANDS_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/CommandsArgumentParser.h b/pi4j-test-harness/src/main/arduino/src/command/CommandsArgumentParser.h new file mode 100644 index 000000000..41fe13085 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/CommandsArgumentParser.h @@ -0,0 +1,282 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#include +#include +#include +#include "util/StringUtil.h" + +#ifndef PI4J_COMMAND_ARGUMENT_PARSER_H +#define PI4J_COMMAND_ARGUMENT_PARSER_H + +/** + * GET I/O PIN VALUE FROM COMMANDS ARGUMENTS + */ +int GetCommandPinArgument(SerialCommands* sender){ + char* pin_str = sender->Next(); + if (pin_str == NULL){ + return ERROR_COMMAND_ARGUMENT_MISSING; // -1 :: missing argument + } + + // validate numeric string + if(!StringUtil::isNumeric(pin_str)){ + return ERROR_COMMAND_ARGUMENT_INVALID; // -2 :: invalid argument + } + int pin = atoi(pin_str); + + // validate pin range + if(pin >= GPIO_MAX_PINS){ + return ERROR_INVALID_PIN_OUT_OF_RANGE; + } + + // validate restricted pins + if(pin >= 0){ + if(pins[pin].restricted){ + return ERROR_INVALID_PIN_RESTRICTED; + } + + // special pin overrides for Raspberry Pi + if(pin == 2){ + return 20; + } + if(pin == 3){ + return 21; + } + } + + + return pin; +} + +/** + * GET I/O DIGITAL PIN STATE FROM COMMANDS ARGUMENTS + */ +int GetCommandDigitalStateArgument(SerialCommands* sender){ + + // get state argument + char* state_str = sender->Next(); + if (state_str == NULL){ + return ERROR_COMMAND_ARGUMENT_MISSING; // -1 :: missing argument + } + String state = String(state_str); + if(state.equalsIgnoreCase("HIGH")){ + return 1; + } else if(state.equalsIgnoreCase("HI")){ + return 1; + } else if(state.equalsIgnoreCase("H")){ + return 1; + } else if(state.equalsIgnoreCase("1")){ + return 1; + } else if(state.equalsIgnoreCase("ON")){ + return 1; + } else if(state.equalsIgnoreCase("LOW")){ + return 0; + } else if(state.equalsIgnoreCase("LO")){ + return 0; + } else if(state.equalsIgnoreCase("L")){ + return 0; + } else if(state.equalsIgnoreCase("0")){ + return 0; + } else if(state.equalsIgnoreCase("OFF")){ + return 0; + } else { + return ERROR_COMMAND_ARGUMENT_INVALID; // -2 :: invalid argument + } +} + + +/** + * GET "ON/OFF" or "ENABLE/DISABLE" FROM COMMANDS ARGUMENTS + */ +int GetCommandBooleanArgument(SerialCommands* sender){ + + // get state argument + char* state_str = sender->Next(); + if (state_str == NULL){ + return ERROR_COMMAND_ARGUMENT_MISSING; // -1 :: missing argument + } + String state = String(state_str); + if(state.equalsIgnoreCase("TRUE")){ + return 1; + } else if(state.equalsIgnoreCase("T")){ + return 1; + } else if(state.equalsIgnoreCase("ENABLE")){ + return 1; + } else if(state.equalsIgnoreCase("ON")){ + return 1; + } else if(state.equalsIgnoreCase("YES")){ + return 0; + } else if(state.equalsIgnoreCase("Y")){ + return 0; + } else if(state.equalsIgnoreCase("1")){ + return 1; + } else if(state.equalsIgnoreCase("FALSE")){ + return 0; + } else if(state.equalsIgnoreCase("F")){ + return 0; + } else if(state.equalsIgnoreCase("DISABLE")){ + return 0; + } else if(state.equalsIgnoreCase("OFF")){ + return 0; + } else if(state.equalsIgnoreCase("NO")){ + return 0; + } else if(state.equalsIgnoreCase("N")){ + return 0; + } else if(state.equalsIgnoreCase("0")){ + return 0; + } else { + return ERROR_COMMAND_ARGUMENT_INVALID; // -2 :: invalid argument + } +} + + +/** + * GET PIN MODE ARGUMENT + */ +int GetCommandPinModeArgument(SerialCommands* sender){ + + // get mode argument + char* mode_str = sender->Next(); + if (mode_str == NULL){ + return ERROR_COMMAND_ARGUMENT_MISSING; // -1 :: missing argument + } + String mode = String(mode_str); + if(mode.equalsIgnoreCase("INPUT")){ + return INPUT; + } else if(mode.equalsIgnoreCase("IN")){ + return INPUT; + } else if(mode.equalsIgnoreCase("I")){ + return INPUT; + } else if(mode.equalsIgnoreCase("INPUT_PULLUP")){ + return INPUT_PULLUP; + } else if(mode.equalsIgnoreCase("IN_PULLUP")){ + return INPUT_PULLUP; + } else if(mode.equalsIgnoreCase("IN_UP")){ + return INPUT_PULLUP; + } else if(mode.equalsIgnoreCase("IUP")){ + return INPUT_PULLUP; + } else if(mode.equalsIgnoreCase("IU")){ + return INPUT_PULLUP; + } else if(mode.equalsIgnoreCase("OUTPUT")){ + return OUTPUT; + } else if(mode.equalsIgnoreCase("OUT")){ + return OUTPUT; + } else if(mode.equalsIgnoreCase("O")){ + return OUTPUT; + } else if(mode.equalsIgnoreCase("HIGH")){ + return PIN_MODE_OUTPUT_HIGH; + } else if(mode.equalsIgnoreCase("HI")){ + return PIN_MODE_OUTPUT_HIGH; + } else if(mode.equalsIgnoreCase("H")){ + return PIN_MODE_OUTPUT_HIGH; + } else if(mode.equalsIgnoreCase("ON")){ + return PIN_MODE_OUTPUT_HIGH; + } else if(mode.equalsIgnoreCase("1")){ + return PIN_MODE_OUTPUT_HIGH; + } else if(mode.equalsIgnoreCase("LOW")){ + return PIN_MODE_OUTPUT_LOW; + } else if(mode.equalsIgnoreCase("LO")){ + return PIN_MODE_OUTPUT_LOW; + } else if(mode.equalsIgnoreCase("L")){ + return PIN_MODE_OUTPUT_LOW; + } else if(mode.equalsIgnoreCase("OFF")){ + return PIN_MODE_OUTPUT_LOW; + } else if(mode.equalsIgnoreCase("0")){ + return PIN_MODE_OUTPUT_LOW; + } else if(mode.equalsIgnoreCase("DISABLE")){ + return PIN_MODE_DISABLE; + } else if(mode.equalsIgnoreCase("STOP")){ + return PIN_MODE_DISABLE; + } else if(mode.equalsIgnoreCase("CANCEL")){ + return PIN_MODE_DISABLE; + } else if(mode.equalsIgnoreCase("RESET")){ + return PIN_MODE_DISABLE; + } else { + return ERROR_COMMAND_ARGUMENT_INVALID; // -2 :: invalid argument + } +} + +/** + * GET I2C BUS VALUE FROM COMMANDS ARGUMENTS + */ +int GetCommandI2cBusArgument(SerialCommands* sender){ + char* bus_str = sender->Next(); + if (bus_str == NULL){ + return ERROR_COMMAND_ARGUMENT_MISSING; // -1 :: missing argument + } + + // validate numeric string + if(!StringUtil::isNumeric(bus_str)){ + return ERROR_COMMAND_ARGUMENT_INVALID; // -2 :: invalid argument + } + int bus = atoi(bus_str); + + // validate bus + if(bus < 0 || bus > 1){ + return ERROR_INVALID_I2C_BUS_OUT_OF_RANGE; + } + + return bus; +} + +/** + * GET I2C DEVICE VALUE FROM COMMANDS ARGUMENTS + */ +int GetCommandI2cDeviceArgument(SerialCommands* sender){ + char* dev_str = sender->Next(); + if (dev_str == NULL){ + return ERROR_COMMAND_ARGUMENT_MISSING; // -1 :: missing argument + } + + // validate numeric string + if(!StringUtil::isNumeric(dev_str)){ + return ERROR_COMMAND_ARGUMENT_INVALID; // -2 :: invalid argument + } + int device = atoi(dev_str); + + // validate device + if(device < 0 || device > 127){ + return ERROR_INVALID_I2C_DEVICE_OUT_OF_RANGE; + } + + return device; +} + + +String GetCommandArgumentError(int error){ + switch(error){ + case ERROR_COMMAND_ARGUMENT_MISSING : {return "MISSING ARGUMENT"; break;} + case ERROR_COMMAND_ARGUMENT_INVALID : {return "INVALID ARGUMENT"; break;} + case ERROR_INVALID_PIN_OUT_OF_RANGE : {return "INVALID PIN NUMBER; OUT OF ACCEPTED RANGE"; break;} + case ERROR_INVALID_PIN_RESTRICTED : {return "INVALID PIN NUMBER; RESTRICTED PIN"; break;} + case ERROR_INVALID_I2C_BUS_OUT_OF_RANGE : {return "INVALID I2C BUS; UNSUPPORTED BUS"; break;} + case ERROR_INVALID_I2C_DEVICE_OUT_OF_RANGE : {return "INVALID I2C DEVICE; OUT OF ACCEPTED RANGE"; break;} + default: return "UNKNOWN ARGUMENT ERROR"; + } +} + +#endif //PI4J_COMMAND_ARGUMENT_PARSER_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/EchoCommand.h b/pi4j-test-harness/src/main/arduino/src/command/EchoCommand.h new file mode 100644 index 000000000..254a3afb4 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/EchoCommand.h @@ -0,0 +1,69 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_ECHO_H +#define PI4J_COMMAND_ECHO_H + +#include +#include +#include + +// create ECHO command invocation handler +void echo_command_execute(SerialCommands* sender){ + DynamicJsonDocument doc(256); + JsonObject response = doc.to(); + + // get enabled argument + int enable = GetCommandBooleanArgument(sender); + + // if a state was not provided, then return the current pin state + if (enable == ERROR_COMMAND_ARGUMENT_MISSING){ + enable = console_pipe.isEcho(); + response["id"] = "get"; + response["echo"] = (enable) ? "on" : "off"; + } + else if(enable < 0) { + response["id"] = "error"; + response["errno"] = enable; + response["arg"] = "state"; + response["msg"] = GetCommandArgumentError(enable); + } + else { + console_pipe.echo(enable); // update echo state on console pipe + response["id"] = "set"; + response["echo"] = (enable) ? "on" : "off"; + } + + // output response + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); +} + +// create ECHO command variable +SerialCommand EchoCommand = SerialCommand("echo", echo_command_execute); + +#endif //PI4J_COMMAND_ECHO_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/FrequencyCommand.h b/pi4j-test-harness/src/main/arduino/src/command/FrequencyCommand.h new file mode 100644 index 000000000..9a3e73345 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/FrequencyCommand.h @@ -0,0 +1,78 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_FREQUENCY_H +#define PI4J_COMMAND_FREQUENCY_H + +#include +#include +#include +#include +#include "CommandsArgumentParser.h" +#include "main.h" + +// create FREQUENCY command invocation handler +void frequency_command_execute(SerialCommands* sender){ + DynamicJsonDocument doc(256); + JsonObject response = doc.to(); + + // get PIN argument + int pin = GetCommandPinArgument(sender); + + // handle pin argument errors + if(pin < 0 && pin != ERROR_INVALID_PIN_DISABLED) { + response["id"] = "error"; + response["errno"] = pin; + response["arg"] = "pin"; + response["msg"] = GetCommandArgumentError(pin); + } + else { + // make sure the reading pin is an input pin + pinMode(pin, INPUT); + pins[pin].enabled = true; + pins[pin].mode = INPUT; + + // count the number of pulses and calculate the frequency in Hz + long frequency = 500000/pulseIn(pin, HIGH); + + // prepare response + response["id"] = "frequency"; + response["pin"] = pin; + response["frequency"] = frequency; + response["units"] = "Hz"; + } + + // output response + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); +} + +// create FREQUENCY command variable +SerialCommand FrequencyCommand = SerialCommand("frequency", frequency_command_execute); +SerialCommand FrequencyShortCommand = SerialCommand("f", frequency_command_execute); + +#endif //PI4J_COMMAND_FREQUENCY_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/I2cCommand.h b/pi4j-test-harness/src/main/arduino/src/command/I2cCommand.h new file mode 100644 index 000000000..dc91cb845 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/I2cCommand.h @@ -0,0 +1,117 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_I2C_H +#define PI4J_COMMAND_I2C_H + +#include +#include +#include +#include +#include +#include "CommandsArgumentParser.h" +#include "main.h" + +// create ECHO command invocation handler +void i2c_command_execute(SerialCommands* sender){ + DynamicJsonDocument doc(256); + JsonObject response = doc.to(); + + // get I2C bus + int bus = GetCommandI2cBusArgument(sender); + + // get I2C device + int device = GetCommandI2cDeviceArgument(sender); + + // get I2C "raw" mode data processing + int rawMode = GetCommandBooleanArgument(sender); + + // handle bus argument errors + if(bus < 0) { + response["id"] = "error"; + response["errno"] = bus; + response["arg"] = "bus"; + response["msg"] = GetCommandArgumentError(bus); + } + + // handle device argument errors + else if(device < 0) { + response["id"] = "error"; + response["errno"] = bus; + response["arg"] = "device"; + response["msg"] = GetCommandArgumentError(device); + } + + else{ + response["id"] = "i2c"; + response["bus"] = bus; + response["device"] = device; + response["raw"] = (bool)rawMode; + + // default callback handlers for Register operations + void (*i2cReceive_ptr)(int) = &receiveI2CData; + void (*i2cSend_ptr)(void) = &sendI2CData; + + // if raw mode is enabled, then swap function pointers + // for the raw data processing callbacks + if(rawMode > 0){ + i2cReceive_ptr = &receiveI2CDataRaw; + i2cSend_ptr = &sendI2CDataRaw; + } + + // end I2C on existing bus if previously assigned + if(i2cCache.wire != nullptr){ + i2cCache.wire->end(); + } + + // reset I2C cache + i2cCache.reset(); + + // setup which I2C bus to enable + if(bus == 0){ + i2cCache.wire = &Wire; // setup I2C BUS 0 + } else if(bus == 1){ + i2cCache.wire = &Wire1; // setup I2C BUS 1 + } + + // initialize i2c as slave + i2cCache.wire->begin(device); + + // define callbacks for i2c communication + i2cCache.wire->onReceive(i2cReceive_ptr); + i2cCache.wire->onRequest(i2cSend_ptr); + } + + // output response + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); +} + +// create I2C command variable +SerialCommand I2cCommand = SerialCommand("i2c", i2c_command_execute); + +#endif //PI4J_COMMAND_I2C_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/InfoCommand.h b/pi4j-test-harness/src/main/arduino/src/command/InfoCommand.h new file mode 100644 index 000000000..f1f7c684e --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/InfoCommand.h @@ -0,0 +1,44 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_INFO_H +#define PI4J_COMMAND_INFO_H + +#include +#include +#include + +// create INFO command invocation handler +void info_command_execute(SerialCommands* sender){ + info(sender->GetSerial()); +} + +// create INFO command variable +SerialCommand InfoCommand = SerialCommand("info", info_command_execute); +SerialCommand InfoCommandKey("?", info_command_execute, true); + +#endif //PI4J_COMMAND_INFO_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/InvalidCommand.h b/pi4j-test-harness/src/main/arduino/src/command/InvalidCommand.h new file mode 100644 index 000000000..d7f8f0f7f --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/InvalidCommand.h @@ -0,0 +1,52 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_INVALID_H +#define PI4J_COMMAND_INVALID_H + +#include +#include +#include + +// create INVALID command invocation handler + +//This is the default handler, and gets called when no other command matches. +// Note: It does not get called for one_key commands that do not match +void invalid_command_handler(SerialCommands* sender, const char* cmd){ + DynamicJsonDocument doc(1024); + JsonObject response = doc.to(); + response["id"] = "error"; + response["errno"] = ERROR_UNSUPPORTED_COMMAND; + response["msg"] = "Invalid or unsupported command"; + response["cmd"] = cmd; + + // output response + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); +} + +#endif //PI4J_COMMAND_INVALID_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/PinCommand.h b/pi4j-test-harness/src/main/arduino/src/command/PinCommand.h new file mode 100644 index 000000000..f984da730 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/PinCommand.h @@ -0,0 +1,229 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_PIN_H +#define PI4J_COMMAND_PIN_H + +#include +#include +#include +#include "CommandsArgumentParser.h" +#include + +// create PIN command invocation handler +void pin_command_execute(SerialCommands* sender){ + DynamicJsonDocument doc(1024); + JsonObject response = doc.to(); + + // get argument + int pin = GetCommandPinArgument(sender); + + // check for missing or invalid argument + if (pin < 0){ + response["id"] = "error"; + response["errno"] = pin; + response["arg"] = "pin"; + response["msg"] = GetCommandArgumentError(pin); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + } + + // get pin argument + int mode = GetCommandPinModeArgument(sender); + + // --------------------------------------------------------------------- + // GET PIN INFO + // --------------------------------------------------------------------- + // if a mode was not provided, then return the current pin state + if (mode == ERROR_COMMAND_ARGUMENT_MISSING){ + + // check to see if this pin is enabled + if(!pins[pin].enabled){ + response["id"] = "error"; + SerializePin(response, pin); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + } + + // get current pin state + pins[pin].value = digitalRead(pin); + + // return current pin state + response["id"] = "get"; + SerializePin(response, pin); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + } + + // --------------------------------------------------------------------- + // SET PIN ERROR + // --------------------------------------------------------------------- + // any other argument errors need to be notified + if (mode < 0){ + response["id"] = "error"; + response["errno"] = mode; + response["pin"] = pin; + response["arg"] = "mode"; + response["msg"] = GetCommandArgumentError(mode); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + + } + + // --------------------------------------------------------------------- + // DISABLE PIN + // --------------------------------------------------------------------- + // disable/reset pin tracking + if (mode == PIN_MODE_DISABLE){ + + // disable and reset pin state + pins[pin].enabled = false; + pins[pin].counter = 0; + pins[pin].value = -1; + pins[pin].mode = -1; + + // reset actual hardware pins to default mode + pinMode(pin, INPUT); + + // return current pin state + response["id"] = "set"; + SerializePin(response, pin, false); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + } + + // --------------------------------------------------------------------- + // ENABLE OUTPUT PIN (and set state to HIGH) + // --------------------------------------------------------------------- + // handle pin output HIGH + else if (mode == PIN_MODE_OUTPUT_HIGH){ + + // setup actual GPIO pin and initial state + pinMode(pin, OUTPUT); + digitalWrite(pin, HIGH); + + // update pin cache + pins[pin].mode = OUTPUT; + pins[pin].value = HIGH; + } + + // --------------------------------------------------------------------- + // ENABLE OUTPUT PIN (and set state to LOW) + // --------------------------------------------------------------------- + // handle pin output LOW + else if (mode == PIN_MODE_OUTPUT_LOW){ + + // setup actual GPIO pin and initial state + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + + // update pin cache + pins[pin].mode = OUTPUT; + pins[pin].value = LOW; + } + + // --------------------------------------------------------------------- + // ENABLE OUTPUT PIN (and set state to use provided value) + // --------------------------------------------------------------------- + // handle pin output + else if (mode == OUTPUT){ + + // get pin argument + int value = GetCommandDigitalStateArgument(sender); + + // if a state was not provided, then return the current pin state + if (value < 0){ + response["id"] = "error"; + response["errno"] = value; + response["pin"] = pin; + response["mode"] = "output"; + response["arg"] = "value"; + response["msg"] = GetCommandArgumentError(value); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + } + + // setup actual GPIO pin + pinMode(pin, OUTPUT); + digitalWrite(pin, value); + + // update pin cache mode + pins[pin].mode = OUTPUT; + } + + // --------------------------------------------------------------------- + // ENABLE INPUT PIN (pull-down) + // --------------------------------------------------------------------- + // handle pin input + else if (mode == INPUT){ + + // setup actual GPIO pin + pinMode(pin, INPUT); + + // update pin cache mode + pins[pin].mode = INPUT; + + } + + // --------------------------------------------------------------------- + // ENABLE INPUT PIN (pull-up) + // --------------------------------------------------------------------- + // handle pin input + else if (mode == INPUT_PULLUP){ + + // setup actual GPIO pin + pinMode(pin, INPUT_PULLUP); + + // update pin cache mode + pins[pin].mode = INPUT_PULLUP; + + } + + // enable and sync pin state + pins[pin].enabled = true; + pins[pin].value = digitalRead(pin); + pins[pin].counter = 0; + + // output status + response["id"] = "set"; + SerializePin(response, pin); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); +} + +// create DOUT command variable +SerialCommand PinCommand = SerialCommand("pin", pin_command_execute); +SerialCommand PinShortCommand = SerialCommand("p", pin_command_execute); + +#endif //PI4J_COMMAND_PIN_H + diff --git a/pi4j-test-harness/src/main/arduino/src/command/PinsCommand.h b/pi4j-test-harness/src/main/arduino/src/command/PinsCommand.h new file mode 100644 index 000000000..df9b49661 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/PinsCommand.h @@ -0,0 +1,100 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_PINS_H +#define PI4J_COMMAND_PINS_H + +#include +#include +#include +#include "CommandsArgumentParser.h" + +// create PINS command invocation handler +void pins_command_execute(SerialCommands* sender){ + + DynamicJsonDocument doc(1024*GPIO_MAX_PINS); + JsonObject response = doc.to(); + + // get argument + int all = GetCommandBooleanArgument(sender); + + // check for missing or invalid argument + if (all != ERROR_COMMAND_ARGUMENT_MISSING && all < 0){ + response["id"] = "error"; + response["errno"] = all; + response["arg"] = "all"; + response["msg"] = GetCommandArgumentError(all); + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); + return; + } + + response["id"] = "pins"; + JsonArray pinscontainer = response.createNestedArray("pins"); + + // iterate pin states in cache + int total, enabled, restricted, disabled, inputs, outputs; + total = enabled = restricted = disabled = inputs = outputs = 0; + for(int p = 0; p < GPIO_MAX_PINS; p++){ + + if(all > 0 || (pins[p].enabled && !pins[p].restricted)){ + int v = digitalRead(p); + pins[p].value = v; + + // increment summary counters + total++; + if(pins[p].restricted) restricted++; + if(pins[p].enabled) enabled++; + if(!pins[p].enabled) disabled++; + if(pins[p].mode == OUTPUT) outputs++; + if(pins[p].mode == INPUT) inputs++; + if(pins[p].mode == INPUT_PULLUP) inputs++; + + // create nested pin object and insert into the container array + JsonObject pincontainer = pinscontainer.createNestedObject(); + SerializePin(pincontainer, p, false); + } + } + + // include summary totals + response["total"] = total; + response["restricted"] = restricted; + response["disabled"] = disabled; + response["enabled"] = enabled; + response["inputs"] = inputs; + response["outputs"] = outputs; + + // serialize and print + serializeJson(doc, *sender->GetSerial()); + sender->GetSerial()->println(); +} + +// create DOUT command variable +SerialCommand PinsCommand = SerialCommand("pins", pins_command_execute); + +#endif //PI4J_COMMAND_PINS_H + diff --git a/pi4j-test-harness/src/main/arduino/src/command/RebootCommand.h b/pi4j-test-harness/src/main/arduino/src/command/RebootCommand.h new file mode 100644 index 000000000..0be3ade76 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/RebootCommand.h @@ -0,0 +1,49 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_REBOOT_H +#define PI4J_COMMAND_REBOOT_H + +#include +#include +#include + +// create REBOOT command invocation handler +void reboot_command_execute(SerialCommands* sender){ + DynamicJsonDocument doc(1024); + JsonObject response = doc.to(); + response["id"] = "reboot"; + response["msg"] = "Rebooting system now"; + serializeJson(doc, console); + console.println(); + reboot(); +} + +// create REBOOT command variable +SerialCommand RebootCommand = SerialCommand("reboot", reboot_command_execute); + +#endif //PI4J_COMMAND_REBOOT_H diff --git a/pi4j-test-harness/src/main/arduino/src/command/ResetCommand.h b/pi4j-test-harness/src/main/arduino/src/command/ResetCommand.h new file mode 100644 index 000000000..c9938c5b5 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/command/ResetCommand.h @@ -0,0 +1,49 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_COMMAND_RESET_H +#define PI4J_COMMAND_RESET_H + +#include +#include +#include + +// create RESET command invocation handler +void reset_command_execute(SerialCommands* sender){ + // DynamicJsonDocument doc(1024); + // JsonObject response = doc.to(); + // response["id"] = "reset"; + // response["msg"] = "Resetting all I/O pins and testing states."; + // serializeJson(doc, console); + // console.println(); + reset(); +} + +// create RESET command variable +SerialCommand ResetCommand = SerialCommand("reset", reset_command_execute); + +#endif //PI4J_COMMAND_RESET_H diff --git a/pi4j-test-harness/src/main/arduino/src/main.cpp b/pi4j-test-harness/src/main/arduino/src/main.cpp new file mode 100644 index 000000000..bf2ef390c --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/main.cpp @@ -0,0 +1,519 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +// include main header +#include "main.h" +#include "pins.h" +#include + +// ------------------------------------------------------------------------------------------------------------------------------ +// INTERACTIVE COMMAND PROCESSOR AND COMMANDS +// ------------------------------------------------------------------------------------------------------------------------------ + +// create data buffer and command processor +char serial_command_buffer_[32]; +SerialCommands processor(&console_pipe, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " "); + +// include Pi4J interactive commands +#include "command/Commands.h" + +/** + * FIRMWARE STARTUP + */ +void setup() { + + // define default UART baud rate for serial interfaces + // @see: https://www.arduino.cc/en/serial/begin + CONSOLE_INTERFACE.begin(CONSOLE_BAUD_RATE); + DEBUG_INTERFACE.begin(DEBUG_BAUD_RATE); + + // initialize the interactive diagnostics console using the piped console serial ports + console.init(&console_pipe); + + // this is an artificial delay to see this data in the debug console when using Arduino/PlatformIO IDE + for(int d = FIRMWARE_BOOT_DELAY; d > 0; d--){ + console.print("... Pi4J test harness firmware will start in "); + console.print(d); + console.println(" second(s)."); + delay(1000); + } + + // print firmware startup banner and program information + info(&console); + + // pins zero and one are reserved for USB programming port + pins[0].restricted = true; + pins[1].restricted = true; + + // initialize firmware + inititalize(); +} + +/** + * INITIALIZE FIRMWARE + */ +void inititalize(){ + // configure interactive serial commands + AddInteractiveCommands(processor); + + // display ready/running message + DynamicJsonDocument doc(1024); + JsonObject response = doc.to(); + response["id"] = "ready"; + serializeJson(doc, console); + console.println(); + + // reset I2C cache + i2cCache.reset(); + + // // initialize i2c as slave + // Wire.begin(I2C_SLAVE_ADDRESS); + + // // define callbacks for i2c communication + // Wire.onReceive(receiveI2CData); + // Wire.onRequest(sendI2CData); + // i2cCache.wire = &Wire; + +} + +void reset(){ + + DynamicJsonDocument doc(1024*GPIO_MAX_PINS); + JsonObject response = doc.to(); + response["id"] = "reset"; + JsonArray pinscontainer = response.createNestedArray("pins"); + + // reset pins states + int total, inputs, outputs; + total = inputs = outputs = 0; + + for(int p = 0; p < GPIO_MAX_PINS; p++){ + bool isEnabled = pins[p].enabled; + if(isEnabled){ + total++; + if(pins[p].mode == OUTPUT) outputs++; + if(pins[p].mode == INPUT) inputs++; + if(pins[p].mode == INPUT_PULLUP) inputs++; + JsonObject pincontainer = pinscontainer.createNestedObject(); + SerializePin(pincontainer, p, false); + } + pins[p].enabled = false; + pins[p].value = -1; + pins[p].mode = -1; + pins[p].counter = 0; + + // reset actual hardware pins to default mode + if(!pins[p].restricted) pinMode(p, INPUT); + } + + // reset I2C cache + i2cCache.reset(); + + // terminate all I2C buses + Wire.end(); + Wire1.end(); + + // include summary totals + response["total"] = total; + response["inputs"] = inputs; + response["outputs"] = outputs; + + serializeJson(doc, console); + console.println(); +} + +/** + * SERVICE LOOP + */ +void loop() { + processor.ReadSerial(); + console_pipe.loop(); + + // for(int p = 0; p < GPIO_MAX_PINS; p++){ + // if(pins[p].enabled && + // !pins[p].restricted && + // (pins[p].mode == INPUT || pins[p].mode == INPUT_PULLUP)){ + // byte v = digitalRead(p); + // if(v != pins[p].value){ + + // // update pin cache + // pins[p].value = v; + // pins[p].counter++; + + // // print pin status + // DynamicJsonDocument doc(1024); + // JsonObject response = doc.to(); + // response["id"] = "change"; + // SerializePin(response, p); + // serializeJson(doc, console); + // console.println(); + // } + // } + // } +} + +/** + * SYSTEM INFO + */ +void info(Stream* out){ + DynamicJsonDocument doc(1024); + JsonObject response = doc.to(); + response["id"] = "info"; + response["name"] = FIRMWARE_NAME; + response["version"] = FIRMWARE_VERSION_STRING; + response["date"] = FIRMWARE_DATE_STRING; + response["copyright"] = COPYRIGHT; + serializeJson(doc, *out); + out->println(); +} + +/** + * HARD REBOOT SYSTEM (in one second) + */ +void reboot() { + console.println(); + console.println(F("****************************************************")); + console.println(F("REBOOT!")); + console.println(F("****************************************************")); + console.println(F("The system has started a REBOOT and will be")); + console.println(F("restarted in one second.")); + console.println(F("****************************************************")); + console.println(); + NVIC_SystemReset(); +} + + +// // callback for received data +// void receiveI2CDataSMBus(int byteCount){ + +// console.print("<-- I2C RX Byte Count: "); +// console.println(byteCount); +// console.println(byteCount); +// console.println(byteCount); + +// // process bytes received +// if(byteCount > 0){ + +// // create a receive data buffer +// uint8_t buffer[36] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + +// // read all available bytes from the I2C bus +// byteCount = Wire.readBytes(buffer, byteCount); + +// console.print(" BYTES READ "); +// console.print(byteCount); +// //console.println(); + +// console.print(" :: "); +// for(int i = 0; i < byteCount; i++){ +// console.print(buffer[i]); +// console.print(", "); +// } +// console.println(); + +// // if the first byte is greater than 10, then treat this as a raw data byte write operation +// if(byteCount == 1 && buffer[0] >= 10){ +// // ------------------------------------------- +// // WRITING RAW BYTE VALUES +// // ------------------------------------------- +// console.print(" WRITING RAW BYTE: "); +// console.println((int)buffer[0]); +// i2cCache.length = 1; +// i2cCache.buffer[0] = buffer[0]; +// } + +// // if the first byte is between 0 and 9, then treat this as a SMBus registry data access operation +// else if(buffer[0] >= 0 && buffer[0] < 10){ + +// // regsiter address is the first byte +// int address = buffer[0]; + +// console.print(" REGISTER "); +// console.print(buffer[0]); +// console.println(); + +// return; + + +// // if we only received a single byte, then this is a READ operation +// if(byteCount == 1){ +// // ------------------------------------------- +// // REQUEST RECEIVED FOR READING A REGISTER +// // ------------------------------------------- +// uint16_t length = i2cCache.reg[address].length; + +// console.print(" REQUEST RECEIVED TO READ REGISTER: "); +// console.print(address); +// console.print("; ("); +// console.print(length); +// console.println(" bytes)"); +// return; +// // copy register data length to read buffer length +// i2cCache.length = length; + +// // copy the data from this register to the read buffer +// for(int i = 0; i < length; i++){ +// i2cCache.buffer[i] = i2cCache.reg[address].data[i]; +// } +// } + +// // if we received multiple bytes, then this is a WRITE operation +// else{ +// // ------------------------------------------- +// // WRITE REGISTER DATA +// // ------------------------------------------- + +// // get the data lenght from the number bytes available subtracting address (first) byte +// uint16_t length = byteCount - 1; + +// // only maximum of 32 bytes are supported; bounds check the data length +// if(length > 32) length = 32; + +// // update I2C register in the cache with the recevied data length +// i2cCache.reg[address].length = length; +// i2cCache.length = length; + +// console.print(" WRITING REGISTER: "); +// console.print(address); +// console.print("; BYTES="); +// console.println(length); + +// // // process data recevied +// // for(int i = 0; i < length; i++){ +// // // copy the received data to this register's storage buffer +// // i2cCache.reg[address].data[i] = buffer[i+1]; + +// // // copy the data from this register to the read buffer +// // i2cCache.buffer[i] = buffer[i+1]; +// // } +// } +// } +// else { +// console.print(" UNSUPPORTED REGISTER ADDRESS: "); +// console.print(buffer[0]); +// console.println(); +// } +// } + +// // drain anything remaining in buffer +// // while(Wire.available()){ +// // Wire.read(); +// // } +// return; + + + + +// // handle single byte values +// if(Wire.available() == 1){ +// int c = Wire.read(); // receive a byte as character + +// if(c > 10){ +// // WRITING RAW BYTE VALUES +// i2cCache.length = 1; +// i2cCache.buffer[0] = c; + +// console.print(" WRITING RAW BYTE: "); +// console.println((int)c); + +// } +// else{ +// // READING A REGISTER +// int address = c; + +// // bail out if address is unsupported; drain buffer +// if(address < 0 || address >= 10){ +// // while(Wire.available()) +// // Wire.read(); +// } + +// console.print(" READING REGISTER: "); +// console.println(address); + +// // copy register data to read buffer +// i2cCache.length = i2cCache.reg[address].length; +// if(i2cCache.length > 0){ +// console.print(" COPYING REGISTER TO READ BUFFER: ("); +// console.print(i2cCache.length); +// console.println(" bytes)"); +// memcpy(i2cCache.buffer, i2cCache.reg[address].data, i2cCache.length); +// } +// } +// } +// else { +// // WRITING A REGISTER +// int address = Wire.read(); // get register address + +// // bail out if address is unsupported; drain buffer +// if(address < 0 || address >= 10){ +// //if(Wire.available()) +// //Wire.read(Wire.av); +// } + +// // get number of bytes still available to read +// int bytesRemaining = Wire.available(); + +// // maximum of 32 bytes supported +// if(bytesRemaining > 32) bytesRemaining = 32; + +// // update I2C cache with data length +// i2cCache.reg[address].length = bytesRemaining; +// i2cCache.length = bytesRemaining; + +// console.print(" WRITING REGISTER: "); +// console.print(address); +// console.print("; BYTES="); +// console.println(bytesRemaining); + +// if(bytesRemaining > 0){ +// //Wire.readBytes(i2cCache.reg[address].data, i2cCache.reg[address].length) ; +// // for(int i = 0; i < bytesRemaining; i++){ +// // char b = Wire.read(); +// // i2cCache.reg[address].data[i] = b; +// // i2cCache.buffer[i] = b; +// // console.print("WRITING VALUE BYTE: "); +// // console.print((uint)b); +// // console.println(); +// // } + +// console.print("WRITING VALUE: "); +// //console.printHex(i2cCache.reg[address].data, bytesRemaining); +// console.println(); +// } +// } + +// // // display ready/running message +// // DynamicJsonDocument doc(512); +// // JsonObject response = doc.to(); +// // response["id"] = "i2c"; +// // response["value"] = i2cValue; +// // serializeJson(doc, console); +// // console.println(); +// } + + + +// callback for sending data +void sendI2CData(){ + uint8_t address = i2cCache.address; + uint8_t length = i2cCache.reg[address].length; + // console.print("--> I2C SEND ["); + // console.print(length); + // console.println("] BYTES"); + + // for(int n = 0; n < length; n++){ + // console.print(" --> "); + // console.println(i2cCache.reg[address].data[n]); + // } + + i2cCache.wire->write(i2cCache.reg[address].data, length); +} + +// callback for sending data +void sendI2CDataRaw(){ + // console.print("--> I2C SEND ["); + // console.print(i2cCache.length); + // console.println("] BYTES"); + // for(int n = 0; n < i2cCache.length; n++){ + // console.print(" --> "); + // console.println(i2cCache.buffer[n]); + // } + + i2cCache.wire->write(i2cCache.buffer, sizeof(i2cCache.buffer)); +} + +// ------------------------------------------- +// WRITING RAW BYTE VALUES +// ------------------------------------------- +// callback for received data +void receiveI2CDataRaw(int byteCount){ + // console.print("<-- I2C RECEIVE ["); + // console.print(byteCount); + // console.println("] BYTES"); + // if(byteCount == 0) return; + + // clear buffer store + memset(i2cCache.buffer, 0, sizeof i2cCache.buffer); + + // read all available bytes from the I2C bus + byteCount = i2cCache.wire->readBytes(i2cCache.buffer, byteCount); + i2cCache.length = byteCount; +} + +// ------------------------------------------- +// WRITING REGISTERS +// ------------------------------------------- +// callback for received data +void receiveI2CData(int byteCount){ + if(byteCount == 0) return; // ignore any zero byte callbacks + uint8_t address = i2cCache.wire->read(); + uint8_t length = byteCount - 1; // substract for address byte + // console.print("--> I2C RECEIVE ["); + // console.print(length); + // console.print("] BYTES"); + // if(length == 0){ + // console.print("; GET"); + // } else { + // console.print("; SET"); + // } + // console.println(); + + // update active register address + i2cCache.address = address; + + // if a data payload is included, then we need to cache + // the value in the register's data store + if(length > 0){ + + // clear register data store + memset(i2cCache.reg[address].data, 0, sizeof i2cCache.reg[address].data); + + // update register data length + i2cCache.reg[address].length = length; + + // read all available bytes from the I2C bus into the register data store + i2cCache.wire->readBytes(i2cCache.reg[address].data, length); + + // for(int n = 0; n < length; n++){ + // console.print(" <-- "); + // console.println(i2cCache.reg[address].data[n]); + // } + + } + + // // display ready/running message + // DynamicJsonDocument doc(512); + // JsonObject response = doc.to(); + // response["id"] = "i2c"; + // response["value"] = i2cValue; + // serializeJson(doc, console); + // console.println(); +} diff --git a/pi4j-test-harness/src/main/arduino/src/main.h b/pi4j-test-harness/src/main/arduino/src/main.h new file mode 100644 index 000000000..b787af0b2 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/main.h @@ -0,0 +1,109 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_MAIN_H +#define PI4J_MAIN_H + +// include platform libraries +#include + +// include platform specific libraries +#include // for I2C comms. (SDA, SCL) +//#include "wiring_private.h" + +// include support for Interactive Serial Commands +// https://raw.githubusercontent.com/ppedro74/Arduino-SerialCommands +#include + +// include Pi4J common +#include "pi4j.h" + +// include Pi4J utility classes +#include "util/Utils.h" + +// ------------------------------------------------------------------------------------------------------------------------------ +// DEFINE FUNTION PROTOTYPES +// ------------------------------------------------------------------------------------------------------------------------------ +void loop(); +void setup(); +void info(Stream* out); +void reboot(); +void inititalize(); +void reset(); +void receiveI2CData(int byteCount); +void sendI2CData(); +void receiveI2CDataRaw(int byteCount); +void sendI2CDataRaw(); + + +// create priped interface for interactive console (muxed serial ports) +StreamPipe console_pipe(&CONSOLE_INTERFACE, &DEBUG_INTERFACE); + + +// ------------------------------------------------------------------------------------------------------------------------------ +// DEFINE RUNTIME COMPONENTS +// ------------------------------------------------------------------------------------------------------------------------------ + +// create NULL stream; used for debugging only +struct NullStream : public Stream{ + NullStream( void ) { return; } + int available( void ) { return 0; } + void flush( void ) { return; } + int peek( void ) { return -1; } + int read( void ){ return -1; } + size_t write( uint8_t u_Data ){ return u_Data; } +} nullStream; + + +struct I2cRegister { + uint8_t data[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + uint16_t length = 0; +}; + +struct I2cCache { + I2cRegister reg[255]; + uint8_t address = 0; + uint8_t buffer[1024]; + uint16_t length = 0; + bool rawMode = false; + TwoWire* wire; + void reset(){ + address = 0; + length = 0; + rawMode = false; + memset(buffer, 0, sizeof buffer); + wire = nullptr; + for(int i = 0; i < 256; i++){ + reg[i].length = 0; + memset(reg[i].data, 0, sizeof reg[i].data); + } + } +}; + +I2cCache i2cCache; + +#endif //PI4J_MAIN_H \ No newline at end of file diff --git a/pi4j-test-harness/src/main/arduino/src/pi4j.h b/pi4j-test-harness/src/main/arduino/src/pi4j.h new file mode 100644 index 000000000..d300c2707 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/pi4j.h @@ -0,0 +1,153 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_H +#define PI4J_H + + +// +// SEE ARDUINO PIN DEFINITIONS FOR SAMD MICROCONTROLLER HERE: +// https://github.com/arduino/ArduinoCore-samd/blob/1.6.19/variants/arduino_zero/variant.cpp +// + +// ----------------------------------------------------------------------------------------------------------- +// GENERIC HELPER MACROS +// ----------------------------------------------------------------------------------------------------------- + +#define STRINGIZE2(s) #s +#define STRINGIZE(s) STRINGIZE2(s) + +// ----------------------------------------------------------------------------------------------------------- +// FIRMWARE METADATA DEFINITIONS +// ----------------------------------------------------------------------------------------------------------- + +// console boot header/banner +#ifndef PI4J_BANNER +#define PI4J_CRLF "\r\n" +#define PI4J_BANNER_LINE "=======================================================" +#define PI4J_BANNER_L1 " " +#define PI4J_BANNER_L2 " The Pi4J Project " +#define PI4J_BANNER_L3 " Arduino Test Harness " +#define PI4J_BANNER_L4 " " +#define PI4J_BANNER PI4J_BANNER_LINE PI4J_CRLF PI4J_BANNER_L1 PI4J_CRLF PI4J_BANNER_L2 PI4J_CRLF PI4J_BANNER_L3 PI4J_CRLF PI4J_BANNER_L4 PI4J_CRLF PI4J_BANNER_LINE +#endif + +// copyright string +#ifndef PI4J_COPYRIGHT +#define PI4J_COPYRIGHT "COPYRIGHT: PI4J, LLC @ 2019, ALL RIGHTS RESERVED" +#endif + +#ifndef BANNER +#define BANNER PI4J_BANNER +#endif + +#ifndef COPYRIGHT +#define COPYRIGHT PI4J_COPYRIGHT +#endif + +// ----------------------------------------------------------------------------------------------------------- +// FIRMWARE METADATA DEFINITIONS +// ----------------------------------------------------------------------------------------------------------- + +// firmware name, version and last updated date +#ifndef FIRMWARE_NAME +#define FIRMWARE_NAME "Pi4J ARDUINO TEST HARNESS" +#endif + +#ifndef FIRMWARE_VERSION +#define FIRMWARE_VERSION 0.0.0 (ALPHA) // <- this is passed in from build environment +#endif +#define FIRMWARE_VERSION_STRING STRINGIZE(FIRMWARE_VERSION) + +#ifndef FIRMWARE_DATE +#define FIRMWARE_DATE 1900-01-01 // <- this is passed in from build environment +#endif +#define FIRMWARE_DATE_STRING STRINGIZE(FIRMWARE_DATE) + +#ifndef HARDWARE_VERSION +#define HARDWARE_VERSION "0.1" +#endif + +#ifndef FIRMWARE_BOOT_DELAY +#define FIRMWARE_BOOT_DELAY 0 +#endif + + +// ----------------------------------------------------------------------------------------------------------- +// FIRMWARE DEVELOPMENT FLAGS +// ----------------------------------------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------------------------------------- +// GENERIC TIME INTERVAL DEFINITIONS (all interval times are in milliseconds) +// ----------------------------------------------------------------------------------------------------------- +#define ONE_SECOND 1000 +#define TEN_SECOND 10*ONE_SECOND +#define ONE_MINUTE 60*ONE_SECOND + +// ----------------------------------------------------------------------------------------------------------- +// DEBUGGING INTERFACE SETTINGS AND HARDWARE DEFINITIONS +// ----------------------------------------------------------------------------------------------------------- + +#define DEBUG_INTERFACE SerialUSB +#define DEBUG_BAUD_RATE 115200 // Debug baud rate + +// ----------------------------------------------------------------------------------------------------------- +// INTERACTIVE DIAGNOSTICS CONSOLE DEFINITIONS +// ----------------------------------------------------------------------------------------------------------- + +#define CONSOLE_INTERFACE Serial +#define CONSOLE_BAUD_RATE 115200 + + +// ----------------------------------------------------------------------------------------------------------- +// I2C DEFINITIONS +// ----------------------------------------------------------------------------------------------------------- + +#define I2C_SLAVE_ADDRESS 0x04 + +// ----------------------------------------------------------------------------------------------------------- +// MISC GLOBAL INCLUDES +// ----------------------------------------------------------------------------------------------------------- + +// include global utility classes +//#include "util/Console.h" + +// include runtime classes +//#include "runtime/Runtime.h" + + +#define ERROR_COMMAND_ARGUMENT_MISSING -1 +#define ERROR_COMMAND_ARGUMENT_INVALID -2 +#define ERROR_INVALID_PIN_OUT_OF_RANGE -3 +#define ERROR_INVALID_PIN_RESTRICTED -4 +#define ERROR_INVALID_PIN_DISABLED -5 +#define ERROR_INVALID_I2C_BUS_OUT_OF_RANGE -11 +#define ERROR_INVALID_I2C_DEVICE_OUT_OF_RANGE -12 +#define ERROR_UNSUPPORTED_COMMAND -99 + + +#endif //PI4J_H diff --git a/pi4j-test-harness/src/main/arduino/src/pins.h b/pi4j-test-harness/src/main/arduino/src/pins.h new file mode 100644 index 000000000..c458fcd47 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/pins.h @@ -0,0 +1,184 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_PINS_H +#define PI4J_PINS_H + +// include platform libraries +#include +#include +#include + +// ------------------------------------------------------------------------------------------------------------------------------ +// DEFINE CONSTANTS +// ------------------------------------------------------------------------------------------------------------------------------ + +#define GPIO_MAX_PINS 54 + +#define PIN_MODE_INPUT INPUT // 0x0 +#define PIN_MODE_OUTPUT OUTPUT // 0x1 +#define PIN_MODE_INPUT_PULLUP INPUT_PULLUP // 0x2 +#define PIN_MODE_OUTPUT_LOW 0x3 +#define PIN_MODE_OUTPUT_HIGH 0x4 +#define PIN_MODE_DISABLE 0xF + + +// ------------------------------------------------------------------------------------------------------------------------------ +// DEFINE FUNTION PROTOTYPES +// ------------------------------------------------------------------------------------------------------------------------------ +void loop(); +void setup(); +void info(Stream* out); +void reboot(); +void inititalize(); +void reset(); + +// ------------------------------------------------------------------------------------------------------------------------------ +// DEFINE RUNTIME COMPONENTS +// ------------------------------------------------------------------------------------------------------------------------------ + +struct PinCache { + bool restricted = false; + bool enabled = false; + int8_t mode = -1; + int16_t value = -1; + uint16_t counter = 0; + + String modeString(){ + switch (mode) + { + case PIN_MODE_INPUT: return "input"; break; + case PIN_MODE_INPUT_PULLUP: return "input_pullup"; break; + case PIN_MODE_OUTPUT: return "output"; break; + case PIN_MODE_DISABLE: return "disabled"; break; + default: return "unknown"; break; + } + } +}; + +PinCache pins[GPIO_MAX_PINS]; + +// void PrintPinStatus2(Stream* out, int pin, String header, bool includeErroNo = true){ +// PinCache p = pins[pin]; + +// // return current pin state +// out->print("<"); +// out->print(header); +// out->print("> PIN="); +// out->print(pin); + +// if(p.restricted){ +// if(includeErroNo){ +// out->print("; ERRNO="); +// out->print(ERROR_INVALID_PIN_RESTRICTED); +// } +// out->print("; ACCESS=RESTRICTED; MSG=Access to this pin is restricted."); +// } +// else if(p.enabled == false){ +// if(includeErroNo){ +// out->print("; ERRNO="); +// out->print(ERROR_INVALID_PIN_DISABLED); +// } +// out->print("; ACCESS=DISABLED; MSG=This pin is not currently in use."); +// } +// else { +// out->print("; MODE="); +// out->print(p.mode); +// out->print("; VALUE="); +// out->print(p.value); +// out->print("; CHANGES="); +// out->print(p.counter); +// } +// out->println(); +// } + + +// void PrintPinStatus(Stream* out, int pin, String header, bool includeErroNo = true){ +// PinCache p = pins[pin]; + +// // Allocate a temporary JsonDocument +// // Use arduinojson.org/v6/assistant to compute the capacity. +// DynamicJsonDocument doc(1024); + + +// doc["type"] = header.c_str(); +// doc["pin"] = pin; + +// if(p.restricted){ +// if(includeErroNo){ +// doc["errno"] = ERROR_INVALID_PIN_RESTRICTED; +// } +// doc["access"] = "restricted"; +// doc["msg"] = "Access to this pin is restricted."; +// } +// else if(p.enabled == false){ +// if(includeErroNo){ +// doc["errno"] = ERROR_INVALID_PIN_DISABLED; +// } +// doc["access"] = "disabled"; +// doc["msg"] = "This pin is not currently in use."; +// } +// else { +// doc["mode"] = p.modeString(); +// doc["value"] = p.value; +// doc["changes"] = p.counter; +// } + +// serializeJsonPretty(doc, *out); +// out->println(); +// } + + + +void SerializePin(JsonObject& json, int pin, bool includeErroNo = true){ + PinCache p = pins[pin]; + + json["pin"] = pin; + + if(p.restricted){ + if(includeErroNo){ + json["errno"] = ERROR_INVALID_PIN_RESTRICTED; + } + json["access"] = "restricted"; + json["msg"] = "Access to this pin is restricted."; + } + else if(p.enabled == false){ + if(includeErroNo){ + json["errno"] = ERROR_INVALID_PIN_DISABLED; + } + json["access"] = "disabled"; + json["msg"] = "This pin is not currently in use."; + } + else { + json["mode"] = p.modeString(); + json["value"] = p.value; + json["changes"] = p.counter; + } +} + + +#endif //PI4J_PINS_H \ No newline at end of file diff --git a/pi4j-test-harness/src/main/arduino/src/src.ino b/pi4j-test-harness/src/main/arduino/src/src.ino new file mode 100644 index 000000000..2bce0ef8c --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/src.ino @@ -0,0 +1,29 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + + // PLEASE SEE "main.h" and "main.cpp" FOR THE PRIMARY PROGRAM + diff --git a/pi4j-test-harness/src/main/arduino/src/util/Console.cpp b/pi4j-test-harness/src/main/arduino/src/util/Console.cpp new file mode 100644 index 000000000..0ff3e1f79 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/Console.cpp @@ -0,0 +1,190 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#include "Console.h" + +void Console::init(Stream* stream){ + if(stream == nullptr) return; + this->_stream = stream; +} + +int Console::available( void ) { + if(this->_stream == nullptr) return -1; + return this->_stream->available(); +} +void Console::flush( void ) { + if(this->_stream == nullptr) return; + this->_stream->flush(); +} +int Console::peek( void ) { + if(this->_stream == nullptr) return -1; + return this->_stream->peek(); +} +int Console::read( void ){ + if(this->_stream == nullptr) return -1; + return this->_stream->read(); +} +size_t Console::write( uint8_t data ){ + if(this->_stream == nullptr) return -1; + return this->_stream->write(data); +} + +size_t Console::print(const std::string &s){ + return this->_stream->print(s.c_str()); +} +size_t Console::print(const __FlashStringHelper *fsh){ + return Stream::print(fsh); +} +size_t Console::print(const String &str){ + return Stream::print(str); +} +size_t Console::print(const char str[]){ + return Stream::print(str); +} +size_t Console::print(char b){ + return Stream::print(b); +} +size_t Console::print(unsigned char c, int base){ + return Stream::print(c, base); +} +size_t Console::print(int n, int base){ + return Stream::print(n, base); +} +size_t Console::print(unsigned int n, int base){ + return Stream::print(n, base); +} +size_t Console::print(long n, int base){ + return Stream::print(n, base); +} +size_t Console::print(unsigned long n, int base){ + return Stream::print(n, base); +} +size_t Console::print(double n, int digits){ + return Stream::print(n, digits); +} +size_t Console::print(const Printable& x){ + return Stream::print(x); +} + +size_t Console::println(const std::string &s){ + return this->_stream->println(s.c_str()); +} +size_t Console::println(const __FlashStringHelper *fsh){ + return Stream::println(fsh); +} +size_t Console::println(const String &str){ + return Stream::println(str); +} +size_t Console::println(const char str[]){ + return Stream::println(str); +} +size_t Console::println(char b){ + return Stream::println(b); +} +size_t Console::println(unsigned char c, int base){ + return Stream::println(c, base); +} +size_t Console::println(int n, int base){ + return Stream::println(n, base); +} +size_t Console::println(unsigned int n, int base){ + return Stream::println(n, base); +} +size_t Console::println(long n, int base){ + return Stream::println(n, base); +} +size_t Console::println(unsigned long n, int base){ + return Stream::println(n, base); +} +size_t Console::println(double n, int digits){ + return Stream::println(n, digits); +} +size_t Console::println(const Printable& x){ + return Stream::println(x); +} +size_t Console::println(void){ + return Stream::println(); +} + +size_t Console::printHex(const char *data, const size_t length){ + size_t size = 0; + size+=this->print("0x"); + for (size_t i=0; iprint("0"); } + size+=this->print(data[i],HEX); + size+=this->print(" "); + } + return size; +} +size_t Console::printHex(const uint8_t *data, const size_t length){ + size_t size = 0; + size+=this->print("0x"); + for (size_t i=0; iprint("0"); } + size+=this->print(data[i],HEX); + size+=this->print(" "); + } + return size; +} +size_t Console::printHex(const uint16_t *data, const size_t length){ + size_t size = 0; + + size+=this->print("0x"); + for (size_t i=0; i>8); + uint8_t LSB=byte(data[i]); + + if (MSB<0x10) { + size+=this->print("0"); + } + size+=this->print(MSB,HEX); + size+=this->print(" "); + if (LSB<0x10) { + size+=this->print("0"); + } + size+=this->print(LSB,HEX); + size+=this->print(" "); + } + return size; +} +size_t Console::printHex(const std::string data, const size_t length){ + return this->printHex(data.c_str(), length); +} +size_t Console::printHex(const std::string data){ + return this->printHex(data.c_str(), data.length()); +} +size_t Console::printHex(const String data, const size_t length){ + return this->printHex(data.c_str(), length); +} +size_t Console::printHex(const String data){ + return this->printHex(data.c_str(), data.length()); +} + + +Console console; + diff --git a/pi4j-test-harness/src/main/arduino/src/util/Console.h b/pi4j-test-harness/src/main/arduino/src/util/Console.h new file mode 100644 index 000000000..c7f01ef69 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/Console.h @@ -0,0 +1,113 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + + +#ifndef PI4J_CONSOLE_H +#define PI4J_CONSOLE_H + +#define CONSOLE_NAME_LEFT '[' +#define CONSOLE_NAME_RIGHT "] " + +//#define __PRINTLN(a) console.println() +#define __PRINTNAME(a) console.printTs(CONSOLE_NAME_LEFT), console.print(a), console.print(CONSOLE_NAME_RIGHT) +#define __PRINT4(a, b, c, d) __PRINTNAME(a), console.print(b), console.print(' '), console.print(c), console.print(' '), console.print(d) +#define __PRINT3(a, b, c) __PRINTNAME(a), console.print(b), console.print(' '), console.print(c) +#define __PRINT2(a, b) __PRINTNAME(a), console.print(b) +#define _GET_OVERRIDE(_1, _2, _3, _4, NAME, ...) NAME +#define CPRINT(...) _GET_OVERRIDE(__VA_ARGS__, __PRINT4, __PRINT3, __PRINT2)(__VA_ARGS__) +#define CPRINTLN(...) _GET_OVERRIDE(__VA_ARGS__, __PRINT4, __PRINT3, __PRINT2)(__VA_ARGS__), console.println() + +// #define CPRINT(...) +// #define CPRINTLN(...) + +#include +#include +#include +#include "ConsoleCommand.h" + +class Console : public Stream +{ + protected: + Stream* _stream = nullptr; + std::map _commands; + void (*_promptCommandInvokeCallback)(); + bool _promptPending; + + public: + void init(Stream* stream); + + int available( void ); + void flush( void ); + int peek( void ); + int read( void ); + size_t write( uint8_t data ); + + size_t print(const __FlashStringHelper *); + size_t print(const String &s); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable&); + + // add 'print' method for std::string + size_t print(const std::string &s); + + size_t println(const __FlashStringHelper *); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable&); + size_t println(void); + + // print buffer as hexadecimal output + size_t printHex(const char *data, const size_t length); + size_t printHex(const uint8_t *data, const size_t length); + size_t printHex(const uint16_t *data, const size_t length); + size_t printHex(const std::string data, const size_t length); + size_t printHex(const String data, const size_t length); + size_t printHex(const std::string data); + size_t printHex(const String data); + + // add 'println' method for std::string + size_t println(const std::string &s); +}; + + +extern Console console; + +#endif //PI4J_CONSOLE_H diff --git a/pi4j-test-harness/src/main/arduino/src/util/ConsoleCommand.h b/pi4j-test-harness/src/main/arduino/src/util/ConsoleCommand.h new file mode 100644 index 000000000..34d7da69f --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/ConsoleCommand.h @@ -0,0 +1,40 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + + +#ifndef PI4J_CONSOLE_COMMAND_H +#define PI4J_CONSOLE_COMMAND_H + +struct ConsoleCommand { + char const * description; + char key; + bool prompt; + char const * promptMessage; + void (*consoleCommandInvokeCallback)(); +}; + +#endif //PI4J_CONSOLE_COMMAND_H diff --git a/pi4j-test-harness/src/main/arduino/src/util/Math.h b/pi4j-test-harness/src/main/arduino/src/util/Math.h new file mode 100644 index 000000000..3fc37b3c1 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/Math.h @@ -0,0 +1,55 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_UTILITY_MATH_H +#define PI4J_UTILITY_MATH_H + +#include +#include "itoa.h" + +class Math +{ + private: + + public: + + static char *ftoa(char *a, double f, int precision) + { + long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000}; + + char *ret = a; + long number = (long)f; + itoa(number, a, 10); + while (*a != '\0') a++; + *a++ = '.'; + long decimal = abs((long)((f - number) * p[precision])); + itoa(decimal, a, 10); + return ret; + } +}; + +#endif //PI4J_UTILITY_MATH_H diff --git a/pi4j-test-harness/src/main/arduino/src/util/StreamPipe.cpp b/pi4j-test-harness/src/main/arduino/src/util/StreamPipe.cpp new file mode 100644 index 000000000..6e50ea405 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/StreamPipe.cpp @@ -0,0 +1,201 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#include "StreamPipe.h" + +// internal stream buffer +LoopbackStream buffer; + +/* +* DEFAULT CONSTRUCTOR +*/ +StreamPipe::StreamPipe(Stream* source, Stream* pipe, bool echo){ + if(source == nullptr) return; + if(pipe == nullptr) return; + this->_source = source; + this->_pipe = pipe; + this->_echo = echo; +} + +int StreamPipe::available( void ) { + return buffer.available(); +} +void StreamPipe::flush( void ) { + buffer.flush(); +} +int StreamPipe::peek( void ) { + return buffer.peek(); +} +int StreamPipe::read( void ){ + return buffer.read(); +} +size_t StreamPipe::write( uint8_t data ){ + // write data to source stream + if(this->_source != nullptr) + this->_source->write(data); + + // write data to piped stream + if(this->_pipe != nullptr) + this->_pipe->write(data); + + // return length of data written + return 1; +} + +size_t StreamPipe::print(const std::string &s){ + // write data to source stream + if(this->_source != nullptr) + this->_source->print(s.c_str()); + + // write data to piped stream + if(this->_pipe != nullptr) + this->_pipe->print(s.c_str()); + + // return length of data written + return s.length(); +} +size_t StreamPipe::print(const __FlashStringHelper *fsh){ + return Stream::print(fsh); +} +size_t StreamPipe::print(const String &str){ + return Stream::print(str); +} +size_t StreamPipe::print(const char str[]){ + return Stream::print(str); +} +size_t StreamPipe::print(char b){ + return Stream::print(b); +} +size_t StreamPipe::print(unsigned char c, int base){ + return Stream::print(c, base); +} +size_t StreamPipe::print(int n, int base){ + return Stream::print(n, base); +} +size_t StreamPipe::print(unsigned int n, int base){ + return Stream::print(n, base); +} +size_t StreamPipe::print(long n, int base){ + return Stream::print(n, base); +} +size_t StreamPipe::print(unsigned long n, int base){ + return Stream::print(n, base); +} +size_t StreamPipe::print(double n, int digits){ + return Stream::print(n, digits); +} +size_t StreamPipe::print(const Printable& x){ + return Stream::print(x); +} + +size_t StreamPipe::println(const std::string &s){ + // write data to source stream + if(this->_source != nullptr) + this->_source->println(s.c_str()); + + // write data to piped stream + if(this->_pipe != nullptr) + this->_pipe->println(s.c_str()); + + // return length of data written + return s.length(); +} +size_t StreamPipe::println(const __FlashStringHelper *fsh){ + return Stream::println(fsh); +} +size_t StreamPipe::println(const String &str){ + return Stream::println(str); +} +size_t StreamPipe::println(const char str[]){ + return Stream::println(str); +} +size_t StreamPipe::println(char b){ + return Stream::println(b); +} +size_t StreamPipe::println(unsigned char c, int base){ + return Stream::println(c, base); +} +size_t StreamPipe::println(int n, int base){ + return Stream::println(n, base); +} +size_t StreamPipe::println(unsigned int n, int base){ + return Stream::println(n, base); +} +size_t StreamPipe::println(long n, int base){ + return Stream::println(n, base); +} +size_t StreamPipe::println(unsigned long n, int base){ + return Stream::println(n, base); +} +size_t StreamPipe::println(double n, int digits){ + return Stream::println(n, digits); +} +size_t StreamPipe::println(const Printable& x){ + return Stream::println(x); +} +size_t StreamPipe::println(void){ + return Stream::println(); +} + +void StreamPipe::echo(bool enabled){ + this->_echo = enabled; +} + +bool StreamPipe::isEcho(){ + return this->_echo; +} + +void StreamPipe::loop(){ + + // check source stream for data, if data is available, + // then read it and copy it in to the buffered stream + if(this->_source != nullptr){ + while (this->_source->available()) { + char b = this->_source->read(); + buffer.write(b); + + // should we echo back to the console? + if(_echo){ + this->_source->write(b); + } + } + } + + // check pipe stream for data, if data is available, + // then read it and copy it in to the buffered stream + if(this->_pipe != nullptr){ + while (this->_pipe->available()) { + char b = this->_pipe->read(); + buffer.write(b); + + // should we echo back to the console? + if(_echo){ + this->_pipe->write(b); + } + } + } +} diff --git a/pi4j-test-harness/src/main/arduino/src/util/StreamPipe.h b/pi4j-test-harness/src/main/arduino/src/util/StreamPipe.h new file mode 100644 index 000000000..180c1855a --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/StreamPipe.h @@ -0,0 +1,94 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_STREAM_MUX_H +#define PI4J_STREAM_MUX_H + +#include +#include +#include "LoopbackStream.h" + +class StreamPipe : public Stream +{ + protected: + Stream* _source = nullptr; + Stream* _pipe = nullptr; + bool _echo = false; + + public: + + /* + * DEFAULT CONSTRUCTOR + */ + StreamPipe(Stream* source, Stream* tap, bool echo=false); + + void loop( void ); + + int available( void ); + void flush( void ); + int peek( void ); + int read( void ); + size_t write( uint8_t data ); + + size_t print(const __FlashStringHelper *); + size_t print(const String &s); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable&); + + // add 'print' method for std::string + size_t print(const std::string &s); + + size_t println(const __FlashStringHelper *); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable&); + size_t println(void); + + // add 'println' method for std::string + size_t println(const std::string &s); + + void echo(bool enabled); + void echoOn() { echo(true); } + void echoOff() { echo(false); } + bool isEcho(); +}; + +#endif //PI4J_STREAM_MUX_H diff --git a/pi4j-test-harness/src/main/arduino/src/util/StringUtil.h b/pi4j-test-harness/src/main/arduino/src/util/StringUtil.h new file mode 100644 index 000000000..810e2bf18 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/StringUtil.h @@ -0,0 +1,172 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#ifndef PI4J_UTILITY_STRING_H +#define PI4J_UTILITY_STRING_H + +#include +#include +#include +#include +#include +#include +#include + +class StringUtil +{ + private: + + public: + + static bool isNumeric(String str){ + for(byte i=0;i& target){ + if( 0 != target.size() ) { //if the size is 0 + std::string wspc (" \t\f\v\n\r");// These are the whitespaces (space, tab, CR, LF) + //finding the last valid character + std::string::size_type posafter = target.find_last_not_of(wspc); + //finding the first valid character + std::string::size_type posbefore=target.find_first_not_of(wspc); + + if((-1 < (int)posafter) && (-1 < (int)posbefore)) //Just Wsp + { + std::string result; + // Cut off the outside parts of found positions + result = target.substr(posbefore,((posafter+1)-posbefore)); + return result; + } + } + return target; + } + + static void trimOn(std::basic_string& target){ + if( 0 != target.size() ) { //if the size is 0 + std::string wspc (" \t\f\v\n\r");// These are the whitespaces (space, tab, CR, LF) + //finding the last valid character + std::string::size_type posafter = target.find_last_not_of(wspc); + //finding the first valid character + std::string::size_type posbefore=target.find_first_not_of(wspc); + + if((-1 < (int)posafter) && (-1 < (int)posbefore)) //Just Wsp + { + // Cut off the outside parts of found positions + target = target.substr(posbefore,((posafter+1)-posbefore)); + } + } + } + + /** + * FORMATTING HELPERS + */ + static void printLeadingZeros(Stream& stream, uint16_t number, uint8_t digits=2) { + if (digits >= 6 && number < 100000) stream.print("0"); // print a 0 before if the number is < than 100000 + if (digits >= 5 && number < 10000) stream.print("0"); // print a 0 before if the number is < than 10000 + if (digits >= 4 && number < 1000) stream.print("0"); // print a 0 before if the number is < than 1000 + if (digits >= 3 && number < 100) stream.print("0"); // print a 0 before if the number is < than 100 + if (digits >= 2 && number < 10) stream.print("0"); // print a 0 before if the number is < than 10 + stream.print(number); + } + + + static std::string toUpper(const std::basic_string& s) { + std::string modified = s; + toUpperOn(modified); + return modified; + } + + static std::string toLower(const std::basic_string& s) { + std::string modified = s; + toLowerOn(modified); + return modified; + } + + static void toUpperOn(std::basic_string& s) { + for (std::basic_string::iterator p = s.begin(); + p != s.end(); ++p) { + *p = toupper(*p); // toupper is for char + } + } + + static void toLowerOn(std::basic_string& s) { + for (std::basic_string::iterator p = s.begin(); + p != s.end(); ++p) { + *p = tolower(*p); + } + } + + static bool endsWith(const char *str, const char *suffix) { + if (!str || !suffix) + return 0; + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) + return 0; + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; + } + + static bool endsWithIgnoreCase(const char *str, const char *suffix) { + if (!str || !suffix) + return 0; + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + + // if no suffix provided, then always return success + if(lensuffix <= 0){ + return 1; + } + + // if the length of the suffix exceeds the length of the target + // data, then always return a failure + if (lensuffix > lenstr) + return 0; + return strncasecmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; + } + + static std::string padRight(std::string const& str, size_t s, const char padchar = ' '){ + if ( str.size() < s ) + return str + std::string(s-str.size(), padchar); + else + return str; + } + + static std::string padLeft(std::string const& str, size_t s, const char padchar = ' '){ + if ( str.size() < s ) + return std::string(s-str.size(), padchar) + str; + else + return str; + } + + + +}; + +#endif //PI4J_UTILITY_STRING_H diff --git a/pi4j-test-harness/src/main/arduino/src/util/Utils.h b/pi4j-test-harness/src/main/arduino/src/util/Utils.h new file mode 100644 index 000000000..79cb8db70 --- /dev/null +++ b/pi4j-test-harness/src/main/arduino/src/util/Utils.h @@ -0,0 +1,31 @@ +/* + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TEST :: Arduino Test Harness + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * + * Copyright (C) 2012 - 2019 Pi4J + * + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ********************************************************************** + */ + +#include "Console.h" +#include "Math.h" +#include "StringUtil.h" +#include "StreamPipe.h" diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/ArduinoTestHarness.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/ArduinoTestHarness.java new file mode 100644 index 000000000..5e2bda79e --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/ArduinoTestHarness.java @@ -0,0 +1,257 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : ArduinoTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fazecast.jSerialComm.SerialPort; +import com.google.gson.Gson; +import org.json.JSONObject; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + + +public class ArduinoTestHarness { + + protected String comport; + protected SerialPort com = null; + protected Gson gson = new Gson(); + + public ArduinoTestHarness(String comport) throws IOException { + + // set local reference + this.comport = comport; + + // get serial port instance + com = SerialPort.getCommPort(comport); + + // configure serial port + com.setBaudRate(115200); + com.setNumDataBits(8); + com.setNumStopBits(1); + com.setParity(0); + com.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED); + + // configure read timeout + com.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0); + } + + public TestHarnessPins reset() throws IOException { + send("reset"); + TestHarnessPins response = read(TestHarnessPins.class); + return response; + } + + public TestHarnessInfo getInfo() throws IOException { + send("info"); + TestHarnessInfo response = read(TestHarnessInfo.class); + return response; + } + + public TestHarnessPin disablePin(int pin) throws IOException { + send("pin " + pin + " disable"); + TestHarnessPin response = read(TestHarnessPin.class); + return response; + } + + public TestHarnessPin getPin(int pin) throws IOException { + send("pin " + pin); + TestHarnessPin response = read(TestHarnessPin.class); + return response; + } + + public TestHarnessPin setInputPin(int pin) throws IOException { + return setInputPin(pin, false); + } + + public TestHarnessPin setInputPin(int pin, boolean pullUp) throws IOException { + String mode = (pullUp)? "input_pullup" : "input"; + send(String.format("pin %d %s", pin, mode)); + TestHarnessPin response = read(TestHarnessPin.class); + return response; + } + + public TestHarnessPin setOutputPin(int pin) throws IOException { + return setOutputPin(pin, false); + } + + public TestHarnessPin setOutputPin(int pin, boolean state) throws IOException { + String stateString = (state)? "high" : "low"; + send(String.format("pin %d output %s", pin, stateString)); + TestHarnessPin response = read(TestHarnessPin.class); + return response; + } + + + public TestHarnessResponse enableI2C(int bus, int device) throws IOException { + return enableI2C(bus, device, false); + } + + public TestHarnessResponse enableI2C(int bus, int device, boolean rawMode) throws IOException { + send(String.format("i2c %d %d %b", bus, device, rawMode)); + TestHarnessResponse response = read(TestHarnessResponse.class); + return response; + } + + public TestHarnessFrequency getFrequency(int pin) throws IOException { + send("frequency " + pin); + TestHarnessFrequency response = read(TestHarnessFrequency.class); + return response; + } + + public void send(String command) throws IOException { + + // validate serial port is connected + if(!com.isOpen()) throw new IOException("Serial port is not open;"); + + // drain any previous data + drain(); + + com.getOutputStream().write(command.getBytes(StandardCharsets.US_ASCII)); + com.getOutputStream().write(0x0D); // + com.getOutputStream().write(0x0A); // + com.getOutputStream().flush(); + } + + protected T read(Class type) throws IOException { + List responses = read(); + TestHarnessError err = null; + for (var response : responses){ + if(TestHarnessError.class.isInstance(response)){ + err = (TestHarnessError)response; + } + else if(type.isInstance(response)){ + return (T)response; + } + } + if(err != null){ + throw new IOException("TEST HARNESS ERROR: [" + err.errno + "] " + err.msg); + } + return null; + } + + protected List read() throws IOException { + + // validate serial port is connected + if(!com.isOpen()) throw new IOException("Serial port is not open;"); + + List responses = new ArrayList<>(); + Scanner in = new Scanner(com.getInputStream()); + while(in.hasNextLine()){ + var received = in.nextLine(); + //System.out.println(received); + JSONObject payload = new JSONObject(received); + + if(payload.has("id")){ + var id = payload.getString("id").toLowerCase(); + + switch (id){ + case "error": { + TestHarnessError response = gson.fromJson(received, TestHarnessError.class); + responses.add(response); + break; + } + case "info": { + TestHarnessInfo response = gson.fromJson(received, TestHarnessInfo.class); + responses.add(response); + break; + } + case "get": { + TestHarnessPin response = gson.fromJson(received, TestHarnessPin.class); + responses.add(response); + break; + } + case "set": { + TestHarnessPin response = gson.fromJson(received, TestHarnessPin.class); + responses.add(response); + break; + } + case "reset": { + TestHarnessPins response = gson.fromJson(received, TestHarnessPins.class); + responses.add(response); + break; + } + case "frequency": { + TestHarnessFrequency response = gson.fromJson(received, TestHarnessFrequency.class); + responses.add(response); + break; + } + default:{ + TestHarnessResponse response = gson.fromJson(received, TestHarnessResponse.class); + responses.add(response); + break; + } + } + } + } + + return responses; + } + + protected void drain() throws IOException { + + // validate serial port is connected + if(!com.isOpen()) throw new IOException("Serial port is not open;"); + + Scanner in = new Scanner(com.getInputStream()); + while(in.hasNextByte()){ + var received = in.nextByte(); + System.out.println("[DRAINED]" + received); + } + } + + public void terminate() throws IOException { + + // validate serial port is connected + if(!com.isOpen()) throw new IOException("Serial port is not open;"); + + // close port when done + com.closePort(); + } + + + public void initialize() throws IOException { + + // validate serial port is connected + //if(com.isOpen()) throw new IOException("Already initialized!"); + + // open com port + com.openPort(); + +// // test serial port was opened +// // if the port is not open, then fail + if(!com.isOpen()) { + throw new IOException("Serial port [" + comport + "] failed to open;"); + } + + // drain serial port buffer + //drain(); + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessError.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessError.java new file mode 100644 index 000000000..f17261b75 --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessError.java @@ -0,0 +1,38 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessError.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessError extends TestHarnessResponse { + + public int errno; + public String msg; + + public TestHarnessError() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessFrequency.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessFrequency.java new file mode 100644 index 000000000..879130b62 --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessFrequency.java @@ -0,0 +1,39 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessFrequency.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessFrequency extends TestHarnessResponse { + + public int pin; + public int frequency; + public String units; + + public TestHarnessFrequency() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessInfo.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessInfo.java new file mode 100644 index 000000000..b271dbfc6 --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessInfo.java @@ -0,0 +1,40 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessInfo.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessInfo extends TestHarnessResponse { + + public String name; + public String version; + public String date; + public String copyright; + + public TestHarnessInfo() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPin.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPin.java new file mode 100644 index 000000000..cad9c3557 --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPin.java @@ -0,0 +1,42 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessPin.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessPin extends TestHarnessResponse { + + public int pin; + public String mode; + public int value; + public int counter; + public String access; + public String msg; + + public TestHarnessPin() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPinSet.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPinSet.java new file mode 100644 index 000000000..0d123450c --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPinSet.java @@ -0,0 +1,40 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessPinSet.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessPinSet extends TestHarnessResponse { + + public int pin; + public String mode; + public int value; + public int counter; + + public TestHarnessPinSet() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPins.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPins.java new file mode 100644 index 000000000..db69d2f1d --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessPins.java @@ -0,0 +1,40 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessPins.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessPins extends TestHarnessResponse { + + public TestHarnessPin[] pins; + public int total; + public int inputs; + public int outputs; + + public TestHarnessPins() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessResponse.java b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessResponse.java new file mode 100644 index 000000000..8a76cd20f --- /dev/null +++ b/pi4j-test-harness/src/main/java/com/pi4j/test/harness/TestHarnessResponse.java @@ -0,0 +1,37 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : TestHarnessResponse.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class TestHarnessResponse { + + public String id; + + public TestHarnessResponse() { + // no-args constructor + } +} diff --git a/pi4j-test-harness/src/main/java/module-info.java b/pi4j-test-harness/src/main/java/module-info.java new file mode 100644 index 000000000..7fcec441a --- /dev/null +++ b/pi4j-test-harness/src/main/java/module-info.java @@ -0,0 +1,37 @@ +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : module-info.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +module pi4j.test.harness { + + // REQUIRES + requires slf4j.api; + requires com.fazecast.jSerialComm; + requires org.json; + requires gson; + + // EXPORTS + exports com.pi4j.test.harness; +} diff --git a/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTest.java b/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTest.java new file mode 100644 index 000000000..4a74395fa --- /dev/null +++ b/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTest.java @@ -0,0 +1,127 @@ +package com.pi4j.test.harness; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Arduino Test Harness + * FILENAME : HardwareHarnessTest.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.fazecast.jSerialComm.SerialPort; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Timeout; + +import java.io.IOException; + + +@TestMethodOrder(OrderAnnotation.class) +public class HardwareHarnessTest { + + @Before + public void beforeTest() { + } + + @After + public void afterTest() { + } + + @Test + @Order(1) + public void testListSerialPorts() { + + System.out.println("-------------------------------------------------"); + System.out.println("UNIT TEST :: LIST SERIAL PORTS"); + System.out.println("-------------------------------------------------"); + + // list all available serial ports + int index = 0; + SerialPort[] ports = SerialPort.getCommPorts(); + for(SerialPort port : ports){ + index++; + System.out.print(index); + System.out.print(" ["); + System.out.print(port.getSystemPortName()); + System.out.print("] - "); + System.out.print(port.getPortDescription()); + System.out.println(); + } + } + + @Test + @Order(2) + public void findTestHarnessSerialPort() { + + System.out.println("-------------------------------------------------"); + System.out.println("UNIT TEST :: FIND TEST HARNESS SERIAL PORT"); + System.out.println("-------------------------------------------------"); + + // list all available serial ports + int index = 0; + SerialPort[] ports = SerialPort.getCommPorts(); + for(SerialPort port : ports){ + //cu.usbmodem142301 "Arduino Due" + if(port.getPortDescription().equalsIgnoreCase("Arduino Due")){ + System.out.print("TEST HARNESS SERIAL PORTS FOUND ["); + System.out.print(port.getSystemPortName()); + System.out.print("] - "); + System.out.print(port.getPortDescription()); + System.out.println(); + return; + } + } + + Assert.fail("The 'Arduino Due' serial port could not be found; therefore " + + " we cannot communicate with the testing harness hardware"); + } + + @Test + @Timeout(5) // seconds + @Order(3) + public void testTestHarnessConnection() throws IOException, InterruptedException { + + // create test harness instance + ArduinoTestHarness harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // close test harness + harness.terminate(); + } +} diff --git a/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTest.old b/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTest.old new file mode 100644 index 000000000..81c8bdd86 --- /dev/null +++ b/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTest.old @@ -0,0 +1,219 @@ +package com.pi4j.library.pigpio.test; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : HardwareHarnessTest.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.fazecast.jSerialComm.SerialPort; +import com.fazecast.jSerialComm.SerialPortEvent; +import com.fazecast.jSerialComm.SerialPortMessageListener; +import org.json.JSONObject; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Timeout; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import static com.fazecast.jSerialComm.SerialPort.*; + + +@TestMethodOrder(OrderAnnotation.class) +public class HardwareHarnessTest { + + @Before + public void beforeTest() { + } + + @After + public void afterTest() { + } + + @Test + @Order(1) + public void testListSerialPorts() { + + System.out.println("-------------------------------------------------"); + System.out.println("UNIT TEST :: LIST SERIAL PORTS"); + System.out.println("-------------------------------------------------"); + + // list all available serial ports + int index = 0; + SerialPort[] ports = SerialPort.getCommPorts(); + for(SerialPort port : ports){ + index++; + System.out.print(index); + System.out.print(" ["); + System.out.print(port.getSystemPortName()); + System.out.print("] - "); + System.out.print(port.getPortDescription()); + System.out.println(); + } + } + + @Test + @Order(2) + public void findTestHarnessSerialPort() { + + System.out.println("-------------------------------------------------"); + System.out.println("UNIT TEST :: FIND TEST HARNESS SERIAL PORT"); + System.out.println("-------------------------------------------------"); + + // list all available serial ports + int index = 0; + SerialPort[] ports = SerialPort.getCommPorts(); + for(SerialPort port : ports){ + //cu.usbmodem142301 "Arduino Due" + if(port.getPortDescription().equalsIgnoreCase("Arduino Due")){ + System.out.print("TEST HARNESS SERIAL PORTS FOUND ["); + System.out.print(port.getSystemPortName()); + System.out.print("] - "); + System.out.print(port.getPortDescription()); + System.out.println(); + return; + } + } + + Assert.fail("The 'Arduino Due' serial port could not be found; therefore " + + " we cannot communicate with the testing harness hardware"); + } + + @Test + @Timeout(5) // seconds + @Order(3) + public void testTestHarnessConnection() throws IOException, InterruptedException { + + final BlockingQueue values = new LinkedBlockingQueue(); + + // get port instance + SerialPort port = SerialPort.getCommPort("tty.usbmodem142301"); + + // configure port + port.setBaudRate(115200); + port.setNumDataBits(8); + port.setNumStopBits(1); + port.setParity(0); + port.setFlowControl(FLOW_CONTROL_DISABLED); + + port.setComPortTimeouts(TIMEOUT_NONBLOCKING, 0, 0); + + // open serial port + port.openPort(); + + // test serial port + Assert.assertTrue("Serial port to test harness failed.", port.isOpen()); + + // if the port is open, then proceed with tests + if(port.isOpen()) { + + // send the "info" command to the test harness + port.getOutputStream().write("info\r\n".getBytes()); + port.getOutputStream().flush(); + +// Scanner in = new Scanner(port.getInputStream()); +// while(!in.hasNextLine()){ +// } +// System.out.println(in.nextLine()); + +// Gson gson = new Gson(); +// Info info = gson.fromJson(received, Info.class); +// System.out.println(info.id); +// System.out.println(info.name); +// System.out.println(info.version); +// System.out.println(info.date); +// System.out.println(info.copyright); + + + SerialPortMessageListener listener = new SerialPortMessageListener() { + @Override + public int getListeningEvents() { + return LISTENING_EVENT_DATA_RECEIVED; + } + + @Override + public void serialEvent(SerialPortEvent serialPortEvent) { + try { + byte[] data = serialPortEvent.getReceivedData(); + String received = new String(data, StandardCharsets.US_ASCII); + + //System.out.println(received); + + JSONObject payload = new JSONObject(received); + if(payload.has("id") && payload.get("id").toString().equalsIgnoreCase("info")){ + // return the info payload to the blocking queue + values.offer(payload); + return; + } + Assert.fail("The response data received is missing the 'info' identifier: " + received); + + // close serial port + port.closePort(); + + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + } + + @Override + public byte[] getMessageDelimiter() { + return new byte[] { 0x0D, 0x0A }; + } + + @Override + public boolean delimiterIndicatesEndOfMessage() { + return true; + } + }; + + // add event listener + port.addDataListener(listener); + + // wait here for "info" payload to be received + JSONObject response = (JSONObject) values.take(); + + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + response.get("name")); + System.out.println("VERSION : " + response.get("version")); + System.out.println("DATE : " + response.get("date")); + System.out.println("COPYRIGHT : " + response.get("copyright")); + System.out.println("----------------------------------------"); + + // success; close port + port.closePort(); + } + } +} diff --git a/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTestSync.old b/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTestSync.old new file mode 100644 index 000000000..a2d5e93ac --- /dev/null +++ b/pi4j-test-harness/src/test/java/com/pi4j/test/harness/HardwareHarnessTestSync.old @@ -0,0 +1,123 @@ +package com.pi4j.library.pigpio.test; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: LIBRARY :: PIGPIO Library + * FILENAME : HardwareHarnessTestSync.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.fazecast.jSerialComm.SerialPort; +import org.json.JSONObject; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Timeout; + +import java.io.IOException; +import java.util.Scanner; + +import static com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_DISABLED; +import static com.fazecast.jSerialComm.SerialPort.TIMEOUT_READ_SEMI_BLOCKING; + + +@TestMethodOrder(OrderAnnotation.class) +public class HardwareHarnessTestSync { + + @Before + public void beforeTest() { + } + + @After + public void afterTest() { + } + + @Test + @Timeout(5) // seconds + @Order(1) + public void testTestHarnessConnectionSync() throws IOException, InterruptedException { + + // get port instance + SerialPort port = SerialPort.getCommPort("tty.usbmodem142301"); + + // configure port + port.setBaudRate(115200); + port.setNumDataBits(8); + port.setNumStopBits(1); + port.setParity(0); + port.setFlowControl(FLOW_CONTROL_DISABLED); + + // configure read timeout + port.setComPortTimeouts(TIMEOUT_READ_SEMI_BLOCKING, 1000, 0); + + // open serial port + port.openPort(); + + // test serial port was opened + // if the port is not open, then fail the test + if(!port.isOpen()) { + Assert.fail("Serial port to test harness failed."); + return; + } + + + // send the "info" command to the test harness + port.getOutputStream().write("info\r\n".getBytes()); + port.getOutputStream().flush(); + + Scanner in = new Scanner(port.getInputStream()); + while(in.hasNextLine()){ + var received = in.nextLine(); + //System.out.println(in.nextLine()); + + JSONObject payload = new JSONObject(received); + if(payload.has("id") && payload.get("id").toString().equalsIgnoreCase("info")){ + + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + payload.get("name")); + System.out.println("VERSION : " + payload.get("version")); + System.out.println("DATE : " + payload.get("date")); + System.out.println("COPYRIGHT : " + payload.get("copyright")); + System.out.println("----------------------------------------"); + + // success + port.closePort(); + return; + } + } + + // test failed + Assert.fail("No 'info' response received from test harness."); + + // close port when done + port.closePort(); + } + +} diff --git a/pi4j-test/pom.xml b/pi4j-test/pom.xml index 08b7af001..d058abc89 100644 --- a/pi4j-test/pom.xml +++ b/pi4j-test/pom.xml @@ -10,7 +10,7 @@ 4.0.0 pi4j-test - Pi4J :: UNITTEST :: Unit/Integration Tests + Pi4J :: TESTING :: Unit/Integration Tests Pi4J Unit and Integration Tests @@ -19,11 +19,6 @@ pi4j-api ${project.version} - - junit - junit - test - org.slf4j slf4j-simple @@ -36,18 +31,6 @@ ${project.version} - - - - - - - - - - - - diff --git a/pi4j-test/src/main/java/com/pi4j/test/About.java b/pi4j-test/src/main/java/com/pi4j/test/About.java index f3fa8455e..fb2f0da66 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/About.java +++ b/pi4j-test/src/main/java/com/pi4j/test/About.java @@ -2,7 +2,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : About.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/Main.java b/pi4j-test/src/main/java/com/pi4j/test/Main.java index e0da767fb..954b1abb2 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/Main.java +++ b/pi4j-test/src/main/java/com/pi4j/test/Main.java @@ -2,7 +2,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : Main.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/platform/TestPlatform.java b/pi4j-test/src/main/java/com/pi4j/test/platform/TestPlatform.java index daae9bb34..a6f6633d6 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/platform/TestPlatform.java +++ b/pi4j-test/src/main/java/com/pi4j/test/platform/TestPlatform.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestPlatform.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInput.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInput.java index 1c9ac5aea..3abe0fbe1 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInput.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInput.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestAnalogInput.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInputProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInputProvider.java index e0a5dd5f6..212dad674 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInputProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogInputProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestAnalogInputProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutput.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutput.java index e40306961..c3b1390b6 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutput.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutput.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestAnalogOutput.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutputProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutputProvider.java index 1c04f1fa1..51a1b20cd 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutputProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestAnalogOutputProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestAnalogOutputProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInput.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInput.java index 2808b92e4..5fcbf02ad 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInput.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInput.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestDigitalInput.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInputProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInputProvider.java index c7092394c..d2b8a6686 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInputProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestDigitalInputProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestDigitalInputProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestI2CProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestI2CProvider.java index df8ec8e8f..bee547583 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestI2CProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestI2CProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestI2CProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestPwmProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestPwmProvider.java index 166fa3b68..a97f3a58b 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestPwmProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestPwmProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestPwmProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestSerialProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestSerialProvider.java index f2eccce5e..071b04e0d 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestSerialProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestSerialProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestSerialProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/TestSpiProvider.java b/pi4j-test/src/main/java/com/pi4j/test/provider/TestSpiProvider.java index 0399c0d2e..8a587b7d6 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/TestSpiProvider.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/TestSpiProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestSpiProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogInputProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogInputProviderImpl.java index 2972fa42d..2eb62b821 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogInputProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogInputProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestAnalogInputProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogOutputProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogOutputProviderImpl.java index 9247ea6a3..09844afeb 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogOutputProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestAnalogOutputProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestAnalogOutputProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestDigitalInputProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestDigitalInputProviderImpl.java index 0ae3c7d78..54aea37c0 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestDigitalInputProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestDigitalInputProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestDigitalInputProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestI2CProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestI2CProviderImpl.java index c9080c7b9..53fb52137 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestI2CProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestI2CProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestI2CProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestPwmProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestPwmProviderImpl.java index eab5fb78d..feb8b8346 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestPwmProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestPwmProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestPwmProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSerialProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSerialProviderImpl.java index d657f2d3f..21e304e7b 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSerialProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSerialProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestSerialProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSpiProviderImpl.java b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSpiProviderImpl.java index 3e2a293bc..a6b79cf85 100644 --- a/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSpiProviderImpl.java +++ b/pi4j-test/src/main/java/com/pi4j/test/provider/impl/TestSpiProviderImpl.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : TestSpiProviderImpl.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/main/java/module-info.java b/pi4j-test/src/main/java/module-info.java index a144e49c0..8fc37fc62 100644 --- a/pi4j-test/src/main/java/module-info.java +++ b/pi4j-test/src/main/java/module-info.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : module-info.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionContextTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionContextTest.java index 268686a28..f8ab6f8a9 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionContextTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionContextTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionContextTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionDigitalInputTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionDigitalInputTest.java index 315193faf..c44a312db 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionDigitalInputTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionDigitalInputTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionDigitalInputTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionNamedProviderTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionNamedProviderTest.java index 5e14e0885..46ee8b860 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionNamedProviderTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionNamedProviderTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionNamedProviderTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformTest.java index a63176e8c..38a264c64 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionPlatformTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformsTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformsTest.java index 65cea987f..db87ff92e 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformsTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionPlatformsTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionPlatformsTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderDefaultTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderDefaultTest.java index 8f5b1e33a..c34368bd2 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderDefaultTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderDefaultTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionProviderDefaultTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderGroupTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderGroupTest.java index 56404aa02..0b01b136f 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderGroupTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderGroupTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionProviderGroupTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderTest.java index 0c1d6f504..f5a6fb5ef 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProviderTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionProviderTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProvidersTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProvidersTest.java index 5a1038d10..3022a50ef 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProvidersTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionProvidersTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionProvidersTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomPlatformTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomPlatformTest.java index 94978949d..6ca643ce3 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomPlatformTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomPlatformTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionRegisterCustomPlatformTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomProviderTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomProviderTest.java index c8c9d6060..94a74f38e 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomProviderTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegisterCustomProviderTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionRegisterCustomProviderTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegistryTest.java b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegistryTest.java index b8d11ef71..fdc6932bc 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegistryTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/annotations/DependencyInjectionRegistryTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : DependencyInjectionRegistryTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java b/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java index 3b15a8f6a..6c7653f27 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/context/ContextTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : ContextTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java new file mode 100644 index 000000000..adb72bf57 --- /dev/null +++ b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRawDataTest.java @@ -0,0 +1,152 @@ +package com.pi4j.test.io.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests + * FILENAME : I2CRawDataTest.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.exception.Pi4JException; +import com.pi4j.io.i2c.I2C; +import com.pi4j.util.StringUtil; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.Random; + +import static org.junit.Assert.assertNotNull; + +public class I2CRawDataTest { + private Context pi4j; + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static byte SAMPLE_BYTE = 0x0d; + private static byte[] SAMPLE_BYTE_ARRAY = new byte[] { 0,1,2,3,4,5,6,7,8,9 }; + private static byte[] SAMPLE_BUFFER_ARRAY = new byte[] { 10,11,12,13,14,15,16,17,18,19 }; + private static ByteBuffer SAMPLE_BUFFER = ByteBuffer.wrap(SAMPLE_BUFFER_ARRAY); + private static String SAMPLE_STRING = "Hello World!"; + + @Before + public void beforeTest() throws Pi4JException { + // Initialize Pi4J with auto context + // An auto context enabled AUTO-DETECT loading + // which will load any detected Pi4J extension + // libraries (Platforms and Providers) from the class path + pi4j = Pi4J.newAutoContext(); + } + + @After + public void afterTest() { + try { + pi4j.shutdown(); + } catch (Pi4JException e) { /* do nothing */ } + } + + @Test + public void testRawDataWriteRead() throws Exception { + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // use try-with-resources to auto-close I2C when complete + try (var i2c = pi4j.i2c().create(config);) { + + // ensure that the I2C instance is not null; + assertNotNull(i2c); + + // write a single byte to the raw I2C device (not to a register) + i2c.write(SAMPLE_BYTE); + + // write an array of data bytes to the raw I2C device (not to a register) + i2c.write(SAMPLE_BYTE_ARRAY); + + // write a buffer of data bytes to the raw I2C device (not to a register) + i2c.write(SAMPLE_BUFFER); + + // write a string of data to the raw I2C device (not to a register) + i2c.write(SAMPLE_STRING); + + // read single byte from the raw I2C device (not from a register) + byte b = (byte)i2c.read(); + Assert.assertEquals(SAMPLE_BYTE, b); + + // read an array of data bytes from the raw I2C device (not from a register) + byte byteArray[] = new byte[SAMPLE_BYTE_ARRAY.length]; + i2c.read(byteArray, 0, byteArray.length); + Assert.assertArrayEquals(SAMPLE_BYTE_ARRAY, byteArray); + + // read a buffer of data bytes from the raw I2C device (not from a register) + ByteBuffer buffer = ByteBuffer.allocate(SAMPLE_BUFFER.capacity()); + i2c.read(buffer, 0, buffer.capacity()); + Assert.assertArrayEquals(SAMPLE_BUFFER_ARRAY, buffer.array()); + + // read a string of data from the raw I2C device (not from a register) + String testString = i2c.readString(SAMPLE_STRING.length()); + Assert.assertEquals(SAMPLE_STRING, testString); + } + } + + @Test + public void testRawDataStream() throws Exception { + // create random set of sample data + Random rand = new Random(); + byte sample[] = new byte[1024]; + rand.nextBytes(sample); + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // use try-with-resources to auto-close I2C when complete + try (var i2c = pi4j.i2c().create(config);) { + + // write sample data using output stream + i2c.out().write(sample); + + // read sample data using input stream + byte[] result = i2c.in().readNBytes(sample.length); + + System.out.println("[SAMPLE DATA] - 0x" + StringUtil.toHexString(sample)); + System.out.println("[READ DATA ] - 0x" + StringUtil.toHexString(result)); + + // copare sample data against returned read data + Assert.assertArrayEquals(sample, result); + } + } +} diff --git a/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java new file mode 100644 index 000000000..b97efa028 --- /dev/null +++ b/pi4j-test/src/test/java/com/pi4j/test/io/i2c/I2CRegisterDataTest.java @@ -0,0 +1,178 @@ +package com.pi4j.test.io.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests + * FILENAME : I2CRegisterDataTest.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.exception.Pi4JException; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CRegister; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +import static org.junit.Assert.assertNotNull; + +public class I2CRegisterDataTest { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int NUMBER_OF_REGISTERS = 100; + + private Context pi4j; + private List samples = new ArrayList<>(); + private List registers = new ArrayList<>(); + + private static byte SAMPLE_BYTE = 0x0d; + private static byte[] SAMPLE_BYTE_ARRAY = new byte[] { 0,1,2,3,4,5,6,7,8,9 }; + private static byte[] SAMPLE_BUFFER_ARRAY = new byte[] { 10,11,12,13,14,15,16,17,18,19 }; + private static ByteBuffer SAMPLE_BUFFER = ByteBuffer.wrap(SAMPLE_BUFFER_ARRAY); + private static String SAMPLE_STRING = "Hello World!"; + + @Before + public void beforeTest() throws Pi4JException { + // Initialize Pi4J with auto context + // An auto context enabled AUTO-DETECT loading + // which will load any detected Pi4J extension + // libraries (Platforms and Providers) from the class path + pi4j = Pi4J.newAutoContext(); + } + + @After + public void afterTest() { + try { + pi4j.shutdown(); + } catch (Pi4JException e) { /* do nothing */ } + } + + public class TestData{ + public final byte byt; + public final int word; + public final byte[] array; + public final ByteBuffer buffer; + public final String str; + + public TestData(){ + Random rand = new Random(); + byt = (byte) rand.nextInt(255); + word = rand.nextInt(65536); + this.array = new byte[rand.nextInt(32)]; + rand.nextBytes(this.array); + byte bufferArray[] = new byte[rand.nextInt(32)]; + rand.nextBytes(bufferArray); + this.buffer = ByteBuffer.wrap(bufferArray); + this.str = UUID.randomUUID().toString(); + } + } + + @Test + public void testRegisterDataWriteRead() throws Exception { + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // use try-with-resources to auto-close I2C when complete + try (var i2c = pi4j.i2c().create(config);) { + + // ensure that the I2C instance is not null; + assertNotNull(i2c); + + // initialize sample data sets and register objects + for(int n = 0; n < NUMBER_OF_REGISTERS; n++) { + samples.add(new TestData()); + registers.add(i2c.register(n)); + } + + // write randomized sample data to all test registers + for(int n = 0; n < NUMBER_OF_REGISTERS; n++) { + writeRegister(registers.get(n), samples.get(n)); + } + + // read and compare sample data from all test registers + for(int n = 0; n < NUMBER_OF_REGISTERS; n++) { + readRegister(registers.get(n), samples.get(n)); + } + } + } + + public void writeRegister(I2CRegister register, TestData sample) throws Exception { + + // write a single byte (8-bit) value to the raw I2C device (not to a register) + register.write(sample.byt); + + // write a single word (16-bit) value to the raw I2C device (not to a register) + register.writeWord(sample.word); + + // write an array of data bytes to the raw I2C device (not to a register) + register.write(sample.array); + + // write a buffer of data bytes to the raw I2C device (not to a register) + register.write(sample.buffer); + + // write a string of data to the raw I2C device (not to a register) + register.write(sample.str); + } + + public void readRegister(I2CRegister register, TestData sample) throws Exception { + + // read single byte (8-bit) value from the raw I2C device (not from a register) + byte b = (byte)register.read(); + Assert.assertEquals(sample.byt, b); + + // read single word (16-bit) value from the raw I2C device (not from a register) + int w = register.readWord(); + Assert.assertEquals(sample.word, w); + + // read an array of data bytes from the raw I2C device (not from a register) + byte byteArray[] = new byte[sample.array.length]; + register.read(byteArray, 0, byteArray.length); + Assert.assertArrayEquals(sample.array, byteArray); + + // read a buffer of data bytes from the raw I2C device (not from a register) + ByteBuffer buffer = ByteBuffer.allocate(sample.buffer.capacity()); + register.read(buffer, 0, buffer.capacity()); + Assert.assertArrayEquals(sample.buffer.array(), buffer.array()); + + // read a string of data from the raw I2C device (not from a register) + String testString = register.readString(sample.str.length()); + Assert.assertEquals(sample.str, testString); + } + +} diff --git a/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java b/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java index 02d706902..645ea15ce 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/platform/AutoPlatformsTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : AutoPlatformsTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsCtorTest.java b/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsCtorTest.java index 61c181a9d..806a8262f 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsCtorTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsCtorTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : ManualPlatformsCtorTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest.java b/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest.java index 7366a1e96..8eefaa30a 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : ManualPlatformsTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest2.java b/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest2.java index 21d8a448f..9a28cca1e 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest2.java +++ b/pi4j-test/src/test/java/com/pi4j/test/platform/ManualPlatformsTest2.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : ManualPlatformsTest2.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/platform/NoPlatformsTest.java b/pi4j-test/src/test/java/com/pi4j/test/platform/NoPlatformsTest.java index db49d5d7b..bf09d3d39 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/platform/NoPlatformsTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/platform/NoPlatformsTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : NoPlatformsTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java b/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java index c1db777f9..388dcd280 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/provider/AutoProvidersTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : AutoProvidersTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersCtorTest.java b/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersCtorTest.java index 3bb9bb8b4..dc8dbcb34 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersCtorTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersCtorTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : ManualProvidersCtorTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersTest.java b/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersTest.java index b517f2698..a0ed032bd 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/provider/ManualProvidersTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : ManualProvidersTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/provider/NoProvidersTest.java b/pi4j-test/src/test/java/com/pi4j/test/provider/NoProvidersTest.java index cacc378ef..48a23eca2 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/provider/NoProvidersTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/provider/NoProvidersTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : NoProvidersTest.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java index d9a195a4e..c4483816e 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java +++ b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstance.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : RegistryGetIoInstance.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstancesByProvider.java b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstancesByProvider.java index f896a96dd..1cb82f1e7 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstancesByProvider.java +++ b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryGetIoInstancesByProvider.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : RegistryGetIoInstancesByProvider.java * * This file is part of the Pi4J project. More information about diff --git a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java index 9c4fd6f3a..61331223b 100644 --- a/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java +++ b/pi4j-test/src/test/java/com/pi4j/test/registry/RegistryTest.java @@ -4,7 +4,7 @@ * #%L * ********************************************************************** * ORGANIZATION : Pi4J - * PROJECT : Pi4J :: UNITTEST :: Unit/Integration Tests + * PROJECT : Pi4J :: TESTING :: Unit/Integration Tests * FILENAME : RegistryTest.java * * This file is part of the Pi4J project. More information about diff --git a/plugins/pi4j-plugin-linuxfs/pom.xml b/plugins/pi4j-plugin-linuxfs/pom.xml index 58e301f98..fc8b98803 100644 --- a/plugins/pi4j-plugin-linuxfs/pom.xml +++ b/plugins/pi4j-plugin-linuxfs/pom.xml @@ -14,13 +14,12 @@ pi4j-plugin-linuxfs Pi4J :: PLUGIN :: LinuxFS I/O Providers Pi4J Library Plugin for Linux File System I/O Providers + jar - com.pi4j - pi4j-api - 2.0-SNAPSHOT + com.jcraft + jsch + 0.1.55 - jar - diff --git a/plugins/pi4j-plugin-linuxfs/src/main/java/com/pi4j/plugin/linuxfs/Main.java b/plugins/pi4j-plugin-linuxfs/src/main/java/com/pi4j/plugin/linuxfs/Main.java new file mode 100644 index 000000000..0900b985e --- /dev/null +++ b/plugins/pi4j-plugin-linuxfs/src/main/java/com/pi4j/plugin/linuxfs/Main.java @@ -0,0 +1,193 @@ +package com.pi4j.plugin.linuxfs; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: LinuxFS I/O Providers + * FILENAME : Main.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.jcraft.jsch.*; +import com.pi4j.plugin.linuxfs.provider.gpio.LinuxCmd; + +import java.io.InputStream; +import java.util.Arrays; + +public class Main { + + public static void main(String[] args) throws Exception { + + + + //var output = connectAndExecute("pi", "rpi4b-1g", "raspberry", LinuxCmd.export(21)); + + var output = sftp("pi", "rpi3bp", "raspberry", LinuxCmd.export(21)); + + + System.out.println(output); + } + + public static String sftp(String user, String host, String password, String command1) { + String CommandOutput = null; + try { + + + java.util.Properties config = new java.util.Properties(); + config.put("StrictHostKeyChecking", "no"); + JSch jsch = new JSch(); + + Session session = jsch.getSession(user, host, 22); + session.setPassword(password); + session.setConfig(config); + session.connect(); + // System.out.println("Connected"); + + Channel channel=session.openChannel("sftp"); + channel.connect(); + channel.setInputStream(null); + ChannelSftp c=(ChannelSftp)channel; + + + +// InputStream is = new ByteArrayInputStream("20".getBytes()); +// c.put(is, "/sys/class/gpio/export"); + //System.out.println(x); + +// var cont = c.stat("/sys/class/gpio/gpio5"); +// System.out.println(cont); +// +// c.ls("/sys/class/gpio").forEach(v->{ +// System.out.println("-> " + v); +// }); + + //System.out.println(c.lstat("/sys/class/gpio/gpio22")); + + //var ls = c.ls("/sys/class/gpio").contains("gpio20"); + //System.out.println(ls); + + //c.get("/sys/class/gpio/gpio20", System.out); + +// InputStream is = new ByteArrayInputStream("out".getBytes()); +// c.put(is, "/sys/class/gpio/gpio20/direction"); +// +// c.put(new ByteArrayInputStream("1".getBytes()), "/sys/class/gpio/gpio20/value"); + + + + InputStream in = c.get("/dev/pigout"); + + //InputStream in = c.get("/sys/class/gpio/gpio5/value"); + System.out.println(Arrays.toString(in.readAllBytes())); +// +// byte[] tmp = new byte[1024]; +// while (true) { +// while (in.available() > 0) { +// int i = in.read(tmp, 0, 1024); +// +// if (i < 0) +// break; +// // System.out.print(new String(tmp, 0, i)); +// CommandOutput = new String(tmp, 0, i); +// } +// +// if (channel.isClosed()) { +// // System.out.println("exit-status: " + +// // channel.getExitStatus()); +// break; +// } +// try { +// Thread.sleep(1000); +// } catch (Exception ee) { +// } +// } + + channel.disconnect(); + session.disconnect(); + + + } catch (Exception e) { + e.printStackTrace(); + } + return CommandOutput; + } + + public static String connectAndExecute(String user, String host, String password, String command1) { + String CommandOutput = null; + try { + + + java.util.Properties config = new java.util.Properties(); + config.put("StrictHostKeyChecking", "no"); + JSch jsch = new JSch(); + + Session session = jsch.getSession(user, host, 22); + session.setPassword(password); + session.setConfig(config); + session.connect(); + // System.out.println("Connected"); + + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand(command1); + channel.setInputStream(null); + ((ChannelExec) channel).setErrStream(System.err); + + InputStream in = channel.getInputStream(); + + channel.connect(); + byte[] tmp = new byte[1024]; + while (true) { + while (in.available() > 0) { + int i = in.read(tmp, 0, 1024); + + if (i < 0) + break; + // System.out.print(new String(tmp, 0, i)); + CommandOutput = new String(tmp, 0, i); + } + + if (channel.isClosed()) { + // System.out.println("exit-status: " + + // channel.getExitStatus()); + break; + } + try { + Thread.sleep(1000); + } catch (Exception ee) { + } + } + channel.disconnect(); + session.disconnect(); + // System.out.println("DONE"); + + } catch (Exception e) { + e.printStackTrace(); + } + return CommandOutput; + + } + +} + + diff --git a/plugins/pi4j-plugin-linuxfs/src/main/java/com/pi4j/plugin/linuxfs/provider/gpio/LinuxCmd.java b/plugins/pi4j-plugin-linuxfs/src/main/java/com/pi4j/plugin/linuxfs/provider/gpio/LinuxCmd.java new file mode 100644 index 000000000..f795eb23e --- /dev/null +++ b/plugins/pi4j-plugin-linuxfs/src/main/java/com/pi4j/plugin/linuxfs/provider/gpio/LinuxCmd.java @@ -0,0 +1,169 @@ +package com.pi4j.plugin.linuxfs.provider.gpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: LinuxFS I/O Providers + * FILENAME : LinuxCmd.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.gpio.digital.DigitalState; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * @see "https://www.kernel.org/doc/Documentation/gpio/sysfs.txt" + */ +public class LinuxCmd { + + public static String DEFAULT_SYSTEM_PATH = "/sys/class/gpio"; + + protected final String systemPath; + protected final int address; + protected final String pinPath; + + public enum Direction{ + IN, + OUT, + UNKNOWN + } + + public enum Edge{ + NONE, + RISING, + FALLING, + BOTH, + UNKNOWN + } + + public LinuxCmd(String systemPath, int address){ + this.address = address; + this.systemPath = systemPath; + this.pinPath = Paths.get(systemPath, String.format("gpio%d", address)).toString(); + } + + public LinuxCmd(int address){ + this(DEFAULT_SYSTEM_PATH, address); + } + + /** + * Export GPIO pin by SoC address + */ + public static String export(int address) throws IOException { + var path = Paths.get(DEFAULT_SYSTEM_PATH, "export"); + return String.format("echo %d > %s", address, path.toString()); + } + + public void unexport() throws IOException { + var path = Paths.get(systemPath, "unexport"); + Files.writeString(path, Integer.toString(address)); + } + + public boolean isExported() throws IOException { + return Files.exists(Paths.get(pinPath)); + } + + public boolean isInterruptSupported() throws IOException { + return Files.exists(Paths.get(pinPath, "edge")); + } + + public void direction(Direction direction) throws IOException { + setDirection(direction); + } + public void setDirection(Direction direction) throws IOException { + var path = Paths.get(pinPath, "direction"); + Files.writeString(path, direction.name().toLowerCase()); + } + + public Direction direction() throws IOException { + return getDirection(); + } + public Direction getDirection() throws IOException { + var path = Paths.get(pinPath, "direction"); + switch(Files.readString(path).trim().toLowerCase()){ + case "in": return Direction.IN; + case "out": return Direction.OUT; + default: return Direction.UNKNOWN; + } + } + + public void state(DigitalState state) throws IOException { + setState(state); + } + public void setState(DigitalState state) throws IOException { + var path = Paths.get(pinPath,"value"); + Files.writeString(path, (state.isHigh() ? "1" : "0")); + } + + public DigitalState state() throws IOException { + return getState(); + } + public DigitalState getState() throws IOException { + var path = Paths.get(pinPath,"value"); + return DigitalState.parse(Files.readString(path).trim()); + } + + public void interruptEdge(Edge edge) throws IOException { + setInterruptEdge(edge); + } + public void setInterruptEdge(Edge edge) throws IOException { + var path = Paths.get(pinPath, "edge"); + Files.writeString(path, edge.name().toLowerCase()); + } + + + public Edge interruptEdge() throws IOException { + return getInterruptEdge(); + } + public Edge getInterruptEdge() throws IOException { + var path = Paths.get(pinPath, "edge"); + switch(Files.readString(path).trim().toLowerCase()){ + case "none": return Edge.NONE; + case "rising": return Edge.RISING; + case "falling": return Edge.FALLING; + case "both": return Edge.BOTH; + default: return Edge.UNKNOWN; + } + } + + + public void activeLow(boolean enabled) throws IOException { + setActiveLow(enabled); + } + public void setActiveLow(boolean enabled) throws IOException { + var path = Paths.get(pinPath,"active_low"); + Files.writeString(path, (enabled ? "1" : "0")); + } + + public boolean activeLow() throws IOException { + return getActiveLow(); + } + public boolean getActiveLow() throws IOException { + var path = Paths.get(pinPath,"active_low"); + return Files.readString(path).trim().equalsIgnoreCase("1"); + } +} diff --git a/plugins/pi4j-plugin-linuxfs/src/main/java/module-info.java b/plugins/pi4j-plugin-linuxfs/src/main/java/module-info.java index 700ba1d7b..c02d49da5 100644 --- a/plugins/pi4j-plugin-linuxfs/src/main/java/module-info.java +++ b/plugins/pi4j-plugin-linuxfs/src/main/java/module-info.java @@ -30,6 +30,7 @@ */ module pi4j.plugin.linuxfs { requires pi4j.api; + requires jsch; exports com.pi4j.plugin.linuxfs; exports com.pi4j.plugin.linuxfs.provider.gpio.digital; diff --git a/plugins/pi4j-plugin-mock/pom.xml b/plugins/pi4j-plugin-mock/pom.xml index 85834b247..3d1e8e361 100644 --- a/plugins/pi4j-plugin-mock/pom.xml +++ b/plugins/pi4j-plugin-mock/pom.xml @@ -17,10 +17,10 @@ com.pi4j - pi4j-api + pi4j-library-pigpio 2.0-SNAPSHOT + compile jar - diff --git a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/platform/MockPlatform.java b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/platform/MockPlatform.java index c9bcbbc1b..759b8086e 100644 --- a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/platform/MockPlatform.java +++ b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/platform/MockPlatform.java @@ -30,6 +30,8 @@ */ import com.pi4j.context.Context; +import com.pi4j.platform.Platform; +import com.pi4j.platform.PlatformBase; import com.pi4j.plugin.mock.Mock; import com.pi4j.plugin.mock.provider.gpio.analog.MockAnalogInputProvider; import com.pi4j.plugin.mock.provider.gpio.analog.MockAnalogOutputProvider; @@ -39,8 +41,6 @@ import com.pi4j.plugin.mock.provider.pwm.MockPwmProvider; import com.pi4j.plugin.mock.provider.serial.MockSerialProvider; import com.pi4j.plugin.mock.provider.spi.MockSpiProvider; -import com.pi4j.platform.Platform; -import com.pi4j.platform.PlatformBase; public class MockPlatform extends PlatformBase implements Platform { diff --git a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/gpio/digital/MockDigitalOutput.java b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/gpio/digital/MockDigitalOutput.java index fa6babd9a..cce0557ed 100644 --- a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/gpio/digital/MockDigitalOutput.java +++ b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/gpio/digital/MockDigitalOutput.java @@ -30,6 +30,7 @@ */ +import com.pi4j.io.exception.IOException; import com.pi4j.io.gpio.digital.*; @@ -38,7 +39,7 @@ public MockDigitalOutput(DigitalOutputProvider provider, DigitalOutputConfig con super(provider, config); } - public MockDigitalOutput mockState(DigitalState state){ + public MockDigitalOutput mockState(DigitalState state) throws IOException { this.state(state); return this; } diff --git a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2C.java b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2C.java index 604c7256f..131211771 100644 --- a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2C.java +++ b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2C.java @@ -34,72 +34,296 @@ import com.pi4j.io.i2c.I2CBase; import com.pi4j.io.i2c.I2CConfig; import com.pi4j.io.i2c.I2CProvider; +import com.pi4j.plugin.mock.Mock; +import com.pi4j.util.StringUtil; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayDeque; +import java.util.Objects; public class MockI2C extends I2CBase implements I2C { + /** + * ATTENTION: The storage and management of the byte arrays + * are terribly inefficient and not intended for real-world + * usage. These are only intended to unit testing the + * Pi4J I2C APIs. + */ + protected ArrayDeque[] registers = new ArrayDeque[256]; // 256 supported registers (0-255) + protected ArrayDeque raw = new ArrayDeque<>(); + public MockI2C(I2CProvider provider, I2CConfig config){ super(provider, config); + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: CREATE(BUS=" + config.bus() + "; DEVICE=" + config.device() + ")"); + System.out.println(); } @Override - public int getAddress() { - return 0; + public void close() throws IOException { + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: CLOSE(BUS=" + config.bus() + "; DEVICE=" + config.device() + ")"); + System.out.println(); + super.close(); } + // ------------------------------------------------------------------- + // RAW DEVICE WRITE FUNCTIONS + // ------------------------------------------------------------------- + @Override public void write(byte b) throws IOException { - + raw.add(b); + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: WRITE(0x"); + System.out.print(StringUtil.toHexString(b)); + System.out.println(")"); } @Override - public void write(byte[] buffer, int offset, int size) throws IOException { - + public int write(ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + for(int p = offset; p-offset < length; p++){ + raw.add(buffer.get(p)); // add to internal buffer + } + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: WRITE(0x"); + System.out.print(StringUtil.toHexString(buffer, offset, length)); + System.out.println(")"); + return length; } @Override - public void write(byte[] buffer) throws IOException { + public int write(String data, Charset charset) throws IOException { + byte[] buffer = data.getBytes(charset); + for(int p = 0; p < buffer.length; p++){ + raw.add(buffer[p]); // add to internal buffer + } + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: WRITE(\""); + System.out.print(data); + System.out.println("\")"); + return data.length(); + } + + // ------------------------------------------------------------------- + // RAW DEVICE READ FUNCTIONS + // ------------------------------------------------------------------- + @Override + public int read() throws IOException{ + if(raw.isEmpty()) return -1; + byte b = raw.pop(); + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: READ(0x"); + System.out.print(StringUtil.toHexString(b)); + System.out.println(")"); + return b; } @Override - public void write(int address, byte b) throws IOException { + public int read(ByteBuffer buffer, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + + if(raw.isEmpty()) return -1; + int counter = 0; + for(int p = 0; p < length; p++) { + if(p+offset > buffer.capacity()) break; + if(raw.isEmpty()) break;; + buffer.put(offset + p, raw.pop()); + counter++; + } + + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: READ(0x"); + System.out.print(StringUtil.toHexString(buffer, offset, length)); + System.out.println(")"); + return counter; } @Override - public void write(int address, byte[] buffer, int offset, int size) throws IOException { + public String readString(int length, Charset charset) throws IOException{ + if(raw.isEmpty()) return null; + byte[] buffer = new byte[length]; + for(int p = 0; p < length; p++) { + if(raw.isEmpty()) break;; + buffer[p] = raw.pop(); + } + String result = new String(buffer, charset); + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: READ(\""); + System.out.print(result); + System.out.println("\")"); + + return result; } + // ------------------------------------------------------------------- + // DEVICE REGISTER WRITE FUNCTIONS + // ------------------------------------------------------------------- + @Override - public void write(int address, byte[] buffer) throws IOException { + public void writeRegister(int register, byte b) throws IOException { + if(registers[register] == null) registers[register] = new ArrayDeque(); + registers[register].add(b); + + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: WRITE("); + System.out.print("REG="); + System.out.print(register); + System.out.print(", 0x"); + System.out.print(StringUtil.toHexString(b)); + System.out.println(")"); } @Override - public int read() throws IOException { - return 0; + public int writeRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + + // add to internal buffer + if(registers[register] == null) registers[register] = new ArrayDeque(); + for(int p = offset; p-offset < length; p++){ + registers[register].add(buffer.get(p)); + } + + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: WRITE("); + System.out.print("REG="); + System.out.print(register); + System.out.print(", 0x"); + System.out.print(StringUtil.toHexString(buffer, offset, length)); + System.out.println(")"); + + return length; } @Override - public int read(byte[] buffer, int offset, int size) throws IOException { - return 0; + public int writeRegister(int register, String data, Charset charset) throws IOException{ + + if(registers[register] == null) registers[register] = new ArrayDeque(); + byte[] buffer = data.getBytes(charset); + for(int p = 0; p < buffer.length; p++){ + registers[register].add(buffer[p]); // add to internal buffer + } + + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: WRITE("); + System.out.print("REG="); + System.out.print(register); + System.out.print(", \""); + System.out.print(data); + System.out.println("\")"); + return data.length(); } + // ------------------------------------------------------------------- + // DEVICE REGISTER READ FUNCTIONS + // ------------------------------------------------------------------- + @Override - public int read(int address) throws IOException { - return 0; + public int readRegister(int register) throws IOException { + if(registers[register] == null) throw new IOException("No available data to read"); + if(registers[register].isEmpty()) throw new IOException("No available data to read"); + byte b = registers[register].pop(); + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: READ("); + System.out.print("REG="); + System.out.print(register); + System.out.print(", 0x"); + System.out.print(StringUtil.toHexString(b)); + System.out.println(")"); + return b; } @Override - public int read(int address, byte[] buffer, int offset, int size) throws IOException { - return 0; + public int readRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException { + if(registers[register] == null) return -1; + if(registers[register].isEmpty()) return -1; + + int counter = 0; + for(int p = 0; p < length; p++) { + if(p+offset > buffer.capacity()) break; + if(registers[register].isEmpty()) break;; + buffer.put(offset + p, registers[register].pop()); + counter++; + } + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: READ("); + System.out.print("REG="); + System.out.print(register); + System.out.print(", 0x"); + System.out.print(StringUtil.toHexString(buffer, offset, length)); + System.out.println(")"); + return counter; } @Override - public int read(byte[] writeBuffer, int writeOffset, int writeSize, byte[] readBuffer, int readOffset, int readSize) throws IOException { - return 0; + public String readRegisterString(int register, int length, Charset charset) throws IOException { + if(registers[register] == null) return null; + if(registers[register].isEmpty()) return null; + + byte[] buffer = new byte[length]; + for(int p = 0; p < length; p++) { + if(registers[register].isEmpty()) break;; + buffer[p] = registers[register].pop(); + } + String result = new String(buffer, charset); + + System.out.print(" ["); + System.out.print(Mock.I2C_PROVIDER_NAME); + System.out.print("::"); + System.out.print(this.id); + System.out.print("] :: READ("); + System.out.print("REG="); + System.out.print(register); + System.out.print(", \""); + System.out.print(result); + System.out.println("\")"); + + return result; } } diff --git a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2CProviderImpl.java b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2CProviderImpl.java index 1fc45124c..c56b87455 100644 --- a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2CProviderImpl.java +++ b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/i2c/MockI2CProviderImpl.java @@ -44,5 +44,4 @@ public MockI2CProviderImpl(){ public I2C create(I2CConfig config) throws Exception { return new MockI2C(this, config); } - } diff --git a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/pwm/MockPwm.java b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/pwm/MockPwm.java index c9aeec855..8a101a15d 100644 --- a/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/pwm/MockPwm.java +++ b/plugins/pi4j-plugin-mock/src/main/java/com/pi4j/plugin/mock/provider/pwm/MockPwm.java @@ -34,9 +34,23 @@ import com.pi4j.io.pwm.PwmConfig; import com.pi4j.io.pwm.PwmProvider; +import java.io.IOException; + public class MockPwm extends PwmBase implements Pwm { public MockPwm(PwmProvider provider, PwmConfig config){ super(provider, config); } + + @Override + public Pwm on() throws IOException { + this.onState = true; + return this; + } + + @Override + public Pwm off() throws IOException { + this.onState = false; + return this; + } } diff --git a/plugins/pi4j-plugin-mock/src/main/java/module-info.java b/plugins/pi4j-plugin-mock/src/main/java/module-info.java index ccf791bcf..bd1f71ac8 100644 --- a/plugins/pi4j-plugin-mock/src/main/java/module-info.java +++ b/plugins/pi4j-plugin-mock/src/main/java/module-info.java @@ -29,6 +29,8 @@ module pi4j.plugin.mock { requires pi4j.api; + uses com.pi4j.extension.Plugin; + exports com.pi4j.plugin.mock; exports com.pi4j.plugin.mock.platform; exports com.pi4j.plugin.mock.provider.gpio.digital; diff --git a/plugins/pi4j-plugin-pigpio/pom.xml b/plugins/pi4j-plugin-pigpio/pom.xml new file mode 100644 index 000000000..12f905a01 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/pom.xml @@ -0,0 +1,34 @@ + + + + pi4j-plugin + com.pi4j + 2.0-SNAPSHOT + ../pi4j-plugin + + 4.0.0 + + + pi4j-plugin-pigpio + Pi4J :: PLUGIN :: PIGPIO I/O Providers + Pi4J Plugin for the PIGPIO I/O Providers + jar + + + + + com.pi4j + pi4j-library-pigpio + 2.0-SNAPSHOT + compile + + + com.pi4j + pi4j-test-harness + 2.0-SNAPSHOT + test + + + diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/PiGpioPlugin.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/PiGpioPlugin.java new file mode 100644 index 000000000..e15b60de7 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/PiGpioPlugin.java @@ -0,0 +1,115 @@ +package com.pi4j.plugin.pigpio; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioPlugin.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.context.Context; +import com.pi4j.extension.Plugin; +import com.pi4j.extension.PluginService; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.gpio.digital.PiGpioDigitalInputProvider; +import com.pi4j.plugin.pigpio.provider.gpio.digital.PiGpioDigitalOutputProvider; +import com.pi4j.plugin.pigpio.provider.i2c.PiGpioI2CProvider; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.provider.Provider; + +import java.io.IOException; + +public class PiGpioPlugin implements Plugin { + + public static final String NAME = "PiGpio"; + public static final String ID = "pigpio"; + + // Digital Input (GPIO) Provider name and unique ID + public static final String DIGITAL_INPUT_PROVIDER_NAME = NAME + " Digital Input (GPIO) Provider"; + public static final String DIGITAL_INPUT_PROVIDER_ID = ID + "-digital-input"; + + // Digital Output (GPIO) Provider name and unique ID + public static final String DIGITAL_OUTPUT_PROVIDER_NAME = NAME + " Digital Output (GPIO) Provider"; + public static final String DIGITAL_OUTPUT_PROVIDER_ID = ID + "-digital-output"; + + // PWM Provider name and unique ID + public static final String PWM_PROVIDER_NAME = NAME + " PWM Provider"; + public static final String PWM_PROVIDER_ID = ID + "-pwm"; + + // PWM Provider name and unique ID + public static final String HW_PWM_PROVIDER_NAME = NAME + " Hardware PWM Provider"; + public static final String HW_PWM_PROVIDER_ID = ID + "-hardware-pwm"; + + // I2C Provider name and unique ID + public static final String I2C_PROVIDER_NAME = NAME + " I2C Provider"; + public static final String I2C_PROVIDER_ID = ID + "-i2c"; + + // SPI Provider name and unique ID + public static final String SPI_PROVIDER_NAME = NAME + " SPI Provider"; + public static final String SPI_PROVIDER_ID = ID + "-spi"; + + // Serial Provider name and unique ID + public static final String SERIAL_PROVIDER_NAME = NAME + " Serial Provider"; + public static final String SERIAL_PROVIDER_ID = ID + "-serial"; + + + protected PiGpio piGpio = null; + + public static String PI4J_HOST_PROPERTY = "pi4j.host"; + public static String PIGPIO_HOST_PROPERTY = "pi4j.pigpio.host"; + public static String PIGPIO_PORT_PROPERTY = "pi4j.pigpio.port"; + public static String DEFAULT_PIGPIO_HOST = "127.0.0.1"; + public static Integer DEFAULT_PIGPIO_PORT = 8888; + + @Override + public void initialize(PluginService service) throws IOException { + + // get PIGPIO hostname/ip-address and ip-port + String host = service.context().properties().get("host", DEFAULT_PIGPIO_HOST); + int port = service.context().properties().getInteger("pigpio.port", DEFAULT_PIGPIO_PORT); + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance(host, port); + + // initialize the PIGPIO library + piGpio.initialize(); + + Provider providers[] = { + PiGpioDigitalInputProvider.newInstance(piGpio), + PiGpioDigitalOutputProvider.newInstance(piGpio), + PiGpioPwmProvider.newInstance(piGpio), + PiGpioI2CProvider.newInstance(piGpio), + }; + + // register all PiGpio I/O Providers with the plugin service + service.register(providers); + } + + @Override + public void shutdown(Context context) throws IOException { + // shutdown the PiGpio library + piGpio.terminate(); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInput.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInput.java new file mode 100644 index 000000000..d9b7fb377 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInput.java @@ -0,0 +1,112 @@ +package com.pi4j.plugin.pigpio.provider.gpio.digital; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioDigitalInput.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + + +import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.exception.ShutdownException; +import com.pi4j.io.gpio.digital.*; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.library.pigpio.PiGpioPud; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class PiGpioDigitalInput extends DigitalInputBase implements DigitalInput { + private final PiGpio piGpio; + private final int pin; + private DigitalState state = DigitalState.LOW; + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + public PiGpioDigitalInput(PiGpio piGpio, DigitalInputProvider provider, DigitalInputConfig config) throws IOException { + super(provider, config); + this.piGpio = piGpio; + this.pin = config.address().intValue(); + } + + @Override + public DigitalInput initialize(Context context) throws InitializeException { + super.initialize(context); + + try { + // configure GPIO pin as an INPUT pin + this.piGpio.gpioSetMode(pin, PiGpioMode.INPUT); + + // if configured, set GPIO pin pull resistance + switch(config.pull()){ + case PULL_DOWN:{ + this.piGpio.gpioSetPullUpDown(pin, PiGpioPud.DOWN); + break; + } + case PULL_UP:{ + this.piGpio.gpioSetPullUpDown(pin, PiGpioPud.UP); + break; + } + } + } catch (IOException e) { + logger.error(e.getMessage(), e); + throw new InitializeException(e); + } + return this; + } + + @Override + public DigitalState state() { + try { + switch (this.piGpio.gpioRead(pin)) { + case LOW: { + this.state = DigitalState.LOW; + break; + } + case HIGH: { + this.state = DigitalState.HIGH; + break; + } + default: { + this.state = DigitalState.UNKNOWN; + break; + } + } + return this.state; + } + catch (Exception e){ + logger.error(e.getMessage(), e); + return DigitalState.UNKNOWN; + } + } + + @Override + public DigitalInput shutdown(Context context) throws ShutdownException { + return super.shutdown(context); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInputProvider.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInputProvider.java new file mode 100644 index 000000000..1cf53543b --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInputProvider.java @@ -0,0 +1,42 @@ +package com.pi4j.plugin.pigpio.provider.gpio.digital; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioDigitalInputProvider.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.gpio.digital.DigitalInputProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.PiGpioPlugin; + +public interface PiGpioDigitalInputProvider extends DigitalInputProvider { + String NAME = PiGpioPlugin.DIGITAL_INPUT_PROVIDER_NAME; + String ID = PiGpioPlugin.DIGITAL_INPUT_PROVIDER_ID; + static PiGpioDigitalInputProvider newInstance(PiGpio piGpio) { + return new PiGpioDigitalInputProviderImpl(piGpio); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInputProviderImpl.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInputProviderImpl.java new file mode 100644 index 000000000..8460cba5c --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalInputProviderImpl.java @@ -0,0 +1,53 @@ +package com.pi4j.plugin.pigpio.provider.gpio.digital; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioDigitalInputProviderImpl.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.gpio.digital.DigitalInput; +import com.pi4j.io.gpio.digital.DigitalInputConfig; +import com.pi4j.io.gpio.digital.DigitalInputProviderBase; +import com.pi4j.library.pigpio.PiGpio; + +import java.io.IOException; + +public class PiGpioDigitalInputProviderImpl extends DigitalInputProviderBase implements PiGpioDigitalInputProvider { + + protected final PiGpio piGpio; + + public PiGpioDigitalInputProviderImpl(PiGpio piGpio){ + this.id = ID; + this.name = NAME; + this.piGpio = piGpio; + } + + @Override + public DigitalInput create(DigitalInputConfig config) throws IOException { + return new PiGpioDigitalInput(piGpio, this, config); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutput.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutput.java new file mode 100644 index 000000000..67e112e61 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutput.java @@ -0,0 +1,79 @@ +package com.pi4j.plugin.pigpio.provider.gpio.digital; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioDigitalOutput.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + + +import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.io.gpio.digital.*; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; +import com.pi4j.library.pigpio.PiGpioState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + + +public class PiGpioDigitalOutput extends DigitalOutputBase implements DigitalOutput { + private final PiGpio piGpio; + private final int pin; + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + public PiGpioDigitalOutput(PiGpio piGpio, DigitalOutputProvider provider, DigitalOutputConfig config) { + super(provider, config); + this.piGpio = piGpio; + this.pin = config.address().intValue(); + } + + @Override + public DigitalOutput initialize(Context context) throws InitializeException { + super.initialize(context); + try { + // configure GPIO pin as an OUTPUT pin + this.piGpio.gpioSetMode(pin, PiGpioMode.OUTPUT); + } catch (IOException e) { + logger.error(e.getMessage(), e); + throw new InitializeException(e); + } + return this; + } + + @Override + public DigitalOutput state(DigitalState state) throws com.pi4j.io.exception.IOException { + try { + this.piGpio.gpioWrite(pin, PiGpioState.from(state.value())); + } catch (IOException e) { + logger.error(e.getMessage(), e); + throw new com.pi4j.io.exception.IOException(e.getMessage(), e); + } + return super.state(state); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutputProvider.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutputProvider.java new file mode 100644 index 000000000..efa99a859 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutputProvider.java @@ -0,0 +1,43 @@ +package com.pi4j.plugin.pigpio.provider.gpio.digital; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioDigitalOutputProvider.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.gpio.digital.DigitalOutputProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.PiGpioPlugin; + +public interface PiGpioDigitalOutputProvider extends DigitalOutputProvider { + String NAME = PiGpioPlugin.DIGITAL_OUTPUT_PROVIDER_NAME; + String ID = PiGpioPlugin.DIGITAL_OUTPUT_PROVIDER_ID; + + static PiGpioDigitalOutputProvider newInstance(PiGpio piGpio) { + return new PiGpioDigitalOutputProviderImpl(piGpio); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutputProviderImpl.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutputProviderImpl.java new file mode 100644 index 000000000..d6393f45e --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/gpio/digital/PiGpioDigitalOutputProviderImpl.java @@ -0,0 +1,51 @@ +package com.pi4j.plugin.pigpio.provider.gpio.digital; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioDigitalOutputProviderImpl.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.gpio.digital.DigitalOutput; +import com.pi4j.io.gpio.digital.DigitalOutputConfig; +import com.pi4j.io.gpio.digital.DigitalOutputProviderBase; +import com.pi4j.library.pigpio.PiGpio; + +public class PiGpioDigitalOutputProviderImpl extends DigitalOutputProviderBase implements PiGpioDigitalOutputProvider { + + protected final PiGpio piGpio; + + public PiGpioDigitalOutputProviderImpl(PiGpio piGpio){ + this.id = ID; + this.name = NAME; + this.piGpio = piGpio; + } + + @Override + public DigitalOutput create(DigitalOutputConfig config) throws Exception { + return new PiGpioDigitalOutput(piGpio,this, config); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2C.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2C.java new file mode 100644 index 000000000..4032ce663 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2C.java @@ -0,0 +1,162 @@ +package com.pi4j.plugin.pigpio.provider.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioI2C.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + + +import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.io.exception.IOReadException; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CBase; +import com.pi4j.io.i2c.I2CConfig; +import com.pi4j.io.i2c.I2CProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Objects; + +public class PiGpioI2C extends I2CBase implements I2C { + + protected final PiGpio piGpio; + protected final int handle; + + public PiGpioI2C(PiGpio piGpio, I2CProvider provider, I2CConfig config) throws IOException { + super(provider, config); + + // set local reference instance + this.piGpio = piGpio; + + // set pin ALT0 modes for I2C BUS<1> or BUS<2> usage on RPI3B + switch(config.bus()) { + case 0: { + piGpio.gpioSetMode(0, PiGpioMode.ALT0); + piGpio.gpioSetMode(1, PiGpioMode.ALT0); + break; + } + case 1: { + piGpio.gpioSetMode(2, PiGpioMode.ALT0); + piGpio.gpioSetMode(3, PiGpioMode.ALT0); + } + } + + // create I2C instance of PIGPIO I2C + this.handle = piGpio.i2cOpen(config.bus(), config.device()); + + // set open state flag + this.isOpen = true; + } + + @Override + public I2C initialize(Context context) throws InitializeException { + super.initialize(context); + return this; + } + + @Override + public void close() throws IOException { + piGpio.i2cClose(this.handle); + super.close(); + } + + // ------------------------------------------------------------------- + // RAW DEVICE WRITE FUNCTIONS + // ------------------------------------------------------------------- + + @Override + public void write(byte b) throws IOException { + piGpio.i2cWriteByte(this.handle, b);; + } + + @Override + public int write(ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + piGpio.i2cWriteDevice(this.handle, buffer, offset, length); + return length; + } + + // ------------------------------------------------------------------- + // RAW DEVICE READ FUNCTIONS + // ------------------------------------------------------------------- + + @Override + public int read() throws IOException{ + return piGpio.i2cReadByte(this.handle); + } + + @Override + public int read(ByteBuffer buffer, int offset, int length) throws IOException{ + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + byte[] data = piGpio.i2cReadDevice(this.handle, length); + if(data.length > 0) + buffer.put(data, offset, length); + return length; + } + + // ------------------------------------------------------------------- + // DEVICE REGISTER WRITE FUNCTIONS + // ------------------------------------------------------------------- + + @Override + public void writeRegister(int register, byte b) throws IOException { + piGpio.i2cWriteByteData(this.handle, register, b); + } + + @Override + public int writeRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + piGpio.i2cWriteI2CBlockData(this.handle, register, buffer, offset, length); + return length; + } + + // ------------------------------------------------------------------- + // DEVICE REGISTER READ FUNCTIONS + // ------------------------------------------------------------------- + + @Override + public int readRegister(int register) throws IOException { + return piGpio.i2cReadByteData(this.handle, register); + } + + @Override + public int readRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException { + Objects.checkFromIndexSize(offset, length, buffer.capacity()); + byte data[] = piGpio.i2cReadI2CBlockData(this.handle, register, length); + if(data.length > 0) + buffer.put(data, offset, length); + return length; + } + + @Override + public int writeReadRegisterWord(int register, int word) throws IOException, IOReadException { + return piGpio.i2cProcessCall(this.handle, register, word); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2CProvider.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2CProvider.java new file mode 100644 index 000000000..7bbb97ff0 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2CProvider.java @@ -0,0 +1,42 @@ +package com.pi4j.plugin.pigpio.provider.i2c; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioI2CProvider.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.i2c.I2CProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.PiGpioPlugin; + +public interface PiGpioI2CProvider extends I2CProvider { + String NAME = PiGpioPlugin.I2C_PROVIDER_NAME; + String ID = PiGpioPlugin.I2C_PROVIDER_ID; + static PiGpioI2CProvider newInstance(PiGpio piGpio) { + return new PiGpioI2CProviderImpl(piGpio); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2CProviderImpl.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2CProviderImpl.java new file mode 100644 index 000000000..527186097 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/i2c/PiGpioI2CProviderImpl.java @@ -0,0 +1,51 @@ +package com.pi4j.plugin.pigpio.provider.i2c; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioI2CProviderImpl.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CConfig; +import com.pi4j.io.i2c.I2CProviderBase; +import com.pi4j.library.pigpio.PiGpio; + +public class PiGpioI2CProviderImpl extends I2CProviderBase implements PiGpioI2CProvider { + + final PiGpio piGpio; + + public PiGpioI2CProviderImpl(PiGpio piGpio){ + this.id = ID; + this.name = NAME; + this.piGpio = piGpio; + } + + @Override + public I2C create(I2CConfig config) throws Exception { + return new PiGpioI2C(piGpio,this, config); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmBase.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmBase.java new file mode 100644 index 000000000..d05d80d2e --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmBase.java @@ -0,0 +1,46 @@ +package com.pi4j.plugin.pigpio.provider.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioPwmBase.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmBase; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmProvider; +import com.pi4j.library.pigpio.PiGpio; + +public abstract class PiGpioPwmBase extends PwmBase implements Pwm { + + protected final PiGpio piGpio; + + public PiGpioPwmBase(PiGpio piGpio, PwmProvider provider, PwmConfig config){ + super(provider, config); + this.piGpio = piGpio; + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmHardware.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmHardware.java new file mode 100644 index 000000000..2d9fb5209 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmHardware.java @@ -0,0 +1,117 @@ +package com.pi4j.plugin.pigpio.provider.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioPwmHardware.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; + +import java.io.IOException; + +public class PiGpioPwmHardware extends PiGpioPwmBase implements Pwm { + + public PiGpioPwmHardware(PiGpio piGpio, PwmProvider provider, PwmConfig config) throws IOException { + super(piGpio, provider, config); + +// 12 PWM channel 0 All models but A and B +// 13 PWM channel 1 All models but A and B +// 18 PWM channel 0 All models +// 19 PWM channel 1 All models but A and B + + // TODO :: SET PIN ALT MODES FOR HARDWARE PWM ON COMPUTE MODULE +// 40 PWM channel 0 Compute module only +// 41 PWM channel 1 Compute module only +// 45 PWM channel 1 Compute module only +// 52 PWM channel 0 Compute module only +// 53 PWM channel 1 Compute module only + + if(this.address() == 12 || this.address() == 13 || this.address() == 41 || this.address() == 42 || this.address() == 45) { + piGpio.gpioSetMode(this.address(), PiGpioMode.ALT0); + } + else if(this.address() == 18 || this.address() == 19) { + piGpio.gpioSetMode(this.address(), PiGpioMode.ALT0); + } +// else{ +// throw new IOException(" UNSUPPORTED HARDWARE PWM PIN: " + this.address()); +// } + + // get current frequency from config or from actual PWM pin + if(config.frequency() != null){ + this.frequency = config.frequency(); + } else { + this.frequency = piGpio.gpioGetPWMfrequency(this.address()); + } + + // ignore any configured range values, + // fixed range for hardware PWM + this.range = 1000000; + + // get current duty-cycle from config or set to default 50% + if(config.dutyCycle() != null){ + this.dutyCycle = config.dutyCycle(); + } + else if(config.dutyCyclePercent() != null){ + dutyCyclePercent(config.dutyCyclePercent()); + } + else { + // get updated duty-cycle value from PiGpio + this.dutyCycle = this.range / 2; // default duty-cycle is 50% of total range + } + } + + public Pwm on() throws IOException{ + // set PWM frequency & duty-cycle; enable PWM signal + piGpio.gpioHardwarePWM(this.address(), this.frequency, this.dutyCycle); + + // update tracking state + this.onState = (this.frequency > 0 && this.dutyCycle > 0); + + return this; + } + + public Pwm off() throws IOException{ + + // set PWM duty-cycle and enable PWM + piGpio.gpioHardwarePWM(this.address(), 0, 0); + + // update tracking state + this.onState = false; + + return this; + } + + @Override + public void setRange(int range) throws IOException { + // NOT SUPPORTED + throw new UnsupportedOperationException("Hardware PWM does not support custom ranges; rage will always be 0-1M"); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmProvider.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmProvider.java new file mode 100644 index 000000000..cfded4176 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmProvider.java @@ -0,0 +1,44 @@ +package com.pi4j.plugin.pigpio.provider.pwm; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioPwmProvider.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.pwm.PwmProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.PiGpioPlugin; + +import java.io.IOException; + +public interface PiGpioPwmProvider extends PwmProvider { + String NAME = PiGpioPlugin.PWM_PROVIDER_NAME; + String ID = PiGpioPlugin.PWM_PROVIDER_ID; + static PiGpioPwmProvider newInstance(PiGpio piGpio) throws IOException { + return new PiGpioPwmProviderImpl(piGpio); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmProviderImpl.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmProviderImpl.java new file mode 100644 index 000000000..9e174a5c7 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmProviderImpl.java @@ -0,0 +1,59 @@ +package com.pi4j.plugin.pigpio.provider.pwm; + +/* + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioPwmProviderImpl.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmProviderBase; +import com.pi4j.io.pwm.PwmType; +import com.pi4j.library.pigpio.PiGpio; + +import java.io.IOException; + +public class PiGpioPwmProviderImpl extends PwmProviderBase implements PiGpioPwmProvider { + + final PiGpio piGpio; + + public PiGpioPwmProviderImpl(PiGpio piGpio) throws IOException { + this.id = ID; + this.name = NAME; + this.piGpio = piGpio; + } + + @Override + public Pwm create(PwmConfig config) throws Exception { + if(config.pwmType() == PwmType.HARDWARE){ + return new PiGpioPwmHardware(piGpio, this, config); + } + else { + return new PiGpioPwmSoftware(piGpio, this, config); + } + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmSoftware.java b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmSoftware.java new file mode 100644 index 000000000..447a55fbd --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/com/pi4j/plugin/pigpio/provider/pwm/PiGpioPwmSoftware.java @@ -0,0 +1,119 @@ +package com.pi4j.plugin.pigpio.provider.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : PiGpioPwmSoftware.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmProvider; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.library.pigpio.PiGpioMode; + +import java.io.IOException; + +public class PiGpioPwmSoftware extends PiGpioPwmBase implements Pwm { + + + public PiGpioPwmSoftware(PiGpio piGpio, PwmProvider provider, PwmConfig config) throws IOException { + super(piGpio, provider, config); + } + + @Override + public Pwm initialize(Context context) throws InitializeException { + super.initialize(context); + + try { + // set pin mode to output + piGpio.gpioSetMode(this.address(), PiGpioMode.OUTPUT); + + // get current frequency from config or from actual PWM pin + if (config.frequency() != null) { + this.frequency = config.frequency(); + } else { + this.frequency = piGpio.gpioGetPWMfrequency(this.address()); + } + + // get current range from config or from actual PWM pin + if (config.range() != null) { + this.range = config.range(); + } else { + this.range = piGpio.gpioGetPWMrange(this.address()); + } + + // get current duty-cycle from config or set to default 50% + if (config.dutyCycle() != null) { + this.dutyCycle = config.dutyCycle(); + } else if (config.dutyCyclePercent() != null) { + dutyCyclePercent(config.dutyCyclePercent()); + } else { + // get updated duty-cycle value from PiGpio + this.dutyCycle = this.range / 2; // default duty-cycle is 50% of total range + } + } + catch (IOException e){ + throw new InitializeException(e); + } + + return this; + } + + public Pwm on() throws IOException{ + + // set PWM range + piGpio.gpioSetPWMrange(this.address(), range); + this.range = piGpio.gpioGetPWMrange(this.address()); + + // set PWM frequency + piGpio.gpioSetPWMfrequency(this.address(), frequency); + + // set PWM duty-cycle and enable PWM + piGpio.gpioPWM(this.address(), this.dutyCycle); + + // get updated duty-cycle value from PiGpio + this.dutyCycle = piGpio.gpioGetPWMdutycycle(this.address()); + + // update tracking state + this.onState = (this.frequency > 0 && this.dutyCycle > 0); + + return this; + } + + public Pwm off() throws IOException{ + + // set PWM duty-cycle and enable PWM + piGpio.gpioPWM(this.address(), 0); + + // update tracking state + this.onState = false; + + return this; + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/main/java/module-info.java b/plugins/pi4j-plugin-pigpio/src/main/java/module-info.java new file mode 100644 index 000000000..41df43e35 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/main/java/module-info.java @@ -0,0 +1,46 @@ +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : module-info.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +module pi4j.plugin.pigpio { + requires pi4j.api; + requires pi4j.library.pigpio; + requires slf4j.api; + + uses com.pi4j.extension.Plugin; + + exports com.pi4j.plugin.pigpio; + //exports com.pi4j.plugin.pigpio.provider.gpio.digital; + //exports com.pi4j.plugin.pigpio.provider.gpio.analog; + exports com.pi4j.plugin.pigpio.provider.pwm; + //exports com.pi4j.plugin.pigpio.provider.serial; + //exports com.pi4j.plugin.pigpio.provider.spi; + exports com.pi4j.plugin.pigpio.provider.i2c; + + provides com.pi4j.extension.Plugin + with com.pi4j.plugin.pigpio.PiGpioPlugin; +} diff --git a/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/gpio/digital/TestDigitalInputUsingTestHarness.java b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/gpio/digital/TestDigitalInputUsingTestHarness.java new file mode 100644 index 000000000..daedca105 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/gpio/digital/TestDigitalInputUsingTestHarness.java @@ -0,0 +1,263 @@ +package com.pi4j.plugin.pigpio.gpio.digital; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : TestDigitalInputUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.io.gpio.digital.DigitalInput; +import com.pi4j.io.gpio.digital.DigitalState; +import com.pi4j.io.gpio.digital.PullResistance; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.gpio.digital.PiGpioDigitalInputProvider; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; + +@DisplayName("PIGPIO Plugin :: Test Digital Input Pins using Test Harness") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestDigitalInputUsingTestHarness { + + private PiGpio piGpio; + private Context pi4j; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + // configure logging output + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestDigitalInputUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestDigitalInputUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // reset I/O pins + harness.reset(); + + // terminate connection to test harness + harness.terminate();; + } + + @BeforeEach + public void beforeEach() throws Exception { + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance("rpi3bp"); + + // initialize the PiGpio library + piGpio.initialize(); + + // create PWM provider instance to test with + var provider = PiGpioDigitalInputProvider.newInstance(piGpio); + + // initialize Pi4J instance with this single provider + pi4j = Pi4J.newContextBuilder().add(provider).build(); + } + + @AfterEach + public void afterEach() throws Exception { + // terminate the PiGpio library after each test + piGpio.terminate(); + } + + @Test + @Order(1) + @DisplayName("DIN :: Test GPIO Digital Input Pins ") + public void testDigitalInputsHigh() throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST DIGITAL INPUT PINS - HIGH"); + System.out.println("----------------------------------------"); + + for(int p = 2; p < 20; p++) { + + // create Digital Input instance config + var config = DigitalInput.newConfigBuilder() + .id("my-din-pin-" + p) + .name("My Digital Input Pin #" + p) + .address(p) + .build(); + + // create Digital Input I/O instance + DigitalInput din = pi4j.create(config); + + // configure output pin to HIGH state on testing harness + harness.setOutputPin(p, true); + + DigitalState state = din.state(); + System.out.println("(PIN #" + p + ") >> STATE = " + state); + Assert.assertEquals("DIGITAL INPUT STATE MISMATCH: " + p, DigitalState.HIGH, state); + } + } + + @Test + @Order(2) + @DisplayName("DIN :: Test GPIO Digital Input Pins ") + public void testDigitalInputsLow() throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST DIGITAL INPUT PINS - LOW"); + System.out.println("----------------------------------------"); + + for(int p = 2; p < 20; p++) { + + // create Digital Input instance config + var config = DigitalInput.newConfigBuilder() + .id("my-din-pin-" + p) + .name("My Digital Input Pin #" + p) + .address(p) + .build(); + + // create Digital Input I/O instance + DigitalInput din = pi4j.create(config); + + // configure output pin to LOW state on testing harness + harness.setOutputPin(p, false); + + DigitalState state = din.state(); + System.out.println("(PIN #" + p + ") >> STATE = " + state); + Assert.assertEquals("DIGITAL INPUT STATE MISMATCH: " + p, DigitalState.LOW, state); + } + } + + @Test + @Order(3) + @DisplayName("DIN :: Test GPIO Digital Input Pins ") + public void testDigitalInputsPullUp() throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST DIGITAL INPUT PINS - PULL UP"); + System.out.println("----------------------------------------"); + + for(int p = 2; p < 20; p++) { + + // the following inputs are skipped because they always fail; possible + // because they are tied to other things that override the pull-up/down + if(p == 5) continue; + if(p == 6) continue; + if(p == 9) continue; + if(p == 10) continue; + if(p == 11) continue; + if(p == 12) continue; + if(p == 13) continue; + if(p == 16) continue; + if(p == 18) continue; + if(p == 19) continue; + + // create Digital Input instance config + var config = DigitalInput.newConfigBuilder() + .id("my-din-pin-" + p) + .name("My Digital Input Pin #" + p) + .address(p) + .pull(PullResistance.PULL_UP) + .build(); + + // create Digital Input I/O instance + DigitalInput din = pi4j.create(config); + + // configure input pin and read value/state on testing harness + harness.setInputPin(p, false); + int pull = harness.getPin(p).value; + + System.out.println("(PIN #" + p + ") >> PULL = " + pull); + Assert.assertEquals("DIGITAL INPUT PULL MISMATCH: " + p, 1, pull); + } + } + + @Test + @Order(4) + @DisplayName("DIN :: Test GPIO Digital Input Pins ") + public void testDigitalInputsPullDown() throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST DIGITAL INPUT PINS - PULL DOWN"); + System.out.println("----------------------------------------"); + + for(int p = 2; p < 20; p++) { + + // create Digital Input instance config + var config = DigitalInput.newConfigBuilder() + .id("my-din-pin-" + p) + .name("My Digital Input Pin #" + p) + .address(p) + .pull(PullResistance.PULL_DOWN) + .build(); + + // create Digital Input I/O instance + DigitalInput din = pi4j.create(config); + + // configure input pin and read value/state on testing harness + harness.setInputPin(p, true); + int pull = harness.getPin(p).value; + + System.out.println("(PIN #" + p + ") >> PULL = " + pull); + Assert.assertEquals("DIGITAL INPUT PULL MISMATCH: " + p, 0, pull); + } + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/gpio/digital/TestDigitalOutputUsingTestHarness.java b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/gpio/digital/TestDigitalOutputUsingTestHarness.java new file mode 100644 index 000000000..d29781d67 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/gpio/digital/TestDigitalOutputUsingTestHarness.java @@ -0,0 +1,211 @@ +package com.pi4j.plugin.pigpio.gpio.digital; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : TestDigitalOutputUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.io.gpio.digital.DigitalOutput; +import com.pi4j.io.gpio.digital.DigitalState; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.gpio.digital.PiGpioDigitalOutputProvider; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; + +@DisplayName("PIGPIO Plugin :: Test Digital Output Pins using Test Harness") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestDigitalOutputUsingTestHarness { + + private PiGpio piGpio; + private Context pi4j; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + // configure logging output + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestDigitalOutputUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestDigitalOutputUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // reset I/O pins + harness.reset(); + + // terminate connection to test harness + harness.terminate(); + ; + } + + @BeforeEach + public void beforeEach() throws Exception { + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance("rpi3bp"); + + // initialize the PiGpio library + piGpio.initialize(); + + // create PWM provider instance to test with + var provider = PiGpioDigitalOutputProvider.newInstance(piGpio); + + // initialize Pi4J instance with this single provider + pi4j = Pi4J.newContextBuilder().add(provider).build(); + } + + @AfterEach + public void afterEach() throws Exception { + // terminate the PiGpio library after each test + piGpio.terminate(); + } + + @Test + @Order(1) + @DisplayName("DIN :: Test GPIO Digital Outpus Pins") + public void testDigitalOutputsHigh() throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST DIGITAL OUTPUT PINS "); + System.out.println("----------------------------------------"); + + for (int p = 2; p < 20; p++) { + + // configure input pin on testing harness + harness.setInputPin(p, true); + + // create Digital Output instance config + var config = DigitalOutput.newConfigBuilder() + .id("my-dout-pin-" + p) + .name("My Digital Output Pin #" + p) + .address(p) + .initial(DigitalState.HIGH) + .shutdown(DigitalState.LOW) + .build(); + + // create Digital Output I/O instance + DigitalOutput dout = pi4j.create(config); + + System.out.println(); + System.out.println("[TEST DIGITAL OUTPUT] :: PIN=" + p); + System.out.println(); + System.out.println(" (PIN #" + p + "; INIT) >> STATE = " + dout.config().getInitialState()); + + // read input state from test harness; compare with expected initial state + int readState = harness.getPin(p).value; + System.out.println(" (PIN #" + p + "; READ) << STATE = " + DigitalState.getState(readState)); + Assert.assertEquals("DIGITAL OUTPUT STATE MISMATCH: " + p, dout.config().getInitialState().value(), readState); + + // set state low + dout.low(); + System.out.println(" (PIN #" + p + "; WRITE) >> STATE = " + "LOW"); + + // read input state from test harness; compare with expected LOW state + readState = harness.getPin(p).value; + System.out.println(" (PIN #" + p + "; READ) << STATE = " + DigitalState.getState(readState)); + Assert.assertEquals("DIGITAL OUTPUT STATE MISMATCH: " + p, DigitalState.LOW.value(), readState); + + // set state high + dout.high(); + System.out.println(" (PIN #" + p + "; WRITE) >> STATE = " + "HIGH"); + + // read input state from test harness; compare with expected HIGH state + readState = harness.getPin(p).value; + System.out.println(" (PIN #" + p + "; READ) << STATE = " + DigitalState.getState(readState)); + Assert.assertEquals("DIGITAL OUTPUT STATE MISMATCH: " + p, DigitalState.HIGH.value(), readState); + + // toggle pin state + dout.toggle(); + System.out.println(" (PIN #" + p + "; WRITE) >> STATE = " + "LOW"); + + // read input state from test harness; compare with expected LOW state + readState = harness.getPin(p).value; + System.out.println(" (PIN #" + p + "; READ) << STATE = " + DigitalState.getState(readState)); + Assert.assertEquals("DIGITAL OUTPUT STATE MISMATCH: " + p, DigitalState.LOW.value(), readState); + + // set state high + dout.setState(1); + System.out.println(" (PIN #" + p + "; WRITE) >> STATE = " + "HIGH"); + + // read input state from test harness; compare with expected HIGH state + readState = harness.getPin(p).value; + System.out.println(" (PIN #" + p + "; READ) << STATE = " + DigitalState.getState(readState)); + Assert.assertEquals("DIGITAL OUTPUT STATE MISMATCH: " + p, DigitalState.HIGH.value(), readState); + + // perform a shutdown on this pin + dout.shutdown(pi4j); + System.out.println(" (PIN #" + p + "; SHUTD) >> STATE = " + dout.config().getShutdownState()); + + // read input state from test harness; compare with expected shutdown state + readState = harness.getPin(p).value; + System.out.println(" (PIN #" + p + "; READ) << STATE = " + DigitalState.getState(readState)); + Assert.assertEquals("DIGITAL OUTPUT STATE MISMATCH: " + p, dout.config().getShutdownState().value(), readState); + } + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/i2c/TestI2cRawUsingTestHarness.java b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/i2c/TestI2cRawUsingTestHarness.java new file mode 100644 index 000000000..58e1973dd --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/i2c/TestI2cRawUsingTestHarness.java @@ -0,0 +1,249 @@ +package com.pi4j.plugin.pigpio.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : TestI2cRawUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.i2c.I2C; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.i2c.PiGpioI2CProvider; +import com.pi4j.plugin.pigpio.provider.i2c.PiGpioI2CProviderImpl; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@DisplayName("PIGPIO Plugin :: Test I2C Raw Communication using Test Harness") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestI2cRawUsingTestHarness { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int I2C_TEST_HARNESS_BUS = 0; + private static int I2C_TEST_HARNESS_DEVICE = 0x04; + + private static byte SAMPLE_BYTE = 0x0d; + private static int SAMPLE_WORD = 0xFFFA; + private static byte[] SAMPLE_BYTE_ARRAY = new byte[] { 0,1,2,3,4,5,6,7,8,9 }; + private static byte[] SAMPLE_BUFFER_ARRAY = new byte[] { 10,11,12,13,14,15,16,17,18,19 }; + private static ByteBuffer SAMPLE_BUFFER = ByteBuffer.wrap(SAMPLE_BUFFER_ARRAY); + private static String SAMPLE_STRING = "Hello World!"; + + private PiGpio piGpio; + private I2C i2c; + + @BeforeAll + public static void initialize() { + // configure logging output + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestI2cRawUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + ArduinoTestHarness harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // enable the I2C bus and device on the test harness hardware + // (enable RAW mode data processing) + harness.enableI2C(I2C_TEST_HARNESS_BUS, I2C_TEST_HARNESS_DEVICE, true); + System.out.println(); + System.out.println("ENABLE I2C BUS [" + I2C_BUS + "] ON TEST HARNESS;"); + + // terminate connection to test harness + harness.terminate(); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestI2cRawUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + } + + @BeforeEach + public void beforeEach() throws Exception { + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance("rpi3bp"); + + // initialize the PiGpio library + piGpio.initialize(); + + // create I2C provider instance to test with + PiGpioI2CProvider provider = new PiGpioI2CProviderImpl(piGpio); + + // use the provider to create a I2C instance + i2c = provider.create(config); + } + + @AfterEach + public void afterEach() throws Exception { + // close I2C (if needed) + if(i2c.isOpen()) + i2c.close(); + + // terminate the PiGpio library after each test + piGpio.terminate(); + } + + @Test + @DisplayName("I2C :: Verify I2C Instance") + @Order(1) + public void testI2CInstance() throws Exception { + // ensure that the I2C instance is not null; + assertNotNull(i2c); + + // ensure connection is open + assertTrue("I2C INSTANCE IS NOT OPEN", i2c.isOpen()); + } + + @Test + @DisplayName("I2C :: Test BYTE (WRITE)") + @Order(2) + public void testI2CSingleByteWrite() throws Exception { + // write a single byte to the raw I2C device (not to a register) + i2c.write(SAMPLE_BYTE); + } + + @Test + @DisplayName("I2C :: Test BYTE (READ)") + @Order(3) + public void testI2CSingleByteRead() throws Exception { + // read single byte from the raw I2C device (not from a register) + Assert.assertEquals(SAMPLE_BYTE, i2c.readByte()); + } + + @Test + @DisplayName("I2C :: Test WORD (WRITE)") + @Order(4) + public void testI2CSingleWordWrite() throws Exception { + // write a single word (16 bits) to the raw I2C device (not to a register) + i2c.writeWord(SAMPLE_WORD); + } + + @Test + @DisplayName("I2C :: Test WORD (READ)") + @Order(5) + public void testI2CSingleWordRead() throws Exception { + // read single word (16 bits) from the raw I2C device (not from a register) + Assert.assertEquals(SAMPLE_WORD, i2c.readWord()); + } + + @Test + @DisplayName("I2C :: Test BYTE-ARRAY (WRITE)") + @Order(6) + public void testI2CByteArrayWrite() throws Exception { + // write an array of data bytes to the raw I2C device (not to a register) + i2c.write(SAMPLE_BYTE_ARRAY); + } + + @Test + @DisplayName("I2C :: Test BYTE-ARRAY (READ)") + @Order(7) + public void testI2CByteArrayRead() throws Exception { + // read an array of data bytes from the raw I2C device (not from a register) + byte[] byteArray = i2c.readArray(SAMPLE_BYTE_ARRAY.length); + Assert.assertArrayEquals(SAMPLE_BYTE_ARRAY, byteArray); + } + + @Test + @DisplayName("I2C :: Test BYTE-BUFFER (WRITE)") + @Order(8) + public void testI2CByteBufferWrite() throws Exception { + // write a buffer of data bytes to the raw I2C device (not to a register) + i2c.write(SAMPLE_BUFFER); + } + + @Test + @DisplayName("I2C :: Test BYTE-BUFFER (READ)") + @Order(9) + public void testI2CByteBufferRead() throws Exception { + // read a buffer of data bytes from the raw I2C device (not from a register) + ByteBuffer buffer = i2c.readBuffer(SAMPLE_BUFFER.capacity()); + Assert.assertArrayEquals(SAMPLE_BUFFER.array(), buffer.array()); + } + + @Test + @DisplayName("I2C :: Test ASCII STRING (WRITE)") + @Order(10) + public void testI2CAsciiStringWrite() throws Exception { + // write a string of data to the raw I2C device (not to a register) + i2c.write(SAMPLE_STRING); + } + + @Test + @DisplayName("I2C :: Test ASCII STRING (READ)") + @Order(11) + public void testI2CAsciiStringRead() throws Exception { + // read a string of data from the raw I2C device (not from a register) + String testString = i2c.readString(SAMPLE_STRING.length()); + Assert.assertEquals(SAMPLE_STRING, testString); + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/i2c/TestI2cUsingTestHarness.java b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/i2c/TestI2cUsingTestHarness.java new file mode 100644 index 000000000..0ad2331ff --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/i2c/TestI2cUsingTestHarness.java @@ -0,0 +1,424 @@ +package com.pi4j.plugin.pigpio.i2c; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : TestI2cUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.io.exception.IOReadException; +import com.pi4j.io.i2c.I2C; +import com.pi4j.io.i2c.I2CRegister; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.i2c.PiGpioI2CProvider; +import com.pi4j.plugin.pigpio.provider.i2c.PiGpioI2CProviderImpl; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import com.pi4j.util.StringUtil; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.*; + +@DisplayName("PIGPIO Plugin :: Test I2C Communication using Test Harness") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestI2cUsingTestHarness { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int I2C_TEST_HARNESS_BUS = 0; + private static int I2C_TEST_HARNESS_DEVICE = 0x04; + private static int MAX_REGISTERS = 100; + + private PiGpio piGpio; + private I2C i2c; + + @BeforeAll + public static void initialize() { + // configure logging output + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestI2cUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + ArduinoTestHarness harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + + // enable the I2C bus and device on the test harness hardware + harness.enableI2C(I2C_TEST_HARNESS_BUS, I2C_TEST_HARNESS_DEVICE); + System.out.println(); + System.out.println("ENABLE I2C BUS [" + I2C_BUS + "] ON TEST HARNESS;"); + + harness.terminate(); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestI2cUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + } + + @BeforeEach + public void beforeEach() throws Exception { + + // create I2C config + var config = I2C.newConfigBuilder() + .id("my-i2c-bus") + .name("My I2C Bus") + .bus(I2C_BUS) + .device(I2C_DEVICE) + .build(); + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance("rpi3bp"); + + // initialize the PiGpio library + piGpio.initialize(); + + // create I2C provider instance to test with + PiGpioI2CProvider provider = new PiGpioI2CProviderImpl(piGpio); + + // use the provider to create a I2C instance + i2c = provider.create(config); + } + + @AfterEach + public void afterEach() throws Exception { + // close I2C (if needed) + if(i2c.isOpen()) + i2c.close(); + + // terminate the PiGpio library after each test + piGpio.terminate(); + } + + + @Test + @DisplayName("I2C :: Test register: BYTE (W/R)") + @Order(1) + public void testI2CByteWriteRead() throws IOException, InterruptedException, IOReadException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE BYTE"); + System.out.println("----------------------------------------"); + + // value cache + byte[] values = new byte[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST WRITE BYTE] :: REGISTER=" + register.address()); + + Random rand = new Random(); + values[r] = (byte) rand.nextInt(0xFF); // max 8 bits (1 bytes) + System.out.println(" (WRITE) >> VALUE = 0x" + StringUtil.toHexString(values[r])); + + // WRITE :: SINGLE BYTE TO REGISTER + register.write(values[r]); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST READ BYTE] :: REGISTER=" + register.address()); + + // READ :: SINGLE RAW BYTE + byte value = register.readByte(); + System.out.println(" (READ) << VALUE = 0x" + StringUtil.toHexString(value) + + "; (EXPECTED=0x" + StringUtil.toHexString(values[r]) + ")"); + + // SECOND ATTEMPT + if(value != values[r]){ + Thread.sleep(500); + value = register.readByte(); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + StringUtil.toHexString(values[r]) + ") <2ND ATTEMPT>"); + + // THIRD ATTEMPT + if(value != values[r]){ + Thread.sleep(1000); + value = register.readByte(); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + StringUtil.toHexString(values[r]) + ") <3RD ATTEMPT>"); + } + } + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C BYTE VALUE MISMATCH", values[r], value); + } + } + + @Test + @DisplayName("I2C :: Test register: WORD (W/R)") + @Order(2) + public void testI2CWordWriteRead() throws IOException, InterruptedException, IOReadException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE WORD"); + System.out.println("----------------------------------------"); + + // value cache + int[] values = new int[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST WRITE WORD] :: REGISTER=" + register.address()); + + Random rand = new Random(); + values[r] = rand.nextInt(0xFFFF); // max 16 bits (2 bytes) + System.out.println(" (WRITE) >> VALUE = " + values[r]); + + // WRITE :: SINGLE WORD TO REGISTER + register.writeWord(values[r]); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST READ WORD] :: REGISTER=" + register.address()); + + // READ :: SINGLE RAW WORD + int value = register.readWord(); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[r] + ")"); + + // SECOND ATTEMPT + if(value != values[r]){ + Thread.sleep(500); + value = register.readWord(); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[r] + ") <2ND ATTEMPT>"); + + // THIRD ATTEMPT + if(value != values[r]){ + Thread.sleep(1000); + value = register.readWord(); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[r] + ") <3RD ATTEMPT>"); + } + } + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C WORD VALUE MISMATCH", values[r], value); + } + } + + @Test + @DisplayName("I2C :: Test register: BYTE-ARRAY (W/R)") + @Order(3) + public void testI2CByteArrayWriteRead() throws IOException, InterruptedException, IOReadException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE BYTE-ARRAY"); + System.out.println("----------------------------------------"); + + // value cache + List values = new ArrayList<>(); + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST WRITE BYTE-ARRAY] :: REGISTER=" + register.address()); + + UUID.randomUUID().toString().substring(0, 30); + + Random rand = new Random(); + byte[] temp = new byte[10]; + rand.nextBytes(temp); + values.add(temp); + System.out.println(" (WRITE) >> VALUE = [0x" + StringUtil.toHexString(temp) + "]"); + + // WRITE :: BYTE-ARRAY TO REGISTER + register.write(temp); + Thread.sleep(10); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST READ BYTE-ARRAY] :: REGISTER=" + register.address()); + + // READ :: BYTE-ARRAY + byte[] value = register.readArray(10); + byte[] expected = values.get(r); + + System.out.println(" (READ) << VALUE = " + StringUtil.toHexString(value) + + "; (EXPECTED=" + StringUtil.toHexString(expected) + ")"); + + if(!Arrays.equals(expected, value)){ + Thread.sleep(500); + // READ :: BYTE-BUFFER + register.readArray(10); + System.out.println(" (READ) << VALUE = " + StringUtil.toHexString(value) + + "; (EXPECTED=" + StringUtil.toHexString(expected) + ") "); + + if(!Arrays.equals(expected, value)){ + Thread.sleep(1000); + // READ :: BYTE-BUFFER + register.readArray(10); + System.out.println(" (READ) << VALUE = " + StringUtil.toHexString(value) + + "; (EXPECTED=" + StringUtil.toHexString(expected) + ") "); + + } + } + // validate read value match with expected value that was writted to this register + Assert.assertArrayEquals("I2C BYTE-ARRAY VALUE MISMATCH", expected, value); + Thread.sleep(10); + } + } + + @Test + @DisplayName("I2C :: Test register: BYTE-BUFFER (W/R)") + @Order(4) + public void testI2CByteBufferWriteRead() throws IOException, InterruptedException, IOReadException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER READ/WRITE BYTE BUFFER"); + System.out.println("----------------------------------------"); + + // value cache + List values = new ArrayList<>(); + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST WRITE BYTE-BUFFER] :: REGISTER=" + register.address()); + + UUID.randomUUID().toString().substring(0, 30); + + Random rand = new Random(); + byte[] temp = new byte[10]; + rand.nextBytes(temp); + ByteBuffer buffer = ByteBuffer.wrap(temp); + values.add(buffer); + System.out.println(" (WRITE) >> VALUE = [0x" + StringUtil.toHexString(buffer) + "]"); + + // WRITE :: BYTE-BUFFER TO REGISTER + register.write(temp); + } + + // READ back the 10 random values from the I2C storage registers on the test harness and compare them. + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST READ BYTE-BUFFER] :: REGISTER=" + register.address()); + + // READ :: BYTE-BUFFER + ByteBuffer value = register.readBuffer(10); + ByteBuffer expected = values.get(r); + + System.out.println(" (READ) << VALUE = " + StringUtil.toHexString(value) + + "; (EXPECTED=" + StringUtil.toHexString(expected) + ")"); + + if(!Arrays.equals(expected.array(), value.array())){ + Thread.sleep(500); + // READ :: BYTE-BUFFER + value = register.readBuffer(10); + System.out.println(" (READ) << VALUE = " + StringUtil.toHexString(value) + + "; (EXPECTED=" + StringUtil.toHexString(expected) + ") "); + + if(!Arrays.equals(expected.array(), value.array())){ + Thread.sleep(500); + // READ :: BYTE-BUFFER + value = register.readBuffer(10); + System.out.println(" (READ) << VALUE = " + StringUtil.toHexString(value) + + "; (EXPECTED=" + StringUtil.toHexString(expected) + ") "); + + } + } + + // validate read value match with expected value that was writted to this register + Assert.assertArrayEquals("I2C BYTE-BUFFER VALUE MISMATCH", expected.array(), value.array()); + } + } + + @Test + @DisplayName("I2C :: Test register: (EXCHANGE) WORD (W->R)") + @Order(5) + public void testI2CWordExchange() throws IOException, InterruptedException, IOReadException { + + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST I2C REGISTER EXCHANGE WORD"); + System.out.println("----------------------------------------"); + + // value cache + int[] values = new int[MAX_REGISTERS]; + + // WRITE random values to the I2C storage registers on the test harness. + // the test harness contains 256 registers from address 0 to 255; + for(int r = 0; r < MAX_REGISTERS; r++) { + I2CRegister register = i2c.register(r); + System.out.println("[TEST WRITE WORD] :: REGISTER=" + register.address()); + + Random rand = new Random(); + values[r] = rand.nextInt(0xFFFF); // max 16 bits (2 bytes) + System.out.println(" (WRITE) >> VALUE = " + values[r]); + + // EXCHANGE :: SINGLE WORD TO REGISTER (this will write a word value and immediately read back the word value) + int value = register.writeReadWord(values[r]); + System.out.println(" (READ) << VALUE = " + value + "; (EXPECTED=" + values[r] + ")"); + + // validate read value match with expected value that was writted to this register + Assert.assertEquals("I2C WORD VALUE MISMATCH", values[r], value); + } + } +} diff --git a/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/pwm/TestHardwarePwmUsingTestHarness.java b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/pwm/TestHardwarePwmUsingTestHarness.java new file mode 100644 index 000000000..a4bfb8a83 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/pwm/TestHardwarePwmUsingTestHarness.java @@ -0,0 +1,288 @@ +package com.pi4j.plugin.pigpio.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : TestHardwarePwmUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmType; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessFrequency; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; + +@DisplayName("PIGPIO Plugin :: Test PWM (Hardware-Generated Signals) using Test Harness") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestHardwarePwmUsingTestHarness { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int I2C_TEST_HARNESS_BUS = 0; + private static int I2C_TEST_HARNESS_DEVICE = 0x04; + + private PiGpio piGpio; + private Context pi4j; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + // configure logging output + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestHardwarePwmUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestHardwarePwmUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // terminate connection to test harness + harness.terminate();; + } + + @BeforeEach + public void beforeEach() throws Exception { + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance("rpi3bp"); + + // initialize the PiGpio library + piGpio.initialize(); + + // create PWM provider instance to test with + var provider = PiGpioPwmProvider.newInstance(piGpio); + + // initialize Pi4J instance with this single provider + pi4j = Pi4J.newContextBuilder().add(provider).build(); + } + + @AfterEach + public void afterEach() throws Exception { + // terminate the PiGpio library after each test + piGpio.terminate(); + } + + @Test + @Order(1) + @DisplayName("PWM :: Test Hardware PWM @ 50 Hz") + public void testPwmAt50Hertz() throws Exception { + testPwm(50); + } + + @Test + @Order(2) + @DisplayName("PWM :: Test Hardware PWM @ 100 Hz") + public void testPwmAt100Hertz() throws Exception { + testPwm(100); + } + + @Test + @Order(3) + @DisplayName("PWM :: Test Hardware PWM @ 700 Hz") + public void testPwmAt700Hertz() throws Exception { + testPwm(700); + } + + @Test + @Order(4) + @DisplayName("PWM :: Test Hardware PWM @ 1000 Hz (1 KHz)") + public void testPwmAt1000Hertz() throws Exception { + testPwm(1000); + } + + @Test + @Order(5) + @DisplayName("PWM :: Test Hardware PWM @ 5000 Hz (5 KHz)") + public void testPwmAt5000Hertz() throws Exception { + testPwm(5000); + } + + @Test + @Order(6) + @DisplayName("PWM :: Test Hardware PWM @ 10000 Hz (10 KHz)") + public void testPwmAt10000Hertz() throws Exception { + testPwm(10000); + } + + @Test + @Order(6) + @DisplayName("PWM :: Test Hardware PWM @ 10000 Hz (10 KHz)") + public void testUnsupportedPin() throws Exception { + + // create PWM instance config + var config = Pwm.newConfigBuilder() + .address(1) + .pwmType(PwmType.HARDWARE) + .build(); + + // create PWM I/O instance + Pwm pwm = pi4j.create(config); + + // when we attempt to turn on the PWM pin, we expect an exception + // to occur because this is not a hardware supported PWM pin + Assertions.assertThrows(IOException.class, () -> { + pwm.on(); + }); + } + + + public void testPwm(int frequency) throws Exception { + testPwm(frequency, 50); // 80% duty-cycle by default + } + public void testPwm(int frequency, int dutyCycle) throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST PWM SIGNALS AT " + frequency + " HZ"); + System.out.println("----------------------------------------"); + + + int pins[] = new int[] { 12, 13,18,19 }; + +// 12 PWM channel 0 All models but A and B +// 13 PWM channel 1 All models but A and B +// 18 PWM channel 0 All models +// 19 PWM channel 1 All models but A and B +// +// 40 PWM channel 0 Compute module only +// 41 PWM channel 1 Compute module only +// 45 PWM channel 1 Compute module only +// 52 PWM channel 0 Compute module only +// 53 PWM channel 1 Compute module only + + for(int p : pins) { + + // create PWM instance config + var config = Pwm.newConfigBuilder() + .id("my-pwm-pin-" + p) + .name("My Test PWM Pin #" + p) + .address(p) + .pwmType(PwmType.HARDWARE) + .build(); + + // create PWM I/O instance + Pwm pwm = pi4j.create(config); + + System.out.println(); + System.out.println("[TEST HARDWARE PWM] :: PIN=" + p); + + // turn on PWM pulses with specified frequency and duty-cycle + pwm.dutyCyclePercent(dutyCycle) + .frequency(frequency) + .on(); + + System.out.println(" (PWM) >> SET FREQUENCY = " + frequency); + System.out.println(" (PWM) >> SET DUTY-CYCLE = " + dutyCycle + "%"); + System.out.println(" (PWM) << GET FREQUENCY = " + pwm.frequency()); + System.out.println(" (PWM) << GET DUTY-CYCLE = " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + System.out.println(" (PWM) << GET RANGE MAX = " + pwm.range()); + + Thread.sleep(10); + // test once .. + if(measureFrequency(pwm) == false){ + // test twice .. + Thread.sleep(1000); + if(measureFrequency(pwm) == false){ + // test thrice .. + Thread.sleep(2000); + if(measureFrequency(pwm) == false){ + // turn off PWM pin + pwm.off(); + + // give up and fail + Assert.fail("PWM MEASURED FREQUENCY OUT OF ACCEPTABLE MARGIN OF ERROR"); + } + } + } + + // turn off PWM pin + pwm.off(); + } + } + + private boolean measureFrequency(Pwm pwm) throws IOException { + int frequency = pwm.frequency(); + TestHarnessFrequency measured = harness.getFrequency(pwm.address()); + System.out.println(" (TEST) << MEASURED FREQUENCY = " + measured.frequency); + + // we allow a 60% margin of error, the testing harness uses a simple pulse counter to crudely + // measure the PWM signal, its not very accurate but should provide sufficient validation testing + // just to verify the applied PWM signal is close to the expected frequency + // calculate margin of error offset value + long marginOfError = Math.round(frequency * .60); + + // test measured value against HI/LOW offsets to determine acceptable range + if(measured.frequency < frequency-marginOfError) return false; + if(measured.frequency > frequency+marginOfError) return false; + + // success + return true; + } + + +} diff --git a/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/pwm/TestSoftwarePwmUsingTestHarness.java b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/pwm/TestSoftwarePwmUsingTestHarness.java new file mode 100644 index 000000000..79eb61692 --- /dev/null +++ b/plugins/pi4j-plugin-pigpio/src/test/java/com/pi4j/plugin/pigpio/pwm/TestSoftwarePwmUsingTestHarness.java @@ -0,0 +1,247 @@ +package com.pi4j.plugin.pigpio.pwm; + +/*- + * #%L + * ********************************************************************** + * ORGANIZATION : Pi4J + * PROJECT : Pi4J :: PLUGIN :: PIGPIO I/O Providers + * FILENAME : TestSoftwarePwmUsingTestHarness.java + * + * This file is part of the Pi4J project. More information about + * this project can be found here: https://pi4j.com/ + * ********************************************************************** + * %% + * Copyright (C) 2012 - 2019 Pi4J + * %% + * This program 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 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.library.pigpio.PiGpio; +import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider; +import com.pi4j.test.harness.ArduinoTestHarness; +import com.pi4j.test.harness.TestHarnessFrequency; +import com.pi4j.test.harness.TestHarnessInfo; +import com.pi4j.test.harness.TestHarnessPins; +import org.junit.Assert; +import org.junit.jupiter.api.*; + +import java.io.IOException; + +@DisplayName("PIGPIO Plugin :: Test PWM (Software-Generated Signals) using Test Harness") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestSoftwarePwmUsingTestHarness { + + private static int I2C_BUS = 1; + private static int I2C_DEVICE = 0x04; + private static int I2C_TEST_HARNESS_BUS = 0; + private static int I2C_TEST_HARNESS_DEVICE = 0x04; + + private PiGpio piGpio; + private Context pi4j; + private static ArduinoTestHarness harness; + + @BeforeAll + public static void initialize() { + // configure logging output + System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO"); + + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("INITIALIZE TEST (" + TestSoftwarePwmUsingTestHarness.class.getName() + ")"); + System.out.println("************************************************************************"); + System.out.println(); + + try { + // create test harness and PIGPIO instances + harness = new ArduinoTestHarness(System.getProperty("pi4j.test.harness.port", "tty.usbmodem142301")); + + // initialize test harness and PIGPIO instances + harness.initialize(); + + // get test harness info + TestHarnessInfo info = harness.getInfo(); + System.out.println("... we are connected to test harness:"); + System.out.println("----------------------------------------"); + System.out.println("NAME : " + info.name); + System.out.println("VERSION : " + info.version); + System.out.println("DATE : " + info.date); + System.out.println("COPYRIGHT : " + info.copyright); + System.out.println("----------------------------------------"); + + // reset all pins on test harness before proceeding with this test + TestHarnessPins reset = harness.reset(); + System.out.println(); + System.out.println("RESET ALL PINS ON TEST HARNESS; (" + reset.total + " pin reset)"); + } catch (IOException e){ + e.printStackTrace(); + } + } + + @AfterAll + public static void terminate() throws IOException { + System.out.println(); + System.out.println("************************************************************************"); + System.out.println("TERMINATE TEST (" + TestSoftwarePwmUsingTestHarness.class.getName() + ") "); + System.out.println("************************************************************************"); + System.out.println(); + + // terminate connection to test harness + harness.terminate();; + } + + @BeforeEach + public void beforeEach() throws Exception { + + // TODO :: THIS WILL NEED TO CHANGE WHEN NATIVE PIGPIO SUPPORT IS ADDED + piGpio = PiGpio.newSocketInstance("rpi3bp"); + + // initialize the PiGpio library + piGpio.initialize(); + + // create PWM provider instance to test with + var provider = PiGpioPwmProvider.newInstance(piGpio); + + // initialize Pi4J instance with this single provider + pi4j = Pi4J.newContextBuilder().add(provider).build(); + } + + @AfterEach + public void afterEach() throws Exception { + // terminate the PiGpio library after each test + piGpio.terminate(); + } + + @Test + @Order(1) + @DisplayName("PWM :: Test Software PWM @ 50 Hz") + public void testPwmAt50Hertz() throws Exception { + testPwm(50); + } + + @Test + @Order(2) + @DisplayName("PWM :: Test Software PWM @ 100 Hz") + public void testPwmAt100Hertz() throws Exception { + testPwm(100); + } + + @Test + @Order(3) + @DisplayName("PWM :: Test Software PWM @ 700 Hz") + public void testPwmAt700Hertz() throws Exception { + testPwm(700); + } + + @Test + @Order(4) + @DisplayName("PWM :: Test Software PWM @ 1000 Hz (1 KHz)") + public void testPwmAt1000Hertz() throws Exception { + testPwm(1000); + } + + @Test + @Order(5) + @DisplayName("PWM :: Test Software PWM @ 5000 Hz (5 KHz)") + public void testPwmAt5000Hertz() throws Exception { + testPwm(5000); + } + + @Test + @Order(6) + @DisplayName("PWM :: Test Software PWM @ 10000 Hz (10 KHz)") + public void testPwmAt10000Hertz() throws Exception { + testPwm(10000); + } + + public void testPwm(int frequency) throws Exception { + testPwm(frequency, 50); // 80% duty-cycle by default + } + public void testPwm(int frequency, int dutyCycle) throws Exception { + System.out.println(); + System.out.println("----------------------------------------"); + System.out.println("TEST PWM SIGNALS AT " + frequency + " HZ"); + System.out.println("----------------------------------------"); + + for(int p = 2; p < 20; p++) { + + // create PWM instance config + var config = Pwm.newConfigBuilder() + .id("my-pwm-pin-" + p) + .name("My Test PWM Pin #" + p) + .address(p) + .build(); + + // create PWM I/O instance + Pwm pwm = pi4j.create(config); + + System.out.println(); + System.out.println("[TEST SOFT PWM] :: PIN=" + p); + + // turn on PWM pulses with specified frequency and duty-cycle + pwm.dutyCyclePercent(dutyCycle).frequency(frequency).on(); + + System.out.println(" (PWM) >> SET FREQUENCY = " + frequency); + System.out.println(" (PWM) >> SET DUTY-CYCLE = " + dutyCycle + "%"); + System.out.println(" (PWM) << GET FREQUENCY = " + pwm.frequency()); + System.out.println(" (PWM) << GET DUTY-CYCLE = " + pwm.dutyCycle() + " (" + pwm.dutyCyclePercent() + "%)"); + System.out.println(" (PWM) << GET RANGE MAX = " + pwm.range()); + + // test once .. + if(measureFrequency(pwm) == false){ + // test twice .. + Thread.sleep(1000); + if(measureFrequency(pwm) == false){ + // test thrice .. + Thread.sleep(2000); + if(measureFrequency(pwm) == false){ + // turn off PWM pin + pwm.off(); + + // give up and fail + Assert.fail("PWM MEASURED FREQUENCY OUT OF ACCEPTABLE MARGIN OF ERROR"); + } + } + } + + // turn off PWM pin + pwm.off(); + } + } + + private boolean measureFrequency(Pwm pwm) throws IOException { + int frequency = pwm.frequency(); + TestHarnessFrequency measured = harness.getFrequency(pwm.address()); + System.out.println(" (TEST) << MEASURED FREQUENCY = " + measured.frequency); + + // we allow a 60% margin of error, the testing harness uses a simple pulse counter to crudely + // measure the PWM signal, its not very accurate but should provide sufficient validation testing + // just to verify the applied PWM signal is close to the expected frequency + // calculate margin of error offset value + long marginOfError = Math.round(frequency * .60); + + // test measured value against HI/LOW offsets to determine acceptable range + if(measured.frequency < frequency-marginOfError) return false; + if(measured.frequency > frequency+marginOfError) return false; + + // success + return true; + } + + +} diff --git a/plugins/pi4j-plugin-raspberrypi/pom.xml b/plugins/pi4j-plugin-raspberrypi/pom.xml index 0c9c2ce53..fbe8ec092 100644 --- a/plugins/pi4j-plugin-raspberrypi/pom.xml +++ b/plugins/pi4j-plugin-raspberrypi/pom.xml @@ -14,13 +14,5 @@ pi4j-plugin-raspberrypi Pi4J :: PLUGIN :: RaspberryPi Platform & Providers Pi4J Library Plugin for the RaspberryPi Platform & I/O Providers - - - com.pi4j - pi4j-api - 2.0-SNAPSHOT - - jar - diff --git a/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/RaspberryPiPlugin.java b/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/RaspberryPiPlugin.java index a40082eef..017083c1b 100644 --- a/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/RaspberryPiPlugin.java +++ b/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/RaspberryPiPlugin.java @@ -31,7 +31,6 @@ import com.pi4j.extension.Plugin; import com.pi4j.extension.PluginService; -import com.pi4j.provider.Provider; import com.pi4j.plugin.raspberrypi.platform.RaspberryPiPlatform; import com.pi4j.plugin.raspberrypi.provider.gpio.digital.RpiDigitalInputProvider; import com.pi4j.plugin.raspberrypi.provider.gpio.digital.RpiDigitalOutputProvider; @@ -39,6 +38,7 @@ import com.pi4j.plugin.raspberrypi.provider.pwm.RpiPwmProvider; import com.pi4j.plugin.raspberrypi.provider.serial.RpiSerialProvider; import com.pi4j.plugin.raspberrypi.provider.spi.RpiSpiProvider; +import com.pi4j.provider.Provider; public class RaspberryPiPlugin implements Plugin { diff --git a/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/i2c/RpiI2C.java b/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/i2c/RpiI2C.java index 5a578b9e8..de98e2948 100644 --- a/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/i2c/RpiI2C.java +++ b/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/i2c/RpiI2C.java @@ -36,6 +36,7 @@ import com.pi4j.io.i2c.I2CProvider; import java.io.IOException; +import java.nio.ByteBuffer; public class RpiI2C extends I2CBase implements I2C { @@ -44,62 +45,42 @@ public RpiI2C(I2CProvider provider, I2CConfig config){ } @Override - public int getAddress() { + public int read() throws IOException { return 0; } @Override - public void write(byte b) throws IOException { - - } - - @Override - public void write(byte[] buffer, int offset, int size) throws IOException { - - } - - @Override - public void write(byte[] buffer) throws IOException { - - } - - @Override - public void write(int address, byte b) throws IOException { - - } - - @Override - public void write(int address, byte[] buffer, int offset, int size) throws IOException { - + public int read(ByteBuffer buffer, int offset, int length) throws IOException { + return 0; } @Override - public void write(int address, byte[] buffer) throws IOException { + public void write(byte b) throws IOException { } @Override - public int read() throws IOException { + public int write(ByteBuffer buffer, int offset, int length) throws IOException { return 0; } @Override - public int read(byte[] buffer, int offset, int size) throws IOException { + public int readRegister(int register) throws IOException { return 0; } @Override - public int read(int address) throws IOException { + public int readRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException { return 0; } @Override - public int read(int address, byte[] buffer, int offset, int size) throws IOException { - return 0; + public void writeRegister(int register, byte b) throws IOException { + } @Override - public int read(byte[] writeBuffer, int writeOffset, int writeSize, byte[] readBuffer, int readOffset, int readSize) throws IOException { + public int writeRegister(int register, ByteBuffer buffer, int offset, int length) throws IOException { return 0; } } diff --git a/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/pwm/RpiPwm.java b/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/pwm/RpiPwm.java index 5108daddf..85773bb5d 100644 --- a/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/pwm/RpiPwm.java +++ b/plugins/pi4j-plugin-raspberrypi/src/main/java/com/pi4j/plugin/raspberrypi/provider/pwm/RpiPwm.java @@ -34,9 +34,23 @@ import com.pi4j.io.pwm.PwmConfig; import com.pi4j.io.pwm.PwmProvider; +import java.io.IOException; + public class RpiPwm extends PwmBase implements Pwm { public RpiPwm(PwmProvider provider, PwmConfig config){ super(provider, config); } + + @Override + public Pwm on() throws IOException { + this.onState = true; + return this; + } + + @Override + public Pwm off() throws IOException { + this.onState = false; + return this; + } } diff --git a/plugins/pi4j-plugin/pom.xml b/plugins/pi4j-plugin/pom.xml index 9762ff056..7615766e1 100644 --- a/plugins/pi4j-plugin/pom.xml +++ b/plugins/pi4j-plugin/pom.xml @@ -12,7 +12,7 @@ pi4j-plugin - Pi4J :: PLUGINS :: Plugins Parent POM + Pi4J :: PLUGIN :: Plugins Parent POM Pi4J Plugin Parent Maven POM pom @@ -23,16 +23,13 @@ pi4j-api ${project.version} - - org.slf4j - slf4j-api - ../pi4j-plugin-mock ../pi4j-plugin-linuxfs + ../pi4j-plugin-pigpio ../pi4j-plugin-raspberrypi @@ -64,6 +61,12 @@ maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + org.apache.maven.plugins diff --git a/pom.xml b/pom.xml index 7b50424fb..b905b2c2f 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,8 @@ pi4j-api + pi4j-test-harness + libraries/pi4j-library plugins/pi4j-plugin pi4j-test pi4j-example @@ -109,7 +111,7 @@ - 192.168.1.100 + rpi3bp.savage.lan 22 pi raspberry @@ -123,23 +125,28 @@ ${pi4j.default.password} pi4j-dev + ${pi4j.default.host} + 8888 + + + + true + tty.usbmodem142301 + - false - raspberrypi - RaspberryPi - ${pi4j.default.host} - ${pi4j.default.port} - ${pi4j.default.user} - ${pi4j.default.password} - ${pi4j.default.directory} + + + + + + + + UTF-8 UTF-8 - - github - 11 ${java.version} @@ -151,8 +158,8 @@ 0.1.53 20020829 4.12 - 1.10.19 - 1.6.5 + 5.5.1 + 1.5.1 1.7.26 @@ -180,7 +187,7 @@ 2.9 - + false @@ -193,11 +200,14 @@ + org.osgi org.osgi.core ${org.osgi.core.version} + + org.apache.ant ant-jsch @@ -213,6 +223,20 @@ ant-contrib ${ant-contrib.version} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + + + junit junit @@ -220,42 +244,58 @@ test - org.mockito - mockito-all - ${mockito.version} + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} test - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - org.powermock - powermock-api-mockito - ${powermock.version} - test - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - slf4j-simple - ${slf4j.version} + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + test + + + + + com.fazecast + jSerialComm + 2.5.1 + test + junit junit + test + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + @@ -291,6 +331,13 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + org.apache.maven.plugins @@ -298,7 +345,6 @@ ${maven-jar-plugin.version} - org.apache.maven.plugins @@ -505,80 +551,6 @@ ${s3-upload-maven-plugin.version} - - - org.eclipse.m2e - lifecycle-mapping - ${lifecycle-mapping.version} - - - - - - org.apache.maven.plugins - maven-antrun-plugin - [1.3,) - - run - - - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - [2.8,) - - copy - copy-dependencies - unpack - - - - - - - - - org.codehaus.mojo - license-maven-plugin - [1.7,) - - - update-file-header - - - - - - - - - - - com.github.dantwining.whitespace-maven-plugin - - - whitespace-maven-plugin - - - [1.0.4,) - - - trim - - - - - - - - - - @@ -744,4 +716,48 @@ + + + + default-profile + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*UsingTestHarness.java + + + + + + + + + + test-hardware + + + + org.apache.maven.plugins + maven-surefire-plugin + + ** + + ${pi4j.test.pigpio.host} + ${pi4j.test.pigpio.port} + ${pi4j.test.harness.port} + + + + + + + +