diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 0841866c9..90be957ef 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -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
diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h
index c303d49b3..380913bfb 100644
--- a/tasmota/include/i18n.h
+++ b/tasmota/include/i18n.h
@@ -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"
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index e9a9f6609..7f07b5a08 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -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"
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index 60b9e8942..90b9b6ade 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -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"
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index bac7413c2..a31228cf0 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -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
diff --git a/tasmota/tasmota_xsns_sensor/xsns_33_qmc5883l.ino b/tasmota/tasmota_xsns_sensor/xsns_33_qmc5883l.ino
new file mode 100644
index 000000000..be66649e3
--- /dev/null
+++ b/tasmota/tasmota_xsns_sensor/xsns_33_qmc5883l.ino
@@ -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 .
+
+ 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 2’s 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 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
+ 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”: Normal“1”: 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} =
, {m} = | , {e} = |
+ "{s}QMC5883L " D_MY "{m}%d " D_UNIT_MICROTESLA "{e}" // {s} = , {m} = | , {e} = |
+ "{s}QMC5883L " D_MZ "{m}%d " D_UNIT_MICROTESLA "{e}" // {s} = , {m} = | , {e} = |
+ "{s}QMC5883L " D_MAGNETICFLD "{m}%d " D_UNIT_MICROTESLA "{e}" // {s} = , {m} = | , {e} = |
+ "{s}QMC5883L " D_HG "{m}%d " D_UNIT_DEGREE "{e}" // {s} = , {m} = | , {e} = |
+ "{s}QMC5883L " D_TEMPERATURE "{m}%d " D_UNIT_DEGREE D_UNIT_CELSIUS "{e}"; // {s} = , {m} = | , {e} = |
+
+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
\ No newline at end of file