UART & Sensors
This commit is contained in:
+101
-24
@@ -1,7 +1,12 @@
|
||||
import json
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
from shared import get_lora, get_mqtt_client, deviceTypes, config
|
||||
import traceback
|
||||
from orchestrateur.sensors import gps
|
||||
from shared import get_lora, get_mqtt_client, deviceTypes, config, payloads
|
||||
from shared.logging import log
|
||||
from sensors import ultrasonicRanger, temp_hum, button, camera
|
||||
|
||||
# --- Read Unique Device ID ---
|
||||
try:
|
||||
@@ -49,6 +54,7 @@ mqtt_client = get_mqtt_client(
|
||||
)
|
||||
mqtt_client.connect()
|
||||
mqtt_client.subscribe(config.MQTT_TOPIC_SENSOR, qos=config.MQTT_QOS)
|
||||
mqtt_client.subscribe(config.MQTT_TOPIC_HELLO, qos=config.MQTT_QOS)
|
||||
print(f"Subscribed to topic: {config.MQTT_TOPIC_SENSOR}")
|
||||
|
||||
# --- THE CRUCIAL PAHO FIX ---
|
||||
@@ -66,51 +72,122 @@ def mqtt_listener():
|
||||
message = mqtt_client.get_message()
|
||||
|
||||
if message:
|
||||
# Try to parse the payload as a python dictionary, but if it fails, just print the raw payload
|
||||
try:
|
||||
payload = json.loads(message['payload'])
|
||||
except Exception as e:
|
||||
print(f"Error parsing MQTT payload: {e}")
|
||||
payload = message['payload'] # Fallback to raw payload if parsing fails
|
||||
|
||||
print(f"\n[Thread MQTT] Message reçu : {message}")
|
||||
data_queue.put({"source": "MQTT", "data": message})
|
||||
data_queue.put({"source": "MQTT", "topic": message['topic'] ,"data": payload})
|
||||
|
||||
# --- THE CPU FIX ---
|
||||
# Sleep for 100ms. Prevents the thread from turning into an infinite 100% CPU hog.
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.2)
|
||||
|
||||
# Button
|
||||
button_state = False
|
||||
def button_callback():
|
||||
global button_state
|
||||
button_state = not button_state
|
||||
print(f"\n[Thread Button] Button state changed to: {button_state}")
|
||||
|
||||
button.set_callback(button_callback)
|
||||
|
||||
# Launch background monitoring workers
|
||||
threading.Thread(target=lora_listener, daemon=True).start()
|
||||
threading.Thread(target=mqtt_listener, daemon=True).start()
|
||||
# threading.Thread(target=lora_listener, daemon=True).start()
|
||||
# threading.Thread(target=mqtt_listener, daemon=True).start()
|
||||
# Launch button monitoring thread
|
||||
button.start_button_monitoring_thread()
|
||||
|
||||
print("Orchestrateur prêt. Le main loop est libre.")
|
||||
|
||||
# Sensor reading
|
||||
def read_sensors():
|
||||
"""Read all sensors and return a dictionary of their values."""
|
||||
log("\nLecture des capteurs...")
|
||||
sensor_data = {}
|
||||
|
||||
# Read Ultrasonic Ranger
|
||||
distance = ultrasonicRanger.get_dish_height()
|
||||
if distance is not None:
|
||||
log(f"\nLecture du capteur Ultrason : {distance}")
|
||||
sensor_data["ultrasonic_distance"] = distance
|
||||
|
||||
# Read Temperature and Humidity
|
||||
temperature, humidity = temp_hum.get_temperature_and_humidity()
|
||||
if temperature is not None and humidity is not None:
|
||||
log(f"\nLecture du capteur Temp/Hum : {temperature}, {humidity}")
|
||||
sensor_data["temperature"] = temperature
|
||||
sensor_data["humidity"] = humidity
|
||||
|
||||
|
||||
# Read GPS Data
|
||||
gps_data = gps.get_gps_data()
|
||||
if gps_data:
|
||||
log(f"\nLecture du capteur GPS : {gps_data}")
|
||||
sensor_data["gps"] = gps_data
|
||||
|
||||
# Camera
|
||||
picture_bytes = None
|
||||
try:
|
||||
picture_bytes = camera.get_picture()
|
||||
log(f"\nLecture du capteur Caméra : {len(picture_bytes)} bytes")
|
||||
sensor_data["camera_image"] = picture_bytes
|
||||
except Exception as e:
|
||||
log(f"Error reading camera data: {e}")
|
||||
|
||||
# Read Button State (last because he can still change state while reading other sensors)
|
||||
sensor_data["button_state"] = button_state
|
||||
|
||||
return sensor_data
|
||||
|
||||
# --- MAIN EXECUTION LOOP ---
|
||||
while True:
|
||||
try:
|
||||
# Check for non-heartbeat data safely
|
||||
# Check for non-heartbeat data
|
||||
try:
|
||||
msg = data_queue.get(block=False)
|
||||
print(f"\n[Main Loop] Données traitées : {msg['data']}")
|
||||
|
||||
# print(msg)
|
||||
|
||||
if msg["source"] == "LoRa":
|
||||
print(f"\n[Main Loop] LoRa : Données traitées : {msg['data']}")
|
||||
elif msg["source"] == "MQTT":
|
||||
if (msg["topic"] == config.MQTT_TOPIC_HELLO.decode('utf-8')):
|
||||
if ("id_orchestrator" in msg["data"] and msg["data"]["id_orchestrator"] == DEVICE_ID):
|
||||
# Do not answer to messages coming from me
|
||||
continue
|
||||
microwave_id = msg["data"]["id_microwave"]
|
||||
print(f"\n[Main Loop] MQTT : Hello reçu de {microwave_id}.")
|
||||
# Responds
|
||||
mqtt_client.publish(config.MQTT_TOPIC_HELLO, payloads.mqtt_hello_ack(DEVICE_ID, microwave_id), qos=config.MQTT_QOS)
|
||||
print(f"[Main Loop] MQTT : Réponse Hello envoyée à {microwave_id}.")
|
||||
# TODO : Save in database
|
||||
|
||||
|
||||
print(f"\n[Main Loop] MQTT : Données traitées : {msg['data']}")
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
# DEBUG : Read sensors
|
||||
sensor_values = read_sensors()
|
||||
if sensor_values:
|
||||
sensor_values_print = sensor_values.copy()
|
||||
if "camera_image" in sensor_values_print:
|
||||
sensor_values_print["camera_image"] = f"<{len(sensor_values_print['camera_image'])} bytes>"
|
||||
print(f"\nCapteurs Données lues : {sensor_values_print}")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Publish debug telemetry message
|
||||
print("[Main Loop] Envoi d'un message de debug sur MQTT...")
|
||||
response = mqtt_client.publish(
|
||||
config.MQTT_TOPIC_COOKING,
|
||||
f"Orchestrateur actif, ID: {DEVICE_ID}",
|
||||
qos=config.MQTT_QOS
|
||||
)
|
||||
|
||||
# This will now unblock instantly because loop_start() handles the delivery confirmation!
|
||||
response.wait_for_publish()
|
||||
print("[Main Loop] Message de debug publié avec succès.")
|
||||
|
||||
time.sleep(9)
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
time.sleep(1) # Prevents rapid error logging in case of persistent issues
|
||||
|
||||
# Clean termination
|
||||
if hasattr(mqtt_client._client, "loop_stop"):
|
||||
mqtt_client._client.loop_stop()
|
||||
mqtt_client.close()
|
||||
mqtt_client.close()
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
paho-mqtt>=1.6,<3
|
||||
pyserial>=3.5,<4
|
||||
pyserial>=3.5,<4
|
||||
# picamera2>=0.3.36,<4 # → Installed with apt install python3-picamera2
|
||||
# OpenCV
|
||||
# sudo apt install -y python3-opencv
|
||||
# sudo apt install -y opencv-data
|
||||
@@ -0,0 +1,7 @@
|
||||
# import grovepi
|
||||
|
||||
import sensors.ultrasonicRanger as ultrasonicRanger
|
||||
import sensors.temp_hum as temp_hum
|
||||
import sensors.button as button
|
||||
import sensors.gps as gps
|
||||
import sensors.camera as camera
|
||||
@@ -0,0 +1,44 @@
|
||||
import grovepi
|
||||
import time
|
||||
import threading
|
||||
from sensors.lock import grove_lock
|
||||
from shared.logging import log
|
||||
|
||||
button = 2
|
||||
button_switch_state = 0
|
||||
grovepi.pinMode(button, "INPUT")
|
||||
|
||||
button_callback = None
|
||||
|
||||
def read_button_state():
|
||||
if not grove_lock.acquire(timeout=0.05):
|
||||
return None
|
||||
try:
|
||||
return grovepi.digitalRead(button)
|
||||
except Exception as e:
|
||||
log(f"BTN Error: {e}")
|
||||
return None
|
||||
finally:
|
||||
grove_lock.release()
|
||||
|
||||
def monitor_button():
|
||||
global button_switch_state
|
||||
last_button_state = button_switch_state
|
||||
|
||||
while True:
|
||||
time.sleep(0.04)
|
||||
|
||||
current_state = read_button_state()
|
||||
|
||||
if current_state is not None:
|
||||
if current_state == 1 and last_button_state == 0:
|
||||
if button_callback:
|
||||
button_callback()
|
||||
last_button_state = current_state
|
||||
|
||||
def start_button_monitoring_thread():
|
||||
threading.Thread(target=monitor_button, daemon=True).start()
|
||||
|
||||
def set_callback(callback):
|
||||
global button_callback
|
||||
button_callback = callback
|
||||
@@ -0,0 +1,33 @@
|
||||
import grovepi
|
||||
import math
|
||||
from sensors.lock import grove_lock
|
||||
from picamera2 import Picamera2, Preview
|
||||
import time
|
||||
|
||||
picam2 = Picamera2()
|
||||
|
||||
camera_config = picam2.create_still_configuration()
|
||||
picam2.configure(camera_config)
|
||||
|
||||
picam2.start()
|
||||
time.sleep(2)
|
||||
|
||||
def preview_camera():
|
||||
picam2.start_preview(Preview.DRM)
|
||||
|
||||
def stop_preview_camera():
|
||||
picam2.stop_preview()
|
||||
|
||||
def take_picture():
|
||||
"""Takes a picture and saves it to the file system"""
|
||||
picam2.capture_file("test.jpg")
|
||||
return "test.jpg"
|
||||
|
||||
def get_picture():
|
||||
"""Returns the image bytes as base64
|
||||
"""
|
||||
file_path = take_picture()
|
||||
with open(file_path, "rb") as f:
|
||||
image_bytes = f.read()
|
||||
return image_bytes
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import serial
|
||||
import time
|
||||
import threading
|
||||
from shared.logging import log
|
||||
from sensors.lock import serial_lock
|
||||
|
||||
def calculate_nmea_checksum(line: str) -> bool:
|
||||
"""Validates standard NMEA 0183 sentence checksum ($...*HH)."""
|
||||
if not line.startswith('$') or '*' not in line:
|
||||
return False
|
||||
|
||||
try:
|
||||
content, checksum_str = line[1:].split('*', 1)
|
||||
calculated_checksum = 0
|
||||
for char in content:
|
||||
calculated_checksum ^= ord(char)
|
||||
|
||||
return calculated_checksum == int(checksum_str[:2], 16)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
class GROVEGPS:
|
||||
def __init__(self, port='/dev/ttyAMA0', baud=9600, timeout=1):
|
||||
self.ser = serial.Serial(port, baud, timeout=timeout)
|
||||
self.clean_data()
|
||||
|
||||
def clean_data(self):
|
||||
self.timestamp = ""
|
||||
self.quality = 0
|
||||
self.satellites = 0
|
||||
self.altitude = -1.0
|
||||
self.latitude = -1.0
|
||||
self.longitude = -1.0
|
||||
|
||||
def read(self):
|
||||
"""Reads the latest GGA sentence from serial, thread-safely."""
|
||||
with serial_lock:
|
||||
# 1. Flush accumulated stale data in the UART buffer
|
||||
if self.ser.in_waiting > 0:
|
||||
self.ser.reset_input_buffer()
|
||||
|
||||
# 2. Try reading up to 15 lines to catch the freshest GGA sentence
|
||||
for _ in range(5):
|
||||
raw_bytes = self.ser.readline()
|
||||
try:
|
||||
line = raw_bytes.decode('utf-8', errors='ignore').strip()
|
||||
# log(f"GPS: Read line: {line}")
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Supports both $GPGGA and modern $GNGGA sentences
|
||||
if (line.startswith('$GPGGA') or line.startswith('$GNGGA')) and calculate_nmea_checksum(line):
|
||||
if self.parse_gga(line):
|
||||
return True
|
||||
return False
|
||||
|
||||
def parse_gga(self, line):
|
||||
self.clean_data()
|
||||
gga = line.split(',')
|
||||
|
||||
if len(gga) < 10:
|
||||
return False
|
||||
|
||||
try:
|
||||
self.timestamp = gga[1]
|
||||
self.quality = int(gga[6]) if gga[6] != "" else 0
|
||||
self.satellites = int(gga[7]) if gga[7] != "" else 0
|
||||
|
||||
# If quality > 0 and coordinates exist, convert NMEA DDDMM.MMMM to decimal degrees
|
||||
if self.quality > 0 and gga[2] != "" and gga[4] != "":
|
||||
lat_raw = float(gga[2])
|
||||
ns = gga[3]
|
||||
lon_raw = float(gga[4])
|
||||
ew = gga[5]
|
||||
|
||||
# Latitude calculation
|
||||
lat_deg = lat_raw // 100
|
||||
lat_min = lat_raw % 100
|
||||
self.latitude = lat_deg + (lat_min / 60.0)
|
||||
if ns == 'S':
|
||||
self.latitude = -self.latitude
|
||||
|
||||
# Longitude calculation
|
||||
lon_deg = lon_raw // 100
|
||||
lon_min = lon_raw % 100
|
||||
self.longitude = lon_deg + (lon_min / 60.0)
|
||||
if ew == 'W':
|
||||
self.longitude = -self.longitude
|
||||
|
||||
self.altitude = float(gga[9]) if gga[9] != "" else -1.0
|
||||
return True
|
||||
else:
|
||||
# No lock on this line
|
||||
return True
|
||||
|
||||
except (ValueError, IndexError):
|
||||
return False
|
||||
|
||||
|
||||
# Shared instance
|
||||
gps = GROVEGPS()
|
||||
|
||||
def get_gps_data():
|
||||
"""Returns GPS dictionary if fix is valid, otherwise returns None."""
|
||||
has_data = gps.read()
|
||||
|
||||
# Strictly check that we have a valid GPS lock (quality > 0 and valid coordinates)
|
||||
if has_data and gps.quality > 0 and gps.latitude != -1.0:
|
||||
return {
|
||||
"timestamp": gps.timestamp,
|
||||
"latitude": round(gps.latitude, 6),
|
||||
"longitude": round(gps.longitude, 6),
|
||||
"altitude": gps.altitude,
|
||||
"quality": gps.quality,
|
||||
"satellites": gps.satellites
|
||||
}
|
||||
else:
|
||||
log(f"GPS: No valid fix or data available. Satellites: {gps.satellites}, Quality: {gps.quality}")
|
||||
|
||||
# Return None so main.py doesn't process or log empty GPS data
|
||||
return None
|
||||
@@ -0,0 +1,2 @@
|
||||
# import orchestrateur.sensors.lib.grovepi_old as grovepi_old
|
||||
# import sensors.lib.grove_i2c_temp_hum_mini as grove_i2c_temp_hum_mini
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# GrovePi Library for using the Grove - Temperature&Humidity Sensor (http://www.seeedstudio.com/depot/Grove-TemperatureHumidity-Sensor-HighAccuracy-Mini-p-1921.html)
|
||||
#
|
||||
# The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi
|
||||
#
|
||||
# Have a question about this library? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi
|
||||
#
|
||||
# Released under the MIT license (http://choosealicense.com/licenses/mit/).
|
||||
# For more information see https://github.com/DexterInd/GrovePi/blob/master/LICENSE
|
||||
#################################################################################################################################################
|
||||
# NOTE:
|
||||
# The software for this sensor is still in development and might make your GrovePi unuable as long as this sensor is connected with the GrovePi
|
||||
#################################################################################################################################################
|
||||
import time,sys
|
||||
import RPi.GPIO as GPIO
|
||||
import smbus
|
||||
|
||||
debug = 0
|
||||
# use the bus that matches your raspi version
|
||||
rev = GPIO.RPI_REVISION
|
||||
if rev == 2 or rev == 3:
|
||||
bus = smbus.SMBus(1)
|
||||
else:
|
||||
bus = smbus.SMBus(0)
|
||||
|
||||
class th02:
|
||||
|
||||
ADDRESS = 0x40
|
||||
|
||||
TH02_REG_STATUS = 0x00
|
||||
TH02_REG_DATA_H = 0x01
|
||||
TH02_REG_DATA_L = 0x02
|
||||
TH02_REG_CONFIG = 0x03
|
||||
TH02_REG_ID = 0x11
|
||||
|
||||
TH02_STATUS_RDY_MASK = 0x01
|
||||
|
||||
TH02_CMD_MEASURE_HUMI = [0x01]
|
||||
TH02_CMD_MEASURE_TEMP = [0x11]
|
||||
|
||||
SUCCESS = 0
|
||||
|
||||
def getTemperature(self):
|
||||
bus.write_i2c_block_data(self.ADDRESS, self.TH02_REG_CONFIG, self.TH02_CMD_MEASURE_TEMP)
|
||||
|
||||
while 1:
|
||||
status=self.getStatus()
|
||||
if debug:
|
||||
print("st:",status)
|
||||
if status:
|
||||
break
|
||||
t_raw=bus.read_i2c_block_data(self.ADDRESS, self.TH02_REG_DATA_H,3)
|
||||
if debug:
|
||||
print(t_raw)
|
||||
temperature = (t_raw[1]<<8|t_raw[2])>>2
|
||||
return (temperature/32.0)-50.0
|
||||
|
||||
def getHumidity(self):
|
||||
bus.write_i2c_block_data(self.ADDRESS, self.TH02_REG_CONFIG, self.TH02_CMD_MEASURE_HUMI)
|
||||
|
||||
while 1:
|
||||
status=self.getStatus()
|
||||
if debug:
|
||||
print("st:",status)
|
||||
if status:
|
||||
break
|
||||
t_raw=bus.read_i2c_block_data(self.ADDRESS, self.TH02_REG_DATA_H,3)
|
||||
if debug:
|
||||
print(t_raw)
|
||||
temperature = (t_raw[1]<<8|t_raw[2])>>4
|
||||
return (temperature/16.0)-24.0
|
||||
|
||||
def getStatus(self):
|
||||
status=bus.read_i2c_block_data(self.ADDRESS, self.TH02_REG_STATUS,1)
|
||||
if debug:
|
||||
print(status)
|
||||
if status[0] & self.TH02_STATUS_RDY_MASK != 1:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
t= th02()
|
||||
while True:
|
||||
print(t.getTemperature(),t.getHumidity())
|
||||
time.sleep(.5)
|
||||
@@ -0,0 +1,691 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# GrovePi Python library
|
||||
# v1.4
|
||||
#
|
||||
# This file provides the basic functions for using the GrovePi
|
||||
#
|
||||
# The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi
|
||||
#
|
||||
# Have a question about this example? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi
|
||||
#
|
||||
'''
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
GrovePi for the Raspberry Pi: an open source platform for connecting Grove Sensors to the Raspberry Pi.
|
||||
Copyright (C) 2017 Dexter Industries
|
||||
|
||||
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.
|
||||
'''
|
||||
# Initial Date: 13 Feb 2014
|
||||
# Last Updated: 11 Nov 2016
|
||||
# http://www.dexterindustries.com/
|
||||
# Author Date Comments
|
||||
# Karan 13 Feb 2014 Initial Authoring
|
||||
# 11 Nov 2016 I2C retries added for faster IO
|
||||
# DHT function updated to look for nan's
|
||||
|
||||
__version__ = '1.4.1'
|
||||
|
||||
import sys
|
||||
import time
|
||||
import math
|
||||
import struct
|
||||
import numpy
|
||||
|
||||
import di_i2c
|
||||
|
||||
def set_bus(bus):
|
||||
global i2c
|
||||
i2c = di_i2c.DI_I2C(bus = bus, address = address)
|
||||
|
||||
address = 0x04
|
||||
max_recv_size = 10
|
||||
set_bus("RPI_1SW")
|
||||
|
||||
if sys.version_info<(3,0):
|
||||
p_version = 2
|
||||
else:
|
||||
p_version = 3
|
||||
|
||||
# Earliest version of the firmware to work with
|
||||
works_with_firmware = [
|
||||
"1.4.0"
|
||||
]
|
||||
|
||||
# interrupt operations
|
||||
COUNT_CHANGES = 0
|
||||
COUNT_LOW_DURATION = 1
|
||||
|
||||
# interrupt trigger mode
|
||||
CHANGE = 1
|
||||
FALLING = 2
|
||||
RISING = 3
|
||||
|
||||
# This allows us to be more specific about which commands contain unused bytes
|
||||
unused = 0
|
||||
retries = 10
|
||||
additional_waiting = 0
|
||||
|
||||
# Get firmware version
|
||||
version_cmd = [8]
|
||||
# No data is available from the GrovePi
|
||||
data_not_available_cmd = [23]
|
||||
|
||||
# Command Format
|
||||
# digitalRead() command format header
|
||||
dRead_cmd = [1]
|
||||
# digitalWrite() command format header
|
||||
dWrite_cmd = [2]
|
||||
# analogRead() command format header
|
||||
aRead_cmd = [3]
|
||||
# analogWrite() command format header
|
||||
aWrite_cmd = [4]
|
||||
# pinMode() command format header
|
||||
pMode_cmd = [5]
|
||||
# Ultrasonic read
|
||||
uRead_cmd = [7]
|
||||
# Accelerometer (+/- 1.5g) read
|
||||
acc_xyz_cmd = [20]
|
||||
# RTC get time
|
||||
rtc_getTime_cmd = [30]
|
||||
# DHT Pro sensor temperature
|
||||
dht_temp_cmd = [40]
|
||||
|
||||
# Grove LED Bar commands
|
||||
# Initialise
|
||||
ledBarInit_cmd = [50]
|
||||
# Set orientation
|
||||
ledBarOrient_cmd = [51]
|
||||
# Set level
|
||||
ledBarLevel_cmd = [52]
|
||||
# Set single LED
|
||||
ledBarSetOne_cmd = [53]
|
||||
# Toggle single LED
|
||||
ledBarToggleOne_cmd = [54]
|
||||
# Set all LEDs
|
||||
ledBarSet_cmd = [55]
|
||||
# Get current state
|
||||
ledBarGet_cmd = [56]
|
||||
|
||||
# Grove 4 Digit Display commands
|
||||
# Initialise
|
||||
fourDigitInit_cmd = [70]
|
||||
# Set brightness, not visible until next cmd
|
||||
fourDigitBrightness_cmd = [71]
|
||||
# Set numeric value without leading zeros
|
||||
fourDigitValue_cmd = [72]
|
||||
# Set numeric value with leading zeros
|
||||
fourDigitValueZeros_cmd = [73]
|
||||
# Set individual digit
|
||||
fourDigitIndividualDigit_cmd = [74]
|
||||
# Set individual leds of a segment
|
||||
fourDigitIndividualLeds_cmd = [75]
|
||||
# Set left and right values with colon
|
||||
fourDigitScore_cmd = [76]
|
||||
# Analog read for n seconds
|
||||
fourDigitAnalogRead_cmd = [77]
|
||||
# Entire display on
|
||||
fourDigitAllOn_cmd = [78]
|
||||
# Entire display off
|
||||
fourDigitAllOff_cmd = [79]
|
||||
|
||||
# Grove Chainable RGB LED commands
|
||||
# Store color for later use
|
||||
storeColor_cmd = [90]
|
||||
# Initialise
|
||||
chainableRgbLedInit_cmd = [91]
|
||||
# Initialise and test with a simple color
|
||||
chainableRgbLedTest_cmd = [92]
|
||||
# Set one or more leds to the stored color by pattern
|
||||
chainableRgbLedSetPattern_cmd = [93]
|
||||
# set one or more leds to the stored color by modulo
|
||||
chainableRgbLedSetModulo_cmd = [94]
|
||||
# sets leds similar to a bar graph, reversible
|
||||
chainableRgbLedSetLevel_cmd = [95]
|
||||
|
||||
# Read the button from IR sensor
|
||||
ir_read_cmd = [21]
|
||||
# Set pin for the IR receiver
|
||||
ir_recv_pin_cmd = [22]
|
||||
# Check if there's data coming from the IR receiver
|
||||
ir_read_isdata = [24]
|
||||
|
||||
# Interrupt-based devices
|
||||
isr_set_cmd = [6]
|
||||
isr_unset_cmd = [9]
|
||||
isr_read_cmd = [10]
|
||||
isr_clear_cmd = [11]
|
||||
isr_active_cmd = [12]
|
||||
|
||||
# Grove Encoders
|
||||
encoder_read_cmd = [13]
|
||||
encoder_en_cmd = [14]
|
||||
encoder_dis_cmd = [15]
|
||||
|
||||
# Dust, Encoder & Flow Sensor commands
|
||||
# dust_sensor_read_cmd=[10]
|
||||
# dust_sensor_en_cmd=[14]
|
||||
# dust_sensor_dis_cmd=[15]
|
||||
# dust_sensor_int_cmd=[9]
|
||||
# dust_sensor_read_int_cmd=[6]
|
||||
# flow_read_cmd=[12]
|
||||
# flow_disable_cmd=[13]
|
||||
# flow_en_cmd=[18]
|
||||
|
||||
|
||||
# Function declarations of the various functions used for encoding and sending
|
||||
# data from RPi to Arduino
|
||||
|
||||
# Write I2C block to the GrovePi
|
||||
def write_i2c_block(block, custom_timing = None):
|
||||
'''
|
||||
Now catches and raises Keyboard Interrupt that the user is responsible to catch.
|
||||
'''
|
||||
counter = 0
|
||||
reg = block[0]
|
||||
data = block[1:]
|
||||
while counter < 3:
|
||||
try:
|
||||
i2c.write_reg_list(reg, data)
|
||||
time.sleep(0.002 + additional_waiting)
|
||||
return
|
||||
except KeyboardInterrupt:
|
||||
raise KeyboardInterrupt
|
||||
except:
|
||||
counter += 1
|
||||
time.sleep(0.003)
|
||||
continue
|
||||
|
||||
# Read I2C block from the GrovePi
|
||||
def read_i2c_block(no_bytes = max_recv_size):
|
||||
'''
|
||||
Now catches and raises Keyboard Interrupt that the user is responsible to catch.
|
||||
'''
|
||||
data = data_not_available_cmd
|
||||
counter = 0
|
||||
while data[0] in [data_not_available_cmd[0], 255] and counter < 3:
|
||||
try:
|
||||
data = i2c.read_list(reg = None, len = no_bytes)
|
||||
time.sleep(0.002 + additional_waiting)
|
||||
if counter > 0:
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
raise KeyboardInterrupt
|
||||
except:
|
||||
counter += 1
|
||||
time.sleep(0.003)
|
||||
|
||||
return data
|
||||
|
||||
def read_identified_i2c_block(read_command_id, no_bytes):
|
||||
data = [-1]
|
||||
while len(data) <= 1:
|
||||
data = read_i2c_block(no_bytes + 1)
|
||||
|
||||
return data[1:]
|
||||
|
||||
# Arduino Digital Read
|
||||
def digitalRead(pin):
|
||||
write_i2c_block(dRead_cmd + [pin, unused, unused])
|
||||
data = read_identified_i2c_block( dRead_cmd, no_bytes = 1)[0]
|
||||
return data
|
||||
|
||||
# Arduino Digital Write
|
||||
def digitalWrite(pin, value):
|
||||
write_i2c_block(dWrite_cmd + [pin, value, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Read analog value from Pin
|
||||
def analogRead(pin):
|
||||
write_i2c_block(aRead_cmd + [pin, unused, unused])
|
||||
number = read_identified_i2c_block(aRead_cmd, no_bytes = 2)
|
||||
return number[0] * 256 + number[1]
|
||||
|
||||
|
||||
# Write PWM
|
||||
def analogWrite(pin, value):
|
||||
write_i2c_block(aWrite_cmd + [pin, value, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Setting Up Pin mode on Arduino
|
||||
def pinMode(pin, mode):
|
||||
if mode == "OUTPUT":
|
||||
write_i2c_block(pMode_cmd + [pin, 1, unused])
|
||||
elif mode == "INPUT":
|
||||
write_i2c_block(pMode_cmd + [pin, 0, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
|
||||
# Read temp in Celsius from Grove Temperature Sensor
|
||||
def temp(pin, model = '1.0'):
|
||||
# each of the sensor revisions use different thermistors, each with their own B value constant
|
||||
if model == '1.2':
|
||||
bValue = 4250 # sensor v1.2 uses thermistor ??? (assuming NCP18WF104F03RC until SeeedStudio clarifies)
|
||||
elif model == '1.1':
|
||||
bValue = 4250 # sensor v1.1 uses thermistor NCP18WF104F03RC
|
||||
else:
|
||||
bValue = 3975 # sensor v1.0 uses thermistor TTC3A103*39H
|
||||
a = analogRead(pin)
|
||||
resistance = (float)(1023 - a) * 10000 / a
|
||||
t = (float)(1 / (math.log(resistance / 10000) / bValue + 1 / 298.15) - 273.15)
|
||||
return t
|
||||
|
||||
|
||||
# Read value from Grove Ultrasonic
|
||||
def ultrasonicRead(pin):
|
||||
write_i2c_block(uRead_cmd + [pin, unused, unused])
|
||||
number = read_identified_i2c_block(uRead_cmd, no_bytes = 2)
|
||||
return (number[0] * 256 + number[1])
|
||||
|
||||
|
||||
# Read the firmware version
|
||||
def version():
|
||||
write_i2c_block(version_cmd + [unused, unused, unused])
|
||||
number = read_identified_i2c_block(version_cmd, no_bytes = 3)
|
||||
return "%s.%s.%s" % (number[0], number[1], number[2])
|
||||
|
||||
|
||||
# Read Grove Accelerometer (+/- 1.5g) XYZ value
|
||||
# Need to investigate why this reports what was read with the previous command
|
||||
# Doesn't look to be implemented on the GrovePi
|
||||
def acc_xyz():
|
||||
write_i2c_block(acc_xyz_cmd + [unused, unused, unused])
|
||||
number = read_identified_i2c_block(acc_xyz_cmd, no_bytes = 3)
|
||||
if number[1] > 32:
|
||||
number[1] = - (number[1] - 224)
|
||||
if number[2] > 32:
|
||||
number[2] = - (number[2] - 224)
|
||||
if number[3] > 32:
|
||||
number[3] = - (number[3] - 224)
|
||||
return (number[0], number[1], number[2])
|
||||
|
||||
|
||||
# Read from Grove RTC
|
||||
# Doesn't look to be implemented on the GrovePi
|
||||
def rtc_getTime():
|
||||
write_i2c_block(rtc_getTime_cmd + [unused, unused, unused])
|
||||
number = read_i2c_block()
|
||||
return number
|
||||
|
||||
# Read and return temperature and humidity from Grove DHT Pro
|
||||
def dht(pin, module_type):
|
||||
write_i2c_block(dht_temp_cmd + [pin, module_type, unused])
|
||||
number = read_identified_i2c_block(dht_temp_cmd, no_bytes = 8)
|
||||
|
||||
if p_version==2:
|
||||
h=''
|
||||
for element in (number[0:4]):
|
||||
h+=chr(element)
|
||||
|
||||
t_val=struct.unpack('f', h)
|
||||
t = round(t_val[0], 2)
|
||||
|
||||
h = ''
|
||||
for element in (number[4:8]):
|
||||
h+=chr(element)
|
||||
|
||||
hum_val=struct.unpack('f',h)
|
||||
hum = round(hum_val[0], 2)
|
||||
else:
|
||||
t_val=bytearray(number[0:4])
|
||||
h_val=bytearray(number[4:8])
|
||||
t=round(struct.unpack('f',t_val)[0],2)
|
||||
hum=round(struct.unpack('f',h_val)[0],2)
|
||||
if t > -100.0 and t <150.0 and hum >= 0.0 and hum<=100.0:
|
||||
return [t, hum]
|
||||
else:
|
||||
return [float('nan'),float('nan')]
|
||||
|
||||
# Grove - Infrared Receiver - get the commands received from the Grove IR sensor
|
||||
def ir_read_signal():
|
||||
write_i2c_block(ir_read_cmd + [unused, unused, unused])
|
||||
data_back = read_identified_i2c_block(ir_read_cmd, no_bytes = 7)
|
||||
|
||||
return (data_back[0],
|
||||
data_back[1] + data_back[2] * 256,
|
||||
data_back[3] + data_back[4] * 256 + data_back[5] * (256 ** 2) + data_back[6] * (256 ** 3))
|
||||
|
||||
# Grove - Infrared Receiver - set the pin on which the Grove IR sensor is connected
|
||||
def ir_recv_pin(pin):
|
||||
write_i2c_block(ir_recv_pin_cmd + [pin, unused, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
|
||||
# Grove - Infrared Receiver - check if there's any data that hasn't been read so far
|
||||
def ir_is_data():
|
||||
write_i2c_block(ir_read_isdata + 3 * [unused])
|
||||
number = read_identified_i2c_block(ir_read_isdata, no_bytes = 1)
|
||||
|
||||
return number[0] != 0
|
||||
|
||||
# after a list of numerical values is provided
|
||||
# the function returns a list with the outlier(or extreme) values removed
|
||||
# make the std_factor_threshold bigger so that filtering becomes less strict
|
||||
# and make the std_factor_threshold smaller to get the opposite
|
||||
def statisticalNoiseReduction(values, std_factor_threshold = 2):
|
||||
if len(values) == 0:
|
||||
return []
|
||||
|
||||
mean = numpy.mean(values)
|
||||
standard_deviation = numpy.std(values)
|
||||
|
||||
if standard_deviation == 0:
|
||||
return values
|
||||
|
||||
filtered_values = [element for element in values if element > mean - std_factor_threshold * standard_deviation]
|
||||
filtered_values = [element for element in filtered_values if element < mean + std_factor_threshold * standard_deviation]
|
||||
|
||||
return filtered_values
|
||||
|
||||
|
||||
# Grove LED Bar - initialise
|
||||
# orientation: (0 = red to green, 1 = green to red)
|
||||
def ledBar_init(pin, orientation):
|
||||
write_i2c_block(ledBarInit_cmd + [pin, orientation, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove LED Bar - set orientation
|
||||
# orientation: (0 = red to green, 1 = green to red)
|
||||
def ledBar_orientation(pin, orientation):
|
||||
write_i2c_block(ledBarOrient_cmd + [pin, orientation, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove LED Bar - set level
|
||||
# level: (0-10)
|
||||
def ledBar_setLevel(pin, level):
|
||||
write_i2c_block(ledBarLevel_cmd + [pin, level, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove LED Bar - set single led
|
||||
# led: which led (1-10)
|
||||
# state: off or on (0-1)
|
||||
def ledBar_setLed(pin, led, state):
|
||||
write_i2c_block(ledBarSetOne_cmd + [pin, led, state])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove LED Bar - toggle single led
|
||||
# led: which led (1-10)
|
||||
def ledBar_toggleLed(pin, led):
|
||||
write_i2c_block(ledBarToggleOne_cmd + [pin, led, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove LED Bar - set all leds
|
||||
# state: (0-1023) or (0x00-0x3FF) or (0b0000000000-0b1111111111) or (int('0000000000',2)-int('1111111111',2))
|
||||
def ledBar_setBits(pin, state):
|
||||
byte1 = state & 255
|
||||
byte2 = state >> 8
|
||||
write_i2c_block(ledBarSet_cmd + [pin, byte1, byte2])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove LED Bar - get current state
|
||||
# state: (0-1023) a bit for each of the 10 LEDs
|
||||
def ledBar_getBits(pin):
|
||||
write_i2c_block(ledBarGet_cmd + [pin, unused, unused])
|
||||
block = read_identified_i2c_block(ledBarGet_cmd, no_bytes = 2)
|
||||
return block[0] ^ (block[1] << 8)
|
||||
|
||||
|
||||
# Grove 4 Digit Display - initialise
|
||||
def fourDigit_init(pin):
|
||||
write_i2c_block(fourDigitInit_cmd + [pin, unused, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - set numeric value with or without leading zeros
|
||||
# value: (0-65535) or (0000-FFFF)
|
||||
def fourDigit_number(pin, value, leading_zero):
|
||||
# split the value into two bytes so we can render 0000-FFFF on the display
|
||||
byte1 = value & 255
|
||||
byte2 = value >> 8
|
||||
# separate commands to overcome current 4 bytes per command limitation
|
||||
if (leading_zero):
|
||||
write_i2c_block(fourDigitValue_cmd + [pin, byte1, byte2])
|
||||
else:
|
||||
write_i2c_block(fourDigitValueZeros_cmd + [pin, byte1, byte2])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - set brightness
|
||||
# brightness: (0-7)
|
||||
def fourDigit_brightness(pin, brightness):
|
||||
# not actually visible until next command is executed
|
||||
write_i2c_block(fourDigitBrightness_cmd + [pin, brightness, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - set individual segment (0-9,A-F)
|
||||
# segment: (0-3)
|
||||
# value: (0-15) or (0-F)
|
||||
def fourDigit_digit(pin, segment, value):
|
||||
write_i2c_block(fourDigitIndividualDigit_cmd + [pin, segment, value])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - set 7 individual leds of a segment
|
||||
# segment: (0-3)
|
||||
# leds: (0-255) or (0-0xFF) one bit per led, segment 2 is special, 8th bit is the colon
|
||||
def fourDigit_segment(pin, segment, leds):
|
||||
write_i2c_block(fourDigitIndividualLeds_cmd + [pin, segment, leds])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - set left and right values (0-99), with leading zeros and a colon
|
||||
# left: (0-255) or (0-FF)
|
||||
# right: (0-255) or (0-FF)
|
||||
# colon will be lit
|
||||
def fourDigit_score(pin, left, right):
|
||||
write_i2c_block(fourDigitScore_cmd + [pin, left, right])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - display analogRead value for n seconds, 4 samples per second
|
||||
# analog: analog pin to read
|
||||
# duration: analog read for this many seconds
|
||||
def fourDigit_monitor(pin, analog, duration):
|
||||
write_i2c_block(fourDigitAnalogRead_cmd + [pin, analog, duration])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
time.sleep(duration)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - turn entire display on (88:88)
|
||||
def fourDigit_on(pin):
|
||||
write_i2c_block(fourDigitAllOn_cmd + [pin, unused, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove 4 Digit Display - turn entire display off
|
||||
def fourDigit_off(pin):
|
||||
write_i2c_block(fourDigitAllOff_cmd + [pin, unused, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove Chainable RGB LED - store a color for later use
|
||||
# red: 0-255
|
||||
# green: 0-255
|
||||
# blue: 0-255
|
||||
def storeColor(red, green, blue):
|
||||
write_i2c_block(storeColor_cmd + [red, green, blue])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove Chainable RGB LED - initialise
|
||||
# numLeds: how many leds do you have in the chain
|
||||
def chainableRgbLed_init(pin, numLeds):
|
||||
write_i2c_block(chainableRgbLedInit_cmd + [pin, numLeds, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove Chainable RGB LED - initialise and test with a simple color
|
||||
# numLeds: how many leds do you have in the chain
|
||||
# testColor: (0-7) 3 bits in total - a bit for red, green and blue, eg. 0x04 == 0b100 (0bRGB) == rgb(255, 0, 0) == #FF0000 == red
|
||||
# ie. 0 black, 1 blue, 2 green, 3 cyan, 4 red, 5 magenta, 6 yellow, 7 white
|
||||
def chainableRgbLed_test(pin, numLeds, testColor):
|
||||
write_i2c_block(chainableRgbLedTest_cmd + [pin, numLeds, testColor])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove Chainable RGB LED - set one or more leds to the stored color by pattern
|
||||
# pattern: (0-3) 0 = this led only, 1 all leds except this led, 2 this led and all leds inwards, 3 this led and all leds outwards
|
||||
# whichLed: index of led you wish to set counting outwards from the GrovePi, 0 = led closest to the GrovePi
|
||||
def chainableRgbLed_pattern(pin, pattern, whichLed):
|
||||
write_i2c_block(chainableRgbLedSetPattern_cmd + [pin, pattern, whichLed])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove Chainable RGB LED - set one or more leds to the stored color by modulo
|
||||
# offset: index of led you wish to start at, 0 = led closest to the GrovePi, counting outwards
|
||||
# divisor: when 1 (default) sets stored color on all leds >= offset, when 2 sets every 2nd led >= offset and so on
|
||||
def chainableRgbLed_modulo(pin, offset, divisor):
|
||||
write_i2c_block(chainableRgbLedSetModulo_cmd + [pin, offset, divisor])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
# Grove Chainable RGB LED - sets leds similar to a bar graph, reversible
|
||||
# level: (0-10) the number of leds you wish to set to the stored color
|
||||
# reversible (0-1) when 0 counting outwards from GrovePi, 0 = led closest to the GrovePi, otherwise counting inwards
|
||||
def chainableRgbLed_setLevel(pin, level, reverse):
|
||||
write_i2c_block(chainableRgbLedSetLevel_cmd + [pin, level, reverse])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
return 1
|
||||
|
||||
def set_pin_interrupt(pin, ftype, interrupt_mode, period):
|
||||
'''
|
||||
Attach an interrupt to a pin.
|
||||
|
||||
pin - D2-D8 pins
|
||||
ftype - 0 for COUNT_CHANGES, 1 for COUNT_LOW_DURATION
|
||||
interrupt_mode - 1 for CHANGE, 2 for FALLING, 3 for RISING
|
||||
period - as measured in ms (max 65535 ms)
|
||||
'''
|
||||
period_high = period >> 8
|
||||
period_low = period & 0xff
|
||||
combined_params = (pin & 0x0f) + ((ftype & 0x03) << 4) + ((interrupt_mode & 0x03) << 6)
|
||||
write_i2c_block(isr_set_cmd + [combined_params, period_high, period_low])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
|
||||
def unset_pin_interrupt(pin):
|
||||
'''
|
||||
Detach an interrupt from a pin.
|
||||
|
||||
pin - D2-D8 pins
|
||||
'''
|
||||
write_i2c_block(isr_unset_cmd + [pin, unused, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
|
||||
def unset_all_interrupts():
|
||||
'''
|
||||
Detach all attached interrupts from all D2-D8 pins.
|
||||
|
||||
pin - D2-D8 pins
|
||||
'''
|
||||
write_i2c_block(isr_clear_cmd + 3 * [unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
|
||||
def is_interrupt_active(pin):
|
||||
write_i2c_block(isr_active_cmd + [pin, unused, unused])
|
||||
data = read_identified_i2c_block(isr_active_cmd, no_bytes = 2)
|
||||
value = data[1] >> pin
|
||||
return value != 0
|
||||
|
||||
def get_active_interrupts():
|
||||
'''
|
||||
Get list of attached interrupts for a given pin or all of them.
|
||||
|
||||
pin - D2-D8 pins; if it's 255 return the state of all pins
|
||||
'''
|
||||
pin = 255
|
||||
write_i2c_block(isr_active_cmd + [pin, unused, unused])
|
||||
data = read_identified_i2c_block(isr_active_cmd, no_bytes = 2)
|
||||
value = data[0] + (data[1] << 8)
|
||||
active_interrupts = [i for i in range(2 * 8) if ((value >> i) & 0x01)]
|
||||
return active_interrupts
|
||||
|
||||
def read_interrupt_state(pin):
|
||||
'''
|
||||
Read number of pulses/changes on given port that occurred within a time period.
|
||||
|
||||
pin - D2-D8 pins
|
||||
'''
|
||||
write_i2c_block(isr_read_cmd + [pin, unused, unused])
|
||||
data = read_identified_i2c_block(isr_read_cmd, no_bytes = 4)
|
||||
value = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
|
||||
return value
|
||||
|
||||
def dust_sensor_en(pin = 2, period = 30000):
|
||||
set_pin_interrupt(pin, ftype=COUNT_LOW_DURATION, interrupt_mode=CHANGE, period=period)
|
||||
|
||||
def dust_sensor_dis(pin = 2):
|
||||
unset_pin_interrupt(pin)
|
||||
|
||||
def dust_sensor_read(pin = 2, period = 30000):
|
||||
'''
|
||||
By default, the sample rate is set to 1 at every 30 seconds and this
|
||||
function was written only for that interval.
|
||||
|
||||
If you wish to use a different
|
||||
interval, then use dust_sensor_read_more function. To set a
|
||||
different interval, use set_dust_sensor_interval function.
|
||||
'''
|
||||
lpo = read_interrupt_state(pin)
|
||||
percentage = 100.0 * lpo / period
|
||||
concentration = 1.1 * percentage ** 3 - 3.8 * percentage ** 2 + 520 * percentage + 0.62
|
||||
|
||||
return lpo, percentage, concentration
|
||||
|
||||
def encoder_en(pin = 2, steps = 32):
|
||||
write_i2c_block(encoder_en_cmd + [pin, steps, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
|
||||
def encoder_dis(pin = 2):
|
||||
write_i2c_block(encoder_dis_cmd + [pin, unused, unused])
|
||||
read_i2c_block(no_bytes = 1)
|
||||
|
||||
def encoderRead(pin = 2):
|
||||
write_i2c_block(encoder_read_cmd + [pin, unused, unused])
|
||||
data = read_identified_i2c_block(encoder_read_cmd, no_bytes = 4)
|
||||
value = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
|
||||
return value
|
||||
|
||||
def flowEnable(pin = 2, period = 2000):
|
||||
set_pin_interrupt(pin, ftype=COUNT_CHANGES, interrupt_mode=RISING, period=period)
|
||||
|
||||
def flowDisable(pin = 2):
|
||||
unset_pin_interrupt(pin)
|
||||
|
||||
def flowRead(pin = 2):
|
||||
val = read_interrupt_state(pin)
|
||||
return val
|
||||
|
||||
def main():
|
||||
print("library supports this fw versions: " +
|
||||
" ".join('{}'.format(k[1]) for k in enumerate(works_with_firmware)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,5 @@
|
||||
import threading
|
||||
# Dedicated lock for I2C bus access (used by GrovePi sensors)
|
||||
grove_lock = threading.Lock()
|
||||
# Dedicated lock for UART/Serial port access
|
||||
serial_lock = threading.Lock()
|
||||
@@ -0,0 +1,52 @@
|
||||
# from sensors.lib import grove_i2c_temp_hum_mini
|
||||
|
||||
# t= grove_i2c_temp_hum_mini.th02()
|
||||
|
||||
# def get_temperature():
|
||||
# """Get the temperature in Celsius from the TH02 sensor."""
|
||||
# # try:
|
||||
# return t.getTemperature()
|
||||
# # except Exception as e:
|
||||
# # print(f"Error reading temperature: {e}")
|
||||
# # return None
|
||||
|
||||
# def get_humidity():
|
||||
# """Get the humidity in percentage from the TH02 sensor."""
|
||||
# # try:
|
||||
# return t.getHumidity()
|
||||
# # except Exception as e:
|
||||
# # print(f"Error reading humidity: {e}")
|
||||
# # return None
|
||||
|
||||
# import seeed_dht
|
||||
|
||||
# sensor = seeed_dht.DHT("11", 4) # DHT11 sensor on GPIO pin 4
|
||||
|
||||
# def get_humidity_and_temperature():
|
||||
# humi, temp = sensor.read()
|
||||
# return humi, temp
|
||||
|
||||
|
||||
# import sensors.lib.grovepi as grovepi
|
||||
import grovepi
|
||||
import math
|
||||
from sensors.lock import grove_lock
|
||||
|
||||
# Connect the Grove Temperature & Humidity Sensor Pro to digital port D3
|
||||
# This example uses the blue colored sensor.
|
||||
# SIG,NC,VCC,GND
|
||||
sensor = 3 # The Sensor goes on digital port 3.
|
||||
|
||||
# temp_humidity_sensor_type
|
||||
# Grove Base Kit comes with the blue sensor.
|
||||
blue = 0 # The Blue colored sensor.
|
||||
white = 1 # The White colored sensor.
|
||||
|
||||
def get_temperature_and_humidity():
|
||||
with grove_lock:
|
||||
[temp,humidity] = grovepi.dht(sensor,blue)
|
||||
if math.isnan(temp) == False and math.isnan(humidity) == False:
|
||||
return temp, humidity
|
||||
else:
|
||||
print("Error reading from DHT sensor")
|
||||
return None, None
|
||||
@@ -0,0 +1,32 @@
|
||||
import grovepi
|
||||
from sensors.lock import grove_lock
|
||||
|
||||
# Connect the Grove Ultrasonic Ranger to digital port D4
|
||||
# SIG,NC,VCC,GND
|
||||
ULTRASONIC_RANGER_PORT = 4
|
||||
|
||||
def read_ultrasonic_ranger(ultrasonic_ranger=ULTRASONIC_RANGER_PORT):
|
||||
if not grove_lock.acquire(timeout=1.0):
|
||||
print("Ultrasonic: Lock acquisition timed out")
|
||||
return None
|
||||
|
||||
try:
|
||||
return grovepi.ultrasonicRead(ultrasonic_ranger)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
finally:
|
||||
grove_lock.release()
|
||||
|
||||
def get_dish_height():
|
||||
"""Returns the height of the dish in centimeters."""
|
||||
distance = read_ultrasonic_ranger()
|
||||
if distance is not None:
|
||||
# Assuming the ultrasonic sensor is mounted at a fixed height above the dish
|
||||
# and pointing downwards, we can calculate the height of the dish.
|
||||
# For example, if the sensor is 30 cm above the dish when it's empty:
|
||||
SENSOR_HEIGHT = 30 # cm
|
||||
dish_height = SENSOR_HEIGHT - distance
|
||||
return max(dish_height, 0) # Ensure height is not negative
|
||||
else:
|
||||
return None
|
||||
Reference in New Issue
Block a user