001package com.pi4j.io.gpio; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : OdroidGpioProvider.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: https://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2019 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032import com.pi4j.io.gpio.event.PinListener; 033import com.pi4j.io.gpio.exception.InvalidPinModeException; 034import com.pi4j.io.gpio.exception.UnsupportedPinModeException; 035import com.pi4j.jni.AnalogInputEvent; 036import com.pi4j.jni.AnalogInputListener; 037import com.pi4j.platform.Platform; 038import com.pi4j.wiringpi.Gpio; 039import com.pi4j.wiringpi.GpioInterruptEvent; 040import com.pi4j.wiringpi.GpioInterruptListener; 041import com.pi4j.wiringpi.GpioUtil; 042 043/** 044 * Odroid-C1/C1+/XU4 {@link GpioProvider} implementation. 045 * 046 * @author Robert Savage (<a 047 * href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>) 048 */ 049@SuppressWarnings("unused") 050public class OdroidGpioProvider extends WiringPiGpioProviderBase implements GpioProvider, GpioInterruptListener, AnalogInputListener { 051 052 public static final String NAME = "Odroid GPIO Provider"; 053 054 // analog input pin addresses are assigned in a virtual range starting above the 055 // maximum number of physical pins supported by WiringPi 056 public static final int AIN_ADDRESS_OFFSET = Gpio.NUM_PINS + 1; 057 058 public static final int DEFAULT_ANALOG_INPUT_POLLING_RATE = 50; // milliseconds 059 public static final double DEFAULT_ANALOG_INPUT_LISTENER_CHANGE_THRESHOLD = 0.0f; 060 061 protected static int analogInputPollingRate = DEFAULT_ANALOG_INPUT_POLLING_RATE; 062 protected static double analogInputListenerChangeThreshold = DEFAULT_ANALOG_INPUT_LISTENER_CHANGE_THRESHOLD; 063 064 /** 065 * Get the analog input monitor polling rate in milliseconds. 066 * This is the rate at which the internal analog input monitoring thread will poll for analog input value changes 067 * and dispatch analog input value change event for subscribed analog input listeners. 068 * 069 * (The DEFAULT polling rate is 50 milliseconds) 070 * 071 * @return polling rate in milliseconds 072 */ 073 public static int getAnalogInputPollingRate(){ 074 return analogInputPollingRate; 075 } 076 077 /** 078 * Get the analog input listener change value threshold. 079 * This is the threshold delta value that the internal analog input monitoring thread must cross before 080 * dispatching a new analog input value change event. The analog input value must change in excess of this 081 * defined value from the last event dispatched before dispatching a new analog input value change event. 082 * 083 * NOTE: This threshold value is a valuable tool to filter/limit the analog input value change events that 084 * your program may receive. 085 * 086 * (The DEFAULT change threshold value is 0) 087 * 088 * @return change threshold value (delta) 089 */ 090 public static double getAnalogInputListenerChangeThreshold(){ 091 return analogInputListenerChangeThreshold; 092 } 093 094 /** 095 * Set the analog input monitor polling rate in milliseconds. 096 * This is the rate at which the internal analog input monitoring thread will poll for analog input value changes 097 * and dispatch analog input value change event for subscribed analog input listeners. 098 * 099 * NOTE: Be aware that lower polling rates can impact/increase the CPU usage for your application. 100 * 101 * (The DEFAULT polling rate is 50 milliseconds) 102 * 103 * @param milliseconds polling rate in milliseconds; this value must be a positive number 104 * else a default polling rate is used 105 */ 106 public static void setAnalogInputPollingRate(int milliseconds){ 107 if(milliseconds > 0) analogInputPollingRate = milliseconds; 108 } 109 110 /** 111 * Set the analog input listener change value threshold. 112 * This is the threshold delta value that the internal analog input monitoring thread must cross before 113 * dispatching a new analog input value change event. The analog input value must change in excess of this 114 * defined value from the last event dispatched before dispatching a new analog input value change event. 115 * 116 * NOTE: This threshold value is a valuable tool to filter/limit the analog input value change events that 117 * your program may receive. 118 * 119 * (The DEFAULT change threshold value is 0) 120 * 121 * @param threshold change threshold value; this value must be zero or greater. If the threshold value is set 122 * to zero, then any change in value will dispatch a new analog input value event. 123 */ 124 public static void setAnalogInputListenerChangeThreshold(double threshold){ 125 if(threshold > 0) analogInputListenerChangeThreshold = threshold; 126 } 127 128 /** 129 * Default Constructor 130 */ 131 public OdroidGpioProvider() { 132 133 // configure the Pi4J platform to use the "odroid" implementation 134 System.setProperty("pi4j.platform", Platform.ODROID.id()); 135 136 // set wiringPi interface for internal use 137 // we will use the WiringPi pin number scheme with the wiringPi library 138 com.pi4j.wiringpi.Gpio.wiringPiSetup(); 139 } 140 141 @Override 142 public String getName() { 143 return NAME; 144 } 145 146 @Override 147 public void export(Pin pin, PinMode mode, PinState defaultState) { 148 // no need to export an Odroid AIN pin 149 if (mode == PinMode.ANALOG_INPUT) { 150 151 // set the pin input/output mode 152 setMode(pin, mode); 153 154 return; 155 } 156 157 super.export(pin, mode, defaultState); 158 } 159 160 161 @Override 162 public boolean isExported(Pin pin) { 163 // Odroid AIN pins are not exported 164 if (getMode(pin) == PinMode.ANALOG_INPUT) { 165 return false; 166 } 167 168 return super.isExported(pin); 169 } 170 171 @Override 172 public void unexport(Pin pin) { 173 // no need to unexport an Odroid AIN pin 174 if (pin.getSupportedPinModes().contains(PinMode.ANALOG_INPUT)) { 175 return; 176 } 177 178 super.unexport(pin); 179 } 180 181 @Override 182 public void setMode(Pin pin, PinMode mode) { 183 184 // no need to export an Odroid AIN pin 185 if (mode == PinMode.ANALOG_INPUT) { 186 187 if (!pin.getSupportedPinModes().contains(mode)) { 188 throw new InvalidPinModeException(pin, "Invalid pin mode [" + mode.getName() + "]; pin [" + pin.getName() + "] does not support this mode."); 189 } 190 191 // local pin mode cache 192 pinModeCache[pin.getAddress()] = mode; 193 194 // cache mode 195 getPinCache(pin).setMode(mode); 196 197 return; 198 } 199 200 super.setMode(pin, mode); 201 } 202 203 @Override 204 public double getValue(Pin pin) { 205 206 // the getMode() will validate the pin exists with the hasPin() function 207 PinMode mode = getMode(pin); 208 209 // handle analog input reading for Odroid boards 210 if (mode == PinMode.ANALOG_INPUT) { 211 // read latest analog input value from WiringPi 212 // we need to re-address the pin for Odroid boards (analog_address = assigned_pin_address - AIN_ADDRESS_OFFSET) 213 double value = com.pi4j.wiringpi.Gpio.analogRead(pin.getAddress() - AIN_ADDRESS_OFFSET); 214 215 // cache latest analog input value 216 getPinCache(pin).setAnalogValue(value); 217 218 // return latest analog input value 219 return value; 220 } 221 222 return super.getValue(pin); 223 } 224 225 @Override 226 public void pinValueChange(AnalogInputEvent event){ 227 228 // we need to re-address the pin for Odroid boards 229 int analogPinAddress = event.getPin() + AIN_ADDRESS_OFFSET; 230 231 // iterate over the pin listeners map 232 for (Pin pin : listeners.keySet()) { 233 // dispatch this event to the listener 234 // if a matching pin address is found 235 if (pin.getAddress() == analogPinAddress) { 236 dispatchPinAnalogValueChangeEvent(pin, event.getValue()); 237 } 238 } 239 } 240 241 // internal 242 @Override 243 protected void updateInterruptListener(Pin pin) { 244 245 // enable or disable single static listener with the native impl 246 if (listeners.size() > 0) { 247 248 // ------------------------ 249 // DIGITAL INPUT PINS 250 // ------------------------ 251 252 // setup interrupt listener native thread and enable callbacks 253 if (!com.pi4j.wiringpi.GpioInterrupt.hasListener(this)) { 254 com.pi4j.wiringpi.GpioInterrupt.addListener(this); 255 } 256 257 // only configure WiringPi interrupts for digital input pins 258 if(pinModeCache[pin.getAddress()] == PinMode.DIGITAL_INPUT) { 259 260 // enable or disable the individual pin listener 261 if(listeners.containsKey(pin) && listeners.get(pin).size() > 0) { 262 // enable interrupt listener for this pin 263 com.pi4j.wiringpi.GpioInterrupt.enablePinStateChangeCallback(pin.getAddress()); 264 } 265 else { 266 // disable interrupt listener for this pin 267 com.pi4j.wiringpi.GpioInterrupt.disablePinStateChangeCallback(pin.getAddress()); 268 } 269 } 270 271 // ------------------------ 272 // ANALOG INPUT PINS 273 // ------------------------ 274 275 // setup analog input monitor/listener native thread and enable callbacks 276 if (!com.pi4j.jni.AnalogInputMonitor.hasListener(this)) { 277 com.pi4j.jni.AnalogInputMonitor.addListener(this); 278 } 279 280 // configure analog monitor for analog input pins 281 if(pinModeCache[pin.getAddress()] == PinMode.ANALOG_INPUT) { 282 // we need to re-address the pin for Odroid boards (analog_address = assigned_pin_address - AIN_ADDRESS_OFFSET) 283 int analogPinAddress = pin.getAddress() - AIN_ADDRESS_OFFSET; 284 285 // enable or disable the individual pin listener 286 if(listeners.containsKey(pin) && listeners.get(pin).size() > 0) { 287 // enable interrupt listener for this pin 288 com.pi4j.jni.AnalogInputMonitor.enablePinValueChangeCallback(analogPinAddress, 289 analogInputPollingRate, 290 analogInputListenerChangeThreshold); 291 } 292 else { 293 // disable interrupt listener for this pin 294 com.pi4j.jni.AnalogInputMonitor.disablePinValueChangeCallback(analogPinAddress); 295 } 296 } 297 } 298 else { 299 // ------------------------ 300 // DIGITAL INPUT PINS 301 // ------------------------ 302 303 // disable interrupt listener for this pins 304 com.pi4j.wiringpi.GpioInterrupt.disablePinStateChangeCallback(pin.getAddress()); 305 306 // remove interrupt listener, disable native thread and callbacks 307 if (com.pi4j.wiringpi.GpioInterrupt.hasListener(this)) { 308 com.pi4j.wiringpi.GpioInterrupt.removeListener(this); 309 } 310 311 // ------------------------ 312 // ANALOG INPUT PINS 313 // ------------------------ 314 315 // we need to re-address the pin for Odroid boards (analog_address = assigned_pin_address - AIN_ADDRESS_OFFSET) 316 int analogPinAddress = pin.getAddress() - AIN_ADDRESS_OFFSET; 317 318 // disable analog input monitor/listener for this pins 319 com.pi4j.jni.AnalogInputMonitor.disablePinValueChangeCallback(analogPinAddress); 320 321 // remove analog input monitor/listener, disable native thread and callbacks 322 if (com.pi4j.jni.AnalogInputMonitor.hasListener(this)) { 323 com.pi4j.jni.AnalogInputMonitor.removeListener(this); 324 } 325 } 326 } 327}