#-------------------------------------------------------------------------------
# qwiic_buzzer.py
#
# Python library for the SparkFun Qwiic Buzzer, available here:
# https://www.sparkfun.com/products/24474
#-------------------------------------------------------------------------------
# Written by SparkFun Electronics, January 2024
#
# This python library supports the SparkFun Electroncis Qwiic ecosystem
#
# More information on Qwiic is at https://www.sparkfun.com/qwiic
#
# Do you like this library? Help support SparkFun. Buy a board!
#===============================================================================
# Copyright (c) 2024 SparkFun Electronics
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#===============================================================================
"""
qwiic_buzzer
============
Python module for the `SparkFun Qwiic Buzzer <https://www.sparkfun.com/products/24474>`_
This is a port of the existing `Arduino Library <https://github.com/sparkfun/SparkFun_Qwiic_Buzzer_Arduino_Library>`_
This package can be used with the overall `SparkFun Qwiic Python Package <https://github.com/sparkfun/Qwiic_Py>`_
New to Qwiic? Take a look at the entire `SparkFun Qwiic ecosystem <https://www.sparkfun.com/qwiic>`_
"""
#==================================================================================
# The Qwiic_I2C_Py platform driver is designed to work on almost any Python
# platform, check it out here: https://github.com/sparkfun/Qwiic_I2C_Py
import qwiic_i2c
import time
# Define the device name and I2C addresses. These are set in the class defintion
# as class variables, making them avilable without having to create a class
# instance. This allows higher level logic to rapidly create a index of Qwiic
# devices at runtine
_DEFAULT_NAME = "Qwiic Buzzer"
# Some devices have multiple available addresses - this is a list of these
# addresses. NOTE: The first address in this list is considered the default I2C
# address for the device.
_DEFAULT_ADDRESS = 0x34
_FULL_ADDRESS_LIST = list(range(0x08, 0x77+1)) # Full address list (excluding reserved addresses)
_FULL_ADDRESS_LIST.remove(_DEFAULT_ADDRESS) # Remove default address from list
_AVAILABLE_I2C_ADDRESS = [_DEFAULT_ADDRESS] # Initialize with default address
_AVAILABLE_I2C_ADDRESS.extend(_FULL_ADDRESS_LIST) # Add full range of I2C addresses
# Define the class that encapsulates the device being created. All information
# associated with this device is encapsulated by this class. The device class
# should be the only value exported from this module.
[docs]class QwiicBuzzer(object):
"""
SparkFun Qwiic Buzzer
Initialise the Qwiic Buzzer at ``address`` with ``i2c_driver``.
:param address: The I2C address to use for the device.
If not provided, the default address is
used.
:param i2c_driver: An existing i2c driver object. If not
provided a driver object is created.
:return: Constructor Initialization
True- Successful
False- Issue loading I2C driver
:rtype: Bool
"""
# Set default name and I2C address(es)
device_name = _DEFAULT_NAME
available_addresses = _AVAILABLE_I2C_ADDRESS
DEFAULT_ADDRESS = _DEFAULT_ADDRESS
_ID = 0x5E
# Register addresses for the Qwiic Buzzer
_REG_ADR_ID = 0x00
_REG_ADR_FW_MIN = 0x01
_REG_ADR_FW_MAJ = 0x02
_REG_ADR_FREQ_MSB = 0x03
_REG_ADR_FREQ_LSB = 0x04
_REG_ADR_VOL = 0x05
_REG_ADR_DUR_MSB = 0x06
_REG_ADR_DUR_LSB = 0x07
_REG_ADR_ACTIVE = 0x08
_REG_ADR_SAVE = 0x09
_REG_ADR_I2C_ADD = 0x0A
RESONANT_FREQUENCY = 2730
VOLUME_MIN = 1
VOLUME_LOW = 2
VOLUME_MID = 3
VOLUME_MAX = 4
NOTE_REST = 0
NOTE_B0 = 31
NOTE_C1 = 33
NOTE_CS1 = 35
NOTE_D1 = 37
NOTE_DS1 = 39
NOTE_E1 = 41
NOTE_F1 = 44
NOTE_FS1 = 46
NOTE_G1 = 49
NOTE_GS1 = 52
NOTE_A1 = 55
NOTE_AS1 = 58
NOTE_B1 = 62
NOTE_C2 = 65
NOTE_CS2 = 69
NOTE_D2 = 73
NOTE_DS2 = 78
NOTE_E2 = 82
NOTE_F2 = 87
NOTE_FS2 = 93
NOTE_G2 = 98
NOTE_GS2 = 104
NOTE_A2 = 110
NOTE_AS2 = 117
NOTE_B2 = 123
NOTE_C3 = 131
NOTE_CS3 = 139
NOTE_D3 = 147
NOTE_DS3 = 156
NOTE_E3 = 165
NOTE_F3 = 175
NOTE_FS3 = 185
NOTE_G3 = 196
NOTE_GS3 = 208
NOTE_A3 = 220
NOTE_AS3 = 233
NOTE_B3 = 247
NOTE_C4 = 262
NOTE_CS4 = 277
NOTE_D4 = 294
NOTE_DS4 = 311
NOTE_E4 = 330
NOTE_F4 = 349
NOTE_FS4 = 370
NOTE_G4 = 392
NOTE_GS4 = 415
NOTE_A4 = 440
NOTE_AS4 = 466
NOTE_B4 = 494
NOTE_C5 = 523
NOTE_CS5 = 554
NOTE_D5 = 587
NOTE_DS5 = 622
NOTE_E5 = 659
NOTE_F5 = 698
NOTE_FS5 = 740
NOTE_G5 = 784
NOTE_GS5 = 831
NOTE_A5 = 880
NOTE_AS5 = 932
NOTE_B5 = 988
NOTE_C6 = 1047
NOTE_CS6 = 1109
NOTE_D6 = 1175
NOTE_DS6 = 1245
NOTE_E6 = 1319
NOTE_F6 = 1397
NOTE_FS6 = 1480
NOTE_G6 = 1568
NOTE_GS6 = 1661
NOTE_A6 = 1760
NOTE_AS6 = 1865
NOTE_B6 = 1976
NOTE_C7 = 2093
NOTE_CS7 = 2217
NOTE_D7 = 2349
NOTE_DS7 = 2489
NOTE_E7 = 2637
NOTE_F7 = 2794
NOTE_FS7 = 2960
NOTE_G7 = 3136
NOTE_GS7 = 3322
NOTE_A7 = 3520
NOTE_AS7 = 3729
NOTE_B7 = 3951
NOTE_C8 = 4186
NOTE_CS8 = 4435
NOTE_D8 = 4699
NOTE_DS8 = 4978
def __init__(self, address=None, i2c_driver=None):
"""
Constructor
:param address: The I2C address to use for the device
If not provided, the default address is used
:type address: int, optional
:param i2c_driver: An existing i2c driver object
If not provided, a driver object is created
:type i2c_driver: I2CDriver, optional
"""
# Use address if provided, otherwise pick the default
if address in self.available_addresses:
self.address = address
else:
self.address = self.available_addresses[0]
# Load the I2C driver if one isn't provided
if i2c_driver is None:
self._i2c = qwiic_i2c.getI2CDriver()
if self._i2c is None:
print("Unable to load I2C driver for this platform.")
return
else:
self._i2c = i2c_driver
[docs] def is_connected(self):
"""
Determines if this device is connected
:return: `True` if connected, otherwise `False`
:rtype: bool
"""
# Check if connected by seeing if an ACK is received
return self._i2c.isDeviceConnected(self.address)
connected = property(is_connected)
[docs] def begin(self):
"""
Initializes this device with default parameters
Run is_connected() and check the ID in the ID register
:return: Returns `True` if successful, otherwise `False`
:rtype: bool
"""
# Confirm device is connected before doing anything
if self.is_connected():
id = self._i2c.readByte(self.address, self._REG_ADR_ID)
if id == self._ID:
return True
return False
[docs] def on(self):
"""
Turns on the buzzer
:return: Returns true if the register write has completed
:rtype: bool
"""
self._i2c.writeByte(self.address, self._REG_ADR_ACTIVE, 1)
[docs] def off(self):
"""
Turns off the buzzer
:return: Returns true if the register write has completed
:rtype: bool
"""
self._i2c.writeByte(self.address, self._REG_ADR_ACTIVE, 0)
[docs] def save_settings(self):
"""
Stores settings to EEPROM
:return: Returns true if the register write has completed
:rtype: bool
"""
self._i2c.writeByte(self.address, self._REG_ADR_SAVE, 1)
[docs] def change_address(self, address):
"""
Changes the I2C address of the Qwiic Buzzer
:param address: New address, must be in the range 0x08 to 0x77
:type address: int
:return: Returns `True` if successful, otherwise `False`
:rtype: bool
"""
# Check whether the address is valid
if address < 0x08 or address > 0x77:
return False
# Write the new address to the device
self._i2c.writeByte(self.address, self._REG_ADR_I2C_ADD, address)
# Update the address of this object
self.address = address
[docs] def get_address(self):
"""
Gets the current I2C address of the Qwiic Buzzer
:return: The current I2C address, 7-bit unshifted
:rtype: int
"""
return self.address
[docs] def firware_version_major(self):
"""
Reads the Firmware Version Major from the Qwiic Buzzer
:return: Firmware Version Major
:rtype: int
"""
return self._i2c.readByte(self.address, self._REG_ADR_FW_MAJ)
[docs] def firware_version_minor(self):
"""
Reads the Firmware Version Minor from the Qwiic Buzzer
:return: Firmware Version Minor
:rtype: int
"""
return self._i2c.readByte(self.address, self._REG_ADR_FW_MIN)
[docs] def play_sound_effect(self, sound_effect_number = 0, volume = VOLUME_MAX):
"""
Plays the desired sound effect at a specified volume
:param sound_effect_number: Which sound effect you'd like to play (0-9)
:type sound_effect_number: int
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
if (sound_effect_number == 0):
self.sound_effect_0(volume)
elif (sound_effect_number == 1):
self.sound_effect_1(volume)
elif (sound_effect_number == 2):
self.sound_effect_2(volume)
elif (sound_effect_number == 3):
self.sound_effect_3(volume)
elif (sound_effect_number == 4):
self.sound_effect_4(volume)
elif (sound_effect_number == 5):
self.sound_effect_5(volume)
elif (sound_effect_number == 6):
self.sound_effect_6(volume)
elif (sound_effect_number == 7):
self.sound_effect_7(volume)
elif (sound_effect_number == 8):
self.sound_effect_8(volume)
elif (sound_effect_number == 9):
self.sound_effect_9(volume)
[docs] def sound_effect_0(self, volume):
"""
Plays sound effect 0 (aka "Siren")
Intended to sound like a siren, starting at a low frequency, and then
increasing rapidly up and then back down. This sound effect does a
single "up and down" cycle.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
for note in range (150, 4000, 150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
for note in range (4000, 150, -150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
[docs] def sound_effect_1(self, volume):
"""
Plays sound effect 1 (aka "3 Fast Sirens")
Intended to sound like a siren, starting at a low frequency, and then
increasing rapidly up and then back down. This sound effect does this
cycle of "up and down" three times rapidly.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
for i in range(3):
for note in range (150, 4000, 150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.002)
for note in range (4000, 150, -150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.002)
self.off()
[docs] def sound_effect_2(self, volume):
"""
Plays sound effect 2 (aka "robot saying 'Yes'")
Intended to sound like a robot saying the word "yes".
It starts at a low frequency and quickly ramps up to a high frequency,
then stops. This can be interpreted by most to be an affirmative
sound to any question you may ask your buzzing robot.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
for note in range (150, 4000, 150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.04)
self.off()
[docs] def sound_effect_3(self, volume):
"""
Plays sound effect 3 (aka "robot yelling 'YES!'" - faster)
Intended to sound like a robot saying the word "yes".
It starts at a low frequency and quickly ramps up to a high frequency,
then stops. This can be interpreted by most to be an affirmative
sound to any question you may ask your buzzing robot. As this sound
is done more quickly, it can add enthusiasm to the buzzing sound.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
for note in range (150, 4000, 150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
[docs] def sound_effect_4(self, volume):
"""
Plays sound effect 4 (aka "robot saying 'No'")
Intended to sound like a robot saying the word "no".
It starts at a high frequency and quickly ramps down to a low frequency,
then stops. This can be interpreted by most to be an negative
sound to any question you may ask your buzzing robot.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
for note in range (4000, 150, -150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.04)
self.off()
[docs] def sound_effect_5(self, volume):
"""
Plays sound effect 5 (aka "robot yelling 'NO!'" - faster)
Intended to sound like a robot saying the word "no".
It starts at a high frequency and quickly ramps down to a low frequency,
then stops. This can be interpreted by most to be an negative
sound to any question you may ask your buzzing robot. As this sound
is done more quickly, it can add enthusiasm to the buzzing sound.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
for note in range (4000, 150, -150):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
[docs] def sound_effect_6(self, volume):
"""
Plays sound effect 6 (aka "Laughing Robot")
Intended to sound like your robot is laughing at you.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
laugh_delay = 0.4
laugh_step = 10
for note in range (1538, 1905, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(laugh_delay)
for note in range (1250, 1515, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(laugh_delay)
for note in range (1111, 1342, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(laugh_delay)
for note in range (1010, 1176, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
[docs] def sound_effect_7(self, volume):
"""
Plays sound effect 7 (aka "Laughing Robot Faster")
Intended to sound like your robot is laughing at you. As this sound
is done more quickly, it can add enthusiasm to the buzzing sound.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
laugh_delay = 0.2
laugh_step = 15
for note in range (1538, 1905, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(laugh_delay)
for note in range (1250, 1515, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(laugh_delay)
for note in range (1111, 1342, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(laugh_delay)
for note in range (1010, 1176, laugh_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
[docs] def sound_effect_8(self, volume):
"""
Plays sound effect 8 (aka "Crying Robot")
Intended to sound like a robot is crying and sad.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
cry_delay = 0.5
cry_step = -10
for note in range (2000, 1429, cry_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(cry_delay)
for note in range (1667, 1250, cry_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(cry_delay)
for note in range (1429, 1053, cry_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
[docs] def sound_effect_9(self, volume):
"""
Plays sound effect 9 (aka "Crying Robot Faster")
Intended to sound like a robot is crying and sad. As this sound
is done more quickly, it can add enthusiasm to the buzzing sound.
:param volume: The volume of the sound effect played (1-4)
:type volume: int
"""
cry_delay = 0.2
cry_step = -20
for note in range (2000, 1429, cry_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(cry_delay)
for note in range (1667, 1250, cry_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()
time.sleep(cry_delay)
for note in range (1429, 1053, cry_step):
self.configure(note, 0, volume)
self.on()
time.sleep(0.01)
self.off()