Browse Source

stuff from the other night

i am a professional and my usage of version control reflects years of
honed experience or something.
master
Brennen Bearnes 3 years ago
parent
commit
27c25c6356
5 changed files with 574 additions and 28 deletions
  1. +0
    -1
      compass.py
  2. +49
    -0
      limor_calibrated_recent.py
  3. +74
    -27
      src/glitterpos.py
  4. +20
    -0
      src/glitterpos_util.py
  5. +431
    -0
      src/lib/adafruit_lsm9ds1.py

+ 0
- 1
compass.py View File

@ -24,7 +24,6 @@ def bearing_to_pixel(bearing, count=16):
def map_range(x, in_min, in_max, out_min, out_max):
"""
Maps a number from one range to another.
Note: This implementation handles values < in_min differently than arduino's map function does.
:return: Returns value mapped to new range
:rtype: float
"""


+ 49
- 0
limor_calibrated_recent.py View File

@ -0,0 +1,49 @@
import time
import math
import board
import busio
import adafruit_lsm9ds1
i2c = busio.I2C(board.SCL, board.SDA)
compass = adafruit_lsm9ds1.LSM9DS1_I2C(i2c)
mag_min = [-0.10206, 0.00238, -0.10458]
mag_max = [0.44926, 0.56938, 0.4501]
def calibrate_compass(compass):
mag_min = [1000, 1000, 1000]
mag_max = [-1000, -1000, -1000]
print("Magnetometer Calibration")
lastDisplayTime = time.monotonic()
while True:
x, y, z = compass.magnetometer
mag_vals = [x, y, z]
for i in range(3):
mag_min[i] = min(mag_min[i], mag_vals[i])
mag_max[i] = max(mag_max[i], mag_vals[i])
# display once every two seconds
if (time.monotonic() - lastDisplayTime >= 1):
print("Mag Min: ", mag_min, "& Max:", mag_max)
lastDisplayTime = time.monotonic();
#calibrate_compass(compass)
def map_range(x, in_min, in_max, out_min, out_max):
mapped = (x-in_min) * (out_max - out_min) / (in_max-in_min) + out_min
if out_min <= out_max:
return max(min(mapped, out_max), out_min)
return min(max(mapped, out_max), out_min)
while True:
x, y, z = compass.magnetometer
print("Uncalibrated: ", x, y, z, end="")
x = map_range(x, mag_min[0], mag_max[0], -1, 1)
y = map_range(y, mag_min[1], mag_max[1], -1, 1)
z = map_range(z, mag_min[2], mag_max[2], -1, 1)
print(" \tCalibrated: ", x, y, z)
compass_heading = (math.atan2(y, x) * 180) / math.pi
if compass_heading < 0:
compass_heading += 360
print("Heading: ", compass_heading)
time.sleep(0.5)

+ 74
- 27
src/glitterpos.py View File

@ -2,16 +2,23 @@
import time
import gc
import math
import adafruit_lsm9ds1
import adafruit_gps
import adafruit_rfm9x
import board
import busio
import crc16pure
import digitalio
import neopixel
import rtc
from glitterpos_util import timestamp, compass_bearing
from glitterpos_id import MY_ID
from glitterpos_util import timestamp, compass_bearing, bearing_to_pixel, map_range
# glitterpos_id.py should be formatted as follows:
#
# MY_ID = 2 # must be a unique integer
# MAG_MIN = (-0.25046, -0.23506, -0.322)
# MAG_MAX = (0.68278, 0.70882, 0.59654)
from glitterpos_id import MY_ID, MAG_MIN, MAG_MAX
# Colors for status lights, etc.
RED = (255, 0, 0)
@ -24,6 +31,10 @@ PURPLE = (180, 0, 255)
MAN_ID = 23
ELECTRICITY_ID = 42
# Magnetic North:
DECLINATION_RAD = 145.4 / 1000.0 # Lyons in radians
# DECLINATION = 235.27 / 1000.0 # Black Rock City in mrad
COLOR_LOOKUP = {
0: GREEN,
1: BLUE,
@ -64,6 +75,7 @@ class GlitterPOS:
# A tuple for our lat/long:
self.coords = (0, 0)
self.heading = 0.0
# Status light on the board, we'll use to indicate GPS fix, etc.:
self.statuslight = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.005, auto_write=True)
@ -71,35 +83,41 @@ class GlitterPOS:
# Neopixel ring:
self.pixels = neopixel.NeoPixel(board.A1, 16, brightness=0.01, auto_write=False)
# Startup animation:
for i in range(len(self.pixels)):
self.pixels[i] = GREEN
self.pixels.show()
time.sleep(0.2)
self.pixels.fill((0,0,0))
self.pixels[0] = PURPLE
self.pixels.show()
self.startup_animation()
time.sleep(2)
self.init_radio()
time.sleep(1)
self.init_gps()
time.sleep(1)
self.init_compass()
self.statuslight.fill(YELLOW)
def startup_animation(self):
self.pixels[bearing_to_pixel(0)] = PURPLE
self.pixels.show()
time.sleep(.5)
self.pixels[bearing_to_pixel(90)] = GREEN
self.pixels.show()
time.sleep(.5)
self.pixels[bearing_to_pixel(180)] = YELLOW
self.pixels.show()
time.sleep(.5)
self.pixels[bearing_to_pixel(270)] = BLUE
self.pixels.show()
def init_radio(self):
"""Set up RFM95."""
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
self.rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ)
self.rfm9x.tx_power = 18 # Default is 13 dB, but the RFM95 can go up to 23 dB
self.radio_tx('d', 'hello world')
time.sleep(1)
def init_gps(self):
"""Some GPS module setup."""
uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=3000)
gps = adafruit_gps.GPS(uart)
time.sleep(1)
# https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf
# Turn on the basic GGA and RMC info (what you typically want), then
@ -109,22 +127,25 @@ class GlitterPOS:
self.gps = gps
def init_compass(self):
i2c = busio.I2C(board.SCL, board.SDA)
self.compass = adafruit_lsm9ds1.LSM9DS1_I2C(i2c)
time.sleep(1)
def advance_frame(self):
"""Essentially our main program loop."""
current = time.monotonic()
self.radio_rx(timeout=0.5)
new_gps_data = self.gps.update()
self.update_heading()
self.display_pixels()
if not self.gps.has_fix:
# Try again if we don't have a fix yet.
self.statuslight.fill(RED)
return
self.display_pixels()
# We want to send coordinates out either on new GPS data or roughly every 15 seconds.
if (not new_gps_data) and (current - self.last_send < 15):
return
@ -148,7 +169,7 @@ class GlitterPOS:
self.gps.latitude,
self.gps.longitude,
self.gps.speed_knots,
self.gps.track_angle_deg
self.heading
)
print(' quality: {}'.format(self.gps.fix_quality))
@ -157,15 +178,33 @@ class GlitterPOS:
# Send a location packet:
self.radio_tx('l', send_packet)
def update_heading(self):
mag_x, mag_y, mag_z = self.compass.magnetometer
# print('Magnetometer: ({0:10.3f}, {1:10.3f}, {2:10.3f})'.format(mag_x, mag_y, mag_z))
mag_x = map_range(mag_x, MAG_MIN[0], MAG_MAX[0], -1, 1)
mag_y = map_range(mag_y, MAG_MIN[1], MAG_MAX[1], -1, 1)
mag_z = map_range(mag_z, MAG_MIN[2], MAG_MAX[2], -1, 1)
heading_mag = (math.atan2(mag_y, mag_x) * 180) / math.pi
if heading_mag < 0:
heading_mag = 360 + heading_mag
# Account for declination (given in radians above):
heading = heading_mag + (DECLINATION_RAD * 180 / math.pi)
if heading > 360:
heading = heading - 360
print('heading: {}'.format(heading))
self.heading = heading
def radio_tx(self, msg_type, msg):
"""send a packet over radio with id prefix and checksum"""
# XXX: implement checksum?
packet = 'e:' + msg_type + ':' + str(self.electron_id) + ':' + msg
packet_with_crc = packet + ':' + str(crc16pure.crc16xmodem(bytes(packet, 'ascii')))
print(' sending: ' + packet_with_crc)
print(' sending: ' + packet)
# Blocking, max of 252 bytes:
self.rfm9x.send(packet_with_crc)
self.rfm9x.send(packet)
self.last_send = time.monotonic()
def radio_rx(self, timeout=0.5):
@ -202,12 +241,20 @@ class GlitterPOS:
def display_pixels(self):
self.pixels.fill((0, 0, 0))
if not self.gps.has_fix:
return
for electron in self.electrons:
angle_to_electron = compass_bearing(self.coords, self.electrons[electron])
# print('angle to ' + str(electron) + ': ' + str(angle_to_electron))
bearing_to_electron = compass_bearing(self.coords, self.electrons[electron])
# Treat current compass heading as our origin point for display purposes:
display_bearing = bearing_to_electron - self.heading
if display_bearing < 0:
display_bearing = display_bearing + 360
pixel = bearing_to_pixel(display_bearing)
# print('display pixel: {}'.format(pixel))
# Subtract from 16 since the neopixel ring runs counterclockwise:
pixel = 16 - int(round((angle_to_electron / 360) * 16))
color = (15, 15, 15)
if electron in COLOR_LOOKUP:
color = COLOR_LOOKUP[electron]


+ 20
- 0
src/glitterpos_util.py View File

@ -1,3 +1,4 @@
"""a handful of utility functions used by GlitterPOS."""
import math
import time
@ -44,6 +45,25 @@ def compass_bearing(pointA, pointB):
return compass_bearing
def bearing_to_pixel(bearing, count=16):
# Subtract from count since the neopixel ring runs counterclockwise:
pixel = count - int(round((bearing / 360) * count))
if pixel == 16:
return 0
return pixel
def map_range(x, in_min, in_max, out_min, out_max):
"""
Maps a number from one range to another.
:return: Returns value mapped to new range
:rtype: float
"""
mapped = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
if out_min <= out_max:
return max(min(mapped, out_max), out_min)
return min(max(mapped, out_max), out_min)
def timestamp():
"""print a human-readable timestamp"""
timestamp = time.localtime()


+ 431
- 0
src/lib/adafruit_lsm9ds1.py View File

@ -0,0 +1,431 @@
# The MIT License (MIT)
#
# Copyright (c) 2017 Tony DiCola for Adafruit 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.
"""
`adafruit_lsm9ds1`
====================================================
CircuitPython module for the LSM9DS1 accelerometer, magnetometer, gyroscope.
Based on the driver from:
https://github.com/adafruit/Adafruit_LSM9DS1
See examples/simpletest.py for a demo of the usage.
* Author(s): Tony DiCola
"""
__version__ = "1.0.1"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LSM9DS1.git"
import time
try:
import struct
except ImportError:
import ustruct as struct
import adafruit_bus_device.i2c_device as i2c_device
import adafruit_bus_device.spi_device as spi_device
from micropython import const
# Internal constants and register values:
# pylint: disable=bad-whitespace
_LSM9DS1_ADDRESS_ACCELGYRO = const(0x6B)
_LSM9DS1_ADDRESS_MAG = const(0x1E)
_LSM9DS1_XG_ID = const(0b01101000)
_LSM9DS1_MAG_ID = const(0b00111101)
_LSM9DS1_ACCEL_MG_LSB_2G = 0.061
_LSM9DS1_ACCEL_MG_LSB_4G = 0.122
_LSM9DS1_ACCEL_MG_LSB_8G = 0.244
_LSM9DS1_ACCEL_MG_LSB_16G = 0.732
_LSM9DS1_MAG_MGAUSS_4GAUSS = 0.14
_LSM9DS1_MAG_MGAUSS_8GAUSS = 0.29
_LSM9DS1_MAG_MGAUSS_12GAUSS = 0.43
_LSM9DS1_MAG_MGAUSS_16GAUSS = 0.58
_LSM9DS1_GYRO_DPS_DIGIT_245DPS = 0.00875
_LSM9DS1_GYRO_DPS_DIGIT_500DPS = 0.01750
_LSM9DS1_GYRO_DPS_DIGIT_2000DPS = 0.07000
_LSM9DS1_TEMP_LSB_DEGREE_CELSIUS = 8 # 1°C = 8, 25° = 200, etc.
_LSM9DS1_REGISTER_WHO_AM_I_XG = const(0x0F)
_LSM9DS1_REGISTER_CTRL_REG1_G = const(0x10)
_LSM9DS1_REGISTER_CTRL_REG2_G = const(0x11)
_LSM9DS1_REGISTER_CTRL_REG3_G = const(0x12)
_LSM9DS1_REGISTER_TEMP_OUT_L = const(0x15)
_LSM9DS1_REGISTER_TEMP_OUT_H = const(0x16)
_LSM9DS1_REGISTER_STATUS_REG = const(0x17)
_LSM9DS1_REGISTER_OUT_X_L_G = const(0x18)
_LSM9DS1_REGISTER_OUT_X_H_G = const(0x19)
_LSM9DS1_REGISTER_OUT_Y_L_G = const(0x1A)
_LSM9DS1_REGISTER_OUT_Y_H_G = const(0x1B)
_LSM9DS1_REGISTER_OUT_Z_L_G = const(0x1C)
_LSM9DS1_REGISTER_OUT_Z_H_G = const(0x1D)
_LSM9DS1_REGISTER_CTRL_REG4 = const(0x1E)
_LSM9DS1_REGISTER_CTRL_REG5_XL = const(0x1F)
_LSM9DS1_REGISTER_CTRL_REG6_XL = const(0x20)
_LSM9DS1_REGISTER_CTRL_REG7_XL = const(0x21)
_LSM9DS1_REGISTER_CTRL_REG8 = const(0x22)
_LSM9DS1_REGISTER_CTRL_REG9 = const(0x23)
_LSM9DS1_REGISTER_CTRL_REG10 = const(0x24)
_LSM9DS1_REGISTER_OUT_X_L_XL = const(0x28)
_LSM9DS1_REGISTER_OUT_X_H_XL = const(0x29)
_LSM9DS1_REGISTER_OUT_Y_L_XL = const(0x2A)
_LSM9DS1_REGISTER_OUT_Y_H_XL = const(0x2B)
_LSM9DS1_REGISTER_OUT_Z_L_XL = const(0x2C)
_LSM9DS1_REGISTER_OUT_Z_H_XL = const(0x2D)
_LSM9DS1_REGISTER_WHO_AM_I_M = const(0x0F)
_LSM9DS1_REGISTER_CTRL_REG1_M = const(0x20)
_LSM9DS1_REGISTER_CTRL_REG2_M = const(0x21)
_LSM9DS1_REGISTER_CTRL_REG3_M = const(0x22)
_LSM9DS1_REGISTER_CTRL_REG4_M = const(0x23)
_LSM9DS1_REGISTER_CTRL_REG5_M = const(0x24)
_LSM9DS1_REGISTER_STATUS_REG_M = const(0x27)
_LSM9DS1_REGISTER_OUT_X_L_M = const(0x28)
_LSM9DS1_REGISTER_OUT_X_H_M = const(0x29)
_LSM9DS1_REGISTER_OUT_Y_L_M = const(0x2A)
_LSM9DS1_REGISTER_OUT_Y_H_M = const(0x2B)
_LSM9DS1_REGISTER_OUT_Z_L_M = const(0x2C)
_LSM9DS1_REGISTER_OUT_Z_H_M = const(0x2D)
_LSM9DS1_REGISTER_CFG_M = const(0x30)
_LSM9DS1_REGISTER_INT_SRC_M = const(0x31)
_MAGTYPE = True
_XGTYPE = False
_SENSORS_GRAVITY_STANDARD = 9.80665
# User facing constants/module globals.
ACCELRANGE_2G = (0b00 << 3)
ACCELRANGE_16G = (0b01 << 3)
ACCELRANGE_4G = (0b10 << 3)
ACCELRANGE_8G = (0b11 << 3)
MAGGAIN_4GAUSS = (0b00 << 5) # +/- 4 gauss
MAGGAIN_8GAUSS = (0b01 << 5) # +/- 8 gauss
MAGGAIN_12GAUSS = (0b10 << 5) # +/- 12 gauss
MAGGAIN_16GAUSS = (0b11 << 5) # +/- 16 gauss
GYROSCALE_245DPS = (0b00 << 4) # +/- 245 degrees/s rotation
GYROSCALE_500DPS = (0b01 << 4) # +/- 500 degrees/s rotation
GYROSCALE_2000DPS = (0b11 << 4) # +/- 2000 degrees/s rotation
# pylint: enable=bad-whitespace
def _twos_comp(val, bits):
# Convert an unsigned integer in 2's compliment form of the specified bit
# length to its signed integer value and return it.
if val & (1 << (bits - 1)) != 0:
return val - (1 << bits)
return val
class LSM9DS1:
"""Driver for the LSM9DS1 accelerometer, magnetometer, gyroscope."""
# Class-level buffer for reading and writing data with the sensor.
# This reduces memory allocations but means the code is not re-entrant or
# thread safe!
_BUFFER = bytearray(6)
def __init__(self):
# soft reset & reboot accel/gyro
self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG8, 0x05)
# soft reset & reboot magnetometer
self._write_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M, 0x0C)
time.sleep(0.01)
# Check ID registers.
if self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_WHO_AM_I_XG) != _LSM9DS1_XG_ID or \
self._read_u8(_MAGTYPE, _LSM9DS1_REGISTER_WHO_AM_I_M) != _LSM9DS1_MAG_ID:
raise RuntimeError('Could not find LSM9DS1, check wiring!')
# enable gyro continuous
self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G, 0xC0) # on XYZ
# Enable the accelerometer continous
self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG5_XL, 0x38)
self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL, 0xC0)
# enable mag continuous
self._write_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG3_M, 0x00)
# Set default ranges for the various sensors
self._accel_mg_lsb = None
self._mag_mgauss_lsb = None
self._gyro_dps_digit = None
self.accel_range = ACCELRANGE_2G
self.mag_gain = MAGGAIN_4GAUSS
self.gyro_scale = GYROSCALE_245DPS
@property
def accel_range(self):
"""Get and set the accelerometer range. Must be a value of:
- ACCELRANGE_2G
- ACCELRANGE_4G
- ACCELRANGE_8G
- ACCELRANGE_16G
"""
reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL)
return (reg & 0b00011000) & 0xFF
@accel_range.setter
def accel_range(self, val):
assert val in (ACCELRANGE_2G, ACCELRANGE_4G, ACCELRANGE_8G,
ACCELRANGE_16G)
reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL)
reg = (reg & ~(0b00011000)) & 0xFF
reg |= val
self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL, reg)
if val == ACCELRANGE_2G:
self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_2G
elif val == ACCELRANGE_4G:
self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_4G
elif val == ACCELRANGE_8G:
self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_8G
elif val == ACCELRANGE_16G:
self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_16G
@property
def mag_gain(self):
"""Get and set the magnetometer gain. Must be a value of:
- MAGGAIN_4GAUSS
- MAGGAIN_8GAUSS
- MAGGAIN_12GAUSS
- MAGGAIN_16GAUSS
"""
reg = self._read_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M)
return (reg & 0b01100000) & 0xFF
@mag_gain.setter
def mag_gain(self, val):
assert val in (MAGGAIN_4GAUSS, MAGGAIN_8GAUSS, MAGGAIN_12GAUSS,
MAGGAIN_16GAUSS)
reg = self._read_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M)
reg = (reg & ~(0b01100000)) & 0xFF
reg |= val
self._write_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M, reg)
if val == MAGGAIN_4GAUSS:
self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_4GAUSS
elif val == MAGGAIN_8GAUSS:
self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_8GAUSS
elif val == MAGGAIN_12GAUSS:
self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_12GAUSS
elif val == MAGGAIN_16GAUSS:
self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_16GAUSS
@property
def gyro_scale(self):
"""Get and set the gyroscope scale. Must be a value of:
- GYROSCALE_245DPS
- GYROSCALE_500DPS
- GYROSCALE_2000DPS
"""
reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G)
return (reg & 0b00110000) & 0xFF
@gyro_scale.setter
def gyro_scale(self, val):
assert val in (GYROSCALE_245DPS, GYROSCALE_500DPS, GYROSCALE_2000DPS)
reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G)
reg = (reg & ~(0b00110000)) & 0xFF
reg |= val
self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G, reg)
if val == GYROSCALE_245DPS:
self._gyro_dps_digit = _LSM9DS1_GYRO_DPS_DIGIT_245DPS
elif val == GYROSCALE_500DPS:
self._gyro_dps_digit = _LSM9DS1_GYRO_DPS_DIGIT_500DPS
elif val == GYROSCALE_2000DPS:
self._gyro_dps_digit = _LSM9DS1_GYRO_DPS_DIGIT_2000DPS
def read_accel_raw(self):
"""Read the raw accelerometer sensor values and return it as a
3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you
want the acceleration in nice units you probably want to use the
accelerometer property!
"""
# Read the accelerometer
self._read_bytes(_XGTYPE, 0x80 | _LSM9DS1_REGISTER_OUT_X_L_XL, 6,
self._BUFFER)
raw_x, raw_y, raw_z = struct.unpack_from('<hhh', self._BUFFER[0:6])
return (raw_x, raw_y, raw_z)
@property
def accelerometer(self):
"""Get the accelerometer X, Y, Z axis values as a 3-tuple of
m/s^2 values.
"""
raw = self.read_accel_raw()
return map(lambda x: x * self._accel_mg_lsb / 1000.0 * _SENSORS_GRAVITY_STANDARD,
raw)
def read_mag_raw(self):
"""Read the raw magnetometer sensor values and return it as a
3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you
want the magnetometer in nice units you probably want to use the
magnetometer property!
"""
# Read the magnetometer
self._read_bytes(_MAGTYPE, 0x80 | _LSM9DS1_REGISTER_OUT_X_L_M, 6,
self._BUFFER)
raw_x, raw_y, raw_z = struct.unpack_from('<hhh', self._BUFFER[0:6])
return (raw_x, raw_y, raw_z)
@property
def magnetometer(self):
"""Get the magnetometer X, Y, Z axis values as a 3-tuple of
gauss values.
"""
raw = self.read_mag_raw()
return map(lambda x: x * self._mag_mgauss_lsb / 1000.0, raw)
def read_gyro_raw(self):
"""Read the raw gyroscope sensor values and return it as a
3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you
want the gyroscope in nice units you probably want to use the
gyroscope property!
"""
# Read the gyroscope
self._read_bytes(_XGTYPE, 0x80 | _LSM9DS1_REGISTER_OUT_X_L_G, 6,
self._BUFFER)
raw_x, raw_y, raw_z = struct.unpack_from('<hhh', self._BUFFER[0:6])
return (raw_x, raw_y, raw_z)
@property
def gyroscope(self):
"""Get the gyroscope X, Y, Z axis values as a 3-tuple of
degrees/second values.
"""
raw = self.read_mag_raw()
return map(lambda x: x * self._gyro_dps_digit, raw)
def read_temp_raw(self):
"""Read the raw temperature sensor value and return it as a 12-bit
signed value. If you want the temperature in nice units you probably
want to use the temperature property!
"""
# Read temp sensor
self._read_bytes(_XGTYPE, 0x80 | _LSM9DS1_REGISTER_TEMP_OUT_L, 2,
self._BUFFER)
temp = ((self._BUFFER[1] << 8) | self._BUFFER[0]) >> 4
return _twos_comp(temp, 12)
@property
def temperature(self):
"""Get the temperature of the sensor in degrees Celsius."""
# This is just a guess since the starting point (21C here) isn't documented :(
# See discussion from:
# https://github.com/kriswiner/LSM9DS1/issues/3
temp = self.read_temp_raw()
temp = 27.5 + temp/16
return temp
def _read_u8(self, sensor_type, address):
# Read an 8-bit unsigned value from the specified 8-bit address.
# The sensor_type boolean should be _MAGTYPE when talking to the
# magnetometer, or _XGTYPE when talking to the accel or gyro.
# MUST be implemented by subclasses!
raise NotImplementedError()
def _read_bytes(self, sensor_type, address, count, buf):
# Read a count number of bytes into buffer from the provided 8-bit
# register address. The sensor_type boolean should be _MAGTYPE when
# talking to the magnetometer, or _XGTYPE when talking to the accel or
# gyro. MUST be implemented by subclasses!
raise NotImplementedError()
def _write_u8(self, sensor_type, address, val):
# Write an 8-bit unsigned value to the specified 8-bit address.
# The sensor_type boolean should be _MAGTYPE when talking to the
# magnetometer, or _XGTYPE when talking to the accel or gyro.
# MUST be implemented by subclasses!
raise NotImplementedError()
class LSM9DS1_I2C(LSM9DS1):
"""Driver for the LSM9DS1 connect over I2C."""
def __init__(self, i2c):
self._mag_device = i2c_device.I2CDevice(i2c, _LSM9DS1_ADDRESS_MAG)
self._xg_device = i2c_device.I2CDevice(i2c, _LSM9DS1_ADDRESS_ACCELGYRO)
super().__init__()
def _read_u8(self, sensor_type, address):
if sensor_type == _MAGTYPE:
device = self._mag_device
else:
device = self._xg_device
with device as i2c:
self._BUFFER[0] = address & 0xFF
i2c.write(self._BUFFER, end=1, stop=False)
i2c.readinto(self._BUFFER, end=1)
return self._BUFFER[0]
def _read_bytes(self, sensor_type, address, count, buf):
if sensor_type == _MAGTYPE:
device = self._mag_device
else:
device = self._xg_device
with device as i2c:
buf[0] = address & 0xFF
i2c.write(buf, end=1, stop=False)
i2c.readinto(buf, end=count)
def _write_u8(self, sensor_type, address, val):
if sensor_type == _MAGTYPE:
device = self._mag_device
else:
device = self._xg_device
with device as i2c:
self._BUFFER[0] = address & 0xFF
self._BUFFER[1] = val & 0xFF
i2c.write(self._BUFFER, end=2)
class LSM9DS1_SPI(LSM9DS1):
"""Driver for the LSM9DS1 connect over SPI."""
def __init__(self, spi, xgcs, mcs):
self._mag_device = spi_device.I2CDevice(spi, mcs)
self._xg_device = spi_device.I2CDevice(spi, xgcs)
super().__init__()
def _read_u8(self, sensor_type, address):
if sensor_type == _MAGTYPE:
device = self._mag_device
else:
device = self._xg_device
with device as spi:
spi.configure(baudrate=200000, phase=0, polarity=0)
self._BUFFER[0] = (address | 0x80) & 0xFF
spi.write(self._BUFFER, end=1)
spi.readinto(self._BUFFER, end=1)
return self._BUFFER[0]
def _read_bytes(self, sensor_type, address, count, buf):
if sensor_type == _MAGTYPE:
device = self._mag_device
else:
device = self._xg_device
with device as spi:
spi.configure(baudrate=200000, phase=0, polarity=0)
buf[0] = (address | 0x80) & 0xFF
spi.write(buf, end=1)
spi.readinto(buf, end=count)
def _write_u8(self, sensor_type, address, val):
if sensor_type == _MAGTYPE:
device = self._mag_device
else:
device = self._xg_device
with device as spi:
spi.configure(baudrate=200000, phase=0, polarity=0)
self._BUFFER[0] = (address & 0x7F) & 0xFF
self._BUFFER[1] = val & 0xFF
spi.write(self._BUFFER, end=2)

Loading…
Cancel
Save