add new driver to support qmc5883L

This commit is contained in:
Helge 2022-10-02 21:41:51 +02:00
parent 16c6a471cc
commit c15d974b44
6 changed files with 418 additions and 0 deletions

View File

@ -105,3 +105,4 @@ Index | Define | Driver | Device | Address(es) | Description
68 | USE_HYT | xsns_97 | HYTxxx | 0x28 | Temperature and Humidity sensor
69 | USE_SGP40 | xsns_98 | SGP40 | 0x59 | Gas (TVOC) and air quality
70 | USE_LUXV30B | xsns_99 | LUXV30B | 0x4A | DFRobot SEN0390 V30B lux sensor
71 | USE_QMC5883L | xsns_33 | QMC5883L | 0x0D | Magnetic Field Sensor

View File

@ -233,6 +233,11 @@
#define D_JSON_SIGNALSTRENGTH "SignalStrength"
#define D_JSON_CHIPTEMPERATURE "ChipTemperature"
#define D_JSON_RAW "Raw"
#define D_JSON_MX "Compass X-Axis"
#define D_JSON_MY "Compass Y-Axis"
#define D_JSON_MZ "Compass Z-Axis"
#define D_JSON_HEADING "Compass Heading"
#define D_JSON_MAGNETICFLD "Magnetic Induction"
#define D_RSLT_ENERGY "ENERGY"
#define D_RSLT_HASS_STATE "HASS_STATE"

View File

@ -564,6 +564,13 @@
#define D_GY_AXIS "Gyroskop Y-Achse"
#define D_GZ_AXIS "Gyroskop Z-Achse"
// xsns_33_QMC5883L.ino
#define D_MX "Kompass X-Achse"
#define D_MY "Kompass Y-Achse"
#define D_MZ "Kompass Z-Achse"
#define D_HG "Kompass Richtung"
#define D_MAGNETICFLD "Magnet Feld Stärke"
// xsns_34_hx711.ino
#define D_HX_CAL_REMOVE "Wägegut entfernen"
#define D_HX_CAL_REFERENCE "Referenzgewicht auflegen"
@ -911,6 +918,7 @@
#define D_UNIT_MICROMETER "µm"
#define D_UNIT_MICROSECOND "µs"
#define D_UNIT_MICROSIEMENS_PER_CM "µS/cm"
#define D_UNIT_MICROTESLA "uT"
#define D_UNIT_MILLIAMPERE "mA"
#define D_UNIT_MILLILITERS "ml"
#define D_UNIT_MILLIMETER "mm"

View File

@ -564,6 +564,13 @@
#define D_GY_AXIS "Gyro Y-Axis"
#define D_GZ_AXIS "Gyro Z-Axis"
// xsns_33_QMC5883L.ino
#define D_MX "Compass X-Axis"
#define D_MY "Compass Y-Axis"
#define D_MZ "Compass Z-Axis"
#define D_HG "Compass Heading"
#define D_MAGNETICFLD "Magnetic Field Strength"
// xsns_34_hx711.ino
#define D_HX_CAL_REMOVE "Remove weight"
#define D_HX_CAL_REFERENCE "Load reference weight"
@ -911,6 +918,7 @@
#define D_UNIT_MICROMETER "µm"
#define D_UNIT_MICROSECOND "µs"
#define D_UNIT_MICROSIEMENS_PER_CM "µS/cm"
#define D_UNIT_MICROTESLA "uT"
#define D_UNIT_MILLIAMPERE "mA"
#define D_UNIT_MILLILITERS "ml"
#define D_UNIT_MILLIMETER "mm"

View File

@ -715,6 +715,8 @@
// Reference: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-led-backpack.pdf
// #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays
// #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D)
#define USE_ // have a compass sensor
#define USE_QMC5883L_Temp 22 // compass sensor temperatur are not calibrated (only relativ measurement) and need an absolute ground value in °C (see datasheet)
#endif // USE_I2C

View File

@ -0,0 +1,394 @@
/*
xsns_99_qmc5883l.ino - QMC5883L 3-Axis Digital Compass sensor support for Tasmota
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/>.
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
00H Data Output X LSB Register XOUT[7:0] Read only
01H Data Output X MSB Register XOUT[15:8] Read only
02H Data Output Y LSB Register YOUT[7:0] Read only
03H Data Output Y MSB Register YOUT[15:8] Read only
04H Data Output Z LSB Register ZOUT[7:0] Read only
05H Data Output Z MSB Register ZOUT[15:8] Read only
06H DOR OVL DRDY Read only
07H TOUT[7:0] Read only
08H TOUT[15:8] Read only
09H OSR[1:0] RNG[1:0] ODR[1:0] MODE[1:0] Read/Write
0AH SOFT_RST ROL_P NT INT_E NB R/W, Read only on blanks
0BH SET/RESET Period FBR [7:0] Read/Write
0CH Reserved Read only
0DH Reserved Read only
9.2 Register Definition
9.2.1 Output Data Register
Registers 00H ~ 05H 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 2s complement, i.e., MSB of 01H/03H/05H 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
00H Data Output X LSB Register XOUT[7:0]
01H Data Output X MSB Register XOUT[15:8]
02H Data Output Y LSB Register YOUT[7:0]
03H Data Output Y MSB Register YOUT[15:8]
04H Data Output Z LSB Register ZOUT[7:0]
05H Data Output Z MSB Register ZOUT[15:8]
9.2.2 Status Register
There are two status registers located in address 06H and 0CH.
Register 06H 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
06H 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 (00H~05H) 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 (00H~05H) through I2C
DOR: 0: normal, 1: data skipped for reading
9.2.3 Temperature Data Registers
Registers 07H-08H store temperature sensor output data. 16 bits temperature sensor output is in 2s 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
07H TOUT[7:0]
08H 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 09H, 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 0AH. 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: 10Hz, 50Hz,
100Hz and 200Hz. For most of compassing applications, we recommend 10 Hz for low power consumption. For
gaming, the high update rate such as 100Hz or 200Hz 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 +/- 2gauss
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
09H 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 10Hz 50Hz 100Hz 200Hz
RNG Full Scale 2G 8G 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 00H ~ 06H, if I2C read begins at any address among 00H~06H.
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: Normal1: Soft reset, restore default value of all registers.
Table 19. Control Register 2
Addr. 7 6 5 4 3 2 1 0
0AH 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 0BH is written by 0x01.
Table 20. SET/RESET Period Register
Addr. 7 6 5 4 3 2 1 0
0BH SET/RESET Perio [7:0]
*/
#ifdef USE_I2C
#ifdef USE_QMC5883L
/*********************************************************************************************\
* QMC5883L is 3-Axis Digital Compass sensor
*
* Source: Helge Scheunemann
*
* I2C Address: 0x0D
\*********************************************************************************************/
// Define driver ID
#define XSNS_33 33
#define XI2C_71 71 // See I2CDEVICES.md
/* 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
/* Apparently M_PI isn't available in all environments. */
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
// data field
struct {
int16_t MX = 0, MY = 0, MZ = 0, HG = 0;
int16_t xhigh = 0, yhigh = 0, xlow = 0, ylow = 0;
int16_t temp = 0;
uint16_t scalar = 0;
uint8_t status;
bool ready = false;
uint8_t i2c_address = QMC5883L_ADDR;
} QMC5883L;
void writeRegister(uint8_t reg, uint8_t val)
{
Wire.beginTransmission(QMC5883L.i2c_address); // start talking
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
int readRegister(uint8_t reg, uint8_t count)
{
Wire.beginTransmission(QMC5883L.i2c_address);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(QMC5883L.i2c_address, count);
int n = Wire.available();
if (n != count) return 0;
return n;
}
// Initialise the device
void QMC5883L_Init()
{
if (!I2cSetDevice(QMC5883L.i2c_address)) { return; }
I2cSetActiveFound(QMC5883L.i2c_address, "QMC5883L");
// reset QMC5883L
writeRegister(QMC5883L_CONFIG2,QMC5883L_CONFIG2_RESET); // Software Reset
writeRegister(QMC5883L_RESET, 0x01);
// write config
writeRegister(QMC5883L_CONFIG, QMC5883L_CONFIG_OS256 | QMC5883L_CONFIG_8GAUSS | QMC5883L_CONFIG_10HZ | QMC5883L_CONFIG_CONT);
QMC5883L.ready = true;
}
//Read the magnetic data
void QMC5883L_read_data(void)
{
if(!QMC5883L.ready) return;
// check if chip is ready to provice data
if (!readRegister(QMC5883L_STATUS, 1)) return; // read error
if (!(Wire.read() & QMC5883L_STATUS_DRDY)) return; // chip not yet ready, next round try again
// QMC5883 reading data
if (readRegister(QMC5883L_X_LSB, 6) != 6) return; // read error, select LSB register
QMC5883L.MX = Wire.read() | (Wire.read() << 8);
QMC5883L.MY = Wire.read() | (Wire.read() << 8);
QMC5883L.MZ = Wire.read() | (Wire.read() << 8);
int16_t x = QMC5883L.MX;
int16_t y = QMC5883L.MY;
// calculate azimut, heading
if(x < QMC5883L.xlow) QMC5883L.xlow = x;
if(x > QMC5883L.xhigh) QMC5883L.xhigh = x;
if(y < QMC5883L.ylow) QMC5883L.ylow = y;
if(y > QMC5883L.yhigh) QMC5883L.yhigh = y;
/* Bail out if not enough data is available. */
if( QMC5883L.xlow == QMC5883L.xhigh || QMC5883L.ylow == QMC5883L.yhigh ) return;
/* Recenter the measurement by subtracting the average */
x -= (QMC5883L.xhigh + QMC5883L.xlow) / 2;
y -= (QMC5883L.yhigh + QMC5883L.ylow) / 2;
/* Rescale the measurement to the range observed. */
float fx = (float) x / (QMC5883L.xhigh - QMC5883L.xlow);
float fy = (float) y / (QMC5883L.yhigh - QMC5883L.ylow);
x = -atan2(fy, fx) * 180.0 / M_PI;
x += 180; // no negative numbers
QMC5883L.HG = x;
// calculate scalar magnetic induction
QMC5883L.scalar = sqrt((QMC5883L.MX * QMC5883L.MX) + (QMC5883L.MY * QMC5883L.MY) + (QMC5883L.MZ * QMC5883L.MZ));
// get temperature
if (readRegister(QMC5883L_TEMP_LSB, 2) != 2) return; // read error
int16_t t = 0;
t = Wire.read() | (Wire.read() << 8);
QMC5883L.temp = (t / 100) + USE_QMC5883L_Temp;
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
#ifdef USE_WEBSERVER
const char HTTP_SNS_QMC5883L[] PROGMEM =
"{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>
"{s}QMC5883L " D_HG "{m}%d " D_UNIT_DEGREE "{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
"{s}QMC5883L " D_TEMPERATURE "{m}%d " D_UNIT_DEGREE D_UNIT_CELSIUS "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
const char HTTP_SNS_QMC5883L_ERROR[] PROGMEM =
"{s}QMC5883L {m} %s {e}";
#endif
void QMC5883L_Show(uint8_t json)
{
if (json)
{
if (!QMC5883L.ready)
{
AddLog(LOG_LEVEL_INFO, PSTR("QMC5883L: " D_ERROR " %x" ), QMC5883L.status);
return;
}
else
{
ResponseAppend_P(PSTR(",\"QMC5883L\":{\"" D_JSON_MX "\":%d,\"" D_JSON_MY "\":%d,\"" D_JSON_MZ "\":%d,\"" D_JSON_MAGNETICFLD "\":%u,\"" D_JSON_HEADING "\":%d,\"" D_JSON_TEMPERATURE "\":%d}"), QMC5883L.MX, QMC5883L.MY, QMC5883L.MZ, QMC5883L.scalar, QMC5883L.HG, QMC5883L.temp);
}
}
#ifdef USE_WEBSERVER
else
{
switch(QMC5883L.ready)
{
case true:
WSContentSend_PD(HTTP_SNS_QMC5883L, QMC5883L.MX, QMC5883L.MY, QMC5883L.MZ, QMC5883L.scalar, QMC5883L.HG, QMC5883L.temp);
break;
case false:
WSContentSend_PD(HTTP_SNS_QMC5883L_ERROR, D_START);
break;
default:
WSContentSend_PD(HTTP_SNS_QMC5883L_ERROR, D_ERROR);
}
}
#endif
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns99(byte function)
{
if (!I2cEnabled(XI2C_69)) { return false; }
bool result = false;
if (FUNC_INIT == function) {
QMC5883L_Init();
}
else if (QMC5883L.ready) {
switch (function) {
case FUNC_JSON_APPEND:
QMC5883L_Show(1);
break;
case FUNC_EVERY_SECOND:
QMC5883L_read_data();
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
QMC5883L_Show(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_QMC5883L
#endif // USE_I2C