001 002package jmri.jmrix.pi; 003 004import com.pi4j.io.gpio.GpioController; 005import com.pi4j.io.gpio.GpioFactory; 006import com.pi4j.io.gpio.GpioPinDigitalInput; 007import com.pi4j.io.gpio.Pin; 008import com.pi4j.io.gpio.PinPullResistance; 009import com.pi4j.io.gpio.PinState; 010import com.pi4j.io.gpio.RaspiPin; 011import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent; 012import com.pi4j.io.gpio.event.GpioPinListenerDigital; 013 014import jmri.Sensor; 015import jmri.implementation.AbstractSensor; 016import jmri.jmrix.pi.simulator.GpioSimulator; 017 018/** 019 * Sensor interface for RaspberryPi GPIO pins. 020 * 021 * @author Paul Bender Copyright (C) 2003-2017 022 */ 023public class RaspberryPiSensor extends AbstractSensor implements GpioPinListenerDigital { 024 025 private static GpioController gpio = null; 026 private GpioPinDigitalInput pin = null; 027 private PinPullResistance pull = PinPullResistance.PULL_DOWN; 028 029 public RaspberryPiSensor(String systemName, String userName) { 030 super(systemName, userName); 031 // default pull is Pull Down 032 init(systemName, PinPullResistance.PULL_DOWN); 033 } 034 035 public RaspberryPiSensor(String systemName, String userName, PinPullResistance p) { 036 super(systemName, userName); 037 init(systemName, p); 038 } 039 040 public RaspberryPiSensor(String systemName) { 041 super(systemName); 042 init(systemName, PinPullResistance.PULL_DOWN); 043 } 044 045 public RaspberryPiSensor(String systemName, PinPullResistance p) { 046 super(systemName); 047 init(systemName, p); 048 } 049 050 /** 051 * Common initialization for all constructors. 052 * <p> 053 * Compare {@link RaspberryPiTurnout} 054 */ 055 private void init(String systemName, PinPullResistance pRes){ 056 log.debug("Provisioning sensor {}", systemName); 057 if (gpio == null) { 058 if (!RaspberryPiAdapter.isSimulator()) { 059 gpio = GpioFactory.getInstance(); 060 } else { 061 gpio = GpioSimulator.getInstance(); 062 } 063 } 064 pull = pRes; 065 int address = Integer.parseInt(systemName.substring(systemName.lastIndexOf("S") + 1)); 066 String pinName = "GPIO " + address; 067 Pin p = RaspiPin.getPinByName(pinName); 068 if (p != null) { 069 try { 070 pin = gpio.provisionDigitalInputPin(p, getSystemName(), pull); 071 } catch (java.lang.RuntimeException re) { 072 log.error("Provisioning sensor {} failed with: {}", systemName, re.getMessage()); 073 throw new IllegalArgumentException(re.getMessage()); 074 } 075 if (pin != null) { 076 pin.setShutdownOptions(true, PinState.LOW, PinPullResistance.OFF); 077 pin.addListener(this); 078 requestUpdateFromLayout(); // set state to match current value. 079 } else { 080 String msg = Bundle.getMessage("ProvisioningFailed", pinName, getSystemName()); 081 log.error(msg); 082 throw new IllegalArgumentException(msg); 083 } 084 } else { 085 String msg = Bundle.getMessage("PinNameNotValid", pinName, systemName); 086 log.error(msg); 087 throw new IllegalArgumentException(msg); 088 } 089 } 090 091 /** 092 * Request an update on status by sending an Instruction to the Pi. 093 */ 094 @Override 095 public void requestUpdateFromLayout() { 096 setStateBeforeInvert(pin.isHigh()); 097 } 098 099 @Override 100 public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event){ 101 // log pin state change 102 log.debug("GPIO PIN STATE CHANGE: {} = {}", event.getPin(), event.getState()); 103 if (event.getPin() == pin){ 104 setStateBeforeInvert(event.getState().isHigh()); 105 } 106 } 107 108 private void setStateBeforeInvert(boolean high) { 109 if ( high ) { 110 setOwnState(!getInverted() ? Sensor.ACTIVE : Sensor.INACTIVE); 111 } else { 112 setOwnState(!getInverted() ? Sensor.INACTIVE : Sensor.ACTIVE); 113 } 114 } 115 116 /** 117 * Set the pull resistance on the pin. 118 * 119 * @param pr The new PinPullResistance value to set. 120 */ 121 private void setPullState(PinPullResistance pr){ 122 pull = pr; 123 pin.setPullResistance(pull); 124 } 125 126 /** 127 * Set the pull resistance. 128 * <p> 129 * In this default implementation, the input value is ignored. 130 * 131 * @param r PullResistance value to use. 132 */ 133 @Override 134 public void setPullResistance(PullResistance r){ 135 if (r == PullResistance.PULL_DOWN) { 136 setPullState(PinPullResistance.PULL_DOWN); 137 } else if(r == PullResistance.PULL_UP ) { 138 setPullState(PinPullResistance.PULL_UP); 139 } else { 140 setPullState(PinPullResistance.OFF); 141 } 142 } 143 144 /** 145 * Get the pull resistance 146 * 147 * @return the currently set PullResistance value. In this default 148 * implementation, PullResistance.PULL_OFF is always returned. 149 */ 150 @Override 151 public PullResistance getPullResistance(){ 152 if (pull == PinPullResistance.PULL_DOWN) { 153 return PullResistance.PULL_DOWN; 154 } else if(pull == PinPullResistance.PULL_UP) { 155 return PullResistance.PULL_UP; 156 } else { 157 return PullResistance.PULL_OFF; 158 } 159 } 160 161 @Override 162 public void dispose() { 163 try { 164 gpio.unprovisionPin(pin); 165 // will remove all listeners and triggers from pin and remove it from the <GpioPin> pins list in _gpio 166 } catch ( com.pi4j.io.gpio.exception.GpioPinNotProvisionedException npe ){ 167 log.trace("Pin not provisioned, was this sensor already disposed?"); 168 } 169 super.dispose(); 170 } 171 172 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RaspberryPiSensor.class); 173 174}