2022-10-02 20:41:51 +01:00
/*
2022-10-05 14:00:03 +01:00
xsns_33_qmc5883l . ino - QMC5883L 3 - Axis Digital Compass sensor support for Tasmota
2022-10-02 20:41:51 +01:00
Copyright ( C ) 2022 Helge Scheunemann
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2022-10-05 14:00:03 +01:00
*/
2022-10-02 20:41:51 +01:00
2022-10-05 14:00:03 +01:00
# ifdef USE_I2C
# ifdef USE_QMC5883L
/*********************************************************************************************\
* QMC5883L is 3 - Axis Digital Compass sensor
*
* I2C Address : 0x0D
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
2022-10-02 20:41:51 +01:00
DATASHEET
The QMC5883L is a multi - chip three - axis magnetic sensor . This
surface - mount , small sized chip has integrated magnetic sensors with
signal condition ASIC , targeted for high precision applications such as
compassing , navigation and gaming in drone , robot , mobile and
personal hand - held devices .
The QMC5883L is based on our state - of - the - art , high resolution ,
magneto - resistive technology licensed from Honeywell AMR technology .
Along with custom - designed 16 - bit ADC ASIC , it offers the advantages of
low noise , high accuracy , low power consumption , offset cancellation and
temperature compensation . QMC5883L enables 1 ° to 2 ° compass
heading accuracy . The I ² C serial bus allows for easy interface
9.1 Register Map
The table below provides a list of the 8 - bit registers embedded in the device and their respective function and
addresses
Table 13. Register Map
Addr . 7 6 5 4 3 2 1 0 Access
00 H Data Output X LSB Register XOUT [ 7 : 0 ] Read only
01 H Data Output X MSB Register XOUT [ 15 : 8 ] Read only
02 H Data Output Y LSB Register YOUT [ 7 : 0 ] Read only
03 H Data Output Y MSB Register YOUT [ 15 : 8 ] Read only
04 H Data Output Z LSB Register ZOUT [ 7 : 0 ] Read only
05 H Data Output Z MSB Register ZOUT [ 15 : 8 ] Read only
06 H DOR OVL DRDY Read only
07 H TOUT [ 7 : 0 ] Read only
08 H TOUT [ 15 : 8 ] Read only
09 H OSR [ 1 : 0 ] RNG [ 1 : 0 ] ODR [ 1 : 0 ] MODE [ 1 : 0 ] Read / Write
0 AH SOFT_RST ROL_P NT INT_E NB R / W , Read only on blanks
0 BH SET / RESET Period FBR [ 7 : 0 ] Read / Write
0 CH Reserved Read only
0 DH Reserved Read only
9.2 Register Definition
9.2 .1 Output Data Register
Registers 00 H ~ 05 H store the measurement data from each axis magnetic sensor in continuous - measurement .
In the continuous measurement mode , the output data is refreshed periodically based on the data update rate
ODR setup in control registers 1. The data stays the same , regardless of reading status through I2C , until new
data replaces them . Each axis has 16 bit data width in 2 ’ s complement , i . e . , MSB of 01 H / 03 H / 05 H indicates the
sign of each axis . The output data of each channel saturates at - 32768 and 32767.
Table 14. Output Data Register
Addr . 7 6 5 4 3 2 1 0
00 H Data Output X LSB Register XOUT [ 7 : 0 ]
01 H Data Output X MSB Register XOUT [ 15 : 8 ]
02 H Data Output Y LSB Register YOUT [ 7 : 0 ]
03 H Data Output Y MSB Register YOUT [ 15 : 8 ]
04 H Data Output Z LSB Register ZOUT [ 7 : 0 ]
05 H Data Output Z MSB Register ZOUT [ 15 : 8 ]
9.2 .2 Status Register
There are two status registers located in address 06 H and 0 CH .
Register 06 H has three bits indicating for status flags , the rest are reserved for factory use . The status registers
are read only bits .
Table 15. Status Register 1
Addr . 7 6 5 4 3 2 1 0
06 H DOR OVL DRDY
Data Ready Register ( DRDY ) , it is set when all three axis data is ready , and loaded to the output data registers in
the continuous measurement mode . It is reset to “ 0 ” by reading any data register ( 00 H ~ 05 H ) through I2C
commends
DRDY : “ 0 ” : no new data , “ 1 ” : new data is ready
Overflow flag ( OVL ) is set to “ 1 ” if any data of three axis magnetic sensor channels is out of range . The output
data of each axis saturates at - 32768 and 32767 , if any of the axis exceeds this range , OVL flag is set to “ 1 ” . This
flag is reset to “ 0 ” if next measurement goes back to the range of ( - 32768 , 32767 ) , otherwise , it keeps as “ 1 ” .
OVL : “ 0 ” : normal , “ 1 ” : data overflow
Data Skip ( DOR ) bit is set to “ 1 ” if all the channels of output data registers are skipped in reading in the
continuous - measurement mode . It is reset to “ 0 ” by reading any data register ( 00 H ~ 05 H ) through I2C
DOR : “ 0 ” : normal , “ 1 ” : data skipped for reading
9.2 .3 Temperature Data Registers
Registers 07 H - 08 H store temperature sensor output data . 16 bits temperature sensor output is in 2 ’ s complement .
Temperature sensor gain is factory - calibrated , but its offset has not been compensated , only relative temperature
value is accurate . The temperature coefficient is about 100 LSB / ℃
Table 17. Temperature Sensor Output
Addr . 7 6 5 4 3 2 1 0
07 H TOUT [ 7 : 0 ]
08 H TOUT [ 15 : 8 ]
9.2 .4 Control Registers
Two 8 - bits registers are used to control the device configurations .
Control register 1 is located in address 09 H , it sets the operational modes ( MODE ) . output data update rate
( ODR ) , magnetic field measurement range or sensitivity of the sensors ( RNG ) and over sampling rate ( OSR ) .
Control register 2 is located in address 0 AH . It controls Interrupt Pin enabling ( INT_ENB ) , Point roll over function
enabling ( POL_PNT ) and soft reset ( SOFT_RST ) .
Two bits of MODE registers can transfer mode of operations in the device , the two modes are Standby , and
Continuous measurements . The default mode after Power - on - Reset ( POR ) is standby . There is no any restriction
in the transferring between the modes .
Output data rate is controlled by ODR registers . Four data update frequencies can be selected : 10 Hz , 50 Hz ,
100 Hz and 200 Hz . For most of compassing applications , we recommend 10 Hz for low power consumption . For
gaming , the high update rate such as 100 Hz or 200 Hz can be used .
Field ranges of the magnetic sensor can be selected through the register RNG . The full scale field range is
determined by the application environments . For magnetic clear environment , low field range such as + / - 2 gauss
can be used . The field range goes hand in hand with the sensitivity of the magnetic sensor . The lowest field range
has the highest sensitivity , therefore , higher resolution .
Over sample Rate ( OSR ) registers are used to control bandwidth of an internal digital filter . Larger OSR value
leads to smaller filter bandwidth , less in - band noise and higher power consumption . It could be used to reach a
good balance between noise and power . Four over sample ratio can be selected , 64 , 128 , 256 or 512.
Table 18. Control Register 1
Addr 7 6 5 4 3 2 1 0
09 H OSR [ 1 : 0 ] RNG [ 1 : 0 ] ODR [ 1 : 0 ] MODE [ 1 : 0 ]
Reg . Definition 00 01 10 11
Mode Mode Control Standby Continuous Reserve Reserve
ODR Output Data Rate 10 Hz 50 Hz 100 Hz 200 Hz
RNG Full Scale 2 G 8 G Reserve Reserve
OSR Over Sample Ratio 512 256 128 64
Interrupt enabling is controlled by register INT_ENB in control register 2. Once the interrupt is enabled , it will flag
when new data is in Data Output Registers .
INT_ENB : “ 0 ” : enable interrupt PIN , “ 1 ” : disable interrupt PIN
Pointer roll - over function is controlled by ROL_PNT register . When the point roll - over function is enabled , the I2C
data pointer automatically rolls between 00 H ~ 06 H , if I2C read begins at any address among 00 H ~ 06 H .
ROL_PNT : “ 0 ” : Normal , “ 1 ” : Enable pointer roll - over function
Soft Reset can be done by changing the register SOFT_RST to set . Soft reset can be invoked at any time of any
mode . For example , if soft reset occurs at the middle of continuous mode reading , QMC5883L immediately
switches to standby mode due to mode register is reset to “ 00 ” in default .
SOFT_RST : “ 0 ” : Normal “ 1 ” : Soft reset , restore default value of all registers .
Table 19. Control Register 2
Addr . 7 6 5 4 3 2 1 0
0 AH SOFT_RST ROL_PNT INT_ENB
9.2 .5 SET / RESET Period Register
SET / RESET Period is controlled by FBR [ 7 : 0 ] , it is recommended that the register 0 BH is written by 0x01 .
Table 20. SET / RESET Period Register
Addr . 7 6 5 4 3 2 1 0
0 BH SET / RESET Perio [ 7 : 0 ]
*/
// Define driver ID
# define XSNS_33 33
# define XI2C_71 71 // See I2CDEVICES.md
2022-10-05 14:00:03 +01:00
# ifndef QMC5883L_TEMP_SHIFT
# define QMC5883L_TEMP_SHIFT 23 // sensor temperature are not calibrated (only relativ measurement) and need an absolute ground value in °C (see datasheet)
# endif
2022-10-02 20:41:51 +01:00
/* The default I2C address of this chip */
# define QMC5883L_ADDR 0x0D
/* Register numbers */
# define QMC5883L_X_LSB 0x00
# define QMC5883L_X_MSB 0x01
# define QMC5883L_Y_LSB 0x02
# define QMC5883L_Y_MSB 0x03
# define QMC5883L_Z_LSB 0x04
# define QMC5883L_Z_MSB 0x05
# define QMC5883L_STATUS 0x06
# define QMC5883L_TEMP_LSB 0x07
# define QMC5883L_TEMP_MSB 0x08
# define QMC5883L_CONFIG 0x09
# define QMC5883L_CONFIG2 0x0a
# define QMC5883L_RESET 0x0b
# define QMC5883L_RESERVED 0x0c
# define QMC5883L_CHIP_ID 0x0d
/* Bit values for the STATUS register */
# define QMC5883L_STATUS_DRDY 1
# define QMC5883L_STATUS_OVL 2
# define QMC5883L_STATUS_DOR 4
/* Oversampling values for the CONFIG register */
# define QMC5883L_CONFIG_OS512 0b00000000
# define QMC5883L_CONFIG_OS256 0b01000000
# define QMC5883L_CONFIG_OS128 0b10000000
# define QMC5883L_CONFIG_OS64 0b11000000
/* Range values for the CONFIG register */
# define QMC5883L_CONFIG_2GAUSS 0b00000000
# define QMC5883L_CONFIG_8GAUSS 0b00010000
/* Rate values for the CONFIG register */
# define QMC5883L_CONFIG_10HZ 0b00000000
# define QMC5883L_CONFIG_50HZ 0b00000100
# define QMC5883L_CONFIG_100HZ 0b00001000
# define QMC5883L_CONFIG_200HZ 0b00001100
/* Mode values for the CONFIG register */
# define QMC5883L_CONFIG_STANDBY 0b00000000
# define QMC5883L_CONFIG_CONT 0b00000001
/* Mode values for the CONFIG2 register */
# define QMC5883L_CONFIG2_RESET 0b10000000
2022-10-04 14:48:27 +01:00
// data field
2022-10-05 14:00:03 +01:00
struct QMC5883L_s {
2022-10-04 14:48:27 +01:00
int16_t MX , MY , MZ ;
2022-10-05 14:00:03 +01:00
float temp ;
2022-10-04 14:48:27 +01:00
uint16_t scalar ;
} * QMC5883L = nullptr ;
2022-10-02 20:41:51 +01:00
// Initialise the device
2022-10-05 14:00:03 +01:00
void QMC5883L_Init ( ) {
if ( ! I2cSetDevice ( QMC5883L_ADDR ) ) { return ; }
2022-10-02 20:41:51 +01:00
// reset QMC5883L
2022-10-05 14:00:03 +01:00
if ( I2cWrite8 ( QMC5883L_ADDR , QMC5883L_CONFIG2 , QMC5883L_CONFIG2_RESET ) = = false ) { return ; }
// Software Reset
if ( I2cWrite8 ( QMC5883L_ADDR , QMC5883L_RESET , 0x01 ) = = false ) { return ; }
2022-10-02 20:41:51 +01:00
// write config
2022-10-05 14:00:03 +01:00
if ( I2cWrite8 ( QMC5883L_ADDR , QMC5883L_CONFIG , QMC5883L_CONFIG_OS256 | QMC5883L_CONFIG_8GAUSS | QMC5883L_CONFIG_100HZ | QMC5883L_CONFIG_CONT ) = = false ) { return ; }
I2cSetActiveFound ( QMC5883L_ADDR , " QMC5883L " ) ;
2022-10-04 20:54:15 +01:00
QMC5883L = ( QMC5883L_s * ) calloc ( 1 , sizeof ( struct QMC5883L_s ) ) ;
2022-10-02 20:41:51 +01:00
}
//Read the magnetic data
2022-10-05 14:00:03 +01:00
void QMC5883L_read_data ( void ) {
2022-10-02 20:41:51 +01:00
// check if chip is ready to provice data
2022-10-05 14:00:03 +01:00
if ( ! ( I2cRead8 ( QMC5883L_ADDR , QMC5883L_STATUS ) & QMC5883L_STATUS_DRDY ) ) { return ; } // Chip not yet ready, next round try again
2022-10-04 14:48:27 +01:00
2022-10-05 14:00:03 +01:00
QMC5883L - > MX = I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_X_LSB ) ; // Select LSB register
2022-10-04 14:48:27 +01:00
QMC5883L - > MY = I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_Y_LSB ) ;
QMC5883L - > MZ = I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_Z_LSB ) ;
2022-10-02 20:41:51 +01:00
// calculate scalar magnetic induction
2022-10-05 14:00:03 +01:00
// QMC5883L->scalar = sqrt((QMC5883L->MX * QMC5883L->MX) + (QMC5883L->MY * QMC5883L->MY) + (QMC5883L->MZ * QMC5883L->MZ)); // 650 bytes larger
QMC5883L - > scalar = SqrtInt ( ( QMC5883L - > MX * QMC5883L - > MX ) + ( QMC5883L - > MY * QMC5883L - > MY ) + ( QMC5883L - > MZ * QMC5883L - > MZ ) ) ;
2022-10-04 14:48:27 +01:00
// get temperature
2022-10-05 14:00:03 +01:00
QMC5883L - > temp = ConvertTemp ( ( I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_TEMP_LSB ) / 100 ) + QMC5883L_TEMP_SHIFT ) ; // Temp in celsius
2022-10-02 20:41:51 +01:00
}
/*********************************************************************************************\
* Presentation
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef USE_WEBSERVER
const char HTTP_SNS_QMC5883L [ ] PROGMEM =
2022-10-05 14:00:03 +01:00
" {s}QMC5883L " D_MX " {m}%d " D_UNIT_MICROTESLA " {e} " // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
" {s}QMC5883L " D_MY " {m}%d " D_UNIT_MICROTESLA " {e} " // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
" {s}QMC5883L " D_MZ " {m}%d " D_UNIT_MICROTESLA " {e} " // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
" {s}QMC5883L " D_MAGNETICFLD " {m}%d " D_UNIT_MICROTESLA " {e} " ; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
2022-10-02 20:41:51 +01:00
# endif
2022-10-05 14:00:03 +01:00
void QMC5883L_Show ( uint8_t json ) {
if ( json ) {
ResponseAppend_P ( PSTR ( " , \" QMC5883L \" :{ \" " D_JSON_MX " \" :%d, \" " D_JSON_MY " \" :%d, \" " D_JSON_MZ " \" :%d, \" " D_JSON_MAGNETICFLD " \" :%u, \" " D_JSON_TEMPERATURE " \" :%*_f} " ) ,
QMC5883L - > MX , QMC5883L - > MY , QMC5883L - > MZ , QMC5883L - > scalar , Settings - > flag2 . temperature_resolution , & QMC5883L - > temp ) ;
2022-10-02 20:41:51 +01:00
# ifdef USE_WEBSERVER
2022-10-05 14:00:03 +01:00
} else {
WSContentSend_PD ( HTTP_SNS_QMC5883L , QMC5883L - > MX , QMC5883L - > MY , QMC5883L - > MZ , QMC5883L - > scalar ) ;
WSContentSend_Temp ( " QMC5883L " , QMC5883L - > temp ) ;
2022-10-02 20:41:51 +01:00
# endif
2022-10-05 14:00:03 +01:00
}
2022-10-02 20:41:51 +01:00
}
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-05 14:00:03 +01:00
2022-11-11 09:44:56 +00:00
bool Xsns33 ( uint32_t function ) {
2022-10-05 14:00:03 +01:00
if ( ! I2cEnabled ( XI2C_71 ) ) { return false ; }
if ( FUNC_INIT = = function ) {
2022-10-04 17:16:49 +01:00
QMC5883L_Init ( ) ;
}
2022-10-05 14:00:03 +01:00
else if ( QMC5883L ! = nullptr ) {
switch ( function ) {
2022-10-04 14:48:27 +01:00
case FUNC_JSON_APPEND :
QMC5883L_Show ( 1 ) ;
break ;
case FUNC_EVERY_SECOND :
QMC5883L_read_data ( ) ;
break ;
2022-10-02 20:41:51 +01:00
# ifdef USE_WEBSERVER
2022-10-04 14:48:27 +01:00
case FUNC_WEB_SENSOR :
QMC5883L_Show ( 0 ) ;
break ;
2022-10-02 20:41:51 +01:00
# endif // USE_WEBSERVER
2022-10-04 17:16:49 +01:00
}
2022-10-02 20:41:51 +01:00
}
2022-10-03 19:42:28 +01:00
return true ;
2022-10-02 20:41:51 +01:00
}
2022-10-05 14:00:03 +01:00
# endif // USE_QMC5883L
# endif // USE_I2C