User avatar
frazelle09
Posts: 70
Joined: Mon May 07, 2012 5:04 am
Location: Mexicali, Baja California, Mexico

How do we get the ADXL345 to output angles?

Sat May 07, 2016 8:28 pm

Joan has very kindly provided us with some code

Code: Select all

#!/usr/bin/env python

# i2c_ADXL345.py  este es el accelerómetro de 3 ejes
# 2015-04-01
# Public Domain

import time
import struct
import sys

import pigpio # http://abyz.co.uk/rpi/pigpio/python.html

if sys.version > '3':
   buffer = memoryview

BUS=0

ADXL345_I2C_ADDR=0x53

#RUNTIME=60.0 This is the original line.
RUNTIME=0.10

pi=pigpio.pi() # open local Pi

h = pi.i2c_open(BUS, ADXL345_I2C_ADDR)

if h >= 0: # Connected OK?

   # Initialise ADXL345.
   pi.i2c_write_byte_data(h, 0x2d, 0)  # POWER_CTL reset.
   pi.i2c_write_byte_data(h, 0x2d, 8)  # POWER_CTL measure.
   pi.i2c_write_byte_data(h, 0x31, 0)  # DATA_FORMAT reset.
   pi.i2c_write_byte_data(h, 0x31, 11) # DATA_FORMAT full res +/- 16g.

   read = 0

   start_time = time.time()

   while (time.time()-start_time) < RUNTIME:

      # 0x32 = X LSB, 0x33 = X MSB
      # 0x34 = Y LSB, 0x35 = Y MSB
      # 0x36 = Z LSB, 0x37 = Z MSB

      # < = little endian

      (s, b) = pi.i2c_read_i2c_block_data(h, 0x32, 6)

      if s >= 0:
         (x, y, z) = struct.unpack('<3h', buffer(b))
         print("{} {} {}".format(x, y, z))
         read += 1

   pi.i2c_close(h)

pi.stop()

print(read, read/RUNTIME)
which we are trying to insert into Jay Dosher's program (https://github.com/polyideas/Raspberry- ... ib/cgi-bin). We have finally been able to "insert/substitute" hers into his but have come to find that the values that the ADXL produces are not angles but some sort of gravity measuements - lol. We understand that they can be translated into angles but haven't been able to find anything on the net which might appear to work with Joan's code which uses her pigpio program.

How might it be possible to convert these values into angles?

Have a great afternoon! :)

Edit: The following is our "version" of Jay's program as of this writing... It shows the incorporation of Joan's scripts.

Code: Select all

#solarrobot7-core.py to be placed in /tools/
#!/usr/bin/python

#Version Notes
#39: Brought over from solar robot 7, cleanup of OpenElectrons code (going back to a Pololu controller)
#40: Code optimizations for Raspberry Pi A+
#42: Code cleanup, added debug toggle

from __future__ import print_function
import time, math
import serial, pysolar, datetime
from dual_mc33926_rpi import motors, MAX_SPEED

import os
assert os.path.exists('/tools/inputs/masterinputs.txt')

#digital stuff
import RPi.GPIO as GPIO


#Beginning of Joan's script for ADXL345
#!/usr/bin/env python

# i2c_ADXL345.py  this is the 3 axis accelerometer
# 2015-04-01 And is Joan's script
# Public Domain
# Note - Don't forget to run pigpiod first

import time
import struct
import sys

import pigpio # http://abyz.co.uk/rpi/pigpio/python.html

if sys.version > '3':
   buffer = memoryview

BUS=0

ADXL345_I2C_ADDR=0x53

pi=pigpio.pi() # open local Pi

h = pi.i2c_open(BUS, ADXL345_I2C_ADDR)

if h >= 0: # Connected OK?

   # Initialise ADXL345.
   pi.i2c_write_byte_data(h, 0x2d, 0)  # POWER_CTL reset.
   pi.i2c_write_byte_data(h, 0x2d, 8)  # POWER_CTL measure.
   pi.i2c_write_byte_data(h, 0x31, 0)  # DATA_FORMAT reset.
   pi.i2c_write_byte_data(h, 0x31, 11) # DATA_FORMAT full res +/- 16g.

   read = 0

(s, b) = pi.i2c_read_i2c_block_data(h, 0x32, 6)

if s >= 0:
         (accelerometerx, y, z) = struct.unpack('<3h', buffer(b))
         print("{}".format(accelerometerx))

         read += 1

pi.i2c_close(h)

pi.stop()

print()

#End of Joan's script for ADXL345

#Beginning of Joan's script for HMC338L

#!/usr/bin/env python

# i2c_HMC5883L.py  este es la brújula (compass) de 3 ejes
# 2015-04-01
# Public Domain

import pigpio # http://abyz.co.uk/rpi/pigpio/python.html

if sys.version > '3':
   buffer = memoryview

BUS=0
M_PI = 3.14159265358979323846

HMC5883L_I2C_ADDR=0x1E

pi=pigpio.pi() # open local Pi

h = pi.i2c_open(BUS, HMC5883L_I2C_ADDR)

if h >= 0: # Connected OK?

   # Initialise HMC5883L.
   pi.i2c_write_byte_data(h, 0x00, 0xF8)  # CRA 75Hz.
   pi.i2c_write_byte_data(h, 0x02, 0x00)  # Mode continuous reads.

   read = 0

(s, b) = pi.i2c_read_i2c_block_data(h, 0x03, 6)

if s >= 0:
         (headingx, y, z) = struct.unpack('>3h', buffer(b))
         headingx = 180 * math.atan2(y,headingx)/M_PI
if headingx < 0:
         headingx += 360
         print("{} {} {}".format(headingx, y, z))
         read += 1

pi.i2c_close(h)

pi.stop()

#End of Joan's HMC5883 script.

#Beginning of Joan's ITG3205 gyroscope script

#!/usr/bin/env python

# i2c_ITG3205.py
# 2015-04-01
# Public Domain

import pigpio # http://abyz.co.uk/rpi/pigpio/python.html

if sys.version > '3':
   buffer = memoryview

BUS=0

ITG_3205_I2C_ADDR=0x68

# Registers.

ITG_3205_WHO_AM_I    = 0x00 # R/W
ITG_3205_SMPLRT_DIV  = 0x15 # R/W
ITG_3205_DLPF_FS     = 0x16 # R/W
ITG_3205_INT_CFG     = 0x17 # R/W
ITG_3205_INT_STATUS  = 0x1A # R
ITG_3205_TEMP_OUT_H  = 0x1B # R
ITG_3205_TEMP_OUT_L  = 0x1C # R
ITG_3205_GYRO_XOUT_H = 0x1D # R
ITG_3205_GYRO_XOUT_L = 0x1E # R
ITG_3205_GYRO_YOUT_H = 0x1F # R
ITG_3205_GYRO_YOUT_L = 0x20 # R
ITG_3205_GYRO_ZOUT_H = 0x21 # R
ITG_3205_GYRO_ZOUT_L = 0x22 # R
ITG_3205_PWR_MGM     = 0x3E # R/W

# DLPF_FS

ITG_3205_FS_SEL_2000_DEG_SEC = 0x18

ITG_3205_DLPF_CFG_256_8 = 0x00
ITG_3205_DLPF_CFG_188_1 = 0x01
ITG_3205_DLPF_CFG_98_1  = 0x02
ITG_3205_DLPF_CFG_42_1  = 0x03
ITG_3205_DLPF_CFG_20_1  = 0x04
ITG_3205_DLPF_CFG_10_1  = 0x05
ITG_3205_DLPF_CFG_5_1   = 0x06

# INT_CFG

ITG_3205_IC_ACTL             = 0x80
ITG_3205_IC_OPEN             = 0x40
ITG_3205_IC_LATCH_INT_EN     = 0x20
ITG_3205_IC_INT_ANYRD_2CLEAR = 0x10
ITG_3205_IC_ITG_RDY_EN       = 0x04
ITG_3205_IC_RAW_RDY_EN       = 0x01

# INT_STATUS

ITG_3205_IS_ITG_RDY      = 0x04
ITG_3205_IS_RAW_DATA_RDY = 0x01

# PWR_MGM

ITG_3205_PM_H_RESET = 0x80
ITG_3205_PM_SLEEP   = 0x40
ITG_3205_PM_STBY_XG = 0x20
ITG_3205_PM_STBY_YG = 0x10
ITG_3205_PM_STBY_ZG = 0x08

ITG_3205_PM_CLK_SEL_INTERNAL  = 0x00
ITG_3205_PM_CLK_SEL_X_GYRO    = 0x01
ITG_3205_PM_CLK_SEL_Y_GYRO    = 0x02
ITG_3205_PM_CLK_SEL_Z_GYRO    = 0x03
ITG_3205_PM_CLK_SEL_EXT_32768 = 0x04
ITG_3205_PM_CLK_SEL_EXT_192   = 0x05

pi=pigpio.pi() # open local Pi

h = pi.i2c_open(BUS, ITG_3205_I2C_ADDR)

if h >= 0: # Connected OK?

   # Initialise ITG_3205.

   pi.i2c_write_byte_data(h, ITG_3205_PWR_MGM, ITG_3205_PM_CLK_SEL_X_GYRO)
   #                         0x3E              0x01

   pi.i2c_write_byte_data(h, ITG_3205_SMPLRT_DIV, 0x07)
   #                         0x15                 0x07

   pi.i2c_write_byte_data(h, ITG_3205_DLPF_FS,
      ITG_3205_FS_SEL_2000_DEG_SEC | ITG_3205_DLPF_CFG_188_1)
   # 0x16  0x18| 0x01

   pi.i2c_write_byte_data(h, ITG_3205_INT_CFG, 0x00)
   # 0x17 0x00

   read = 0

(s, b) = pi.i2c_read_i2c_block_data(h, ITG_3205_TEMP_OUT_H, 8)

if s >= 0:
         (tiltt,tiltx, tilty, tiltz) = struct.unpack('>4h', buffer(b))
         tiltt = 35 + ((tiltt + 13200) / 280.0)
         print("{:.1f} {} {} {}".format(tiltt, tiltx, tilty, tiltz))
         read += 1

pi.i2c_close(h)

pi.stop()

print()

#End of Joan's ITG3205 script.

#support for storing our values in a config file
import configparser

# for the motor control we need the libraries for this controller:

import os, sys
print(((str(sys.argv[1:])[2:])[:-2]))
if (((str(sys.argv[1:])[2:])[:-2]) == "debug"):
	debug = True
else:
	debug = False

#read in all our variables
config = configparser.ConfigParser()
config.read("/tools/inputs/masterinputs.txt")
#our latitude
maplat = float(config.get("myvars", "maplat"))
#our longitude
maplon = float(config.get("myvars", "maplon"))
#time limits to keep the robot from doing crazy things
pan_time_limit = int(config.get("myvars", "pan_time_limit"))
tilt_time_limit = int(config.get("myvars", "tilt_time_limit"))

#the lowest angle the IMU and mechanical hardware will reliably support
lowestangle = int(config.get("myvars", "lowestangle"))

#motor speed settings
motor1max_speed = int(config.get("myvars", "motor1max_speed"))
#motor2 is the panning motor
motor2max_speed = int(config.get("myvars", "motor2max_speed"))

#sleep tolerance is the margin by which difference between dawn target and actual heading
#this keeps the robot from moving during the night as the compass shifts
sleep_tolerance = int(config.get("myvars", "sleep_tolerance"))

#This is my magnetic declination *offset*
#If your declination is 11, your offset is -11
MagneticDeclination = float(config.get("myvars", "MagneticDeclination"))

#Calibration of the IMU
HorizontalCalibration = int(config.get("myvars", "HorizontalCalibration"))
AngleOffset = int(config.get("myvars", "AngleOffset"))

#Since the heading can fluctuate, we give a small margin to the compass
#Smaller numbers mean more accurate heading, but motor must go slower
hmargin = int(config.get("myvars", "hmargin"))

#Pololu Motor Stuff
# Set up sequences of motor speeds.
test_forward_speeds = list(range(0, MAX_SPEED, 1)) + [MAX_SPEED] * 200 + list(range(MAX_SPEED, 0, -1)) + [0]
test_reverse_speeds = list(range(0, -MAX_SPEED, -1)) + [-MAX_SPEED] * 200 + list(range(-MAX_SPEED, 0, 1)) + [0]
motors.enable()
motors.setSpeeds(0, 0)

#prep the digital ports
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #this pin is for the override mode switch
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #this pin is for horizon mode, on=do no motor movement at all
GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #unused but wired
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #unused but wired

#These are our global motor speed variables- don't touch
global motor2speed
motor2speed = 0

#Open the serial port for the IMU
#serialport = serial.Serial("/dev/ttyAMA0", 57600, timeout=5)

#Make sure the motors aren't doing anything before we start
motors.setSpeeds(0, 0)

#Calibrate the heading
Declination = MagneticDeclination + HorizontalCalibration

#Give the serial port time to open, so wait 2 seconds
#time.sleep(1)

#Parse the IMU string to get TILT, which we don't really use
#Besides making sure the robot hasn't turned on its side
#Not sure that this formula replacemente is correct yet or not.
def getcurtilt():
	curtilt = tiltx
	return curtilt

#Parse the IMU string to get HEADING
def getcurheading():
	curheading = headingx
	return curheading

#Parse the IMU string to get the ANGLE OF INCLINE (forwards/backwards)
#This is what we use for the solar panels, so we have to switch
#from 0 degrees on the normal axis to 0 degrees on the horizon
def getcurangle():
	curangle = accelerometerx #Este es un "guess" - todavía no sé si es accelerometría ó qué
	return curangle + AngleOffset

#For troubleshooting, we use raw Azimuth from the calc
#Since this is in Azimuth:
# and we need true heading:
# and Azimuth actually is the direction of the shadow, not the sun
from pysolar import solar
def getrawazimuth():
	Azimuth = solar.get_azimuth(maplat, maplon, datetime.datetime.utcnow())
	return Azimuth

#Convert Azimuth (the direction of the shadow, degrees from south)
# to heading, we have to deal with a few cases
def getsolarheading():
	Azimuth = solar.get_azimuth(maplat, maplon, datetime.datetime.utcnow())
	if Azimuth < 0:
		if (Azimuth >= -180):
			solarheading = ((Azimuth * -1) + 180)
		if (Azimuth < -180):
			solarheading = ((Azimuth * -1) - 180)
	if Azimuth >= 0:
		solarheading = Azimuth
	return solarheading


def tomorrow_heading():
	increment_min = 1
	incrementeddatetime = 0
	tomorrow_corrected = 90
	if solar.get_altitude(maplat, maplon, datetime.datetime.utcnow()) < 0:
		while solar.get_altitude(maplat, maplon, (datetime.datetime.utcnow() + datetime.timedelta(minutes=incrementeddatetime))) < 0:
		  incrementeddatetime = incrementeddatetime + increment_min
		sunrise_time=(datetime.datetime.utcnow() + datetime.timedelta(minutes=incrementeddatetime))
		tomorrow_heading = solar.get_azimuth(maplat, maplon, sunrise_time)
		if tomorrow_heading < 0:
			if (tomorrow_heading >= -180):
				tomorrow_corrected = ((tomorrow_heading * -1) + 180)
			if (tomorrow_heading < -180):
				tomorrow_corrected = ((tomorrow_heading * -1) - 180)
		if tomorrow_heading >= 0:
			tomorrow_corrected = tomorrow_heading
	return tomorrow_corrected

def getsolarangle(): #to rotate
	solarangle = solar.get_altitude(maplat, maplon, datetime.datetime.utcnow())
	return solarangle

def motor2neg():
	global motor2speed
	if (motor2speed < motor2max_speed):
		motor2speed = motor2speed + 5
	motors.motor2.setSpeed((motor2speed*-1))
	return

def motor2backup():
	motor2speed = 0
	backupsecs = 4
	backup_start_time = datetime.datetime.utcnow()
	while (datetime.datetime.utcnow() < (backup_start_time + datetime.timedelta(seconds=backupsecs))):
		while motor2speed < motor2max_speed:
			motor2speed = motor2speed + 1
#Backup
#Doesn't do anything right now
	return

def motor2pos():
	global motor2speed
	if (motor2speed < motor2max_speed):
		motor2speed = motor2speed + 5
	motors.motor2.setSpeed(motor2speed)
	return

tomorrow_static = tomorrow_heading()
#Here we check to make sure horizon (19) and overide (16) digital pins aren't on
#print("GPIO 16 (ovveride) is " + str(GPIO.input(16)))
#print("GPIO 19 (horizon) is " + str(GPIO.input(19)))
#print(GPIO.input(19))
if (GPIO.input(16) == False) and (GPIO.input(19) == False): #check to see if the passive mode switch is on
# GPIO 16 is for override and GPIO 19 is for horizon mode

#In this section we rotate as needed using motor2
	starttime = datetime.datetime.utcnow()
	if (getcurheading() > getsolarheading()) and (getsolarangle() > 2) and (getcurheading() != 999):
		while (getcurheading() > (getsolarheading() + hmargin)) and (starttime + datetime.timedelta(seconds=pan_time_limit) > datetime.datetime.utcnow()):
			if debug == True:
				print("1: Moving " + str(getcurheading()) + " to " + str(getsolarheading()))
			motor2neg()
		motors.setSpeeds(0, 0)

	starttime = datetime.datetime.utcnow()
	if (getcurheading() < getsolarheading()) and (getsolarangle() > 2) and (getcurheading() != 999):
		while (getcurheading() < (getsolarheading() - hmargin)) and (starttime + datetime.timedelta(seconds=pan_time_limit) > datetime.datetime.utcnow()):
			if debug == True:
				print("2: Moving " + str(getcurheading()) + " to " + str(getsolarheading()))
			motor2pos()
		motors.setSpeeds(0, 0)

	starttime = datetime.datetime.utcnow()
	if (getcurheading() > tomorrow_static) and (getsolarangle()<0) and (getcurheading() != 999):
		if (getcurheading() - tomorrow_static) > sleep_tolerance:
			while (getcurheading() > (tomorrow_static + hmargin)) and (starttime + datetime.timedelta(seconds=pan_time_limit) > datetime.datetime.utcnow()):
				if debug == True:
					print("3: Moving " + str(getcurheading()) + " to " + str(tomorrow_static + hmargin))
				motor2neg()
			motors.setSpeeds(0, 0)

	starttime = datetime.datetime.utcnow()
	if (getcurheading() < tomorrow_static) and (getsolarangle()<0) and (getcurheading != 999):
		if (tomorrow_static - getcurheading()) > sleep_tolerance:
			while (getcurheading() < (tomorrow_static - hmargin)) and (starttime + datetime.timedelta(seconds=pan_time_limit) > datetime.datetime.utcnow()):
				if debug == True:
					print("4: Moving " + str(getcurheading()) + " to " + str(tomorrow_static + hmargin))
				motor2pos()
			motors.setSpeeds(0, 0)

#This is horizon mode- if the GPIO switch for pin 19 is flipped
#we lower the panel to 20 degrees. This is good for installing the solar panel,
#since it keeps the robot from moving any more while we're working on it
if (GPIO.input(19) == True) and (GPIO.input(16) == False):
	while (getcurangle() > 20):
		motor1lower()
	motors.setSpeeds(0, 0)

logsolar = open('/tools/inputs/solarvalues.txt','w')
writeline=("[myvars]\n")
logsolar.write(writeline)
writeline=("solar_heading: " + str(round((float(getsolarheading())),1)) + "\n")
logsolar.write(writeline)
writeline=("solar_elevation: " + str(round((float(getsolarangle())),1))+ "\n")
logsolar.write(writeline)
print ("Getcurangle: ", getcurangle())
writeline=("actual_elevation: " + str(round((float(getcurangle())),1))+ "\n")
logsolar.write(writeline)
print ("Getcurheading: ", getcurheading())
writeline=("actual_heading: " + str(round((float(getcurheading())),1))+ "\n")
logsolar.write(writeline)
logsolar.close()

print (datetime.datetime.now(), ",", 'rawazimuth',getrawazimuth(), ",", 'solarheading',getsolarheading(), ",", 'curheading',getcurheading(), ",", 'solarangle',getsolarangle(),",", 'curangle',getcurangle())

motors.setSpeeds(0, 0)
motors.disable()
"The earth is one country and mankind its citizens."
Bahá'u'lláh
"La tierra es un sólo país y la humanidad sus ciudadanos."

scotty101
Posts: 3735
Joined: Fri Jun 08, 2012 6:03 pm

Re: How do we get the ADXL345 to output angles?

Mon May 09, 2016 10:31 am

Google is your friend

You need to have the Pi perform some trigonometry to convert the acceleration values to angles.

The link below gives an example of a method of doing this in C rather than python but the algorithm is the same.

http://wizmoz.blogspot.co.uk/2013/01/si ... js44Sjbntq
Last edited by scotty101 on Mon May 09, 2016 10:53 am, edited 1 time in total.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

TheRayTracer
Posts: 51
Joined: Tue Oct 13, 2015 12:23 am
Location: Australia

Re: How do we get the ADXL345 to output angles?

Mon May 09, 2016 10:49 am

but have come to find that the values that the ADXL produces are not angles but some sort of gravity measuements - lol.
Yes, the ADXL class of devices measure acceleration (and thus gravity). I would highly recommend that you read the datasheet: http://www.analog.com/media/en/technica ... DXL345.pdf

Next I would recommend that you read the AN-1057 for this device: http://www.analog.com/media/en/technica ... N-1057.pdf This AN includes the calculations you need to perform to measure the angles. Don't worry, they are an easy read.

Lastly, I have some Python code for this device that I have abandoned. Not because it is faulty, but rather my project requirements changed. While the code does not calculate angles, it does make it easy to read and work with the device:

Code: Select all

import sys
import spidev
import time
import math
import RPi.GPIO as gpio

EARTH_GRAVITY_MS2       =  9.80665
SCALE_MULTIPLIER        =  0.00390 # per LSB
OFFSET_SCALE_MULTIPLIER = 63.50000 # 1G / 0.0156 per LSB
THRESH_SCALE_MULTIPLIER = 16.00000 # 1G / 0.0625 per LSB

DEVID                   = 0x00

NO_OP                   = 0x00
WRITE_OP                = 0x00
MB_OP                   = 0x40
READ_OP                 = 0x80

OFFSET_BLOCK            = 0x1E
OFFSET_X                = 0x1E
OFFSET_Y                = 0x1F
OFFSET_Z                = 0x20

ACT_THRESH              = 0x24
INACT_THRESH            = 0x25
INACT_TIME              = 0x26
ACT_INACT_CTL           = 0x27

BW_RATE                 = 0x2C
POWER_CTL               = 0x2D
INT_ENABLE              = 0x2E
INT_MAP                 = 0x2F
INT_SOURCE              = 0x30
DATA_FORMAT             = 0x31
AXES_DATA_BLOCK         = 0x32

BW_RATE_1600HZ          = 0x0F
BW_RATE_800HZ           = 0x0E
BW_RATE_400HZ           = 0x0D
BW_RATE_200HZ           = 0x0C
BW_RATE_100HZ           = 0x0B
BW_RATE_50HZ            = 0x0A
BW_RATE_25HZ            = 0x09

RANGE_2G                = 0x00
RANGE_4G                = 0x01
RANGE_8G                = 0x02
RANGE_16G               = 0x03
FULL_RES                = 0x08

MEASURE                 = 0x08

INACT_ENABLE_Z          = 0x01
INACT_ENABLE_Y          = 0x02
INACT_ENABLE_X          = 0x04

ACT_ENABLE_Z            = 0x10
ACT_ENABLE_Y            = 0x20
ACT_ENABLE_X            = 0x40

INT_ENABLE_INACTIVITY   = 0x08
INT_ENABLE_ACTIVITY     = 0x10

INT_MAP_INACTIVITY_INT1 = 0x00
INT_MAP_INACTIVITY_INT2 = 0x08
INT_MAP_ACTIVITY_INT1   = 0x00
INT_MAP_ACTIVITY_INT2   = 0x10

class ADXL345:
    def __init__(self, cs):
        self.cs = cs
        self.offset = {"x": 0, "y": 0, "z": 0}
        # Setup GPIO.
        gpio.setmode(gpio.BCM)
        gpio.setup(self.cs, gpio.OUT)
        gpio.output(self.cs, gpio.HIGH)
        self.__OpenSPI() # Setup SPI.
        self.__Setup()
        return

    def __OpenSPI(self):
        self.spi = spidev.SpiDev()
        self.spi.open(0, 0)
        self.spi.mode = 3
        self.spi.max_speed_hz = 5000000
        return

    def __Setup(self):
        self.__WriteCommand(BW_RATE, [BW_RATE_100HZ])
        self.__WriteCommand(DATA_FORMAT, [FULL_RES | RANGE_16G])
        self.__WriteCommand(POWER_CTL, [MEASURE])
        time.sleep(0.1)
        return

    def __WriteCommand(self, address, data):
        address = (address | WRITE_OP)
        if len(data) > 1:
            address = address | MB_OP
        if isinstance(data, list) or isinstance(data, tuple):
            gpio.output(self.cs, gpio.LOW)
            self.spi.xfer2([address] + data + [NO_OP])
            gpio.output(self.cs, gpio.HIGH)
        return

    def __ReadCommand(self, address, n = 1):
        address = (address | READ_OP)
        if n > 1:
            address = address | MB_OP
        gpio.output(self.cs, gpio.LOW)
        t = self.spi.xfer2([address] + ([NO_OP] * n))
        gpio.output(self.cs, gpio.HIGH)
        return t

    def Calibrate(self, samples = 10, x = 0.0, y = 0.0, z = 1.0, t = 0.05):
        self.__WriteCommand(OFFSET_BLOCK, [0x00, 0x00, 0x00])
        time.sleep(0.1)
        data = self.GetMeasures(0.0, 0.0, 0.0)
        self.offset['x'] = -data['x'] + x
        self.offset['y'] = -data['y'] + y
        self.offset['z'] = -data['z'] + z
        time.sleep(0.1)
        for i in xrange(1, samples, 1):
            data = self.GetMeasures(0.0, 0.0, 0.0)
            self.offset['x'] = (self.offset['x'] + -data['x'] + x) / 2.0
            self.offset['y'] = (self.offset['y'] + -data['y'] + y) / 2.0
            self.offset['z'] = (self.offset['z'] + -data['z'] + z) / 2.0
            time.sleep(0.1)
        xo = int(self.offset['x'] * OFFSET_SCALE_MULTIPLIER)
        yo = int(self.offset['y'] * OFFSET_SCALE_MULTIPLIER)
        zo = int(self.offset['z'] * OFFSET_SCALE_MULTIPLIER)
        self.__WriteCommand(OFFSET_BLOCK, [min(127, max(-127, xo)), min(127, max(-127, yo)), min(127, max(-127, zo))])
        time.sleep(0.1)
        calibration = {"x": True, "y": True, "z": True}
        data = self.GetMeasures(0.0, 0.0, 0.0)
        if abs(data['x']) > t:
            calibration['x'] = False
            self.__WriteCommand(OFFSET_X, [0x00])
        if abs(data['y']) > t:
            calibration['y'] = False
            self.__WriteCommand(OFFSET_Y, [0x00])
        if abs(data['z']) > t:
            calibration['z'] = False
            self.__WriteCommand(OFFSET_Z, [0x00])
        time.sleep(0.1)
        return calibration

    def CombinedAxisSqrtActivity(self, offset_x = 0.0, offset_y = 0.0, offset_z = 0.0):
        data = self.GetMeasures(offset_x, offset_y, offset_z)
        return math.sqrt(data['x'] * data['x'] + data['y'] * data['y'] + data['z'] * data['z'])

    def GetID(self):
        id = self.__ReadCommand(DEVID)
        return id[1]

    def GetMeasures(self, offset_x = 0.0, offset_y = 0.0, offset_z = 0.0):
        data = self.__ReadCommand(AXES_DATA_BLOCK, 6)
        x = int((data[2] << 8) | data[1])
        if x & 0x800:
            x = x - 0xFFFF
        y = int((data[4] << 8) | data[3])
        if y & 0x800:
            y = y - 0xFFFF
        z = int((data[6] << 8) | data[5])
        if z & 0x800:
            z = z - 0xFFFF
        x = x * SCALE_MULTIPLIER
        y = y * SCALE_MULTIPLIER
        z = z * SCALE_MULTIPLIER
        x = x + offset_x
        y = y + offset_y
        z = z + offset_z
        return {"x": x, "y": y, "z": z}

    def GetOffsetsFromCalibration(self):
        return self.offset

    def SetupInterruptAbove(self, gpio_pin, int_pin, axis_x, axis_y, axis_z, thresh):
        gpio.setup(gpio_pin, gpio.IN)
        to = int(thresh * THRESH_SCALE_MULTIPLIER)
        axs_ctl = 0
        if axis_x:
            axs_ctl = axs_ctl | ACT_ENABLE_X
        if axis_y:
            axs_ctl = axs_ctl | ACT_ENABLE_Y
        if axis_z:
            axs_ctl = axs_ctl | ACT_ENABLE_Z
        if int_pin == 1:
            int_map = INT_MAP_ACTIVITY_INT1
        elif int_pin == 2:
            int_map = INT_MAP_ACTIVITY_INT2
        else:
            raise ValueError('Invalid INT pin requested.')
        self.__WriteCommand(ACT_THRESH,    [min(0xFF, max(0x00, to))])
        self.__WriteCommand(ACT_INACT_CTL, [axs_ctl])
        self.__WriteCommand(INT_MAP,       [int_map])
        self.__WriteCommand(INT_ENABLE,    [INT_ENABLE_ACTIVITY])
        time.sleep(0.1)
        return

    def SetupInterruptBelow(self, gpio_pin, int_pin, axis_x, axis_y, axis_z, thresh, t_time = 0):
        gpio.setup(gpio_pin, gpio.IN)
        to = int(thresh * THRESH_SCALE_MULTIPLIER)
        axs_ctl = 0
        if axis_x:
            axs_ctl = axs_ctl | INACT_ENABLE_X
        if axis_y:
            axs_ctl = axs_ctl | INACT_ENABLE_Y
        if axis_z:
            axs_ctl = axs_ctl | INACT_ENABLE_Z
        if int_pin == 1:
            int_map = INT_MAP_ACTIVITY_INT1
        elif int_pin == 2:
            int_map = INT_MAP_ACTIVITY_INT2
        else:
            raise ValueError('Invalid INT pin requested.')
        self.__WriteCommand(INACT_THRESH,  [min(0xFF, max(0x00, to))])
        self.__WriteCommand(ACT_INACT_CTL, [axs_ctl])
        self.__WriteCommand(INT_MAP,       [int_map])
        self.__WriteCommand(INACT_TIME,    [t_time])
        self.__WriteCommand(INT_ENABLE,    [INT_ENABLE_INACTIVITY])
        time.sleep(0.1)
        return

    def ResetInterrupts(self):
        self.__WriteCommand(ACT_THRESH,    [0x00])
        self.__WriteCommand(INACT_THRESH,  [0x00])
        self.__WriteCommand(ACT_INACT_CTL, [0x00])
        self.__WriteCommand(INT_MAP,       [0x00])
        self.__WriteCommand(INACT_TIME,    [0x00])
        self.__WriteCommand(INT_ENABLE,    [0x00])
        time.sleep(0.1)
        return

    def Remove(self):
        self.ResetInterrupts()
        gpio.cleanup()
        return

    def ClearInterrupts(self):
        self.__ReadCommand(INT_SOURCE) # Just reading shall clear the flags.
        return

def ConvertGravityToAcceleration(g):
    return g * EARTH_GRAVITY_MS2

def ConvertAccelerationToGravity(a):
    return a / EARTH_GRAVITY_MS2

ADXL_CS  = 17
INT1_PIN = 23
INT2_PIN = 24

if __name__ == "__main__":
    adxl345 = ADXL345(ADXL_CS)
    print "Device ID = 0x%x" % (adxl345.GetID())
    axis_health = adxl345.Calibrate(10, 0.0, 0.0, 1.0, 0.05)
    axis_offset = adxl345.GetOffsetsFromCalibration()
    if axis_health['x'] == False:
        print "Device x-axis calibration: Failed. Using software offset."
    else:
        print "Device x-axis calibration: Success."
        axis_offset['x'] = 0.0
    if axis_health['y'] == False:
        print "Device y-axis calibration: Failed. Using software offset."
    else:
        print "Device y-axis calibration: Success."
        axis_offset['y'] = 0.0
    if axis_health['z'] == False:
        print "Device z-axis calibration: Failed. Using software offset."
    else:
        print "Device z-axis calibration: Success."
        axis_offset['z'] = 0.0
    if axis_health['x'] or axis_health['y'] or axis_health['z']:
        print "Setting up an Interrupt (2G)."
        adxl345.SetupInterruptAbove(INT1_PIN, 1, axis_health['x'], axis_health['y'], axis_health['z'], 2.0)
        adxl345.ClearInterrupts()
        try:
            while True:
                input_state = gpio.input(INT1_PIN)
                if input_state != False:
                    print "Interrupt fired!"
                    adxl345.ClearInterrupts()
                    data = adxl345.GetMeasures(axis_offset['x'], axis_offset['y'], axis_offset['z'])
                    print "x = %.3f" % (data['x'])
                    print "y = %.3f" % (data['y'])
                    print "z = %.3f" % (data['z'])
                    print ""
        finally:
            adxl345.Remove()
    else:
        print "Unable to setup an interrupt. Using manual detection."
        try:
            while True:
                if adxl345.CombinedAxisSqrtActivity(axis_offset['x'], axis_offset['y'], axis_offset['z']) > 1.2:
                    print "Movement detected!"
                    data = adxl345.GetMeasures(axis_offset['x'], axis_offset['y'], axis_offset['z'])
                    print "x = %.3f" % (data['x'])
                    print "y = %.3f" % (data['y'])
                    print "z = %.3f" % (data['z'])
                    print ""
                    time.sleep(1.0)
        finally:
            adxl345.Remove()

User avatar
frazelle09
Posts: 70
Joined: Mon May 07, 2012 5:04 am
Location: Mexicali, Baja California, Mexico

Re: How do we get the ADXL345 to output angles?

Mon May 09, 2016 3:08 pm

scotty101 and TheRayTracer! Thanks for your input! It looks like we have some more reading to do.

Thanks again and have a great morning! :)

User avatar
mwilliams03
Posts: 123
Joined: Mon Oct 29, 2012 2:56 pm
Contact: Website

Re: How do we get the ADXL345 to output angles?

Tue May 10, 2016 6:05 am

some more python code here, which is for a difference accelerometer...but the math should be the same;
https://github.com/mwilliams03/BerryIMU ... -simple.py

A snippet that would interest you;

Code: Select all

RAD_TO_DEG = 57.29578
M_PI = 3.14159265358979323846

#Convert raw Accelerometer values to degrees
AccXangle =  (math.atan2(ACCy,ACCz)+M_PI)*RAD_TO_DEG
AccYangle =  (math.atan2(ACCz,ACCx)+M_PI)*RAD_TO_DEG
ACCy, ACCz and ACCx above are raw values from the accelerometer
Mark -- OzzMaker.com -- Creator of;
+ BerryIMU
+ Berry GPS-IMU
+BerryGPS-GSM
+ PiScreen - A 3.5" TFT for the Rasspberry Pi

User avatar
frazelle09
Posts: 70
Joined: Mon May 07, 2012 5:04 am
Location: Mexicali, Baja California, Mexico

Re: How do we get the ADXL345 to output angles?

Tue May 10, 2016 6:13 am

mwilliams - uf - this looks like a winner. Want to try it out... tomorrow. It's a little late right now, but thanks.

Again, many thanks a lot for your work and posting and have a great evening! :)

Edit: BTW, i think i had seen that script before in one of my many searches but couldn't figure out which part of the program to use - lol. Just goes to show how little of a programmer i am, but i keep hoping that what i lack in programming skills i can make up for in perserverance...

Return to “Automation, sensing and robotics”