diff --git a/biffobear_as3935.py b/biffobear_as3935.py index c57cc37..b3f63c4 100755 --- a/biffobear_as3935.py +++ b/biffobear_as3935.py @@ -98,13 +98,13 @@ def _value_is_in_range(value, *, lo_limit, hi_limit): assert lo_limit <= value <= hi_limit except AssertionError as error: raise ValueError( - "Value must be in the range %s to %s inclusive." % (lo_limit, hi_limit) + "Value must be in the range %s to %s, inclusive." % (lo_limit, hi_limit) ) from error return value class AS3935_Sensor: - """Driver for the Franklin AS3935 lightning detector chip.""" + """Register handling for the Franklin AS3935 PSI and I2C drivers.""" # Constants to make register values human readable in the code DATA_PURGE = _0X00 # 0x00 - Distance recalculated after purging old data @@ -199,8 +199,8 @@ def indoor(self, value): @property def watchdog(self): - """int: Watchdog thresholdin the range 0 - 10 (default is 2). Higher thresholds reduce - triggers by disturbers but decrease sensitivity to lightning strikes. + """int: Watchdog threshold in the range 0 - 10 (default is 2). Higher thresholds reduce + triggers from disturbers but decrease sensitivity to lightning strikes. """ return self._get_register(self._WDTH) @@ -265,7 +265,7 @@ def interrupt_status(self): """int: Status of the interrupt register. These constants are defined as helpers: LIGHTNING, DISTURBER. NOISE, DATA_PURGE. - note:: This register is automatically cleared by the sensor after it is read. + Note: This register is automatically cleared by the sensor after it is read. """ # Wait a minimum of 2 ms between the interrupt pin going high and reading the register time.sleep(0.0002) @@ -330,7 +330,7 @@ def power_down(self, value): self._set_register(self._PWD, 0x00) # RCO clocks need to be calibrated when powering back up from a power_down # Procedure as per AS3935 datasheet - self._calibrate_clocks() + self.calibrate_clocks() self._check_clock_calibration() self._set_register(self._DISP_FLAGS, 0x02) time.sleep(0.002) @@ -399,9 +399,9 @@ def output_trco(self, value): @property def tuning_capacitance(self): """int: The tuning capacitance for the RLC antenna in pF. This capacitance - is added to the antenna to tune it within 3.5 % of 500 kHz (483 - 517 kHz). + is added to the antenna to tune it to within 3.5 % of 500 kHz (483 - 517 kHz). - Capacitance must be in the range 0 - 120. Any of these values may be set, + Capacitance must be in the range 0 - 120. Any of these values may be used, however, the capacitance is set in steps of 8 pF, so values less than 120 will be rounded down to the nearest step. Default is 0. """ @@ -432,8 +432,9 @@ def _check_clock_calibration(self): if _0X01 in [trco_result, srco_result]: raise RuntimeError("AS3935 RCO clock calibration failed.") - def _calibrate_clocks(self): - """Recalibrate the internal clocks.""" + def calibrate_clocks(self): + """Recalibrate the internal clocks. The clocks rely on the tuning frequency of + the antenna, so adjust that to 500 KHz +/- 3.5 % before calibrating.""" # Send the direct command to the CALIB_RCO register to start automatic RCO calibration # then check that the calibration has succeeded self._set_register(self._CALIB_RCO, self.DIRECT_COMMAND) @@ -466,7 +467,7 @@ def _startup_checks(self): # checking the clock calibration status tells the that the clocks are OK and if # the calibration times out, we know that there are no comms with the sensor self.reset() - self._calibrate_clocks() + self.calibrate_clocks() self._check_clock_calibration() diff --git a/tests/test_biffobear_as3935.py b/tests/test_biffobear_as3935.py index e6abec7..11fbea0 100755 --- a/tests/test_biffobear_as3935.py +++ b/tests/test_biffobear_as3935.py @@ -403,7 +403,7 @@ def test_power_down_setter_false_and_power_is_down( # If turning on power after power_down was previously set, clocks must be calibrated. get_reg.return_value = 0x01 mock_calibrate_clocks = mocker.patch.object( - as3935.AS3935_Sensor, "_calibrate_clocks", autospec=True + as3935.AS3935_Sensor, "calibrate_clocks", autospec=True ) mock_check_clock_calibration = mocker.patch.object( as3935.AS3935_Sensor, "_check_clock_calibration", autospec=True @@ -430,7 +430,7 @@ def test_power_down_setter_false_and_power_is_up(mocker, set_reg, get_reg, test_ # If turning on power with power already on then do nothing. get_reg.return_value = 0x00 mock_calibrate_clocks = mocker.patch.object( - as3935.AS3935_Sensor, "_calibrate_clocks", autospec=True + as3935.AS3935_Sensor, "calibrate_clocks", autospec=True ) test_device.power_down = False get_reg.assert_called_once_with(test_device, as3935.AS3935_Sensor._PWD) @@ -565,7 +565,7 @@ def test_calibrate_clocks_calls_correct_register_then_checks_calibration( mock_check_clock_calibration = mocker.patch.object( as3935.AS3935_Sensor, "_check_clock_calibration" ) - test_device._calibrate_clocks() + test_device.calibrate_clocks() set_reg.assert_called_once_with(test_device, as3935.AS3935_Sensor._CALIB_RCO, 0x96) mock_check_clock_calibration.assert_called_once() @@ -640,7 +640,7 @@ def test_startup_checks(mocker): as3935.AS3935_Sensor, "reset", autospec=True, return_value=None ) mock_calibrate_clocks = mocker.patch.object( - as3935.AS3935_Sensor, "_calibrate_clocks", autospec=True, return_value=None + as3935.AS3935_Sensor, "calibrate_clocks", autospec=True, return_value=None ) mock_check_clock_calibration = mocker.patch.object( as3935.AS3935_Sensor, diff --git a/tests/test_board_responses_spi.py b/tests/test_board_responses.py similarity index 87% rename from tests/test_board_responses_spi.py rename to tests/test_board_responses.py index ca8d410..3e7d548 100755 --- a/tests/test_board_responses_spi.py +++ b/tests/test_board_responses.py @@ -31,13 +31,13 @@ # All tests skipped unless environment variable SENSOR_ATTACHED is set to any value. try: - sensor_attached = int(os.environ["SENSOR_ATTACHED"]) + sensor_attached = os.environ["SENSOR_ATTACHED"] except (KeyError, AttributeError): sensor_attached = False try: pytestmark = pytest.mark.skipif( - sensor_attached is False, reason="No as3935 board connected." + sensor_attached not in ["SPI", "I2C"], reason="No as3935 board connected." ) except NameError: pass @@ -48,18 +48,26 @@ def setup_module(): # Returns an instance of the AS3935 driver - print("Setting up SPI connection...") global device - spi = board.SPI() - try: - cs = board.D24 - interrupt = board.D25 - except AttributeError: - cs = board.D5 - interrupt = board.D7 - - device = as3935.AS3935_SPI(spi, cs, interrupt_pin=interrupt) - device.reset() + if sensor_attached == "SPI": + print("Setting up SPI connection...") + spi = board.SPI() + try: + cs = board.D24 + interrupt = board.D25 + except AttributeError: + cs = board.D5 + interrupt = board.D7 + device = as3935.AS3935(spi, cs, interrupt_pin=interrupt) + + else: + print("Setting up I2C connection...") + i2c = board.I2C() + try: + interrupt = board.D25 + except AttributeError: + interrupt = board.D7 + device = as3935.AS3935_I2C(i2c, interrupt_pin=interrupt) def teardown_module(): @@ -165,7 +173,7 @@ def test_reset(): def test_commands_which_do_not_change_readable_values(): # Call to see if an exception is raised device.clear_stats() - device._calibrate_clocks() + device.calibrate_clocks() def test_registers_with_unpredictable_states(): @@ -184,6 +192,7 @@ def test_read_interrupt_pin(): print("setup...") setup_module() + device.reset() print("test_indoor_outdoor...") test_indoor_outdoor() print("power_down...") diff --git a/tests/test_board_responses_i2c.py b/tests/test_board_responses_i2c.py deleted file mode 100755 index d778ad1..0000000 --- a/tests/test_board_responses_i2c.py +++ /dev/null @@ -1,220 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021 Martin Stephens -# -# SPDX-License-Identifier: MIT -"""These tests are run with a sensor connected to confirm that the correct -responses are received from the sensor. - -The try - except clauses and an if __name__ == "__main__" allow the code to be -run with pytest on a Raspberry Pi or as a stand alone file copied into main.py -on a CircuitPython board. To run on a board also copy 'biffobear_as3935.py' to -the lib folder. -""" - -# Many Pylnt conventions are broken for the sake of test readability -# Others fail because Pylint doesn't understand Pytest. -# Therefore skip this file. -# pylint: skip-file - -import os -import time - -try: - import pytest -except ImportError: - pass -import board - -try: - from CircuitPython_AS3935 import biffobear_as3935 as as3935 -except ImportError: - import biffobear_as3935 as as3935 - -# All tests skipped unless environment variable SENSOR_ATTACHED is set to any value. -try: - sensor_attached = int(os.environ["SENSOR_ATTACHED"]) -except (KeyError, AttributeError): - sensor_attached = False - -try: - pytestmark = pytest.mark.skipif( - sensor_attached is False, reason="No as3935 board connected." - ) -except NameError: - pass - - -device = None - - -def setup_module(): - # Returns an instance of the AS3935 driver - print("Setting up I2C connection...") - global device - i2c = board.I2C() - try: - interrupt = board.D25 - except AttributeError: - interrupt = board.D7 - - device = as3935.AS3935_I2C(i2c, interrupt_pin=interrupt) - device.reset() - - -def teardown_module(): - # Reset the chip between runs for consistent test results - device.reset() - - -def test_indoor_outdoor(): - # Out = not indoor so no seperate test required. - assert device.indoor is True # Chip default - device.indoor = False - assert device.indoor is False - - -def test_power_down(): - assert device.power_down is False # Chip default - device.power_down = True - assert device.power_down is True - device.power_down = False - assert device.power_down is False - - -def test_noise_floor_level(): - assert device.noise_floor_limit == 0x02 # Chip default - # Test possible values - for level in range(8): - device.noise_floor_limit = level - assert device.noise_floor_limit == level - - -def test_watchdog(): - assert device.watchdog == 0x02 # Chip default - # Test possible values - for level in range(11): - device.watchdog = level - assert device.watchdog == level - - -def test_spike_rejection(): - assert device.spike_threshold == 0x02 # Chip default - # Test possible values - for level in range(12): - device.spike_threshold = level - assert device.spike_threshold == level - - -def test_disturber_mask(): - assert device.disturber_mask is False # Chip default - device.disturber_mask = True - assert device.disturber_mask is True - - -def test_strike_count_threshold(): - assert device.strike_count_threshold == 1 - # Test possible values - for level in (1, 5, 9, 16): - device.strike_count_threshold = level - assert device.strike_count_threshold == level - - -def test_freq_divisor(): - assert device.freq_divisor == 16 # Chip default - # Test possible values - for divisor in (16, 32, 64, 128): - device.freq_divisor = divisor - assert device.freq_divisor == divisor - - -def test_output_antenna_freq(): - assert device.output_antenna_freq is False - device.output_antenna_freq = True - assert device.output_antenna_freq is True - - -def test_output_srco(): - assert device.output_srco is False # Chip default - device.output_srco = True - assert device.output_srco is True - - -def test_output_trco(): - assert device.output_trco is False # Chip default - device.output_trco = True - assert device.output_trco is True - - -def test_tuning_capacitance(): - assert device.tuning_capacitance == 0 # Chip default - # Test possible values - for capacitance in range(0, 128, 8): - device.tuning_capacitance = capacitance - assert device.tuning_capacitance == capacitance - - -def test_reset(): - # Set a none default value - device.freq_divisor = 32 - assert device.freq_divisor == 32 - device.reset() - # Confirm that is reset to default - assert device.freq_divisor == 16 # Chip default - - -def test_commands_which_do_not_change_readable_values(): - # Call to see if an exception is raised - device.clear_stats() - device._calibrate_clocks() - - -def test_registers_with_unpredictable_states(): - # Just read them to see if an error occurs since value depends on presence of lightning. - device.energy - device.distance - device.interrupt_status - - -def test_read_interrupt_pin(): - # The state of the pin is unknown, so just read it error free. - device.interrupt_set - - -if __name__ == "__main__": - - print("setup...") - setup_module() - print("test_indoor_outdoor...") - test_indoor_outdoor() - print("power_down...") - test_power_down() - print("noise_floor_level...") - test_noise_floor_level() - print("watchdog...") - test_watchdog() - print("spike_rejection...") - test_spike_rejection() - print("strike_count_threshold...") - test_strike_count_threshold() - print("disturber_mask...") - test_disturber_mask() - print("freq_divisor...") - test_freq_divisor() - print("output_antenna_freq...") - test_output_antenna_freq() - print("output_srco...") - test_output_srco() - print("output_trco...") - test_output_trco() - print("tuning_capacitance...") - test_tuning_capacitance() - print("reset...") - test_reset() - print("commands_which_do_not_change_readable_values...") - test_commands_which_do_not_change_readable_values() - print("registers_with_unpredictable_states...") - test_registers_with_unpredictable_states() - print("Interrupt pin...") - test_read_interrupt_pin() - print("teardown...") - teardown_module() - print("Tests complete.")