@@ -572,6 +572,194 @@ def _read(self):
572
572
LightSensor .wait_for_dark = LightSensor .wait_for_inactive
573
573
574
574
575
+ class DistanceSensor (SmoothedInputDevice ):
576
+ """
577
+ Extends :class:`SmoothedInputDevice` and represents an HC-SR04 ultrasonic
578
+ distance sensor, as found in the `CamJam #3 EduKit`_.
579
+
580
+ The distance sensor requires two GPIO pins: one for the *trigger* (marked
581
+ TRIG on the sensor) and another for the *echo* (marked ECHO on the sensor).
582
+ However, a voltage divider is required to ensure the 5V from the ECHO pin
583
+ doesn't damage the Pi. Wire your sensor according to the following
584
+ instructions:
585
+
586
+ 1. Connect the GND pin of the sensor to a ground pin on the Pi.
587
+
588
+ 2. Connect the TRIG pin of the sensor a GPIO pin.
589
+
590
+ 3. Connect a 330Ω resistor from the ECHO pin of the sensor to a different
591
+ GPIO pin.
592
+
593
+ 4. Connect a 470Ω resistor from ground to the ECHO GPIO pin. This forms
594
+ the required voltage divider.
595
+
596
+ 5. Finally, connect the VCC pin of the sensor to a 5V pin on the Pi.
597
+
598
+ The following code will periodically report the distance measured by the
599
+ sensor in cm assuming the TRIG pin is connected to GPIO17, and the ECHO
600
+ pin to GPIO18::
601
+
602
+ from gpiozero import DistanceSensor
603
+ from time import sleep
604
+
605
+ sensor = DistanceSensor(18, 17)
606
+ while True:
607
+ print('Distance: ', sensor.distance * 100)
608
+ sleep(1)
609
+
610
+ :param int echo:
611
+ The GPIO pin which the ECHO pin is attached to. See :doc:`notes` for
612
+ valid pin numbers.
613
+
614
+ :param int trigger:
615
+ The GPIO pin which the TRIG pin is attached to. See :doc:`notes` for
616
+ valid pin numbers.
617
+
618
+ :param int queue_len:
619
+ The length of the queue used to store values read from the sensor.
620
+ This defaults to 30.
621
+
622
+ :param float max_distance:
623
+ The :attr:`value` attribute reports a normalized value between 0 (too
624
+ close to measure) and 1 (maximum distance). This parameter specifies
625
+ the maximum distance expected in meters. This defaults to 1.
626
+
627
+ :param float threshold_distance:
628
+ Defaults to 0.3. This is the distance (in meters) that will trigger the
629
+ ``in_range`` and ``out_of_range`` events when crossed.
630
+
631
+ :param bool partial:
632
+ When ``False`` (the default), the object will not return a value for
633
+ :attr:`~SmoothedInputDevice.is_active` until the internal queue has
634
+ filled with values. Only set this to ``True`` if you require values
635
+ immediately after object construction.
636
+
637
+ .. _CamJam #3 EduKit: http://camjam.me/?page_id=1035
638
+ """
639
+ def __init__ (
640
+ self , echo = None , trigger = None , queue_len = 30 , max_distance = 1 ,
641
+ threshold_distance = 0.3 , partial = False ):
642
+ if not (max_distance > 0 ):
643
+ raise ValueError ('invalid maximum distance (must be positive)' )
644
+ self ._trigger = None
645
+ super (DistanceSensor , self ).__init__ (
646
+ echo , pull_up = False , threshold = threshold_distance / max_distance ,
647
+ queue_len = queue_len , sample_wait = 0.0 , partial = partial
648
+ )
649
+ try :
650
+ self .speed_of_sound = 343.26 # m/s
651
+ self ._max_distance = max_distance
652
+ self ._trigger = GPIODevice (trigger )
653
+ self ._echo = Event ()
654
+ self ._trigger .pin .function = 'output'
655
+ self ._trigger .pin .state = False
656
+ self .pin .edges = 'both'
657
+ self .pin .bounce = None
658
+ self .pin .when_changed = self ._echo .set
659
+ self ._queue .start ()
660
+ except :
661
+ self .close ()
662
+ raise
663
+
664
+ def close (self ):
665
+ try :
666
+ self ._trigger .close ()
667
+ except AttributeError :
668
+ if self ._trigger is not None :
669
+ raise
670
+ else :
671
+ self ._trigger = None
672
+ super (DistanceSensor , self ).close ()
673
+
674
+ @property
675
+ def max_distance (self ):
676
+ """
677
+ The maximum distance that the sensor will measure in meters. This value
678
+ is specified in the constructor and is used to provide the scaling
679
+ for the :attr:`value` attribute. When :attr:`distance` is equal to
680
+ :attr:`max_distance`, :attr:`value` will be 1.
681
+ """
682
+ return self ._max_distance
683
+
684
+ @max_distance .setter
685
+ def max_distance (self , value ):
686
+ if not (value > 0 ):
687
+ raise ValueError ('invalid maximum distance (must be positive)' )
688
+ t = self .threshold_distance
689
+ self ._max_distance = value
690
+ self .threshold_distance = t
691
+
692
+ @property
693
+ def threshold_distance (self ):
694
+ """
695
+ The distance, measured in meters, that will trigger the
696
+ :attr:`when_in_range` and :attr:`when_out_of_range` events when
697
+ crossed. This is simply a meter-scaled variant of the usual
698
+ :attr:`threshold` attribute.
699
+ """
700
+ return self .threshold * self .max_distance
701
+
702
+ @threshold_distance .setter
703
+ def threshold_distance (self , value ):
704
+ self .threshold = value / self .max_distance
705
+
706
+ @property
707
+ def distance (self ):
708
+ """
709
+ Returns the current distance measured by the sensor in meters. Note
710
+ that this property will have a value between 0 and
711
+ :attr:`max_distance`.
712
+ """
713
+ return self .value * self ._max_distance
714
+
715
+ @property
716
+ def trigger (self ):
717
+ """
718
+ Returns the :class:`Pin` that the sensor's trigger is connected to.
719
+ """
720
+ return self ._trigger .pin
721
+
722
+ @property
723
+ def echo (self ):
724
+ """
725
+ Returns the :class:`Pin` that the sensor's echo is connected to. This
726
+ is simply an alias for the usual :attr:`pin` attribute.
727
+ """
728
+ return self .pin
729
+
730
+ def _read (self ):
731
+ # Make sure the echo event is clear
732
+ self ._echo .clear ()
733
+ # Fire the trigger
734
+ self ._trigger .pin .state = True
735
+ sleep (0.00001 )
736
+ self ._trigger .pin .state = False
737
+ # Wait up to 1 second for the echo pin to rise
738
+ if self ._echo .wait (1 ):
739
+ start = time ()
740
+ self ._echo .clear ()
741
+ # Wait up to 40ms for the echo pin to fall (35ms is maximum pulse
742
+ # time so any longer means something's gone wrong). Calculate
743
+ # distance as time for echo multiplied by speed of sound divided by
744
+ # two to compensate for travel to and from the reflector
745
+ if self ._echo .wait (0.04 ):
746
+ distance = (time () - start ) * self .speed_of_sound / 2.0
747
+ return min (1.0 , distance / self ._max_distance )
748
+ else :
749
+ # If we only saw one edge it means we missed the echo because
750
+ # it was too fast; report minimum distance
751
+ return 0.0
752
+ else :
753
+ # The echo pin never rose or fell; something's gone horribly
754
+ # wrong (XXX raise a warning?)
755
+ return 1.0
756
+
757
+ DistanceSensor .when_out_of_range = DistanceSensor .when_activated
758
+ DistanceSensor .when_in_range = DistanceSensor .when_deactivated
759
+ DistanceSensor .wait_for_out_of_range = DistanceSensor .wait_for_active
760
+ DistanceSensor .wait_for_in_range = DistanceSensor .wait_for_inactive
761
+
762
+
575
763
class AnalogInputDevice (CompositeDevice ):
576
764
"""
577
765
Represents an analog input device connected to SPI (serial interface).
0 commit comments