001package com.pi4j.io.gpio; 002 003import com.pi4j.io.gpio.event.PinListener; 004import com.pi4j.io.gpio.exception.InvalidPinException; 005import com.pi4j.io.gpio.exception.InvalidPinModeException; 006import com.pi4j.io.gpio.exception.UnsupportedPinModeException; 007import com.pi4j.wiringpi.Gpio; 008import com.pi4j.wiringpi.GpioInterruptEvent; 009import com.pi4j.wiringpi.GpioInterruptListener; 010import com.pi4j.wiringpi.GpioUtil; 011 012/* 013 * #%L 014 * ********************************************************************** 015 * ORGANIZATION : Pi4J 016 * PROJECT : Pi4J :: Java Library (Core) 017 * FILENAME : WiringPiGpioProviderBase.java 018 * 019 * This file is part of the Pi4J project. More information about 020 * this project can be found here: https://www.pi4j.com/ 021 * ********************************************************************** 022 * %% 023 * Copyright (C) 2012 - 2019 Pi4J 024 * %% 025 * This program is free software: you can redistribute it and/or modify 026 * it under the terms of the GNU Lesser General Public License as 027 * published by the Free Software Foundation, either version 3 of the 028 * License, or (at your option) any later version. 029 * 030 * This program is distributed in the hope that it will be useful, 031 * but WITHOUT ANY WARRANTY; without even the implied warranty of 032 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 033 * GNU General Lesser Public License for more details. 034 * 035 * You should have received a copy of the GNU General Lesser Public 036 * License along with this program. If not, see 037 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 038 * #L% 039 */ 040 041/** 042 * BananaPro {@link GpioProvider} implementation. 043 * 044 * @author Robert Savage (<a 045 * href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>) 046 */ 047@SuppressWarnings("unused") 048public abstract class WiringPiGpioProviderBase extends GpioProviderBase implements GpioProvider, GpioInterruptListener { 049 050 // the pin cache should support the maximum number of pins supported by wiringPi plus some 051 // additional overhead for virtual analog input pins used by providers like Odroid C1/C1+/C2/XU4 052 protected static short MAX_PIN_CACHE = Gpio.NUM_PINS + 5; 053 054 // need enough space in array for maximum number of pins. 055 // Currently the Computer module supports the highest number of pins. 056 protected static short pinSupportedCache[] = new short[MAX_PIN_CACHE]; 057 protected static PinMode pinModeCache[] = new PinMode[MAX_PIN_CACHE]; 058 059 public abstract String getName(); 060 061 062 @Override 063 public boolean hasPin(Pin pin) { 064 if(pinSupportedCache[pin.getAddress()] == 1) { 065 return true; 066 } 067 else if(pinSupportedCache[pin.getAddress()] == -1) { 068 return false; 069 } 070 else{ 071 // add pin support to cache 072 if(GpioUtil.isPinSupported(pin.getAddress()) > 0) { 073 pinSupportedCache[pin.getAddress()] = 1; 074 return true; 075 } 076 else{ 077 pinSupportedCache[pin.getAddress()] = -1; 078 return false; 079 } 080 } 081 } 082 083 @Override 084 public void export(Pin pin, PinMode mode) { 085 export(pin, mode, null); 086 } 087 088 @Override 089 public void export(Pin pin, PinMode mode, PinState defaultState) { 090 super.export(pin, mode); 091 092 //System.out.println("-- EXPORTING PIN [" + pin.getAddress() + "] to mode [" + mode.getName() + "]"); 093 094 // get mode configured direction value 095 int direction = mode.getDirection().getValue(); 096 097 // if a default state was provided and the direction is OUT, then override the 098 // pin direction to include initial state value 099 if(defaultState != null && mode.getDirection() == PinDirection.OUT){ 100 if(defaultState == PinState.LOW) 101 direction = GpioUtil.DIRECTION_LOW; 102 else if(defaultState == PinState.HIGH) 103 direction = GpioUtil.DIRECTION_HIGH; 104 } 105 106 // if not already exported, export the pin and set the pin direction 107 if(!GpioUtil.isExported(pin.getAddress())){ 108 GpioUtil.export(pin.getAddress(), direction); 109 } 110 // if the pin is already exported, then check its current configured direction 111 // if the direction does not match, then set the new direction for the pin 112 else if(GpioUtil.getDirection(pin.getAddress()) != mode.getDirection().getValue()){ 113 GpioUtil.setDirection(pin.getAddress(), direction); 114 } 115 116 // set the pin input/output mode 117 setMode(pin, mode); 118 } 119 120 @Override 121 public boolean isExported(Pin pin) { 122 super.isExported(pin); 123 124 // return the pin exported state 125 return GpioUtil.isExported(pin.getAddress()); 126 } 127 128 @Override 129 public void unexport(Pin pin) { 130 super.unexport(pin); 131 132 // unexport the pins 133 GpioUtil.unexport(pin.getAddress()); 134 } 135 136 @Override 137 public void setMode(Pin pin, PinMode mode) { 138 super.setMode(pin, mode); 139 140 // local pin mode cache 141 pinModeCache[pin.getAddress()] = mode; 142 143 if (!pin.getSupportedPinModes().contains(mode)) { 144 throw new InvalidPinModeException(pin, "Invalid pin mode [" + mode.getName() + "]; pin [" + pin.getName() + "] does not support this mode."); 145 } 146 147 if (!pin.getSupportedPinModes().contains(mode)) { 148 throw new UnsupportedPinModeException(pin, mode); 149 } 150 151 // cache mode 152 getPinCache(pin).setMode(mode); 153 154 // set pin mode on hardware 155 com.pi4j.wiringpi.Gpio.pinMode(pin.getAddress(), mode.getValue()); 156 } 157 158 @Override 159 public PinMode getMode(Pin pin) { 160 return pinModeCache[pin.getAddress()]; 161 } 162 163 @Override 164 public void setPullResistance(Pin pin, PinPullResistance resistance) { 165 super.setPullResistance(pin, resistance); 166 167 com.pi4j.wiringpi.Gpio.pullUpDnControl(pin.getAddress(), resistance.getValue()); 168 } 169 170// @Override 171// public PinPullResistance getPullResistance(Pin pin) { 172// // TODO : get actual pin pull resistance from native impl 173// return super.getPullResistance(pin); 174// } 175 176 @Override 177 public void setState(Pin pin, PinState state) { 178 179 // validate pin 180 if (!hasPin(pin)) { 181 throw new InvalidPinException(pin); 182 } 183 184 // only permit invocation on pins set to DIGITAL_OUTPUT modes 185 if (pinModeCache[pin.getAddress()] != PinMode.DIGITAL_OUTPUT) { 186 throw new InvalidPinModeException(pin, "Invalid pin mode on pin [" + pin.getName() + "]; cannot setState() when pin mode is [" + pinModeCache[pin.getAddress()].getName() + "]"); 187 } 188 189 // control GPIO pin 190 com.pi4j.wiringpi.Gpio.digitalWrite(pin.getAddress(), state.getValue()); 191 192 // for digital output pins, we will echo the event feedback 193 dispatchPinDigitalStateChangeEvent(pin, state); 194 195 // for the Raspberry pi, we will not cache pin state since we never use the cache to get state. 196 } 197 198 @Override 199 public PinState getState(Pin pin) { 200 super.getState(pin); 201 202 // return pin state 203 PinState state = null; 204 int ret = com.pi4j.wiringpi.Gpio.digitalRead(pin.getAddress()); 205 if (ret >= 0) { 206 state = PinState.getState(ret); 207 } 208 return state; 209 } 210 211 @Override 212 public void setValue(Pin pin, double value) { 213 super.setValue(pin, value); 214 throw new RuntimeException("This GPIO provider does not support analog pins."); 215 } 216 217 @Override 218 public double getValue(Pin pin) { 219 super.getValue(pin); 220 throw new RuntimeException("This GPIO provider does not support analog pins."); 221 } 222 223 @Override 224 public void setPwm(Pin pin, int value) { 225 // validate pin 226 if (!hasPin(pin)) { 227 throw new InvalidPinException(pin); 228 } 229 230 // get pin configured mode 231 PinMode mode = getMode(pin); 232 233 // validate mode; set PWM value based on pin mode 234 if (mode == PinMode.PWM_OUTPUT) { 235 // set pin hardware PWM value 236 com.pi4j.wiringpi.Gpio.pwmWrite(pin.getAddress(), value); 237 } 238 else if(mode == PinMode.SOFT_PWM_OUTPUT) { 239 // set pin software emulated PWM value 240 com.pi4j.wiringpi.SoftPwm.softPwmWrite(pin.getAddress(), value); 241 } 242 else { 243 // unsupported pin mode 244 throw new InvalidPinModeException(pin, "Invalid pin mode [" + mode.getName() + "]; unable to setPwm(" + value + ")"); 245 } 246 247 // cache updated pin PWM value 248 getPinCache(pin).setPwmValue(value); 249 } 250 251 @Override 252 public void setPwmRange(Pin pin, int range){ 253 // validate pin 254 if (!hasPin(pin)) { 255 throw new InvalidPinException(pin); 256 } 257 258 // get pin configured mode 259 PinMode mode = getMode(pin); 260 261 // validate mode; set PWM value based on pin mode 262 if (mode == PinMode.PWM_OUTPUT) { 263 // set pin hardware PWM value 264 com.pi4j.wiringpi.Gpio.pwmSetRange(range); 265 } 266 else if(mode == PinMode.SOFT_PWM_OUTPUT) { 267 // first, stop the software emulated PWM driver for this pin 268 com.pi4j.wiringpi.SoftPwm.softPwmStop(pin.getAddress()); 269 // update the PWM range for this pin 270 com.pi4j.wiringpi.SoftPwm.softPwmCreate(pin.getAddress(), 0, range); 271 } 272 else { 273 // unsupported operation 274 throw new UnsupportedOperationException(); 275 } 276 } 277 278 @Override 279 public int getPwm(Pin pin) { 280 return super.getPwm(pin); 281 } 282 283 @Override 284 public void pinStateChange(GpioInterruptEvent event) { 285 // iterate over the pin listeners map 286 for (Pin pin : listeners.keySet()) { 287 // dispatch this event to the listener 288 // if a matching pin address is found 289 if (pin.getAddress() == event.getPin()) { 290 dispatchPinDigitalStateChangeEvent(pin, PinState.getState(event.getState())); 291 } 292 } 293 } 294 295 @Override 296 public void addListener(Pin pin, PinListener listener) { 297 super.addListener(pin, listener); 298 299 // update the native interrupt listener thread for callbacks 300 updateInterruptListener(pin); 301 } 302 303 @Override 304 public void removeListener(Pin pin, PinListener listener) { 305 super.removeListener(pin, listener); 306 307 // update the native interrupt listener thread for callbacks 308 updateInterruptListener(pin); 309 } 310 311 // internal 312 protected void updateInterruptListener(Pin pin) { 313 // enable or disable single static listener with the native impl 314 if (listeners.size() > 0) { 315 // setup interrupt listener native thread and enable callbacks 316 if (!com.pi4j.wiringpi.GpioInterrupt.hasListener(this)) { 317 com.pi4j.wiringpi.GpioInterrupt.addListener(this); 318 } 319 320 // only configure WiringPi interrupts for digital input pins 321 if(pinModeCache[pin.getAddress()] == PinMode.DIGITAL_INPUT) { 322 // enable or disable the individual pin listener 323 if(listeners.containsKey(pin) && listeners.get(pin).size() > 0) { 324 // enable interrupt listener for this pin 325 com.pi4j.wiringpi.GpioInterrupt.enablePinStateChangeCallback(pin.getAddress()); 326 } 327 else { 328 // disable interrupt listener for this pin 329 com.pi4j.wiringpi.GpioInterrupt.disablePinStateChangeCallback(pin.getAddress()); 330 } 331 } 332 } 333 else { 334 // disable interrupt listener for this pins 335 com.pi4j.wiringpi.GpioInterrupt.disablePinStateChangeCallback(pin.getAddress()); 336 337 // remove interrupt listener, disable native thread and callbacks 338 if (com.pi4j.wiringpi.GpioInterrupt.hasListener(this)) { 339 com.pi4j.wiringpi.GpioInterrupt.removeListener(this); 340 } 341 } 342 } 343}