package com.pi4j.gpio.extension.olimex; import com.pi4j.gpio.extension.serial.SerialCommandQueueProcessingThread; import com.pi4j.io.gpio.GpioProvider; import com.pi4j.io.gpio.GpioProviderBase; import com.pi4j.io.gpio.Pin; import com.pi4j.io.gpio.PinMode; import com.pi4j.io.gpio.PinState; import com.pi4j.io.gpio.event.PinDigitalStateChangeEvent; import com.pi4j.io.gpio.event.PinListener; import com.pi4j.io.serial.Serial; import com.pi4j.io.serial.SerialDataEvent; import com.pi4j.io.serial.SerialDataListener; import com.pi4j.io.serial.SerialFactory; /* * #%L * ********************************************************************** * ORGANIZATION : Pi4J * PROJECT : Pi4J :: GPIO Extension * FILENAME : OlimexAVRIOGpioProvider.java * * This file is part of the Pi4J project. More information about * this project can be found here: http://www.pi4j.com/ * ********************************************************************** * %% * Copyright (C) 2012 - 2013 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% */ /** * <p> * This GPIO provider implements the Olimex AVR-IO-M-16 expansion board as native Pi4J GPIO pins. * More information about the board can be found here: * * https://www.olimex.com/Products/AVR/Development/AVR-IO-M16/ * </p> * * <p> * The Olimex AVR-IO board is connected via RS232 serial connection to the Raspberry Pi and provides * 4 electromechanical RELAYs and 4 opto-isolated INPUT pins. * </p> * * @see https://www.olimex.com/Products/AVR/Development/AVR-IO-M16/ * @author Robert Savage * */ public class OlimexAVRIOGpioProvider extends GpioProviderBase implements GpioProvider { public static final String NAME = "com.pi4j.gpio.extension.olimex.OlimexAVRIOGpioProvider"; public static final String DESCRIPTION = "Olimex AVR-IO GPIO Provider"; private Serial com; private int currentStates = 0; private SerialCommandQueueProcessingThread queue; public OlimexAVRIOGpioProvider(String serialDevice) { // create serial communications instance com = SerialFactory.createInstance(); // create serial data listener SerialExampleListener listener = new SerialExampleListener(); // add/register the serial data listener com.addListener(listener); // open serial port for communication com.open(serialDevice, 19200); // create and start the serial command processing queue thread // set the delay time to 100 ms; this works well for the AVR-IO queue = new SerialCommandQueueProcessingThread(com, 50); queue.start(); queue.put("?"); // query for current status } @Override public String getName() { return NAME; } @Override public void setMode(Pin pin, PinMode mode) { // ALL PIN MODES ARE PREDEFINED // // an exception will be throw by the base impl // if an alternate mode is selected for a pin // instance super.setMode(pin, mode); } @Override public PinMode getMode(Pin pin) { super.getMode(pin); // return first mode found; this device has singular fixed pin modes for(PinMode mode : pin.getSupportedPinModes()) return mode; return null; } @Override public void setState(Pin pin, PinState state) { super.setState(pin, state); // turn ON/OFF relay pins if (state == PinState.HIGH) { queue.put("+" + pin.getAddress()); } else { queue.put("-" + pin.getAddress()); } } @Override public PinState getState(Pin pin) { super.getState(pin); // calculate current state from the bitmask value int bit = (int)Math.pow(2, (pin.getAddress()-1)); int state = (currentStates & bit); return (state == bit) ? PinState.HIGH : PinState.LOW; } @Override public void shutdown() { // prevent reentrant invocation if(isShutdown()) return; // perform shutdown login in base super.shutdown(); // if a serial processing queue is running, then shut it down now if (queue != null) { // shutdown serial data processing thread queue.shutdown(); //queue.interrupt(); queue = null; } // close the serial port communication com.close(); // shutdown any serial data monitoring threads com.shutdown(); } /** * This class implements the serial data listener interface with the callback method for event * notifications when data is received on the serial port. * * @see SerialDataListener * @author Robert Savage */ class SerialExampleListener implements SerialDataListener { private StringBuilder buffer = new StringBuilder(); public void dataReceived(SerialDataEvent event) { String data = event.getData(); // append received data into buffer if (data != null && !data.isEmpty()) { buffer.append(data); } int start = buffer.indexOf("$"); int stop = buffer.indexOf("\n"); while (stop >= 0) { // process data buffer if(start >= 0 && stop > start) { // get command String command = buffer.substring(start, stop+1); buffer.delete(start, stop+1).toString(); // remove terminating characters command = command.replace("$", ""); command = command.replace("\n", ""); command = command.replace("\r", ""); // print out the data received to the console //System.out.println("<<< COM RX >>> " + command); int value = Integer.parseInt(command, 16); // process each INPUT pin for changes; // dispatch change events if needed for (Pin pin : OlimexAVRIOPin.INPUTS) { evaluatePinForChange(pin, value); } // update the current value tracking variable currentStates = value; } else if (stop >= 0) { // invalid data command; purge buffer.delete(0, stop+1); //System.out.println("PURGE >>> " + removed); } // seek to next command in buffer start = buffer.indexOf("$"); stop = buffer.indexOf("\n"); } } private void evaluatePinForChange(Pin pin, int value) { int bit = (int)Math.pow(2, (pin.getAddress()-1)); if ((value & bit) != (currentStates & bit)) { // change detected for INPUT PIN //System.out.println("<<< CHANGE >>> " + pin.getName()); dispatchPinChangeEvent(pin.getAddress(), ((value & bit) == bit) ? PinState.HIGH : PinState.LOW); } } private void dispatchPinChangeEvent(int pinAddress, PinState state) { // iterate over the pin listeners map for (Pin pin : listeners.keySet()) { //System.out.println("<<< DISPATCH >>> " + pin.getName() + " : " + state.getName()); // dispatch this event to the listener // if a matching pin address is found if (pin.getAddress() == pinAddress) { // dispatch this event to all listener handlers for (PinListener listener : listeners.get(pin)) { listener.handlePinEvent(new PinDigitalStateChangeEvent(this, pin, state)); } } } } } }