001package com.pi4j.io.gpio.impl; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : GpioPinImpl.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 - 2021 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.*; 033import com.pi4j.io.gpio.event.GpioPinListener; 034import com.pi4j.io.gpio.event.PinListener; 035import com.pi4j.io.gpio.trigger.GpioTrigger; 036 037import java.util.*; 038import java.util.concurrent.Callable; 039import java.util.concurrent.ConcurrentHashMap; 040import java.util.concurrent.Future; 041import java.util.concurrent.TimeUnit; 042 043public class GpioPinImpl implements GpioPin, 044 GpioPinDigitalInput, 045 GpioPinDigitalOutput, 046 GpioPinDigitalMultipurpose, 047 GpioPinAnalogInput, 048 GpioPinAnalogOutput, 049 GpioPinPwmOutput, 050 GpioPinInput, 051 GpioPinOutput { 052 053 @SuppressWarnings("unused") 054 private String name = null; 055 private Object tag = null; 056 private final GpioProvider provider; 057 private final Pin pin; 058 private PinListener monitor; 059 private final GpioPinShutdownImpl shutdownOptions; 060 private final Map<String, String> properties = new ConcurrentHashMap<>(); 061 private final List<GpioPinListener> listeners = new ArrayList<>(); 062 private final List<GpioTrigger> triggers = new ArrayList<>(); 063 private final Map<PinState, Integer> debounce = new HashMap<>(); 064 protected final int NO_DEBOUCE = 0; 065 066 @SuppressWarnings("unused") 067 public GpioPinImpl(GpioController gpio, GpioProvider provider, Pin pin) { 068 this.provider = provider; 069 this.pin = pin; 070 shutdownOptions = new GpioPinShutdownImpl(); 071 } 072 073 @Override 074 public Pin getPin() { 075 return this.pin; 076 } 077 078 @Override 079 public GpioProvider getProvider() { 080 return this.provider; 081 } 082 083 @Override 084 public void setName(String name) { 085 this.name = name; 086 } 087 088 @Override 089 public String getName() { 090 if (name == null || name.length() == 0) { 091 return pin.toString(); 092 } 093 return name; 094 } 095 096 @Override 097 public void setTag(Object tag) { 098 this.tag = tag; 099 } 100 101 @Override 102 public Object getTag() { 103 return tag; 104 } 105 106 @Override 107 public void setProperty(String key, String value) { 108 properties.put(key, value); 109 } 110 111 @Override 112 public boolean hasProperty(String key) { 113 return properties.containsKey(key); 114 } 115 116 @Override 117 public String getProperty(String key, String defaultValue) { 118 if (properties.containsKey(key)) { 119 if (properties.get(key) == null || properties.get(key).isEmpty()) 120 return defaultValue; 121 else 122 return properties.get(key); 123 } 124 return defaultValue; 125 } 126 127 @Override 128 public String getProperty(String key) { 129 return getProperty(key, null); 130 } 131 132 @Override 133 public Map<String, String> getProperties() { 134 return properties; 135 } 136 137 @Override 138 public void removeProperty(String key) { 139 if (properties.containsKey(key)) { 140 properties.remove(key); 141 } 142 } 143 144 @Override 145 public void clearProperties() { 146 properties.clear(); 147 } 148 149 @Override 150 public void export(PinMode mode) { 151 // export the pin 152 provider.export(pin, mode); 153 } 154 155 @Override 156 public void export(PinMode mode, PinState defaultState) { 157 // export the pin 158 provider.export(pin, mode, defaultState); 159 } 160 161 @Override 162 public void unexport() { 163 // unexport the pin 164 provider.unexport(pin); 165 } 166 167 @Override 168 public boolean isExported() { 169 return provider.isExported(pin); 170 } 171 172 @Override 173 public void setMode(PinMode mode) { 174 provider.setMode(pin, mode); 175 } 176 177 @Override 178 public PinMode getMode() { 179 return provider.getMode(pin); 180 } 181 182 @Override 183 public boolean isMode(PinMode mode) { 184 return (getMode() == mode); 185 } 186 187 @Override 188 public void setPullResistance(PinPullResistance resistance) { 189 provider.setPullResistance(pin, resistance); 190 } 191 192 @Override 193 public PinPullResistance getPullResistance() { 194 return provider.getPullResistance(pin); 195 } 196 197 @Override 198 public boolean isPullResistance(PinPullResistance resistance) { 199 return (getPullResistance() == resistance); 200 } 201 202 @Override 203 public void high() { 204 setState(PinState.HIGH); 205 } 206 207 @Override 208 public void low() { 209 setState(PinState.LOW); 210 } 211 212 @Override 213 public void toggle() { 214 if (getState() == PinState.HIGH) { 215 setState(PinState.LOW); 216 } else { 217 setState(PinState.HIGH); 218 } 219 } 220 221 @Override 222 public Future<?> blink(long delay) { 223 return blink(delay, PinState.HIGH); 224 } 225 226 @Override 227 public Future<?> blink(long delay, TimeUnit timeUnit) { 228 return blink(delay, PinState.HIGH, timeUnit); 229 } 230 231 @Override 232 public Future<?> blink(long delay, PinState blinkState) { 233 // NOTE: a value of 0 milliseconds will stop the blinking 234 return blink(delay, 0, blinkState); 235 } 236 237 @Override 238 public Future<?> blink(long delay, PinState blinkState, TimeUnit timeUnit) { 239 return blink(delay, 0, blinkState, timeUnit); 240 } 241 242 @Override 243 public Future<?> blink(long delay, long duration) { 244 return blink(delay, duration, PinState.HIGH); 245 } 246 247 @Override 248 public Future<?> blink(long delay, long duration, TimeUnit timeUnit) { 249 return blink(delay, duration, PinState.HIGH, timeUnit); 250 } 251 252 @Override 253 public Future<?> blink(long delay, long duration, PinState blinkState) { 254 // NOTE: a value of 0 milliseconds will stop the blinking 255 return GpioScheduledExecutorImpl.blink(this, delay, duration, blinkState, TimeUnit.MILLISECONDS); 256 } 257 258 @Override 259 public Future<?> blink(long delay, long duration, PinState blinkState, TimeUnit timeUnit) { 260 return GpioScheduledExecutorImpl.blink(this, delay, duration, blinkState, timeUnit); 261 } 262 263 @Override 264 public Future<?> pulse(long duration) { 265 return pulse(duration, false); 266 } 267 268 @Override 269 public Future<?> pulse(long duration, TimeUnit timeUnit) { 270 return pulse(duration, false, timeUnit); 271 } 272 273 @Override 274 public Future<?> pulse(long duration, Callable<Void> callback) { 275 return pulse(duration, false, callback); 276 } 277 278 @Override 279 public Future<?> pulse(long duration, Callable<Void> callback, TimeUnit timeUnit) { 280 return pulse(duration, false, callback, timeUnit); 281 } 282 283 @Override 284 public Future<?> pulse(long duration, PinState pulseState) { 285 return pulse(duration, pulseState, false); 286 } 287 288 @Override 289 public Future<?> pulse(long duration, PinState pulseState, TimeUnit timeUnit) { 290 return pulse(duration, pulseState, false, timeUnit); 291 } 292 293 @Override 294 public Future<?> pulse(long duration, PinState pulseState, Callable<Void> callback) { 295 return pulse(duration, pulseState, false, callback); 296 } 297 298 @Override 299 public Future<?> pulse(long duration, PinState pulseState, Callable<Void> callback, TimeUnit timeUnit) { 300 return pulse(duration, pulseState, false, callback, timeUnit); 301 } 302 303 @Override 304 public Future<?> pulse(long duration, boolean blocking) { 305 return pulse(duration, PinState.HIGH, blocking); 306 } 307 308 @Override 309 public Future<?> pulse(long duration, boolean blocking, TimeUnit timeUnit) { 310 return pulse(duration, PinState.HIGH, blocking, timeUnit); 311 } 312 313 @Override 314 public Future<?> pulse(long duration, boolean blocking, Callable<Void> callback) { 315 return pulse(duration, PinState.HIGH, blocking, callback); 316 } 317 318 @Override 319 public Future<?> pulse(long duration, boolean blocking, Callable<Void> callback, TimeUnit timeUnit) { 320 return pulse(duration, PinState.HIGH, blocking, callback, timeUnit); 321 } 322 323 @Override 324 public Future<?> pulse(long duration, PinState pulseState, boolean blocking) { 325 return pulse(duration, pulseState, blocking, null, TimeUnit.MILLISECONDS); 326 } 327 328 @Override 329 public Future<?> pulse(long duration, PinState pulseState, boolean blocking, TimeUnit timeUnit) { 330 return pulse(duration, pulseState, blocking, null, timeUnit); 331 } 332 333 @Override 334 public Future<?> pulse(long duration, PinState pulseState, boolean blocking, Callable<Void> callback, TimeUnit timeUnit) { 335 336 // validate duration argument 337 if (duration <= 0) 338 throw new IllegalArgumentException("Pulse duration must be greater than 0 milliseconds."); 339 340 // if this is a blocking pulse, then execute the pulse 341 // and sleep the caller's thread to block the operation 342 // until the pulse is complete 343 if (blocking) { 344 // start the pulse state 345 setState(pulseState); 346 347 // block the current thread for the pulse duration 348 try { 349 Thread.sleep(duration); 350 } catch (InterruptedException e) { 351 throw new RuntimeException("Pulse blocking thread interrupted.", e); 352 } 353 354 // end the pulse state 355 setState(PinState.getInverseState(pulseState)); 356 357 // invoke callback if one was defined 358 if (callback != null) { 359 try { 360 callback.call(); 361 } catch (Exception e) { 362 e.printStackTrace(); 363 } 364 } 365 366 // we are done; no future is returned for blocking pulses 367 return null; 368 } else { 369 // if this is not a blocking call, then setup the pulse 370 // instruction to be completed in a background worker 371 // thread pool using a scheduled executor 372 return GpioScheduledExecutorImpl.pulse(this, duration, pulseState, callback, timeUnit); 373 } 374 } 375 376 @Override 377 public Future<?> pulse(long duration, PinState pulseState, boolean blocking, Callable<Void> callback) { 378 return pulse(duration, pulseState, blocking, callback, TimeUnit.MILLISECONDS); 379 } 380 381 @Override 382 public void pulseSync(long duration) throws InterruptedException{ 383 pulseSync(duration, PinState.HIGH, TimeUnit.MILLISECONDS); 384 } 385 386 @Override 387 public void pulseSync(long duration, TimeUnit timeUnit) throws InterruptedException{ 388 pulseSync(duration, PinState.HIGH, timeUnit); 389 } 390 391 @Override 392 public void pulseSync(long duration, PinState pulseState) throws InterruptedException{ 393 pulseSync(duration, pulseState, TimeUnit.MILLISECONDS); 394 } 395 396 @Override 397 public void pulseSync(long duration, PinState pulseState, TimeUnit timeUnit) throws InterruptedException{ 398 399 // validate duration argument 400 if (duration <= 0) 401 throw new IllegalArgumentException("Pulse duration must be greater than 0 milliseconds."); 402 403 // start the pulse state 404 setState(pulseState); 405 406 // block the current thread for the pulse duration 407 Thread.sleep(duration); 408 409 // end the pulse state 410 setState(PinState.getInverseState(pulseState)); 411 } 412 413 @Override 414 public void setState(PinState state) { 415 provider.setState(pin, state); 416 } 417 418 @Override 419 public void setState(boolean state) { 420 provider.setState(pin, (state) ? PinState.HIGH : PinState.LOW); 421 } 422 423 @Override 424 public boolean isHigh() { 425 return (getState() == PinState.HIGH); 426 } 427 428 @Override 429 public boolean isLow() { 430 return (getState() == PinState.LOW); 431 } 432 433 @Override 434 public PinState getState() { 435 return provider.getState(pin); 436 } 437 438 @Override 439 public boolean isState(PinState state) { 440 return (getState() == state); 441 } 442 443 @Override 444 public boolean hasDebounce(PinState state) { 445 return (getDebounce(state) > NO_DEBOUCE); 446 } 447 448 @Override 449 public int getDebounce(PinState state) { 450 if (debounce.containsKey(state)) { 451 return debounce.get(state).intValue(); 452 } 453 return NO_DEBOUCE; 454 } 455 456 @Override 457 public void setDebounce(int debounce, PinState... state) { 458 for (PinState ps : state) { 459 this.debounce.put(ps, new Integer(debounce)); 460 } 461 } 462 463 @Override 464 public void setDebounce(int debounce) { 465 setDebounce(debounce, PinState.HIGH, PinState.LOW); 466 } 467 468 @Override 469 public void setValue(double value) { 470 provider.setValue(pin, value); 471 } 472 473 @Override 474 public void setValue(Number value) { 475 setValue(value.doubleValue()); 476 } 477 478 @Override 479 public double getValue() { 480 return provider.getValue(pin); 481 } 482 483 @Override 484 public void setPwm(int value) { 485 provider.setPwm(pin, value); 486 } 487 488 @Override 489 public void setPwmRange(int range) { 490 provider.setPwmRange(pin, range); 491 } 492 493 @Override 494 public int getPwm() { 495 return provider.getPwm(pin); 496 } 497 498 private synchronized void updateInterruptListener() { 499 if (listeners.size() > 0 || triggers.size() > 0) { 500 if (monitor == null) { 501 // create new monitor and register for event callbacks 502 monitor = new GpioEventMonitorExecutorImpl(this); 503 provider.addListener(pin, monitor); 504 } 505 } else { 506 if (monitor != null) { 507 // remove monitor and unregister for event callbacks 508 provider.removeListener(pin, monitor); 509 510 // destroy monitor instance 511 monitor = null; 512 } 513 } 514 } 515 516 /** 517 * @param listener gpio pin listener interface 518 */ 519 public synchronized void addListener(GpioPinListener... listener) { 520 if (listener == null || listener.length == 0) { 521 throw new IllegalArgumentException("Missing listener argument."); 522 } 523 Collections.addAll(listeners, listener); 524 updateInterruptListener(); 525 } 526 527 public synchronized void addListener(List<? extends GpioPinListener> listeners) { 528 for (GpioPinListener listener : listeners) { 529 addListener(listener); 530 } 531 } 532 533 /** 534 * 535 */ 536 public synchronized Collection<GpioPinListener> getListeners() { 537 return listeners; 538 } 539 540 @Override 541 public boolean hasListener(GpioPinListener... listener) { 542 if (listener == null || listener.length == 0) { 543 throw new IllegalArgumentException("Missing listener argument."); 544 } 545 for (GpioPinListener lsnr : listener) { 546 if (!listeners.contains(lsnr)) { 547 return false; 548 } 549 } 550 551 return true; 552 } 553 554 public synchronized void removeListener(GpioPinListener... listener) { 555 if (listener == null || listener.length == 0) { 556 throw new IllegalArgumentException("Missing listener argument."); 557 } 558 for (GpioPinListener lsnr : listener) { 559 listeners.remove(lsnr); 560 } 561 562 updateInterruptListener(); 563 } 564 565 public synchronized void removeListener(List<? extends GpioPinListener> listeners) { 566 for (GpioPinListener listener : listeners) { 567 removeListener(listener); 568 } 569 } 570 571 public synchronized void removeAllListeners() { 572 List<GpioPinListener> listeners_copy = new ArrayList<>(listeners); 573 for (int index = (listeners_copy.size() - 1); index >= 0; index--) { 574 GpioPinListener listener = listeners_copy.get(index); 575 removeListener(listener); 576 } 577 } 578 579 /** 580 * 581 */ 582 public synchronized Collection<GpioTrigger> getTriggers() { 583 return triggers; 584 } 585 586 public synchronized void addTrigger(GpioTrigger... trigger) { 587 if (trigger == null || trigger.length == 0) { 588 throw new IllegalArgumentException("Missing trigger argument."); 589 } 590 Collections.addAll(triggers, trigger); 591 updateInterruptListener(); 592 } 593 594 public synchronized void addTrigger(List<? extends GpioTrigger> triggers) { 595 for (GpioTrigger trigger : triggers) { 596 addTrigger(trigger); 597 } 598 } 599 600 /** 601 * @param trigger GPIO trigger interface 602 */ 603 public synchronized void removeTrigger(GpioTrigger... trigger) { 604 if (trigger == null || trigger.length == 0) { 605 throw new IllegalArgumentException("Missing trigger argument."); 606 } 607 for (GpioTrigger trgr : trigger) { 608 triggers.remove(trgr); 609 } 610 611 updateInterruptListener(); 612 } 613 614 public synchronized void removeTrigger(List<? extends GpioTrigger> triggers) { 615 for (GpioTrigger trigger : triggers) { 616 removeTrigger(trigger); 617 } 618 } 619 620 public synchronized void removeAllTriggers() { 621 List<GpioTrigger> triggers_copy = new ArrayList<>(triggers); 622 for (int index = triggers_copy.size() - 1; index >= 0; index--) { 623 GpioTrigger trigger = triggers_copy.get(index); 624 removeTrigger(trigger); 625 } 626 } 627 628 @Override 629 public String toString() { 630 if (name != null && !name.isEmpty()) { 631 return String.format("\"%s\" <%s>", name, pin.toString()); 632 } else { 633 return pin.toString(); 634 } 635 } 636 637 @Override 638 public GpioPinShutdown getShutdownOptions() { 639 return shutdownOptions; 640 } 641 642 @Override 643 public void setShutdownOptions(GpioPinShutdown options) { 644 shutdownOptions.setUnexport(options.getUnexport()); 645 shutdownOptions.setState(options.getState()); 646 shutdownOptions.setMode(options.getMode()); 647 shutdownOptions.setPullResistor(options.getPullResistor()); 648 } 649 650 @Override 651 public void setShutdownOptions(Boolean unexport) { 652 setShutdownOptions(unexport, null); 653 } 654 655 @Override 656 public void setShutdownOptions(Boolean unexport, PinState state) { 657 setShutdownOptions(unexport, state, null); 658 } 659 660 @Override 661 public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance) { 662 setShutdownOptions(unexport, state, resistance, null); 663 } 664 665 @Override 666 public void setShutdownOptions(Boolean unexport, PinState state, PinPullResistance resistance, PinMode mode) { 667 shutdownOptions.setUnexport(unexport); 668 shutdownOptions.setState(state); 669 shutdownOptions.setMode(mode); 670 shutdownOptions.setPullResistor(resistance); 671 } 672 673}