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
2024-05-07 09:06:57 +01:00
Copyright ( C ) 2022 Helge Scheunemann + fb - pilot
2022-10-02 20:41:51 +01:00
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
2024-02-18 14:24:21 +00:00
*
2024-05-07 09:06:57 +01:00
* # define QMC5883L_OVERSAMPLE 0 // 0 .. 3 => over Sample Ratio : 512, 256, 128, 64
* # define QMC5883L_GAUSS 1 // 0 .. 1 => full Scale : 2GAUSS, 8GAUSS
* # define QMC5883L_FILTER 0 // 0 .. 3 => Output Data Rate : 10HZ, 50HZ, 109HZ, 200HZ
* # define QMC5883L_TEMP_SHIFT 23.0f // sensor temperature are not calibrated (only relativ measurement) and need an absolute ground value in °C (see datasheet)
* # define QMC5883L_DECIMAL 4 // Decimals for display
2022-10-05 14:00:03 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-02-18 14:24:21 +00:00
2024-05-07 09:06:57 +01:00
# ifndef QMC5883L_TEMP_SHIFT
# define QMC5883L_TEMP_SHIFT 23.0f // sensor temperature is not calibrated (only relativ measurement) and need an absolute ground value in °C (see datasheet)
# endif
# ifndef QMC5883L_DECIMAL
# define QMC5883L_DECIMAL 4
# endif
2022-10-05 14:00:03 +01:00
/*
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
2024-02-18 14:24:21 +00:00
07 H TOUT [ 7 : 0 ] Read only
08 H TOUT [ 15 : 8 ] Read only
2022-10-02 20:41:51 +01:00
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
2024-05-07 09:06:57 +01:00
/* 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 // SET/RESET Period it is recommended that the register 0BH is written by 0x01.
// #define QMC5883L_RESERVED 0x0c
/*
2022-10-02 20:41:51 +01:00
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.
2024-05-07 09:06:57 +01:00
* Calibration :
* valid relations : 1 T = 10000 G . . . 1 G = 0.1 mT = 100 µ T
* Dynamic Output Field Range ± 2 or ± 8 Gauss = = > ± 200 µ T or ± 800 µ T
* Sensitivity [ 1 ]
* Field Range = ± 2 G 12000 LSB / G - - > factor = 120
* Field Range = ± 8 G 3000 LSB / G - - > factor = 30
*/
# if QMC5883L_GAUSS == 0
# define QMC5883L_FACTOR 120.0f
# else
# define QMC5883L_FACTOR 30.0f
# endif
2022-10-02 20:41:51 +01:00
2024-05-07 09:06:57 +01:00
/*
2022-10-02 20:41:51 +01:00
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
2024-05-07 09:06:57 +01:00
06 H DOR OVL DRDY
*/
# define QMC5883L_STATUS_DRDY 1
# define QMC5883L_STATUS_OVL 2
// #define QMC5883L_STATUS_DOR 4
/*
2022-10-02 20:41:51 +01:00
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
2024-05-07 09:06:57 +01:00
09 H OSR [ 1 : 0 ] RNG [ 1 : 0 ] ODR [ 1 : 0 ] MODE [ 1 : 0 ]
2022-10-02 20:41:51 +01:00
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
2024-05-07 09:06:57 +01:00
*/
// #define QMC5883L_CONFIG_STANDBY 0b00000000
# define QMC5883L_CONFIG_CONT 0b00000001
# define QMC5883L_SHIFT_ODR 2 // for QMC5883L_FILTER
# define QMC5883L_SHIFT_RNG 4 // for QMC5883L_GAUSS
# define QMC5883L_SHIFT_OSR 6 // for QMC5883L_OVERSAMPLE
/*
2022-10-02 20:41:51 +01:00
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
2024-05-07 09:06:57 +01:00
*/
# define QMC5883L_CONFIG2_RESET 0b10000000
/*
2022-10-02 20:41:51 +01:00
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
2024-02-18 14:24:21 +00:00
# define XSNS_33 33
# define XI2C_71 71 // See I2CDEVICES.md
2022-10-02 20:41:51 +01:00
/* The default I2C address of this chip */
# define QMC5883L_ADDR 0x0D
# define QMC5883L_CHIP_ID 0x0d
2024-02-18 14:24:21 +00:00
# ifndef QMC5883L_OVERSAMPLE
# define QMC5883L_OVERSAMPLE 1
# elif (QMC5883L_OVERSAMPLE>3)
# undef QMC5883L_OVERSAMPLE
# define QMC5883L_OVERSAMPLE 3
# endif
# ifndef QMC5883L_GAUSS
# define QMC5883L_GAUSS 1
# elif (QMC5883L_GAUSS>1)
# undef QMC5883L_GAUSS
# define QMC5883L_GAUSS 1
# endif
# ifndef QMC5883L_FILTER
2024-05-07 09:06:57 +01:00
# define QMC5883L_FILTER 0
2024-02-18 14:24:21 +00:00
# elif (QMC5883L_FILTER>3)
# undef QMC5883L_FILTER
# define QMC5883L_FILTER 3
# endif
2024-05-07 09:06:57 +01:00
# define QMC5883L_OVL INFINITY
2024-02-18 14:24:21 +00:00
2022-10-04 14:48:27 +01:00
// data field
2022-10-05 14:00:03 +01:00
struct QMC5883L_s {
2024-05-07 09:06:57 +01:00
float MX , MY , MZ ;
2024-02-18 14:24:21 +00:00
float temp ;
bool ovl ;
2022-10-04 14:48:27 +01:00
} * 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 ; }
2024-02-18 14:24:21 +00:00
// Software Reset QMC5883L #define QMC5883L_CONFIG2 0x0a
2022-10-05 14:00:03 +01:00
if ( I2cWrite8 ( QMC5883L_ADDR , QMC5883L_CONFIG2 , QMC5883L_CONFIG2_RESET ) = = false ) { return ; }
2024-02-18 14:24:21 +00:00
// SET/RESET Period it is recommended that the register 0BH is written by 0x01.
2022-10-05 14:00:03 +01:00
if ( I2cWrite8 ( QMC5883L_ADDR , QMC5883L_RESET , 0x01 ) = = false ) { return ; }
2024-02-18 14:24:21 +00:00
/* write config
Addr 7 6 5 4 3 2 1 0
09 H OSR [ 1 : 0 ] RNG [ 1 : 0 ] ODR [ 1 : 0 ] MODE [ 1 : 0 ] */
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " QMC: QMC5883L_STATUS_REG 0x%X, size of buffer %d " ) ,
2024-05-07 09:06:57 +01:00
( ( QMC5883L_OVERSAMPLE < < QMC5883L_SHIFT_OSR ) | ( QMC5883L_GAUSS < < QMC5883L_SHIFT_RNG ) | ( QMC5883L_FILTER < < QMC5883L_SHIFT_ODR ) | QMC5883L_CONFIG_CONT ) , sizeof ( struct QMC5883L_s ) ) ;
if ( I2cWrite8 ( QMC5883L_ADDR , QMC5883L_CONFIG , ( ( QMC5883L_OVERSAMPLE < < QMC5883L_SHIFT_OSR ) | ( QMC5883L_GAUSS < < QMC5883L_SHIFT_RNG ) | ( QMC5883L_FILTER < < QMC5883L_SHIFT_ODR ) | QMC5883L_CONFIG_CONT ) ) = = false ) { return ; }
2022-10-05 14:00:03 +01:00
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 ) {
2024-05-07 09:06:57 +01:00
/* check if chip is ready to provide data
Table 15. Status Register 1
Addr . 7 6 5 4 3 2 1 0
06 H DOR OVL DRDY
*/
2024-02-18 14:24:21 +00:00
switch ( I2cRead8 ( QMC5883L_ADDR , QMC5883L_STATUS ) & ( QMC5883L_STATUS_DRDY | QMC5883L_STATUS_OVL ) ) {
case 1 :
QMC5883L - > ovl = false ;
2024-05-07 09:06:57 +01:00
QMC5883L - > MX = ( float ) I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_X_LSB ) / QMC5883L_FACTOR ; // Select LSB register
QMC5883L - > MY = ( float ) I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_Y_LSB ) / QMC5883L_FACTOR ;
QMC5883L - > MZ = ( float ) I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_Z_LSB ) / QMC5883L_FACTOR ;
2024-02-18 14:24:21 +00:00
break ;
case 3 :
QMC5883L - > ovl = true ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " QMC: QMC5883L_STATUS_Overflow " ) ) ;
2024-05-07 09:06:57 +01:00
QMC5883L - > MX = \
QMC5883L - > MY = \
QMC5883L - > MZ = QMC5883L_OVL ;
2024-02-18 14:24:21 +00:00
break ;
default :
return ;
break ;
}
2022-10-04 14:48:27 +01:00
// get temperature
2024-02-18 14:24:21 +00:00
QMC5883L - > temp = ConvertTemp ( ( I2cReadS16_LE ( QMC5883L_ADDR , QMC5883L_TEMP_LSB ) / 100.0f ) + QMC5883L_TEMP_SHIFT ) ; // Temp in celsius
2022-10-02 20:41:51 +01:00
}
/*********************************************************************************************\
* Presentation
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef USE_WEBSERVER
2024-05-07 09:06:57 +01:00
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
2022-10-02 20:41:51 +01:00
const char HTTP_SNS_QMC5883L [ ] PROGMEM =
2024-05-07 09:06:57 +01:00
" {s}QMC5883L " D_MX " {m}%*_f " D_UNIT_MICROTESLA " {e} "
" {s}QMC5883L " D_MY " {m}%*_f " D_UNIT_MICROTESLA " {e} "
" {s}QMC5883L " D_MZ " {m}%*_f " D_UNIT_MICROTESLA " {e} "
" {s}QMC5883L " D_MAGNETICFLD " {m}%*_f " D_UNIT_MICROTESLA " {e} " ;
2022-10-02 20:41:51 +01:00
# endif
2024-05-07 09:06:57 +01:00
void QMC5883L_Show ( uint8_t json )
{
float QMC5883L_MF = ( float ) sqrt ( ( QMC5883L - > MX * QMC5883L - > MX ) + ( QMC5883L - > MY * QMC5883L - > MY ) + ( QMC5883L - > MZ * QMC5883L - > MZ ) ) ;
2022-10-05 14:00:03 +01:00
if ( json ) {
2024-05-07 09:06:57 +01:00
ResponseAppend_P ( PSTR ( " , \" QMC5883L \" :{ \" " \
D_JSON_MX " \" :%*_f, \" " D_JSON_MY " \" :%*_f, \" " D_JSON_MZ " \" :%*_f, \" " \
D_JSON_MAGNETICFLD " \" :%*_f, \" " D_JSON_TEMPERATURE " \" :%*_f} " ) , \
QMC5883L_DECIMAL , & QMC5883L - > MX , \
QMC5883L_DECIMAL , & QMC5883L - > MY , \
QMC5883L_DECIMAL , & QMC5883L - > MZ , \
QMC5883L_DECIMAL , & QMC5883L_MF , \
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 {
2024-05-07 09:06:57 +01:00
WSContentSend_PD ( HTTP_SNS_QMC5883L , QMC5883L_DECIMAL , & QMC5883L - > MX , \
QMC5883L_DECIMAL , & QMC5883L - > MY , \
QMC5883L_DECIMAL , & QMC5883L - > MZ , \
QMC5883L_DECIMAL , & QMC5883L_MF ) ;
2024-02-18 14:24:21 +00:00
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 ; }
2024-01-25 07:43:56 +00:00
bool result = false ;
2022-10-05 14:00:03 +01:00
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 ) {
2024-01-25 07:43:56 +00:00
// case FUNC_COMMAND_SENSOR:
// if (XSNS_33 == XdrvMailbox.index) {
// result = QMC5883L_CmndSensor();
// }
// break;
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
}
2024-01-25 07:43:56 +00:00
return result ;
2022-10-02 20:41:51 +01:00
}
2022-10-05 14:00:03 +01:00
# endif // USE_QMC5883L
2024-05-07 09:06:57 +01:00
# endif // USE_I2C