/ adafruit_clue.py
adafruit_clue.py
1 # SPDX-FileCopyrightText: Copyright (c) 2020 Kattni Rembor for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 # The MIT License (MIT) 5 # 6 # Copyright (c) 2020 Kattni Rembor for Adafruit Industries 7 # 8 # Permission is hereby granted, free of charge, to any person obtaining a copy 9 # of this software and associated documentation files (the "Software"), to deal 10 # in the Software without restriction, including without limitation the rights 11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 # copies of the Software, and to permit persons to whom the Software is 13 # furnished to do so, subject to the following conditions: 14 # 15 # The above copyright notice and this permission notice shall be included in 16 # all copies or substantial portions of the Software. 17 # 18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 # THE SOFTWARE. 25 """ 26 `adafruit_clue` 27 ================================================================================ 28 29 A high level library representing all the features of the Adafruit CLUE. 30 31 32 * Author(s): Kattni Rembor 33 34 Implementation Notes 35 -------------------- 36 37 **Hardware:** 38 39 .. "* `Adafruit CLUE - nRF52840 Express with Bluetooth LE <https://www.adafruit.com/product/4500>`_" 40 41 **Software and Dependencies:** 42 43 * Adafruit CircuitPython firmware for the supported boards: 44 https://github.com/adafruit/circuitpython/releases 45 46 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 47 * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 48 * Adafruit's LSM6DS CircuitPython Library: 49 https://github.com/adafruit/Adafruit_CircuitPython_LSM6DS 50 * Adafruit's LIS3MDL CircuitPython Library: 51 https://github.com/adafruit/Adafruit_CircuitPython_LIS3MDL 52 * Adafruit's APDS9960 CircuitPython Library: 53 https://github.com/adafruit/Adafruit_CircuitPython_APDS9960 54 * Adafruit's BMP280 CircuitPython Library: 55 https://github.com/adafruit/Adafruit_CircuitPython_BMP280 56 * Adafruit's SHT31D CircuitPython Library: 57 https://github.com/adafruit/Adafruit_CircuitPython_SHT31D 58 * Adafruit's NeoPixel CircuitPython Library: 59 https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel 60 """ 61 62 import time 63 import array 64 import math 65 import board 66 import digitalio 67 import neopixel 68 import adafruit_apds9960.apds9960 69 import adafruit_bmp280 70 import adafruit_lis3mdl 71 import adafruit_lsm6ds.lsm6ds33 72 import adafruit_sht31d 73 import audiobusio 74 import audiopwmio 75 import audiocore 76 import gamepad 77 import touchio 78 79 __version__ = "0.0.0-auto.0" 80 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_CLUE.git" 81 82 83 class _ClueSimpleTextDisplay: 84 """Easily display lines of text on CLUE display.""" 85 86 def __init__( # pylint: disable=too-many-arguments 87 self, 88 title=None, 89 title_color=0xFFFFFF, 90 title_scale=1, 91 text_scale=1, 92 font=None, 93 colors=None, 94 ): 95 # pylint: disable=import-outside-toplevel 96 import displayio 97 import terminalio 98 from adafruit_display_text import label 99 100 # pylint: enable=import-outside-toplevel 101 102 if not colors: 103 colors = ( 104 Clue.VIOLET, 105 Clue.GREEN, 106 Clue.RED, 107 Clue.CYAN, 108 Clue.ORANGE, 109 Clue.BLUE, 110 Clue.MAGENTA, 111 Clue.SKY, 112 Clue.YELLOW, 113 Clue.PURPLE, 114 ) 115 116 self._colors = colors 117 self._label = label 118 self._display = board.DISPLAY 119 self._font = terminalio.FONT 120 if font: 121 self._font = font 122 123 self.text_group = displayio.Group(max_size=20, scale=text_scale) 124 125 if title: 126 # Fail gracefully if title is longer than 60 characters. 127 if len(title) > 60: 128 raise ValueError("Title must be 60 characters or less.") 129 130 title = label.Label( 131 self._font, 132 text=title, 133 max_glyphs=60, 134 color=title_color, 135 scale=title_scale, 136 ) 137 title.x = 0 138 title.y = 8 139 self._y = title.y + 18 140 141 self.text_group.append(title) 142 else: 143 self._y = 3 144 145 self._lines = [] 146 for num in range(1): 147 self._lines.append(self.add_text_line(color=colors[num % len(colors)])) 148 149 def __getitem__(self, item): 150 """Fetch the Nth text line Group""" 151 if len(self._lines) - 1 < item: 152 for _ in range(item - (len(self._lines) - 1)): 153 self._lines.append( 154 self.add_text_line(color=self._colors[item % len(self._colors)]) 155 ) 156 return self._lines[item] 157 158 def add_text_line(self, color=0xFFFFFF): 159 """Adds a line on the display of the specified color and returns the label object.""" 160 text_label = self._label.Label(self._font, text="", max_glyphs=45, color=color) 161 text_label.x = 0 162 text_label.y = self._y 163 self._y = text_label.y + 13 164 self.text_group.append(text_label) 165 166 return text_label 167 168 def show(self): 169 """Call show() to display the data list.""" 170 self._display.show(self.text_group) 171 172 def show_terminal(self): 173 """Revert to terminalio screen.""" 174 self._display.show(None) 175 176 177 class Clue: # pylint: disable=too-many-instance-attributes, too-many-public-methods 178 """Represents a single CLUE.""" 179 180 # Color variables available for import. 181 RED = (255, 0, 0) 182 YELLOW = (255, 255, 0) 183 ORANGE = (255, 150, 0) 184 GREEN = (0, 255, 0) 185 TEAL = (0, 255, 120) 186 CYAN = (0, 255, 255) 187 BLUE = (0, 0, 255) 188 PURPLE = (180, 0, 255) 189 MAGENTA = (255, 0, 150) 190 WHITE = (255, 255, 255) 191 BLACK = (0, 0, 0) 192 193 GOLD = (255, 222, 30) 194 PINK = (242, 90, 255) 195 AQUA = (50, 255, 255) 196 JADE = (0, 255, 40) 197 AMBER = (255, 100, 0) 198 VIOLET = (255, 0, 255) 199 SKY = (0, 180, 255) 200 201 RAINBOW = (RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE) 202 203 def __init__(self): 204 # Define I2C: 205 self._i2c = board.I2C() 206 207 # Define touch: 208 # Initially, self._touches stores the pin used for a particular touch. When that touch is 209 # used for the first time, the pin is replaced with the corresponding TouchIn object. 210 # This saves a little RAM over using a separate read-only pin tuple. 211 # For example, after `clue.touch_2`, self._touches is equivalent to: 212 # [board.D0, board.D1, touchio.TouchIn(board.D2)] 213 self._touches = [board.D0, board.D1, board.D2] 214 self._touch_threshold_adjustment = 0 215 216 # Define buttons: 217 self._a = digitalio.DigitalInOut(board.BUTTON_A) 218 self._a.switch_to_input(pull=digitalio.Pull.UP) 219 self._b = digitalio.DigitalInOut(board.BUTTON_B) 220 self._b.switch_to_input(pull=digitalio.Pull.UP) 221 self._gamepad = gamepad.GamePad(self._a, self._b) 222 223 # Define LEDs: 224 self._white_leds = digitalio.DigitalInOut(board.WHITE_LEDS) 225 self._white_leds.switch_to_output() 226 self._pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) 227 self._red_led = digitalio.DigitalInOut(board.L) 228 self._red_led.switch_to_output() 229 230 # Define audio: 231 self._mic = audiobusio.PDMIn( 232 board.MICROPHONE_CLOCK, 233 board.MICROPHONE_DATA, 234 sample_rate=16000, 235 bit_depth=16, 236 ) 237 self._sample = None 238 self._samples = None 239 self._sine_wave = None 240 self._sine_wave_sample = None 241 242 # Define sensors: 243 # Accelerometer/gyroscope: 244 self._accelerometer = adafruit_lsm6ds.lsm6ds33.LSM6DS33(self._i2c) 245 246 # Magnetometer: 247 self._magnetometer = adafruit_lis3mdl.LIS3MDL(self._i2c) 248 249 # DGesture/proximity/color/light sensor: 250 self._sensor = adafruit_apds9960.apds9960.APDS9960(self._i2c) 251 252 # Humidity sensor: 253 self._humidity = adafruit_sht31d.SHT31D(self._i2c) 254 255 # Barometric pressure sensor: 256 self._pressure = adafruit_bmp280.Adafruit_BMP280_I2C(self._i2c) 257 258 # Create displayio object for passing. 259 self.display = board.DISPLAY 260 261 def _touch(self, i): 262 if not isinstance(self._touches[i], touchio.TouchIn): 263 # First time referenced. Get the pin from the slot for this touch 264 # and replace it with a TouchIn object for the pin. 265 self._touches[i] = touchio.TouchIn(self._touches[i]) 266 self._touches[i].threshold += self._touch_threshold_adjustment 267 return self._touches[i].value 268 269 @property 270 def touch_0(self): 271 """Detect touch on capacitive touch pad 0. 272 273 .. image :: ../docs/_static/pad_0.jpg 274 :alt: Pad 0 275 276 This example prints when pad 0 is touched. 277 278 To use with the CLUE: 279 280 .. code-block:: python 281 282 from adafruit_clue import clue 283 284 while True: 285 if clue.touch_0: 286 print("Touched pad 0") 287 """ 288 return self._touch(0) 289 290 @property 291 def touch_1(self): 292 """Detect touch on capacitive touch pad 1. 293 294 .. image :: ../docs/_static/pad_1.jpg 295 :alt: Pad 1 296 297 This example prints when pad 1 is touched. 298 299 To use with the CLUE: 300 301 .. code-block:: python 302 303 from adafruit_clue import clue 304 305 while True: 306 if clue.touch_1: 307 print("Touched pad 1") 308 """ 309 return self._touch(1) 310 311 @property 312 def touch_2(self): 313 """Detect touch on capacitive touch pad 2. 314 315 .. image :: ../docs/_static/pad_2.jpg 316 :alt: Pad 2 317 318 This example prints when pad 2 is touched. 319 320 To use with the CLUE: 321 322 .. code-block:: python 323 324 from adafruit_clue import clue 325 326 while True: 327 if clue.touch_2: 328 print("Touched pad 2") 329 """ 330 return self._touch(2) 331 332 @property 333 def button_a(self): 334 """``True`` when Button A is pressed. ``False`` if not. 335 336 .. image :: ../docs/_static/button_a.jpg 337 :alt: Button A 338 339 This example prints when button A is pressed. 340 341 To use with the CLUE: 342 343 .. code-block:: python 344 345 from adafruit_clue import clue 346 347 while True: 348 if clue.button_a: 349 print("Button A pressed") 350 """ 351 return not self._a.value 352 353 @property 354 def button_b(self): 355 """``True`` when Button B is pressed. ``False`` if not. 356 357 .. image :: ../docs/_static/button_b.jpg 358 :alt: Button B 359 360 This example prints when button B is pressed. 361 362 To use with the CLUE: 363 364 .. code-block:: python 365 366 from adafruit_clue import clue 367 368 while True: 369 if clue.button_b: 370 print("Button B pressed") 371 """ 372 return not self._b.value 373 374 @property 375 def were_pressed(self): 376 """Returns a set of the buttons that have been pressed. 377 378 .. image :: ../docs/_static/button_b.jpg 379 :alt: Button B 380 381 To use with the CLUE: 382 383 .. code-block:: python 384 385 from adafruit_clue import clue 386 387 while True: 388 print(clue.were_pressed) 389 """ 390 ret = set() 391 pressed = self._gamepad.get_pressed() 392 for button, mask in (("A", 0x01), ("B", 0x02)): 393 if mask & pressed: 394 ret.add(button) 395 return ret 396 397 def shake(self, shake_threshold=30, avg_count=10, total_delay=0.1): 398 """ 399 Detect when the accelerometer is shaken. Optional parameters: 400 401 :param shake_threshold: Increase or decrease to change shake sensitivity. This 402 requires a minimum value of 10. 10 is the total 403 acceleration if the board is not moving, therefore 404 anything less than 10 will erroneously report a constant 405 shake detected. (Default 30) 406 407 :param avg_count: The number of readings taken and used for the average 408 acceleration. (Default 10) 409 410 :param total_delay: The total time in seconds it takes to obtain avg_count 411 readings from acceleration. (Default 0.1) 412 """ 413 shake_accel = (0, 0, 0) 414 for _ in range(avg_count): 415 # shake_accel creates a list of tuples from acceleration data. 416 # zip takes multiple tuples and zips them together, as in: 417 # In : zip([-0.2, 0.0, 9.5], [37.9, 13.5, -72.8]) 418 # Out: [(-0.2, 37.9), (0.0, 13.5), (9.5, -72.8)] 419 # map applies sum to each member of this tuple, resulting in a 420 # 3-member list. tuple converts this list into a tuple which is 421 # used as shake_accel. 422 shake_accel = tuple(map(sum, zip(shake_accel, self.acceleration))) 423 time.sleep(total_delay / avg_count) 424 avg = tuple(value / avg_count for value in shake_accel) 425 total_accel = math.sqrt(sum(map(lambda x: x * x, avg))) 426 return total_accel > shake_threshold 427 428 @property 429 def acceleration(self): 430 """Obtain acceleration data from the x, y and z axes. 431 432 .. image :: ../docs/_static/accelerometer.jpg 433 :alt: Accelerometer 434 435 This example prints the values. Try moving the board to see how the printed values change. 436 437 To use with the CLUE: 438 439 .. code-block:: python 440 441 from adafruit_clue import clue 442 443 while True: 444 print("Accel: {:.2f} {:.2f} {:.2f}".format(*clue.acceleration)) 445 """ 446 return self._accelerometer.acceleration 447 448 @property 449 def gyro(self): 450 """Obtain x, y, z angular velocity values in degrees/second. 451 452 .. image :: ../docs/_static/accelerometer.jpg 453 :alt: Gyro 454 455 This example prints the values. Try moving the board to see how the printed values change. 456 457 To use with the CLUE: 458 459 .. code-block:: python 460 461 from adafruit_clue import clue 462 463 while True: 464 print("Gyro: {:.2f} {:.2f} {:.2f}".format(*clue.gyro)) 465 """ 466 return self._accelerometer.gyro 467 468 @property 469 def magnetic(self): 470 """Obtain x, y, z magnetic values in microteslas. 471 472 .. image :: ../docs/_static/magnetometer.jpg 473 :alt: Magnetometer 474 475 This example prints the values. Try moving the board to see how the printed values change. 476 477 To use with the CLUE: 478 479 .. code-block:: python 480 481 from adafruit_clue import clue 482 483 while True: 484 print("Magnetic: {:.3f} {:.3f} {:.3f}".format(*clue.magnetic)) 485 """ 486 return self._magnetometer.magnetic 487 488 @property 489 def proximity(self): 490 """A relative proximity to the sensor in values from 0 - 255. 491 492 .. image :: ../docs/_static/proximity.jpg 493 :alt: Proximity sensor 494 495 This example prints the value. Try moving your hand towards and away from the front of the 496 board to see how the printed values change. 497 498 To use with the CLUE: 499 500 .. code-block:: python 501 502 from adafruit_clue import clue 503 504 while True: 505 print("Proximity: {}".format(clue.proximity)) 506 """ 507 self._sensor.enable_proximity = True 508 return self._sensor.proximity 509 510 @property 511 def color(self): 512 """The red, green, blue, and clear light values. (r, g, b, c) 513 514 .. image :: ../docs/_static/proximity.jpg 515 :alt: Color sensor 516 517 This example prints the values. Try holding something up to the sensor to see the values 518 change. Works best with white LEDs enabled. 519 520 To use with the CLUE: 521 522 .. code-block:: python 523 524 from adafruit_clue import clue 525 526 while True: 527 print("Color: R: {} G: {} B: {} C: {}".format(*clue.color)) 528 """ 529 self._sensor.enable_color = True 530 return self._sensor.color_data 531 532 @property 533 def gesture(self): 534 """A gesture code if gesture is detected. Shows ``0`` if no gesture detected. 535 ``1`` if an UP gesture is detected, ``2`` if DOWN, ``3`` if LEFT, and ``4`` if RIGHT. 536 537 .. image :: ../docs/_static/proximity.jpg 538 :alt: Gesture sensor 539 540 This example prints the gesture values. Try moving your hand up, down, left or right over 541 the sensor to see the value change. 542 543 To use with the CLUE: 544 545 .. code-block:: python 546 547 from adafruit_clue import clue 548 549 while True: 550 print("Gesture: {}".format(clue.gesture)) 551 """ 552 self._sensor.enable_gesture = True 553 return self._sensor.gesture() 554 555 @property 556 def humidity(self): 557 """The measured relative humidity in percent. 558 559 .. image :: ../docs/_static/humidity.jpg 560 :alt: Humidity sensor 561 562 This example prints the value. Try breathing on the sensor to see the values change. 563 564 To use with the CLUE: 565 566 .. code-block:: python 567 568 from adafruit_clue import clue 569 570 while True: 571 print("Humidity: {:.1f}%".format(clue.humidity)) 572 """ 573 return self._humidity.relative_humidity 574 575 @property 576 def pressure(self): 577 """The barometric pressure in hectoPascals. 578 579 .. image :: ../docs/_static/pressure.jpg 580 :alt: Barometric pressure sensor 581 582 This example prints the value. 583 584 To use with the CLUE: 585 586 .. code-block:: python 587 588 from adafruit_clue import clue 589 590 print("Pressure: {:.3f}hPa".format(clue.pressure)) 591 """ 592 return self._pressure.pressure 593 594 @property 595 def temperature(self): 596 """The temperature in degrees Celsius. 597 598 .. image :: ../docs/_static/pressure.jpg 599 :alt: Temperature sensor 600 601 This example prints the value. Try touching the sensor to see the value change. 602 603 To use with the CLUE: 604 605 .. code-block:: python 606 607 from adafruit_clue import clue 608 609 print("Temperature: {:.1f}C".format(clue.temperature)) 610 """ 611 return self._pressure.temperature 612 613 @property 614 def altitude(self): 615 """The altitude in meters based on the sea level pressure at your location. You must set 616 ``sea_level_pressure`` to receive an accurate reading. 617 618 .. image :: ../docs/_static/pressure.jpg 619 :alt: Altitude sensor 620 621 This example prints the value. Try moving the board vertically to see the value change. 622 623 .. code-block:: python 624 625 from adafruit_clue import clue 626 627 clue.sea_level_pressure = 1015 628 629 print("Altitude: {:.1f}m".format(clue.altitude)) 630 """ 631 return self._pressure.altitude 632 633 @property 634 def sea_level_pressure(self): 635 """Set to the pressure at sea level at your location, before reading altitude for 636 the most accurate altitude measurement. 637 638 .. image :: ../docs/_static/pressure.jpg 639 :alt: Barometric pressure sensor 640 641 This example prints the value. 642 643 To use with the CLUE: 644 645 .. code-block:: python 646 647 from adafruit_clue import clue 648 649 clue.sea_level_pressure = 1015 650 651 print("Pressure: {:.3f}hPa".format(clue.pressure)) 652 """ 653 return self._pressure.sea_level_pressure 654 655 @sea_level_pressure.setter 656 def sea_level_pressure(self, value): 657 self._pressure.sea_level_pressure = value 658 659 @property 660 def white_leds(self): 661 """The red led next to the USB plug labeled LED. 662 663 .. image :: ../docs/_static/white_leds.jpg 664 :alt: White LEDs 665 666 This example turns on the white LEDs. 667 668 To use with the CLUE: 669 670 .. code-block:: python 671 672 from adafruit_clue import clue 673 674 clue.white_leds = True 675 """ 676 return self._white_leds.value 677 678 @white_leds.setter 679 def white_leds(self, value): 680 self._white_leds.value = value 681 682 @property 683 def red_led(self): 684 """The red led next to the USB plug labeled LED. 685 686 .. image :: ../docs/_static/red_led.jpg 687 :alt: Red LED 688 689 This example turns on the red LED. 690 691 To use with the CLUE: 692 693 .. code-block:: python 694 695 from adafruit_clue import clue 696 697 clue.red_led = True 698 """ 699 return self._red_led.value 700 701 @red_led.setter 702 def red_led(self, value): 703 self._red_led.value = value 704 705 @property 706 def pixel(self): 707 """The NeoPixel RGB LED. 708 709 .. image :: ../docs/_static/neopixel.jpg 710 :alt: NeoPixel 711 712 This example turns the NeoPixel purple. 713 714 To use with the CLUE: 715 716 .. code-block:: python 717 718 from adafruit_clue import clue 719 720 while True: 721 clue.pixel.fill((255, 0, 255)) 722 """ 723 return self._pixel 724 725 @staticmethod 726 def _sine_sample(length): 727 tone_volume = (2 ** 15) - 1 728 shift = 2 ** 15 729 for i in range(length): 730 yield int(tone_volume * math.sin(2 * math.pi * (i / length)) + shift) 731 732 def _generate_sample(self, length=100): 733 if self._sample is not None: 734 return 735 self._sine_wave = array.array("H", self._sine_sample(length)) 736 self._sample = audiopwmio.PWMAudioOut(board.SPEAKER) 737 self._sine_wave_sample = audiocore.RawSample(self._sine_wave) 738 739 def play_tone(self, frequency, duration): 740 """ Produce a tone using the speaker. Try changing frequency to change 741 the pitch of the tone. 742 743 :param int frequency: The frequency of the tone in Hz 744 :param float duration: The duration of the tone in seconds 745 746 .. image :: ../docs/_static/speaker.jpg 747 :alt: Speaker 748 749 This example plays a 880 Hz tone for a duration of 1 second. 750 751 To use with the CLUE: 752 753 .. code-block:: python 754 755 from adafruit_clue import clue 756 757 clue.play_tone(880, 1) 758 """ 759 # Play a tone of the specified frequency (hz). 760 self.start_tone(frequency) 761 time.sleep(duration) 762 self.stop_tone() 763 764 def start_tone(self, frequency): 765 """ Produce a tone using the speaker. Try changing frequency to change 766 the pitch of the tone. 767 768 :param int frequency: The frequency of the tone in Hz 769 770 .. image :: ../docs/_static/speaker.jpg 771 :alt: Speaker 772 773 This example plays a 523Hz tone when button A is pressed and a 587Hz tone when button B is 774 pressed, only while the buttons are being pressed. 775 776 To use with the CLUE: 777 778 .. code-block:: python 779 780 from adafruit_clue import clue 781 782 while True: 783 if clue.button_a: 784 clue.start_tone(523) 785 elif clue.button_b: 786 clue.start_tone(587) 787 else: 788 clue.stop_tone() 789 """ 790 length = 100 791 if length * frequency > 350000: 792 length = 350000 // frequency 793 self._generate_sample(length) 794 # Start playing a tone of the specified frequency (hz). 795 self._sine_wave_sample.sample_rate = int(len(self._sine_wave) * frequency) 796 if not self._sample.playing: 797 self._sample.play(self._sine_wave_sample, loop=True) 798 799 def stop_tone(self): 800 """ Use with start_tone to stop the tone produced. 801 802 .. image :: ../docs/_static/speaker.jpg 803 :alt: Speaker 804 805 This example plays a 523Hz tone when button A is pressed and a 587Hz tone when button B is 806 pressed, only while the buttons are being pressed. 807 808 To use with the CLUE: 809 810 .. code-block:: python 811 812 from adafruit_clue import clue 813 814 while True: 815 if clue.button_a: 816 clue.start_tone(523) 817 elif clue.button_b: 818 clue.start_tone(587) 819 else: 820 clue.stop_tone() 821 """ 822 # Stop playing any tones. 823 if self._sample is not None and self._sample.playing: 824 self._sample.stop() 825 self._sample.deinit() 826 self._sample = None 827 828 @staticmethod 829 def _normalized_rms(values): 830 mean_values = int(sum(values) / len(values)) 831 return math.sqrt( 832 sum( 833 float(sample - mean_values) * (sample - mean_values) 834 for sample in values 835 ) 836 / len(values) 837 ) 838 839 @property 840 def sound_level(self): 841 """Obtain the sound level from the microphone (sound sensor). 842 843 .. image :: ../docs/_static/microphone.jpg 844 :alt: Microphone (sound sensor) 845 846 This example prints the sound levels. Try clapping or blowing on 847 the microphone to see the levels change. 848 849 .. code-block:: python 850 851 from adafruit_clue import clue 852 853 while True: 854 print(clue.sound_level) 855 """ 856 if self._sample is None: 857 self._samples = array.array("H", [0] * 160) 858 self._mic.record(self._samples, len(self._samples)) 859 return self._normalized_rms(self._samples) 860 861 def loud_sound(self, sound_threshold=200): 862 """Utilise a loud sound as an input. 863 864 :param int sound_threshold: Threshold sound level must exceed to return true (Default: 200) 865 866 .. image :: ../docs/_static/microphone.jpg 867 :alt: Microphone (sound sensor) 868 869 This example turns the NeoPixel LED blue each time you make a loud sound. 870 Try clapping or blowing onto the microphone to trigger it. 871 872 .. code-block:: python 873 874 from adafruit_clue import clue 875 876 while True: 877 if clue.loud_sound(): 878 clue.pixel.fill((0, 50, 0)) 879 else: 880 clue.pixel.fill(0) 881 882 You may find that the code is not responding how you would like. 883 If this is the case, you can change the loud sound threshold to 884 make it more or less responsive. Setting it to a higher number 885 means it will take a louder sound to trigger. Setting it to a 886 lower number will take a quieter sound to trigger. The following 887 example shows the threshold being set to a higher number than 888 the default. 889 890 .. code-block:: python 891 892 from adafruit_clue import clue 893 894 while True: 895 if clue.loud_sound(sound_threshold=300): 896 clue.pixel.fill((0, 50, 0)) 897 else: 898 clue.pixel.fill(0) 899 """ 900 901 return self.sound_level > sound_threshold 902 903 @staticmethod 904 def simple_text_display( # pylint: disable=too-many-arguments 905 title=None, 906 title_color=(255, 255, 255), 907 title_scale=1, 908 text_scale=1, 909 font=None, 910 colors=None, 911 ): 912 """Display lines of text on the CLUE display. Lines of text are created in order as shown 913 in the example below. If you skip a number, the line will be shown blank on the display, 914 e.g. if you include ``[0]`` and ``[2]``, the second line on the display will be empty, and 915 the text specified for lines 0 and 2 will be displayed on the first and third line. 916 Remember, Python begins counting at 0, so the first line on the display is 0 in the code. 917 918 Setup occurs before the loop. For data to be dynamically updated on the display, you must 919 include the data call in the loop by using ``.text =``. For example, if setup is saved as 920 ``clue_data = simple_text_display()`` then ``clue_data[0].text = clue.proximity`` must be 921 inside the ``while True:`` loop for the proximity data displayed to update as the 922 values change. You must call ``show()`` at the end of the list for anything to display. 923 See example below for usage. 924 925 :param str title: The title displayed above the data. Set ``title="Title text"`` to provide 926 a title. Defaults to None. 927 :param title_color: The color of the title. Not necessary if no title is provided. Defaults 928 to white (255, 255, 255). 929 :param int title_scale: Scale the size of the title. Not necessary if no title is provided. 930 Defaults to 1. 931 :param int text_scale: Scale the size of the data lines. Scales the title as well. 932 Defaults to 1. 933 :param str font: The font to use to display the title and data. Defaults to built in 934 ``terminalio.FONT``. 935 :param colors: A list of colors for the lines of data on the display. If you provide a 936 single color, all lines will be that color. Otherwise it will cycle through 937 the list you provide if the list is less than the number of lines displayed. 938 Default colors are used if ``colors`` is not set. For example, if creating 939 two lines of data, ``colors=((255, 255, 255), (255, 0, 0))`` would set the 940 first line white and the second line red, and if you created four lines of 941 data with the same setup, it would alternate white and red. 942 943 .. image :: ../docs/_static/display_clue_data.jpg 944 :alt: Display Clue Data demo 945 946 This example displays three lines with acceleration, gyro and magnetic data on the display. 947 Remember to call ``show()`` after the list to update the display. 948 949 .. code-block:: python 950 951 from adafruit_clue import clue 952 953 clue_data = clue.simple_text_display(title="CLUE Sensor Data!", title_scale=2) 954 955 while True: 956 clue_data[0].text = "Acceleration: {:.2f} {:.2f} {:.2f}".format(*clue.acceleration) 957 clue_data[1].text = "Gyro: {:.2f} {:.2f} {:.2f}".format(*clue.gyro) 958 clue_data[2].text = "Magnetic: {:.3f} {:.3f} {:.3f}".format(*clue.magnetic) 959 clue_data.show() 960 """ 961 return _ClueSimpleTextDisplay( 962 title=title, 963 title_color=title_color, 964 title_scale=title_scale, 965 text_scale=text_scale, 966 font=font, 967 colors=colors, 968 ) 969 970 971 clue = Clue() # pylint: disable=invalid-name 972 """Object that is automatically created on import. 973 974 To use, simply import it from the module: 975 976 .. code-block:: python 977 978 from adafruit_clue import clue 979 """