Compare commits
3 Commits
2c951c7324
...
7327828136
Author | SHA1 | Date | |
---|---|---|---|
|
7327828136 | ||
|
2824211898 | ||
|
d4c3060008 |
252
src/ads1x15.py
Normal file
252
src/ads1x15.py
Normal file
@ -0,0 +1,252 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2016 Radomir Dopieralski (@deshipu),
|
||||
# 2017 Robert Hammelrath (@robert-hh)
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
import utime as time
|
||||
|
||||
_REGISTER_MASK = const(0x03)
|
||||
_REGISTER_CONVERT = const(0x00)
|
||||
_REGISTER_CONFIG = const(0x01)
|
||||
_REGISTER_LOWTHRESH = const(0x02)
|
||||
_REGISTER_HITHRESH = const(0x03)
|
||||
|
||||
_OS_MASK = const(0x8000)
|
||||
_OS_SINGLE = const(0x8000) # Write: Set to start a single-conversion
|
||||
_OS_BUSY = const(0x0000) # Read: Bit=0 when conversion is in progress
|
||||
_OS_NOTBUSY = const(0x8000) # Read: Bit=1 when no conversion is in progress
|
||||
|
||||
_MUX_MASK = const(0x7000)
|
||||
_MUX_DIFF_0_1 = const(0x0000) # Differential P = AIN0, N = AIN1 (default)
|
||||
_MUX_DIFF_0_3 = const(0x1000) # Differential P = AIN0, N = AIN3
|
||||
_MUX_DIFF_1_3 = const(0x2000) # Differential P = AIN1, N = AIN3
|
||||
_MUX_DIFF_2_3 = const(0x3000) # Differential P = AIN2, N = AIN3
|
||||
_MUX_SINGLE_0 = const(0x4000) # Single-ended AIN0
|
||||
_MUX_SINGLE_1 = const(0x5000) # Single-ended AIN1
|
||||
_MUX_SINGLE_2 = const(0x6000) # Single-ended AIN2
|
||||
_MUX_SINGLE_3 = const(0x7000) # Single-ended AIN3
|
||||
|
||||
_PGA_MASK = const(0x0E00)
|
||||
_PGA_6_144V = const(0x0000) # +/-6.144V range = Gain 2/3
|
||||
_PGA_4_096V = const(0x0200) # +/-4.096V range = Gain 1
|
||||
_PGA_2_048V = const(0x0400) # +/-2.048V range = Gain 2 (default)
|
||||
_PGA_1_024V = const(0x0600) # +/-1.024V range = Gain 4
|
||||
_PGA_0_512V = const(0x0800) # +/-0.512V range = Gain 8
|
||||
_PGA_0_256V = const(0x0A00) # +/-0.256V range = Gain 16
|
||||
|
||||
_MODE_MASK = const(0x0100)
|
||||
_MODE_CONTIN = const(0x0000) # Continuous conversion mode
|
||||
_MODE_SINGLE = const(0x0100) # Power-down single-shot mode (default)
|
||||
|
||||
_DR_MASK = const(0x00E0) # Values ADS1015/ADS1115
|
||||
_DR_128SPS = const(0x0000) # 128 /8 samples per second
|
||||
_DR_250SPS = const(0x0020) # 250 /16 samples per second
|
||||
_DR_490SPS = const(0x0040) # 490 /32 samples per second
|
||||
_DR_920SPS = const(0x0060) # 920 /64 samples per second
|
||||
_DR_1600SPS = const(0x0080) # 1600/128 samples per second (default)
|
||||
_DR_2400SPS = const(0x00A0) # 2400/250 samples per second
|
||||
_DR_3300SPS = const(0x00C0) # 3300/475 samples per second
|
||||
_DR_860SPS = const(0x00E0) # - /860 samples per Second
|
||||
|
||||
_CMODE_MASK = const(0x0010)
|
||||
_CMODE_TRAD = const(0x0000) # Traditional comparator with hysteresis (default)
|
||||
_CMODE_WINDOW = const(0x0010) # Window comparator
|
||||
|
||||
_CPOL_MASK = const(0x0008)
|
||||
_CPOL_ACTVLOW = const(0x0000) # ALERT/RDY pin is low when active (default)
|
||||
_CPOL_ACTVHI = const(0x0008) # ALERT/RDY pin is high when active
|
||||
|
||||
_CLAT_MASK = const(0x0004) # Determines if ALERT/RDY pin latches once asserted
|
||||
_CLAT_NONLAT = const(0x0000) # Non-latching comparator (default)
|
||||
_CLAT_LATCH = const(0x0004) # Latching comparator
|
||||
|
||||
_CQUE_MASK = const(0x0003)
|
||||
_CQUE_1CONV = const(0x0000) # Assert ALERT/RDY after one conversions
|
||||
_CQUE_2CONV = const(0x0001) # Assert ALERT/RDY after two conversions
|
||||
_CQUE_4CONV = const(0x0002) # Assert ALERT/RDY after four conversions
|
||||
# Disable the comparator and put ALERT/RDY in high state (default)
|
||||
_CQUE_NONE = const(0x0003)
|
||||
|
||||
_GAINS = (
|
||||
_PGA_6_144V, # 2/3x
|
||||
_PGA_4_096V, # 1x
|
||||
_PGA_2_048V, # 2x
|
||||
_PGA_1_024V, # 4x
|
||||
_PGA_0_512V, # 8x
|
||||
_PGA_0_256V # 16x
|
||||
)
|
||||
|
||||
_GAINS_V = (
|
||||
6.144, # 2/3x
|
||||
4.096, # 1x
|
||||
2.048, # 2x
|
||||
1.024, # 4x
|
||||
0.512, # 8x
|
||||
0.256 # 16x
|
||||
)
|
||||
|
||||
_CHANNELS = {
|
||||
(0, None): _MUX_SINGLE_0,
|
||||
(1, None): _MUX_SINGLE_1,
|
||||
(2, None): _MUX_SINGLE_2,
|
||||
(3, None): _MUX_SINGLE_3,
|
||||
(0, 1): _MUX_DIFF_0_1,
|
||||
(0, 3): _MUX_DIFF_0_3,
|
||||
(1, 3): _MUX_DIFF_1_3,
|
||||
(2, 3): _MUX_DIFF_2_3,
|
||||
}
|
||||
|
||||
_RATES = (
|
||||
_DR_128SPS, # 128/8 samples per second
|
||||
_DR_250SPS, # 250/16 samples per second
|
||||
_DR_490SPS, # 490/32 samples per second
|
||||
_DR_920SPS, # 920/64 samples per second
|
||||
_DR_1600SPS, # 1600/128 samples per second (default)
|
||||
_DR_2400SPS, # 2400/250 samples per second
|
||||
_DR_3300SPS, # 3300/475 samples per second
|
||||
_DR_860SPS # - /860 samples per Second
|
||||
)
|
||||
|
||||
|
||||
class ADS1115:
|
||||
def __init__(self, i2c, address=0x48, gain=1):
|
||||
self.i2c = i2c
|
||||
self.address = address
|
||||
self.gain = gain
|
||||
self.temp2 = bytearray(2)
|
||||
|
||||
def _write_register(self, register, value):
|
||||
self.temp2[0] = value >> 8
|
||||
self.temp2[1] = value & 0xff
|
||||
self.i2c.writeto_mem(self.address, register, self.temp2)
|
||||
|
||||
def _read_register(self, register):
|
||||
self.i2c.readfrom_mem_into(self.address, register, self.temp2)
|
||||
return (self.temp2[0] << 8) | self.temp2[1]
|
||||
|
||||
def raw_to_v(self, raw):
|
||||
v_p_b = _GAINS_V[self.gain] / 32767
|
||||
return raw * v_p_b
|
||||
|
||||
def set_conv(self, rate=4, channel1=0, channel2=None):
|
||||
"""Set mode for read_rev"""
|
||||
self.mode = (_CQUE_NONE | _CLAT_NONLAT |
|
||||
_CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] |
|
||||
_MODE_SINGLE | _OS_SINGLE | _GAINS[self.gain] |
|
||||
_CHANNELS[(channel1, channel2)])
|
||||
|
||||
def read(self, rate=4, channel1=0, channel2=None):
|
||||
"""Read voltage between a channel and GND.
|
||||
Time depends on conversion rate."""
|
||||
self._write_register(_REGISTER_CONFIG, (_CQUE_NONE | _CLAT_NONLAT |
|
||||
_CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] |
|
||||
_MODE_SINGLE | _OS_SINGLE | _GAINS[self.gain] |
|
||||
_CHANNELS[(channel1, channel2)]))
|
||||
while not self._read_register(_REGISTER_CONFIG) & _OS_NOTBUSY:
|
||||
time.sleep_ms(1)
|
||||
res = self._read_register(_REGISTER_CONVERT)
|
||||
return res if res < 32768 else res - 65536
|
||||
|
||||
def read_rev(self):
|
||||
"""Read voltage between a channel and GND. and then start
|
||||
the next conversion."""
|
||||
res = self._read_register(_REGISTER_CONVERT)
|
||||
self._write_register(_REGISTER_CONFIG, self.mode)
|
||||
return res if res < 32768 else res - 65536
|
||||
|
||||
def alert_start(self, rate=4, channel1=0, channel2=None,
|
||||
threshold_high=0x4000, threshold_low=0, latched=False) :
|
||||
"""Start continuous measurement, set ALERT pin on threshold."""
|
||||
self._write_register(_REGISTER_LOWTHRESH, threshold_low)
|
||||
self._write_register(_REGISTER_HITHRESH, threshold_high)
|
||||
self._write_register(_REGISTER_CONFIG, _CQUE_1CONV |
|
||||
_CLAT_LATCH if latched else _CLAT_NONLAT |
|
||||
_CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] |
|
||||
_MODE_CONTIN | _GAINS[self.gain] |
|
||||
_CHANNELS[(channel1, channel2)])
|
||||
|
||||
def conversion_start(self, rate=4, channel1=0, channel2=None):
|
||||
"""Start continuous measurement, trigger on ALERT/RDY pin."""
|
||||
self._write_register(_REGISTER_LOWTHRESH, 0)
|
||||
self._write_register(_REGISTER_HITHRESH, 0x8000)
|
||||
self._write_register(_REGISTER_CONFIG, _CQUE_1CONV | _CLAT_NONLAT |
|
||||
_CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] |
|
||||
_MODE_CONTIN | _GAINS[self.gain] |
|
||||
_CHANNELS[(channel1, channel2)])
|
||||
|
||||
def alert_read(self):
|
||||
"""Get the last reading from the continuous measurement."""
|
||||
res = self._read_register(_REGISTER_CONVERT)
|
||||
return res if res < 32768 else res - 65536
|
||||
|
||||
|
||||
class ADS1113(ADS1115):
|
||||
def __init__(self, i2c, address=0x48):
|
||||
super().__init__(i2c, address, 1)
|
||||
|
||||
def raw_to_v(self, raw):
|
||||
return super().raw_to_v(raw)
|
||||
|
||||
def read(self, rate=4):
|
||||
return super().read(rate, 0, 1)
|
||||
|
||||
def alert_start(self, rate=4, threshold_high=0x4000, threshold_low=0, latched=False):
|
||||
return super().alert_start(rate, 0, 1, threshold_high, threshold_low, latched)
|
||||
|
||||
def alert_read(self):
|
||||
return super().alert_read()
|
||||
|
||||
|
||||
class ADS1114(ADS1115):
|
||||
def __init__(self, i2c, address=0x48, gain=1):
|
||||
super().__init__(i2c, address, gain)
|
||||
|
||||
def raw_to_v(self, raw):
|
||||
return super().raw_to_v(raw)
|
||||
|
||||
def read(self, rate=4):
|
||||
return super().read(rate, 0, 1)
|
||||
|
||||
def alert_start(self, rate=4, threshold_high=0x4000, threshold_low=0, latched=False):
|
||||
return super().alert_start(rate, 0, 1, threshold_high,
|
||||
threshold_low, latched)
|
||||
|
||||
def alert_read(self):
|
||||
return super().alert_read()
|
||||
|
||||
|
||||
class ADS1015(ADS1115):
|
||||
def __init__(self, i2c, address=0x48, gain=1):
|
||||
super().__init__(i2c, address, gain)
|
||||
|
||||
def raw_to_v(self, raw):
|
||||
return super().raw_to_v(raw << 4)
|
||||
|
||||
def read(self, rate=4, channel1=0, channel2=None):
|
||||
return super().read(rate, channel1, channel2) >> 4
|
||||
|
||||
def alert_start(self, rate=4, channel1=0, channel2=None, threshold_high=0x400,
|
||||
threshold_low=0, latched=False):
|
||||
return super().alert_start(rate, channel1, channel2, threshold_high << 4,
|
||||
threshold_low << 4, latched)
|
||||
|
||||
def alert_read(self):
|
||||
return super().alert_read() >> 4
|
32
src/alarm.py
32
src/alarm.py
@ -1,8 +1,9 @@
|
||||
import time
|
||||
from machine import Pin
|
||||
from machine import Pin, Timer
|
||||
import buzzer
|
||||
from display import Display
|
||||
import constants
|
||||
import parameters
|
||||
|
||||
|
||||
SATURATED_MESSAGE = """Filtro saturado."""
|
||||
@ -10,21 +11,28 @@ HOLE_MESSAGE = """Filtro ou linha \nfurado."""
|
||||
|
||||
class Alarm:
|
||||
reset_btnn = Pin(constants.RESET_BUTTON_PIN, mode=Pin.IN)
|
||||
led = Pin(constants.ALARM_LED_PIN, mode=Pin.OUT)
|
||||
interrupt_timer = None
|
||||
|
||||
@staticmethod
|
||||
def trigger_saturated_alarm():
|
||||
Display.print(SATURATED_MESSAGE)
|
||||
buzzer.sing_alarm()
|
||||
while Alarm.reset_button_not_pressed():
|
||||
time.sleep(0.05)
|
||||
Alarm.stop_alarm()
|
||||
Alarm.blink_led_and_wait_for_reset()
|
||||
|
||||
@staticmethod
|
||||
def trigger_hole_alarm():
|
||||
Display.print(HOLE_MESSAGE)
|
||||
Alarm.blink_led_and_wait_for_reset()
|
||||
|
||||
@staticmethod
|
||||
def blink_led_and_wait_for_reset():
|
||||
buzzer.sing_alarm()
|
||||
Alarm.led.on()
|
||||
while Alarm.reset_button_not_pressed():
|
||||
time.sleep(0.05)
|
||||
Alarm.stop_alarm()
|
||||
Alarm.led.off()
|
||||
Alarm.sleep_alarm()
|
||||
|
||||
@staticmethod
|
||||
def stop_alarm():
|
||||
@ -35,3 +43,17 @@ class Alarm:
|
||||
def reset_button_not_pressed():
|
||||
was_not_pressed = Alarm.reset_btnn.value() < 1
|
||||
return was_not_pressed
|
||||
|
||||
@staticmethod
|
||||
def sleep_alarm():
|
||||
interrupt_timer = Timer(
|
||||
period=int(parameters.ALARM_PAUSE_PERIOD * 1e3),
|
||||
mode=Timer.ONE_SHOT,
|
||||
callback=Alarm.__wake_up_alarm
|
||||
)
|
||||
Alarm.interrupt_timer = interrupt_timer
|
||||
|
||||
@staticmethod
|
||||
def __wake_up_alarm(*args, **kargs):
|
||||
Alarm.interrupt_timer.deinit()
|
||||
Alarm.blink_led_and_wait_for_reset()
|
||||
|
@ -1,15 +1,17 @@
|
||||
"""RASPBERRY PICO PINS"""
|
||||
"""PINOUTS"""
|
||||
PRESSURE_SENSOR_PIN = 26
|
||||
HALL_SENSOR_PIN = 27
|
||||
RESET_BUTTON_PIN = 22
|
||||
BUZZER_PIN = 15
|
||||
ALARM_LED_PIN = 26
|
||||
LCD_SDA = 0
|
||||
LCD_SCL = 1
|
||||
PRESSURE_SENSOR_ADC_SDA = 10
|
||||
PRESSURE_SENSOR_ADC_SCL = 11
|
||||
|
||||
|
||||
"""FIXED NUMBERS"""
|
||||
ANNOYTING_BUZZER_TONE = 51250
|
||||
NORMAL_BUZZER_TONE = 200
|
||||
ADC_RESOLUTION = 65535.0
|
||||
|
||||
ADC_RESOLUTION = 32768.0
|
||||
MAX_PRESSURE_SENSOR_VOTLAGE = 6.144
|
||||
|
@ -11,7 +11,7 @@ previous_time = time.ticks_ms()
|
||||
def hall_sensor_interruption(*args, **kargs):
|
||||
global ii
|
||||
ii += 1
|
||||
|
||||
|
||||
|
||||
def start_hall_sensor():
|
||||
global previous_time
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
from machine import ADC, I2C, Pin, PWM
|
||||
import time
|
||||
import hall_sensor
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""Percentage that the previous pressure can be lower than current one without triggering the hole alarm."""
|
||||
PRESSURE_TOLERANCE_PERCENTAGE = 10
|
||||
PRESSURE_TOLERANCE_PERCENTAGE = 1
|
||||
|
||||
"""Pressure saturation threshold in mV to trigger the saturation alarm."""
|
||||
PRESSURE_SATURATION_THRESHOLD = 2.8
|
||||
PRESSURE_SATURATION_THRESHOLD = 1.65
|
||||
|
||||
"""Lower limit of frequency in Hz to be considered inside interest rotation interval. (type: float)"""
|
||||
REFERENCE_ROTATION_DOWN_LIMIT = 250.0
|
||||
@ -13,8 +13,8 @@ REFERENCE_ROTATION_UPPER_LIMIT = 270.0
|
||||
"""Weather or not the buzzer should be set to an annoying tone (in oder to keep the mental healthness of the developer)."""
|
||||
ANNOYING_BUZZER_ENABLED = False
|
||||
|
||||
"""Period in milisseconds between each storage event."""
|
||||
PRESSURE_STORE_PREIOD = int(60*1e3)
|
||||
|
||||
"""How many samples of pressure should be used in smoother."""
|
||||
PRESSURE_SMOOTH_LENGTH = 10
|
||||
PRESSURE_SMOOTH_LENGTH = 25
|
||||
|
||||
"""Period in seconds to wait when alarm reset button is presset until it starts again."""
|
||||
ALARM_PAUSE_PERIOD = 5
|
||||
|
@ -1,15 +1,26 @@
|
||||
from machine import ADC, Pin
|
||||
from machine import I2C, Pin, Timer
|
||||
import ads1x15
|
||||
|
||||
import constants
|
||||
import parameters
|
||||
|
||||
|
||||
pressure_sensor = ADC(Pin(constants.PRESSURE_SENSOR_PIN, mode=Pin.IN))
|
||||
class PressureSensor:
|
||||
|
||||
def get_pressure():
|
||||
global pressure_sensor
|
||||
pressure_value = 0
|
||||
for i in range(parameters.PRESSURE_SMOOTH_LENGTH):
|
||||
pressure_value += pressure_sensor.read_u16()
|
||||
print(pressure_value / parameters.PRESSURE_SMOOTH_LENGTH)
|
||||
pressure_measure = float(pressure_value) / constants.ADC_RESOLUTION * 3.3
|
||||
return pressure_measure
|
||||
_I2C = I2C(1, scl=Pin(constants.PRESSURE_SENSOR_ADC_SCL), sda=Pin(constants.PRESSURE_SENSOR_ADC_SDA), freq=400000)
|
||||
_I2C_ADDR = _I2C.scan()[0]
|
||||
_ADC = ads1x15.ADS1115(_I2C, _I2C_ADDR, 0)
|
||||
_SMOOTH_FACTOR = parameters.PRESSURE_SMOOTH_LENGTH * constants.ADC_RESOLUTION / constants.MAX_PRESSURE_SENSOR_VOTLAGE
|
||||
|
||||
@staticmethod
|
||||
def read():
|
||||
measure = PressureSensor._ADC.read(0)
|
||||
return measure
|
||||
|
||||
@staticmethod
|
||||
def get_pressure():
|
||||
pressure_value = 0
|
||||
for i in range(parameters.PRESSURE_SMOOTH_LENGTH):
|
||||
pressure_value += PressureSensor.read()
|
||||
pressure_measure = float(pressure_value) / PressureSensor._SMOOTH_FACTOR
|
||||
return pressure_measure
|
||||
|
@ -1,33 +1,29 @@
|
||||
import time
|
||||
from display import Display
|
||||
import time
|
||||
import hall_sensor
|
||||
import pressure_sensor
|
||||
from pressure_sensor import PressureSensor
|
||||
import parameters
|
||||
from alarm import Alarm
|
||||
from storage import Storage
|
||||
|
||||
|
||||
class StateMachine:
|
||||
pressure= None
|
||||
|
||||
def __init__(self) -> None:
|
||||
Storage.enable_store_in_next_call()
|
||||
self.__read_previous_pressure()
|
||||
self.__measure_pressure()
|
||||
|
||||
def start_task_loop(self):
|
||||
self.previous_pressure = PressureSensor.get_pressure()
|
||||
time.sleep(0.02)
|
||||
self.wait_for_correct_rotation_frequency()
|
||||
self.handle_pressure()
|
||||
self.__measure_pressure()
|
||||
|
||||
def start_task_loop(self):
|
||||
while True:
|
||||
self.wait_for_correct_rotation_frequency()
|
||||
self.handle_pressure()
|
||||
time.sleep(0.03)
|
||||
|
||||
def wait_for_correct_rotation_frequency(self):
|
||||
while self.__rotation_is_within_limits():
|
||||
print('waiting correct rotation')
|
||||
time.sleep(0.02)
|
||||
continue
|
||||
|
||||
while not self.__rotation_is_within_limits():
|
||||
time.sleep(0.03)
|
||||
|
||||
def __measure_pressure(self):
|
||||
self.current_pressure = pressure_sensor.get_pressure()
|
||||
self.current_pressure = PressureSensor.get_pressure()
|
||||
|
||||
def handle_pressure(self):
|
||||
self.__measure_pressure()
|
||||
@ -41,15 +37,6 @@ class StateMachine:
|
||||
there_is_a_hole = self.__pressure_is_lower_than_before()
|
||||
if there_is_a_hole:
|
||||
Alarm.trigger_hole_alarm()
|
||||
else:
|
||||
self.__save_current_pressure()
|
||||
|
||||
def __save_current_pressure(self):
|
||||
Storage.save_pressure(self.current_pressure)
|
||||
self.previous_pressure = self.current_pressure
|
||||
|
||||
def __read_previous_pressure(self):
|
||||
self.previous_pressure = Storage.retrieve_pressure()
|
||||
|
||||
def __pressure_is_lower_than_before(self) -> bool:
|
||||
pressure_is_lower = (
|
||||
|
Loading…
x
Reference in New Issue
Block a user