Skip to content

Commit 20ae439

Browse files
committed
Module for HMC5883L magnetometer
1 parent 33e530e commit 20ae439

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Python library for HMC5883L magnetometer.
5+
6+
# Code based on:
7+
# * http://think-bowl.com/i2c-python-libraries-for-the-raspberry-pi/
8+
# License: http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US
9+
#
10+
# * https://github.com/adafruit/Adafruit_HMC5883_Unified
11+
# License:
12+
# Permission is hereby granted, free of charge, to any person obtaining a
13+
# copy of this software and associated documentation files (the "Software"),
14+
# to deal in the Software without restriction, including without limitation
15+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
16+
# and/or sell copies of the Software, and to permit persons to whom the
17+
# Software is furnished to do so, subject to the following conditions:
18+
19+
# The above copyright notice and this permission notice shall be included
20+
# in all copies or substantial portions of the Software.
21+
22+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28+
# DEALINGS IN THE SOFTWARE.
29+
30+
from Adafruit_I2C import Adafruit_I2C
31+
import math
32+
33+
class Adafruit_HMC5883L(object):
34+
35+
# Default device address
36+
HMC5883_ADDRESS = (0x3C >> 1)
37+
38+
# REGISTERS
39+
HMC5883_REGISTER_MAG_CRA_REG_M = 0x00
40+
HMC5883_REGISTER_MAG_CRB_REG_M = 0x01
41+
HMC5883_REGISTER_MAG_MR_REG_M = 0x02
42+
HMC5883_REGISTER_MAG_OUT_X_H_M = 0x03
43+
HMC5883_REGISTER_MAG_OUT_X_L_M = 0x04
44+
HMC5883_REGISTER_MAG_OUT_Z_H_M = 0x05
45+
HMC5883_REGISTER_MAG_OUT_Z_L_M = 0x06
46+
HMC5883_REGISTER_MAG_OUT_Y_H_M = 0x07
47+
HMC5883_REGISTER_MAG_OUT_Y_L_M = 0x08
48+
HMC5883_REGISTER_MAG_SR_REG_Mg = 0x09
49+
HMC5883_REGISTER_MAG_IRA_REG_M = 0x0A
50+
HMC5883_REGISTER_MAG_IRB_REG_M = 0x0B
51+
HMC5883_REGISTER_MAG_IRC_REG_M = 0x0C
52+
HMC5883_REGISTER_MAG_TEMP_OUT_H_M = 0x31
53+
HMC5883_REGISTER_MAG_TEMP_OUT_L_M = 0x32
54+
55+
# MAGNETOMETER GAIN SETTINGS
56+
# See http://www.adafruit.com/datasheets/HMC5883L_3-Axis_Digital_Compass_IC.pdf
57+
# (Reg , factor)
58+
HMC5883_MAGGAIN_0_88 = (0x00, 0.73) # +/- 0.88
59+
HMC5883_MAGGAIN_1_3 = (0x20, 0.92) # +/- 1.3
60+
HMC5883_MAGGAIN_1_9 = (0x40, 1.22) # +/- 1.9
61+
HMC5883_MAGGAIN_2_5 = (0x60, 1.52) # +/- 2.5
62+
HMC5883_MAGGAIN_4_0 = (0x80, 2.27) # +/- 4.0
63+
HMC5883_MAGGAIN_4_7 = (0xA0, 2.56) # +/- 4.7
64+
HMC5883_MAGGAIN_5_6 = (0xC0, 3.03) # +/- 5.6
65+
HMC5883_MAGGAIN_8_1 = (0xE0, 4.53) # +/- 8.1
66+
67+
HMC5883_MODE_CONTINUOUS = 0x00
68+
HMC5883_MODE_SINGLE = 0x01
69+
HMC5883_MODE_IDLE = 0x10
70+
71+
def __init__(self, busnum=-1, debug=False, declination=(0,0)):
72+
self.bus = Adafruit_I2C(self.HMC5883_ADDRESS, busnum, debug)
73+
self.setDeclination(declination[0], declination[1])
74+
# Set initial scaling factor
75+
self.gain=self.HMC5883_MAGGAIN_1_3
76+
self.setScale(self.gain)
77+
# Set measurement mode to continuous intially
78+
self.setMode(self.HMC5883_MODE_CONTINUOUS)
79+
80+
self.debug = debug
81+
82+
def __str__(self):
83+
ret_str = ""
84+
(x, y, z) = self.getAxes()
85+
ret_str += "Axis X: "+str(x)+"\n"
86+
ret_str += "Axis Y: "+str(y)+"\n"
87+
ret_str += "Axis Z: "+str(z)+"\n"
88+
ret_str += "Declination: "+ self.getDeclinationString() +"\n"
89+
ret_str += "Heading: " + self.getHeadingString() + "\n"
90+
return ret_str
91+
92+
def setMode(self, mode=None):
93+
if mode == None:
94+
self.mode = self.HMC5883_MODE_SINGLE
95+
else:
96+
self.mode = mode
97+
self.bus.write8(self.HMC5883_REGISTER_MAG_MR_REG_M, self.mode)
98+
99+
def setScale(self, gain):
100+
self.gain = gain
101+
self.setOption(self.HMC5883_REGISTER_MAG_CRB_REG_M, self.gain[0])
102+
103+
def setOption(self, register, *function_set):
104+
options = 0x00
105+
for function in function_set:
106+
options = options | function
107+
self.bus.write8(register, options)
108+
109+
# Adds to existing options of register
110+
def addOption(self, register, *function_set):
111+
options = self.bus.read_byte(register)
112+
for function in function_set:
113+
options = options | function
114+
self.bus.write8(register, options)
115+
116+
# Removes options of register
117+
def removeOption(self, register, *function_set):
118+
options = self.bus.read_byte(register)
119+
for function in function_set:
120+
options = options & (function ^ 0b11111111)
121+
self.bus.write8(register, options)
122+
123+
def setDeclination(self, degree, min = 0):
124+
self.declinationDeg = degree
125+
self.declinationMin = min
126+
self.declination = (degree+min/60) * (math.pi/180)
127+
128+
def getDeclination(self):
129+
return (self.declinationDeg, self.declinationMin)
130+
131+
def getDeclinationString(self):
132+
return str(self.declinationDeg)+"° "+str(self.declinationMin)+"'"
133+
134+
# Returns heading
135+
def getHeading(self, DMS=False):
136+
(scaled_x, scaled_y, scaled_z) = self.getAxes()
137+
138+
headingRad = math.atan2(scaled_y, scaled_x)
139+
headingRad += self.declination
140+
141+
# Correct for reversed heading
142+
if(headingRad < 0):
143+
headingRad += 2*math.pi
144+
145+
# Check for wrap and compensate
146+
if(headingRad > 2*math.pi):
147+
headingRad -= 2*math.pi
148+
149+
# Convert to degrees from radians
150+
headingDeg = headingRad * 180/math.pi
151+
degrees = math.floor(headingDeg)
152+
minutes = round(((headingDeg - degrees) * 60))
153+
if DMS:
154+
return (degrees, minutes)
155+
else:
156+
return headingDeg
157+
158+
def getHeadingString(self):
159+
(degrees, minutes) = self.getHeading(True)
160+
return str(degrees)+"° "+str(minutes)+"'"
161+
162+
def getAxes(self):
163+
(magno_x, magno_z, magno_y) = ( self.bus.readS16(self.HMC5883_REGISTER_MAG_OUT_X_H_M, False),
164+
self.bus.readS16(self.HMC5883_REGISTER_MAG_OUT_Z_H_M, False),
165+
self.bus.readS16(self.HMC5883_REGISTER_MAG_OUT_Y_H_M, False) )
166+
167+
if (magno_x == -4096):
168+
magno_x = None
169+
else:
170+
magno_x = round(magno_x * self.gain[1], 4)
171+
172+
if (magno_y == -4096):
173+
magno_y = None
174+
else:
175+
magno_y = round(magno_y * self.gain[1], 4)
176+
177+
if (magno_z == -4096):
178+
magno_z = None
179+
else:
180+
magno_z = round(magno_z * self.gain[1], 4)
181+
182+
return (magno_x, magno_y, magno_z)
183+
184+
# Simple example prints heading and x, y & z data once per second:
185+
if __name__ == '__main__':
186+
187+
from time import sleep
188+
189+
# Set declination for your location as (degrees, minutes)
190+
# See http://magnetic-declination.com
191+
mag = Adafruit_HMC5883L(declination=(11,35))
192+
193+
while True:
194+
print mag
195+
sleep(1) # Output is fun to watch if this is commented out

0 commit comments

Comments
 (0)