Refactor framework -- point to a tests/* directory for the user supplied tests
This commit is contained in:
4
Makefile
4
Makefile
@ -16,8 +16,8 @@ OBJDIR = build
|
|||||||
LIBDIR = libs
|
LIBDIR = libs
|
||||||
BINDIR = .
|
BINDIR = .
|
||||||
|
|
||||||
SRCS := $(shell find -L $(SRCDIR) $(LIBDIR) -type f -name '*.c')
|
SRCS := $(shell find -L $(SRCDIR) $(LIBDIR) -type f -name '*.c' | egrep -v 'deps/|build/' )
|
||||||
INCS := $(shell find -L $(SRCDIR) $(LIBDIR) $(INCDIR) -type d -name 'include')
|
INCS := $(shell find -L $(SRCDIR) $(LIBDIR) $(INCDIR) -type d -name 'include'| egrep -v 'deps/|build/' )
|
||||||
INCFLAGS := $(patsubst %,-I %, $(INCS))
|
INCFLAGS := $(patsubst %,-I %, $(INCS))
|
||||||
OBJS := $(patsubst %.c, build/%.o, $(SRCS))
|
OBJS := $(patsubst %.c, build/%.o, $(SRCS))
|
||||||
RM = rm -f
|
RM = rm -f
|
||||||
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# A Mongoose OS I2C Mock
|
||||||
|
|
||||||
|
Files in this repository are a Linux I2C implementation of the Mongoose OS
|
||||||
|
I2C interface.
|
||||||
|
|
||||||
|
## Howto
|
||||||
|
|
||||||
|
Register tests in `tests/*.[ch]` and they will be compiled in. Each test must
|
||||||
|
have functions uniquely named:
|
||||||
|
```
|
||||||
|
bool test_*_create();
|
||||||
|
bool test_*_run();
|
||||||
|
bool test_*_destroy();
|
||||||
|
```
|
||||||
|
|
||||||
|
And static variables named:
|
||||||
|
```
|
||||||
|
uint32_t test_*_period_ms;
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `make` will then generate the test skeleton, recursively compile
|
||||||
|
all sources found in `src/`, `tests/`, and `libs/`, and for each, call the
|
||||||
|
`create()` function, then the `run()` function each `period_ms` milliseconds,
|
||||||
|
and upon exiting, call each `destroy()` function and clean up.
|
162
include/mgos_gpio.h
Normal file
162
include/mgos_gpio.h
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2018 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the ""License"");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an ""AS IS"" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CS_FW_INCLUDE_MGOS_GPIO_H_
|
||||||
|
#define CS_FW_INCLUDE_MGOS_GPIO_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
enum mgos_gpio_mode {
|
||||||
|
MGOS_GPIO_MODE_INPUT = 0,
|
||||||
|
MGOS_GPIO_MODE_OUTPUT = 1,
|
||||||
|
MGOS_GPIO_MODE_OUTPUT_OD = 2, /* open-drain output */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mgos_gpio_pull_type {
|
||||||
|
MGOS_GPIO_PULL_NONE = 0, /* floating */
|
||||||
|
MGOS_GPIO_PULL_UP = 1,
|
||||||
|
MGOS_GPIO_PULL_DOWN = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mgos_gpio_int_mode {
|
||||||
|
MGOS_GPIO_INT_NONE = 0,
|
||||||
|
MGOS_GPIO_INT_EDGE_POS = 1, /* positive edge */
|
||||||
|
MGOS_GPIO_INT_EDGE_NEG = 2, /* negative edge */
|
||||||
|
MGOS_GPIO_INT_EDGE_ANY = 3, /* any edge - positive or negative */
|
||||||
|
MGOS_GPIO_INT_LEVEL_HI = 4, /* high voltage level */
|
||||||
|
MGOS_GPIO_INT_LEVEL_LO = 5 /* low voltage level */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* GPIO interrupt handler signature. */
|
||||||
|
typedef void (*mgos_gpio_int_handler_f)(int pin, void *arg);
|
||||||
|
|
||||||
|
/* Set mode - input or output. */
|
||||||
|
bool mgos_gpio_set_mode(int pin, enum mgos_gpio_mode mode);
|
||||||
|
|
||||||
|
/* Set pull-up or pull-down type. */
|
||||||
|
bool mgos_gpio_set_pull(int pin, enum mgos_gpio_pull_type pull);
|
||||||
|
|
||||||
|
/* Sets up a pin as an input and confiures pull-up or pull-down. */
|
||||||
|
bool mgos_gpio_setup_input(int pin, enum mgos_gpio_pull_type pull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets up pin output while avoiding spurious transitions:
|
||||||
|
* desired output level is configured first, then mode.
|
||||||
|
*/
|
||||||
|
bool mgos_gpio_setup_output(int pin, bool level);
|
||||||
|
|
||||||
|
/* Read pin input level. */
|
||||||
|
bool mgos_gpio_read(int pin);
|
||||||
|
|
||||||
|
/* Set pin's output level. */
|
||||||
|
void mgos_gpio_write(int pin, bool level);
|
||||||
|
|
||||||
|
/* Flip output pin value. Returns value that was written. */
|
||||||
|
bool mgos_gpio_toggle(int pin);
|
||||||
|
|
||||||
|
/* Read the value of the output register. */
|
||||||
|
bool mgos_gpio_read_out(int pin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Install a GPIO interrupt handler.
|
||||||
|
*
|
||||||
|
* This will invoke handler on the main task, which makes it possible to use
|
||||||
|
* any functions but may delay servicing of the interrupt. If lower latency
|
||||||
|
* is required, use `mgos_gpio_set_int_handler_isr`, but you'll need to
|
||||||
|
* understand the implications, which are platform-specific.
|
||||||
|
*
|
||||||
|
* Interrupt is automatically cleared once upon triggering.
|
||||||
|
* Then it is disabled until the handler gets a chance to run, at which point
|
||||||
|
* it is re-enabled. At this point it may re-trigger immediately if the
|
||||||
|
* interrupt condition arose again while the handler was pending or running.
|
||||||
|
* Handler may use `mgos_gpio_clear_int` to explicitly clear the condition.
|
||||||
|
*
|
||||||
|
* Note that this will not enable the interrupt, this must be done explicitly
|
||||||
|
* with `mgos_gpio_enable_int()`.
|
||||||
|
*/
|
||||||
|
bool mgos_gpio_set_int_handler(int pin, enum mgos_gpio_int_mode mode,
|
||||||
|
mgos_gpio_int_handler_f cb, void *arg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as mgos_gpio_set_int_handler but invokes handler in ISR context,
|
||||||
|
* without the overhead of a context switch. GPIO interrupts are disabled while
|
||||||
|
* the handler is running.
|
||||||
|
*/
|
||||||
|
bool mgos_gpio_set_int_handler_isr(int pin, enum mgos_gpio_int_mode mode,
|
||||||
|
mgos_gpio_int_handler_f cb, void *arg);
|
||||||
|
|
||||||
|
/* Enable interrupt on the specified pin. */
|
||||||
|
bool mgos_gpio_enable_int(int pin);
|
||||||
|
|
||||||
|
/* Disables interrupt (without removing the handler). */
|
||||||
|
bool mgos_gpio_disable_int(int pin);
|
||||||
|
|
||||||
|
/* Clears a GPIO interrupt flag. */
|
||||||
|
void mgos_gpio_clear_int(int pin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Removes a previosuly set interrupt handler.
|
||||||
|
*
|
||||||
|
* If `cb` and `arg` are not NULL, they will contain previous handler and arg.
|
||||||
|
*/
|
||||||
|
void mgos_gpio_remove_int_handler(int pin, mgos_gpio_int_handler_f *old_cb,
|
||||||
|
void **old_arg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle a button on the specified pin.
|
||||||
|
*
|
||||||
|
* Configures the pin for input with specified pull-up and performs debouncing:
|
||||||
|
* upon first triggering user's callback is invoked immediately but further
|
||||||
|
* interrupts are inhibited for the following debounce_ms millseconds.
|
||||||
|
*
|
||||||
|
* Typically 50 ms of debouncing time is sufficient.
|
||||||
|
* int_mode is one of the `MGOS_GPIO_INT_EDGE_*` values and will specify whether
|
||||||
|
* the handler triggers when button is pressed, released or both.
|
||||||
|
* Which is which depends on how the button is wired: if the normal state is
|
||||||
|
* pull-up (typical), then `MGOS_GPIO_INT_EDGE_NEG` is press and
|
||||||
|
* `_POS` is release.
|
||||||
|
*
|
||||||
|
* Calling with `cb` = NULL will remove a previously installed handler.
|
||||||
|
*
|
||||||
|
* Note: implicitly enables the interrupt.
|
||||||
|
*/
|
||||||
|
bool mgos_gpio_set_button_handler(int pin, enum mgos_gpio_pull_type pull_type,
|
||||||
|
enum mgos_gpio_int_mode int_mode,
|
||||||
|
int debounce_ms, mgos_gpio_int_handler_f cb,
|
||||||
|
void *arg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A utility function that takes care of blinking an LED.
|
||||||
|
* The pin must be configured as output first.
|
||||||
|
* On (output "1") and off ("0") times are specified in milliseconds.
|
||||||
|
* Set to (0, 0) to disable.
|
||||||
|
*/
|
||||||
|
bool mgos_gpio_blink(int pin, int on_ms, int off_ms);
|
||||||
|
|
||||||
|
/* String representation of pin number.
|
||||||
|
* Will return "PA5" or "PK3" for platforms that have port banks. */
|
||||||
|
const char *mgos_gpio_str(int pin_def, char buf[8]);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* CS_FW_INCLUDE_MGOS_GPIO_H_ */
|
@ -22,6 +22,8 @@
|
|||||||
#include "mgos.h"
|
#include "mgos.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include "mgos_gpio.h"
|
||||||
|
#include "mgos_i2c.h"
|
||||||
|
|
||||||
// mgos_log
|
// mgos_log
|
||||||
enum cs_log_level
|
enum cs_log_level
|
||||||
|
190
src/main.c
190
src/main.c
@ -1,29 +1,4 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
#include "mgos.h"
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_sht31.h"
|
|
||||||
#include "mgos_si7021.h"
|
|
||||||
#include "mgos_htu21df.h"
|
|
||||||
#include "mgos_mcp9808.h"
|
|
||||||
#include "mgos_ccs811.h"
|
|
||||||
#include "mgos_mpu9250.h"
|
|
||||||
#include "mgos_imu.h"
|
|
||||||
#include "mgos_barometer.h"
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@ -31,8 +6,9 @@
|
|||||||
#define I2CBUSNR 7
|
#define I2CBUSNR 7
|
||||||
|
|
||||||
bool i2c_dumpregs(struct mgos_i2c *i2c, uint8_t i2caddr);
|
bool i2c_dumpregs(struct mgos_i2c *i2c, uint8_t i2caddr);
|
||||||
|
void i2c_scanner(struct mgos_i2c *i2c, bool dumpregs);
|
||||||
|
|
||||||
static void i2c_scanner(struct mgos_i2c *i2c, bool dumpregs) {
|
void i2c_scanner(struct mgos_i2c *i2c, bool dumpregs) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!i2c) {
|
if (!i2c) {
|
||||||
@ -69,122 +45,8 @@ bool i2c_dumpregs(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool do_ccs811(struct mgos_ccs811 *sensor) {
|
|
||||||
float eco2, tvoc;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
eco2 = mgos_ccs811_get_eco2(sensor);
|
|
||||||
tvoc = mgos_ccs811_get_tvoc(sensor);
|
|
||||||
LOG(LL_INFO, ("eCO2=%.0fppm TVOC=%.0fppb", eco2, tvoc));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool do_sht31(struct mgos_sht31 *sensor) {
|
|
||||||
float temp, humid;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = mgos_sht31_getTemperature(sensor);
|
|
||||||
humid = mgos_sht31_getHumidity(sensor);
|
|
||||||
LOG(LL_INFO, ("temperature=%.2fC humidity=%.1f%%", temp, humid));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool do_si7021(struct mgos_si7021 *sensor) {
|
|
||||||
float temp, humid;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = mgos_si7021_getTemperature(sensor);
|
|
||||||
humid = mgos_si7021_getHumidity(sensor);
|
|
||||||
LOG(LL_INFO, ("temperature=%.2fC humidity=%.1f%%", temp, humid));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool do_htu21df(struct mgos_htu21df *sensor) {
|
|
||||||
float temp, humid;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = mgos_htu21df_getTemperature(sensor);
|
|
||||||
humid = mgos_htu21df_getHumidity(sensor);
|
|
||||||
LOG(LL_INFO, ("temperature=%.2fC humidity=%.1f%%", temp, humid));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool do_mcp9808(struct mgos_mcp9808 *sensor) {
|
|
||||||
float temp;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = mgos_mcp9808_getTemperature(sensor);
|
|
||||||
LOG(LL_INFO, ("temperature=%.2fC", temp));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool do_mpu9250(struct mgos_mpu9250 *sensor) {
|
|
||||||
float ax, ay, az;
|
|
||||||
float gx, gy, gz;
|
|
||||||
float mx, my, mz;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mgos_mpu9250_get_accelerometer(sensor, &ax, &ay, &az)) {
|
|
||||||
LOG(LL_INFO, ("Accel X=%.2f Y=%.2f Z=%.2f", ax, ay, az));
|
|
||||||
}
|
|
||||||
if (mgos_mpu9250_get_gyroscope(sensor, &gx, &gy, &gz)) {
|
|
||||||
LOG(LL_INFO, ("Gyro X=%.2f Y=%.2f Z=%.2f", gx, gy, gz));
|
|
||||||
}
|
|
||||||
if (mgos_mpu9250_get_magnetometer(sensor, &mx, &my, &mz)) {
|
|
||||||
LOG(LL_INFO, ("Mag X=%.2f Y=%.2f Z=%.2f", mx, my, mz));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool do_baro(struct mgos_barometer *sensor) {
|
|
||||||
float pressure, temperature;
|
|
||||||
|
|
||||||
if (!sensor)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!mgos_barometer_get_pressure(sensor, &pressure))
|
|
||||||
pressure=NAN;
|
|
||||||
|
|
||||||
if (!mgos_barometer_get_temperature(sensor, &temperature))
|
|
||||||
temperature=NAN;
|
|
||||||
|
|
||||||
LOG(LL_INFO, ("type=%s temperature=%.2fC pressure=%.0fPa", mgos_barometer_get_device_name(sensor), temperature, pressure));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv, char **environ) {
|
int main(int argc, char **argv, char **environ) {
|
||||||
struct mgos_i2c * i2c = NULL;
|
struct mgos_i2c * i2c = NULL;
|
||||||
struct mgos_si7021 * si7021 = NULL;
|
|
||||||
struct mgos_sht31 * sht31 = NULL;
|
|
||||||
struct mgos_htu21df *htu21df = NULL;
|
|
||||||
struct mgos_mcp9808 *mcp9808 = NULL;
|
|
||||||
struct mgos_ccs811 * ccs811 = NULL;
|
|
||||||
struct mgos_mpu9250 *mpu9250 = NULL;
|
|
||||||
struct mgos_barometer *baro = NULL;
|
|
||||||
|
|
||||||
if (!mgos_i2c_open(I2CBUSNR)) {
|
if (!mgos_i2c_open(I2CBUSNR)) {
|
||||||
LOG(LL_ERROR, ("Cannot open I2C bus %u", I2CBUSNR));
|
LOG(LL_ERROR, ("Cannot open I2C bus %u", I2CBUSNR));
|
||||||
@ -197,54 +59,6 @@ int main(int argc, char **argv, char **environ) {
|
|||||||
|
|
||||||
i2c_scanner(i2c, true);
|
i2c_scanner(i2c, true);
|
||||||
|
|
||||||
/*
|
|
||||||
* if (!(sht31 = mgos_sht31_create(i2c, 0x44)))
|
|
||||||
* LOG(LL_ERROR, ("Cannot create SHT31 device"));
|
|
||||||
* if (!(si7021 = mgos_si7021_create(i2c, 0x40)))
|
|
||||||
* LOG(LL_ERROR, ("Cannot create SI7021 device"));
|
|
||||||
* if (!(htu21df = mgos_htu21df_create(i2c, 0x40)))
|
|
||||||
* LOG(LL_ERROR, ("Cannot create HTU21DF device"));
|
|
||||||
* if (!(mcp9808 = mgos_mcp9808_create(i2c, 0x18)))
|
|
||||||
* LOG(LL_ERROR, ("Cannot create MCP9808 device"));
|
|
||||||
* if (!(ccs811 = mgos_ccs811_create(i2c, 0x5A)))
|
|
||||||
* LOG(LL_ERROR, ("Cannot create CCS811 device"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
if (!(mpu9250 = mgos_mpu9250_create(i2c, 0x68))) {
|
|
||||||
LOG(LL_ERROR, ("Cannot create MPU9250 device"));
|
|
||||||
} else {
|
|
||||||
mgos_mpu9250_set_accelerometer_range(mpu9250, RANGE_2G);
|
|
||||||
mgos_mpu9250_set_gyroscope_range(mpu9250, RANGE_GYRO_250);
|
|
||||||
mgos_mpu9250_set_magnetometer_scale(mpu9250, SCALE_14_BITS);
|
|
||||||
mgos_mpu9250_set_magnetometer_speed(mpu9250, MAG_100_HZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(baro=mgos_barometer_create_i2c(i2c, 0x76, BARO_BME280))) {
|
|
||||||
LOG(LL_ERROR, ("Cannot create barometer"));
|
|
||||||
} else {
|
|
||||||
mgos_barometer_set_cache_ttl(baro, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
do_sht31(sht31);
|
|
||||||
do_si7021(si7021);
|
|
||||||
do_htu21df(htu21df);
|
|
||||||
do_mcp9808(mcp9808);
|
|
||||||
do_ccs811(ccs811);
|
|
||||||
do_mpu9250(mpu9250);
|
|
||||||
do_baro(baro);
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mgos_sht31_destroy(&sht31);
|
|
||||||
mgos_si7021_destroy(&si7021);
|
|
||||||
mgos_htu21df_destroy(&htu21df);
|
|
||||||
mgos_mcp9808_destroy(&mcp9808);
|
|
||||||
mgos_ccs811_destroy(&ccs811);
|
|
||||||
mgos_mpu9250_destroy(&mpu9250);
|
|
||||||
mgos_barometer_destroy(&baro);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
@ -1,238 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_ccs811_internal.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
|
|
||||||
// Datasheet:
|
|
||||||
// https://cdn-shop.adafruit.com/product-files/2857/Sensirion_Humidity_SHT3x_Datasheet_digital-767294.pdf
|
|
||||||
|
|
||||||
// Private functions follow
|
|
||||||
static bool mgos_ccs811_getStatus(struct mgos_ccs811 *sensor, uint8_t *status) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ret = mgos_i2c_read_reg_b(sensor->i2c, sensor->i2caddr, MGOS_CCS811_REG_STATUS);
|
|
||||||
if (ret < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*status = (uint8_t)ret;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool mgos_ccs811_getMeasMode(struct mgos_ccs811 *sensor, uint8_t *meas_mode) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ret = mgos_i2c_read_reg_b(sensor->i2c, sensor->i2caddr, MGOS_CCS811_REG_MEAS_MODE);
|
|
||||||
if (ret < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*meas_mode = (uint8_t)ret;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if sensor status register has error-bit set (or upon read failure)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* static bool mgos_ccs811_error(struct mgos_ccs811 *sensor) {
|
|
||||||
* uint8_t status;
|
|
||||||
*
|
|
||||||
* if (!mgos_ccs811_getStatus(sensor, &status))
|
|
||||||
* return true;
|
|
||||||
* // bits -- 7:FW_MODE 6:APP_ERASE 5:APP_VERIFY 4:APP_VALID 3:DATA_READY 0:ERROR
|
|
||||||
* return (status & MGOS_CCS811_STATUS_ERR);
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Return true if sensor status register has data-ready set (false upon read failure)
|
|
||||||
static bool mgos_ccs811_dataready(struct mgos_ccs811 *sensor) {
|
|
||||||
uint8_t status;
|
|
||||||
|
|
||||||
if (!mgos_ccs811_getStatus(sensor, &status)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return status & MGOS_CCS811_STATUS_DATA_READY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool mgos_ccs811_reset(struct mgos_ccs811 *sensor) {
|
|
||||||
uint8_t data[5] = { MGOS_CCS811_REG_SW_RESET, 0x11, 0xE5, 0x72, 0x8A };
|
|
||||||
|
|
||||||
if (!sensor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mgos_i2c_write(sensor->i2c, sensor->i2caddr, data, 5, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private functions end
|
|
||||||
|
|
||||||
// Public functions follow
|
|
||||||
struct mgos_ccs811 *mgos_ccs811_create(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
struct mgos_ccs811 *sensor;
|
|
||||||
int success = 0;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!i2c) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mgos_i2c_read_reg_b(i2c, i2caddr, MGOS_CCS811_REG_HW_ID);
|
|
||||||
if (ret != MGOS_CCS811_HW_ID_CODE) {
|
|
||||||
LOG(LL_ERROR, ("Failed to detect CCS811 at I2C 0x%02x", i2caddr));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sensor = calloc(1, sizeof(struct mgos_ccs811));
|
|
||||||
if (!sensor) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(sensor, 0, sizeof(struct mgos_ccs811));
|
|
||||||
sensor->i2caddr = i2caddr;
|
|
||||||
sensor->i2c = i2c;
|
|
||||||
sensor->eco2 = 400;
|
|
||||||
|
|
||||||
// Boot the application on CCS811.
|
|
||||||
mgos_ccs811_reset(sensor);
|
|
||||||
mgos_usleep(2000);
|
|
||||||
|
|
||||||
uint8_t cmd = MGOS_CCS811_BOOTLOADER_REG_APP_START;
|
|
||||||
uint8_t status = MGOS_CCS811_STATUS_ERR;
|
|
||||||
uint8_t drive_mode = CCS811_DRIVE_MODE_IDLE;
|
|
||||||
mgos_i2c_write(sensor->i2c, sensor->i2caddr, &cmd, 1, true);
|
|
||||||
mgos_usleep(2000);
|
|
||||||
|
|
||||||
// Read status (expecting FW_MODE to be set and ERR to be clear)
|
|
||||||
mgos_ccs811_getStatus(sensor, &status);
|
|
||||||
if (!(status & MGOS_CCS811_STATUS_FW_MODE) || (status & MGOS_CCS811_STATUS_ERR)) {
|
|
||||||
LOG(LL_ERROR, ("CCS811 invalid firmware mode, and/or status error"));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgos_ccs811_setDriveMode(sensor, CCS811_DRIVE_MODE_1SEC);
|
|
||||||
mgos_ccs811_getDriveMode(sensor, &drive_mode);
|
|
||||||
if (drive_mode != CCS811_DRIVE_MODE_1SEC) {
|
|
||||||
LOG(LL_ERROR, ("CCS811 failed to set drive mode"));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
success = 1;
|
|
||||||
LOG(LL_INFO, ("CCS811 created at I2C 0x%02x", i2caddr));
|
|
||||||
exit:
|
|
||||||
if (!success) {
|
|
||||||
free(sensor);
|
|
||||||
sensor = NULL;
|
|
||||||
}
|
|
||||||
return sensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_ccs811_getDriveMode(struct mgos_ccs811 *sensor, uint8_t *mode) {
|
|
||||||
uint8_t meas_mode;
|
|
||||||
|
|
||||||
if (!mgos_ccs811_getMeasMode(sensor, &meas_mode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// bits -- 6:4 DRIVE_MOCE 3: Interrupt enable 2: Int on Threshhold
|
|
||||||
meas_mode >>= 4;
|
|
||||||
meas_mode &= 0x07;
|
|
||||||
*mode = meas_mode;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_ccs811_setDriveMode(struct mgos_ccs811 *sensor, enum mgos_ccs811_drive_mode_t mode) {
|
|
||||||
uint8_t meas_mode;
|
|
||||||
|
|
||||||
// bits -- 6:4 DRIVE_MOCE 3: Interrupt enable 2: Int on Threshhold
|
|
||||||
meas_mode = (mode << 4);
|
|
||||||
return mgos_i2c_write_reg_b(sensor->i2c, sensor->i2caddr, MGOS_CCS811_REG_MEAS_MODE, meas_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mgos_ccs811_destroy(struct mgos_ccs811 **sensor) {
|
|
||||||
if (!*sensor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
free(*sensor);
|
|
||||||
*sensor = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_ccs811_read(struct mgos_ccs811 *sensor) {
|
|
||||||
double start = mg_time();
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sensor->stats.read++;
|
|
||||||
|
|
||||||
if (start - sensor->stats.last_read_time < MGOS_CCS811_READ_DELAY) {
|
|
||||||
sensor->stats.read_success_cached++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Read out sensor data here
|
|
||||||
//
|
|
||||||
if (!mgos_ccs811_dataready(sensor)) {
|
|
||||||
sensor->stats.read_success_cached++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t data[8];
|
|
||||||
uint8_t cmd = MGOS_CCS811_REG_ALG_RESULT_DATA;
|
|
||||||
|
|
||||||
data[4] = MGOS_CCS811_STATUS_ERR;
|
|
||||||
mgos_i2c_write(sensor->i2c, sensor->i2caddr, &cmd, 1, false);
|
|
||||||
mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 8, true);
|
|
||||||
|
|
||||||
// bytes 0-1:eco2 2-3:tvoc 4:status 5:error 6-7: raw_data
|
|
||||||
if (data[4] & MGOS_CCS811_STATUS_ERR) {
|
|
||||||
LOG(LL_ERROR, ("Read error 0x%02x", data[5]));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sensor->eco2 = ((uint16_t)data[0] << 8) | ((uint16_t)data[1]);
|
|
||||||
sensor->tvoc = ((uint16_t)data[2] << 8) | ((uint16_t)data[3]);
|
|
||||||
LOG(LL_DEBUG, ("eCO2=%u TVOC=%u", sensor->eco2, sensor->tvoc));
|
|
||||||
|
|
||||||
sensor->stats.read_success++;
|
|
||||||
sensor->stats.read_success_usecs += 1000000 * (mg_time() - start);
|
|
||||||
sensor->stats.last_read_time = start;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float mgos_ccs811_get_eco2(struct mgos_ccs811 *sensor) {
|
|
||||||
if (!mgos_ccs811_read(sensor)) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return (float)sensor->eco2;
|
|
||||||
}
|
|
||||||
|
|
||||||
float mgos_ccs811_get_tvoc(struct mgos_ccs811 *sensor) {
|
|
||||||
if (!mgos_ccs811_read(sensor)) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return (float)sensor->tvoc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_ccs811_i2c_init(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public functions end
|
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_ccs811.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MGOS_SHT31_DEFAULT_I2CADDR (0x5A)
|
|
||||||
|
|
||||||
// Registers
|
|
||||||
#define MGOS_CCS811_REG_STATUS (0x00)
|
|
||||||
#define MGOS_CCS811_REG_MEAS_MODE (0x01)
|
|
||||||
#define MGOS_CCS811_REG_ALG_RESULT_DATA (0x02)
|
|
||||||
#define MGOS_CCS811_REG_RAW_DATA (0x03)
|
|
||||||
#define MGOS_CCS811_REG_ENV_DATA (0x05)
|
|
||||||
#define MGOS_CCS811_REG_NTC (0x06)
|
|
||||||
#define MGOS_CCS811_REG_THRESHOLDS (0x10)
|
|
||||||
#define MGOS_CCS811_REG_BASELINE (0x11)
|
|
||||||
#define MGOS_CCS811_REG_HW_ID (0x20)
|
|
||||||
#define MGOS_CCS811_REG_HW_VERSION (0x21)
|
|
||||||
#define MGOS_CCS811_REG_FW_BOOT_VERSION (0x23)
|
|
||||||
#define MGOS_CCS811_REG_FW_APP_VERSION (0x24)
|
|
||||||
#define MGOS_CCS811_REG_ERROR_ID (0xE0)
|
|
||||||
#define MGOS_CCS811_REG_SW_RESET (0xFF)
|
|
||||||
|
|
||||||
// Bootloader registers
|
|
||||||
#define MGOS_CCS811_BOOTLOADER_REG_APP_ERASE (0xF1)
|
|
||||||
#define MGOS_CCS811_BOOTLOADER_REG_APP_DATA (0xF2)
|
|
||||||
#define MGOS_CCS811_BOOTLOADER_REG_APP_VERIFY (0xF3)
|
|
||||||
#define MGOS_CCS811_BOOTLOADER_REG_APP_START (0xF4)
|
|
||||||
|
|
||||||
// Status register bits
|
|
||||||
#define MGOS_CCS811_STATUS_ERR (0x01)
|
|
||||||
#define MGOS_CCS811_STATUS_DATA_READY (0x08)
|
|
||||||
#define MGOS_CCS811_STATUS_APP_VALID (0x10)
|
|
||||||
#define MGOS_CCS811_STATUS_FW_MODE (0x80)
|
|
||||||
|
|
||||||
// Other defines
|
|
||||||
#define MGOS_CCS811_HW_ID_CODE (0x81)
|
|
||||||
#define MGOS_CCS811_REF_RESISTOR (100000)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mgos_ccs811 {
|
|
||||||
struct mgos_i2c * i2c;
|
|
||||||
uint8_t i2caddr;
|
|
||||||
struct mgos_ccs811_stats stats;
|
|
||||||
|
|
||||||
float temperature_offset;
|
|
||||||
uint16_t tvoc;
|
|
||||||
uint16_t eco2;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_htu21df_internal.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
|
|
||||||
// Datasheet:
|
|
||||||
// https://cdn-shop.adafruit.com/datasheets/1899_HTU21D.pdf
|
|
||||||
|
|
||||||
// Private functions follow
|
|
||||||
static bool mgos_htu21df_cmd(struct mgos_htu21df *sensor, uint8_t cmd) {
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!mgos_i2c_write(sensor->i2c, sensor->i2caddr, &cmd, 1, true)) {
|
|
||||||
LOG(LL_ERROR, ("I2C=0x%02x cmd=%u (0x%02x) write error", sensor->i2caddr, cmd, cmd));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG(LL_DEBUG, ("I2C=0x%02x cmd=%u (0x%02x) write success", sensor->i2caddr, cmd, cmd));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t crc8(const uint8_t *data, int len) {
|
|
||||||
const uint8_t poly=0x31;
|
|
||||||
uint8_t crc=0x00;
|
|
||||||
|
|
||||||
for (int j=len; j; --j) {
|
|
||||||
crc^=*data++;
|
|
||||||
for (int i=8; i; --i)
|
|
||||||
crc=(crc & 0x80) ? (crc << 1) ^ poly : (crc << 1);
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Private functions end
|
|
||||||
|
|
||||||
// Public functions follow
|
|
||||||
struct mgos_htu21df *mgos_htu21df_create(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
struct mgos_htu21df *sensor;
|
|
||||||
uint8_t version;
|
|
||||||
|
|
||||||
if (!i2c) return NULL;
|
|
||||||
|
|
||||||
sensor=calloc(1, sizeof(struct mgos_htu21df));
|
|
||||||
if (!sensor) return NULL;
|
|
||||||
sensor->i2caddr=i2caddr;
|
|
||||||
sensor->i2c=i2c;
|
|
||||||
sensor->last_read_time=0;
|
|
||||||
|
|
||||||
mgos_htu21df_cmd(sensor, MGOS_HTU21DF_RESET);
|
|
||||||
mgos_usleep(25000);
|
|
||||||
|
|
||||||
mgos_htu21df_cmd(sensor, MGOS_HTU21DF_READREG);
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, &version, 1, false)) {
|
|
||||||
LOG(LL_ERROR, ("Could not read command"));
|
|
||||||
free(sensor);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (version == 0x02) {
|
|
||||||
LOG(LL_INFO, ("HTU21DF created at I2C 0x%02x", i2caddr));
|
|
||||||
return sensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(LL_ERROR, ("Failed to create HTU21DF at I2C 0x%02x", i2caddr));
|
|
||||||
free(sensor);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mgos_htu21df_destroy(struct mgos_htu21df **sensor) {
|
|
||||||
if (!*sensor) return;
|
|
||||||
free (*sensor);
|
|
||||||
*sensor=NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_htu21df_read(struct mgos_htu21df *sensor) {
|
|
||||||
double now = mg_time();
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (now - sensor->last_read_time < MGOS_HTU21DF_READ_DELAY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Read out sensor data here
|
|
||||||
//
|
|
||||||
uint8_t data[3];
|
|
||||||
|
|
||||||
mgos_htu21df_cmd(sensor, MGOS_HTU21DF_READTEMP);
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 3, false)) {
|
|
||||||
LOG(LL_ERROR, ("Could not read command"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (data[2] != crc8(data, 2)) {
|
|
||||||
LOG(LL_ERROR, ("CRC error on temperature data"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t temp = (data[0]<<8)+data[1];
|
|
||||||
float temperature = temp;
|
|
||||||
temperature *= 175.72;
|
|
||||||
temperature /= 65536;
|
|
||||||
temperature -= 46.85;
|
|
||||||
sensor->temperature=temperature;
|
|
||||||
|
|
||||||
mgos_htu21df_cmd(sensor, MGOS_HTU21DF_READHUM);
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 3, false)) {
|
|
||||||
LOG(LL_ERROR, ("Could not read command"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (data[2] != crc8(data, 2)) {
|
|
||||||
LOG(LL_ERROR, ("CRC error on temperature data"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t hum = (data[0]<<8)+data[1];
|
|
||||||
float humidity = hum;
|
|
||||||
humidity *= 125;
|
|
||||||
humidity /= 65536;
|
|
||||||
humidity -= 6;
|
|
||||||
sensor->humidity=humidity;
|
|
||||||
|
|
||||||
LOG(LL_DEBUG, ("temperature=%.2fC humidity=%.1f%%", sensor->temperature, sensor->humidity));
|
|
||||||
sensor->last_read_time=now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_htu21df_getTemperature(struct mgos_htu21df *sensor) {
|
|
||||||
if (!mgos_htu21df_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_htu21df_getHumidity(struct mgos_htu21df *sensor) {
|
|
||||||
if (!mgos_htu21df_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->humidity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_htu21df_i2c_init(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Public functions end
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_htu21df.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MGOS_HTU21DF_DEFAULT_I2CADDR (0x40)
|
|
||||||
|
|
||||||
#define MGOS_HTU21DF_READTEMP (0xE3)
|
|
||||||
#define MGOS_HTU21DF_READHUM (0xE5)
|
|
||||||
#define MGOS_HTU21DF_WRITEREG (0xE6)
|
|
||||||
#define MGOS_HTU21DF_READREG (0xE7)
|
|
||||||
#define MGOS_HTU21DF_RESET (0xFE)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mgos_htu21df
|
|
||||||
{
|
|
||||||
struct mgos_i2c *i2c;
|
|
||||||
uint8_t i2caddr;
|
|
||||||
double last_read_time;
|
|
||||||
|
|
||||||
float humidity, temperature;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_mcp9808_internal.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
|
|
||||||
// Datasheet:
|
|
||||||
// https://cdn-shop.adafruit.com/datasheets/MCP9808.pdf
|
|
||||||
|
|
||||||
// Private functions follow
|
|
||||||
static bool is_mcp9808(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
uint16_t manufacturer_id, device_id;
|
|
||||||
|
|
||||||
manufacturer_id=mgos_i2c_read_reg_w(i2c, i2caddr, MGOS_MCP9808_REG_MANUF_ID);
|
|
||||||
device_id=mgos_i2c_read_reg_w(i2c, i2caddr, MGOS_MCP9808_REG_DEVICE_ID);
|
|
||||||
|
|
||||||
return ((manufacturer_id == 0x0054) && (device_id == 0x0400));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void mgos_mcp9808_reset(struct mgos_mcp9808 *sensor) {
|
|
||||||
if (!sensor) return;
|
|
||||||
mgos_i2c_write_reg_w(sensor->i2c, sensor->i2caddr, MGOS_MCP9808_REG_CONFIG, 0x0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Private functions end
|
|
||||||
|
|
||||||
// Public functions follow
|
|
||||||
struct mgos_mcp9808 *mgos_mcp9808_create(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
struct mgos_mcp9808 *sensor;
|
|
||||||
|
|
||||||
if (!i2c) return NULL;
|
|
||||||
|
|
||||||
if (!is_mcp9808(i2c, i2caddr)) {
|
|
||||||
LOG(LL_ERROR, ("Failed to create MCP9808 at I2C 0x%02x", i2caddr));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sensor=calloc(1, sizeof(struct mgos_mcp9808));
|
|
||||||
if (!sensor) return NULL;
|
|
||||||
sensor->i2caddr=i2caddr;
|
|
||||||
sensor->i2c=i2c;
|
|
||||||
sensor->last_read_time=0;
|
|
||||||
|
|
||||||
mgos_mcp9808_reset(sensor);
|
|
||||||
|
|
||||||
LOG(LL_INFO, ("MCP9808 created at I2C 0x%02x", i2caddr));
|
|
||||||
return sensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mgos_mcp9808_destroy(struct mgos_mcp9808 **sensor) {
|
|
||||||
if (!*sensor) return;
|
|
||||||
free (*sensor);
|
|
||||||
*sensor=NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_mcp9808_read(struct mgos_mcp9808 *sensor) {
|
|
||||||
double now = mg_time();
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (now - sensor->last_read_time < MGOS_MCP9808_READ_DELAY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Read out sensor data here
|
|
||||||
//
|
|
||||||
uint16_t temp_reg = mgos_i2c_read_reg_w(sensor->i2c, sensor->i2caddr, MGOS_MCP9808_REG_AMBIENT_TEMP);
|
|
||||||
|
|
||||||
float temperature = temp_reg & 0x0FFF;
|
|
||||||
temperature /= 16.0;
|
|
||||||
if (temp_reg & 0x1000) temperature -= 256;
|
|
||||||
sensor->temperature=temperature;
|
|
||||||
|
|
||||||
LOG(LL_DEBUG, ("temperature=%.2fC", sensor->temperature));
|
|
||||||
sensor->last_read_time=now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_mcp9808_getTemperature(struct mgos_mcp9808 *sensor) {
|
|
||||||
if (!mgos_mcp9808_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mgos_mcp9808_enable(struct mgos_mcp9808 *sensor, bool enable) {
|
|
||||||
uint16_t conf_reg = mgos_i2c_read_reg_w(sensor->i2c, sensor->i2caddr, MGOS_MCP9808_REG_CONFIG);
|
|
||||||
if (enable)
|
|
||||||
conf_reg &= ~MGOS_MCP9808_REG_CONFIG_SHUTDOWN;
|
|
||||||
else
|
|
||||||
conf_reg |= MGOS_MCP9808_REG_CONFIG_SHUTDOWN;
|
|
||||||
mgos_i2c_write_reg_w(sensor->i2c, sensor->i2caddr, MGOS_MCP9808_REG_CONFIG, conf_reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_mcp9808_i2c_init(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Public functions end
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_mcp9808.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MGOS_MCP9808_DEFAULT_I2CADDR (0x18)
|
|
||||||
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG (0x01)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_SHUTDOWN (0x0100)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_CRITLOCKED (0x0080)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_WINLOCKED (0x0040)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_INTCLR (0x0020)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_ALERTSTAT (0x0010)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_ALERTCTRL (0x0008)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_ALERTSEL (0x0004)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_ALERTPOL (0x0002)
|
|
||||||
#define MGOS_MCP9808_REG_CONFIG_ALERTMODE (0x0001)
|
|
||||||
|
|
||||||
#define MGOS_MCP9808_REG_UPPER_TEMP (0x02)
|
|
||||||
#define MGOS_MCP9808_REG_LOWER_TEMP (0x03)
|
|
||||||
#define MGOS_MCP9808_REG_CRIT_TEMP (0x04)
|
|
||||||
#define MGOS_MCP9808_REG_AMBIENT_TEMP (0x05)
|
|
||||||
#define MGOS_MCP9808_REG_MANUF_ID (0x06)
|
|
||||||
#define MGOS_MCP9808_REG_DEVICE_ID (0x07)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mgos_mcp9808
|
|
||||||
{
|
|
||||||
struct mgos_i2c *i2c;
|
|
||||||
uint8_t i2caddr;
|
|
||||||
double last_read_time;
|
|
||||||
|
|
||||||
float temperature;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -81,3 +81,33 @@ double mg_time(void) {
|
|||||||
void mgos_usleep(uint32_t usecs) {
|
void mgos_usleep(uint32_t usecs) {
|
||||||
usleep(usecs);
|
usleep(usecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool mgos_gpio_set_int_handler(int pin, enum mgos_gpio_int_mode mode, mgos_gpio_int_handler_f cb, void *arg) {
|
||||||
|
LOG(LL_INFO, ("Not implemented."));
|
||||||
|
return true;
|
||||||
|
(void)pin;
|
||||||
|
(void)mode;
|
||||||
|
(void)cb;
|
||||||
|
(void)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool mgos_gpio_enable_int(int pin) {
|
||||||
|
LOG(LL_INFO, ("Not implemented."));
|
||||||
|
return true;
|
||||||
|
(void)pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mgos_gpio_disable_int(int pin) {
|
||||||
|
LOG(LL_INFO, ("Not implemented."));
|
||||||
|
return true;
|
||||||
|
(void)pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mgos_gpio_clear_int(int pin) {
|
||||||
|
LOG(LL_INFO, ("Not implemented."));
|
||||||
|
return;
|
||||||
|
(void)pin;
|
||||||
|
}
|
||||||
|
@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_mpu9250_internal.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
|
|
||||||
// Datasheet:
|
|
||||||
//
|
|
||||||
|
|
||||||
// Private functions follow
|
|
||||||
static bool mgos_mpu9250_ak8963_init(struct mgos_mpu9250 *imu, uint8_t i2caddr) {
|
|
||||||
int device_id;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
imu->i2caddr_ak8963 = i2caddr;
|
|
||||||
|
|
||||||
device_id = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_WHO_AM_I);
|
|
||||||
if (device_id != MGOS_MPU9250_DEVID_AK8963) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG(LL_INFO, ("Detected AK8963 at I2C 0x%02x", i2caddr));
|
|
||||||
|
|
||||||
mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL, 0x00);
|
|
||||||
mgos_usleep(10000);
|
|
||||||
|
|
||||||
mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL, 0x0F);
|
|
||||||
mgos_usleep(10000);
|
|
||||||
|
|
||||||
uint8_t data[3];
|
|
||||||
if (!mgos_i2c_read_reg_n(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_ASAX, 3, data)) {
|
|
||||||
LOG(LL_ERROR, ("Could not read magnetometer adjustment registers"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
imu->mag_adj[0] = (float)(data[0] - 128) / 256. + 1.;
|
|
||||||
imu->mag_adj[1] = (float)(data[1] - 128) / 256. + 1.;
|
|
||||||
imu->mag_adj[2] = (float)(data[2] - 128) / 256. + 1.;
|
|
||||||
LOG(LL_DEBUG, ("magnetometer adjustment %.2f %.2f %.2f", imu->mag_adj[0], imu->mag_adj[1], imu->mag_adj[2]));
|
|
||||||
|
|
||||||
mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL, 0x00);
|
|
||||||
mgos_usleep(10000);
|
|
||||||
|
|
||||||
// Set magnetometer data resolution and sample ODR
|
|
||||||
mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL, 0x16);
|
|
||||||
mgos_usleep(10000);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private functions end
|
|
||||||
|
|
||||||
// Public functions follow
|
|
||||||
struct mgos_mpu9250 *mgos_mpu9250_create(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
struct mgos_mpu9250 *imu;
|
|
||||||
int device_id;
|
|
||||||
|
|
||||||
if (!i2c) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
imu = calloc(1, sizeof(struct mgos_mpu9250));
|
|
||||||
if (!imu) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
imu->i2caddr = i2caddr;
|
|
||||||
imu->i2c = i2c;
|
|
||||||
|
|
||||||
device_id = mgos_i2c_read_reg_b(i2c, i2caddr, MGOS_MPU9250_REG_WHO_AM_I);
|
|
||||||
switch (device_id) {
|
|
||||||
case MGOS_MPU9250_DEVID_9250:
|
|
||||||
LOG(LL_INFO, ("Detected MPU9250 at I2C 0x%02x", i2caddr));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MGOS_MPU9250_DEVID_9255:
|
|
||||||
LOG(LL_INFO, ("Detected MPU9255 at I2C 0x%02x", i2caddr));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG(LL_ERROR, ("Failed to detect MPU9250 at I2C 0x%02x (device_id=0x%02x)", i2caddr, device_id));
|
|
||||||
free(imu);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
mgos_i2c_write_reg_b(i2c, i2caddr, MGOS_MPU9250_REG_PWR_MGMT_1, 0x80);
|
|
||||||
mgos_usleep(100000);
|
|
||||||
|
|
||||||
// Enable imus
|
|
||||||
mgos_i2c_write_reg_b(i2c, i2caddr, MGOS_MPU9250_REG_PWR_MGMT_2, 0x00);
|
|
||||||
|
|
||||||
// Magnetometer enable
|
|
||||||
mgos_i2c_write_reg_b(i2c, i2caddr, MGOS_MPU9250_REG_INT_PIN_CFG, 0x02);
|
|
||||||
|
|
||||||
// TODO(pim): is the mag always on 0x0C ?
|
|
||||||
if (false == (imu->mag_enabled = mgos_mpu9250_ak8963_init(imu, MGOS_AK8963_DEFAULT_I2CADDR))) {
|
|
||||||
LOG(LL_ERROR, ("Could not detect/initialize AK8963 magnetometer, disabling"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return imu;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mgos_mpu9250_destroy(struct mgos_mpu9250 **imu) {
|
|
||||||
if (!*imu) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
free(*imu);
|
|
||||||
*imu = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_set_accelerometer_range(struct mgos_mpu9250 *imu, enum mgos_mpu9250_accelerometer_range range) {
|
|
||||||
int val;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((val = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_ACCEL_CONFIG)) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
val &= 0xE7; // 11100111
|
|
||||||
val |= range << 3;
|
|
||||||
|
|
||||||
return mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_ACCEL_CONFIG, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_accelerometer_range(struct mgos_mpu9250 *imu, enum mgos_mpu9250_accelerometer_range *range) {
|
|
||||||
int val;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((val = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_ACCEL_CONFIG)) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
val &= 0x18; // 00011000
|
|
||||||
val >>= 3;
|
|
||||||
*range = val;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_accelerometer(struct mgos_mpu9250 *imu, float *x, float *y, float *z) {
|
|
||||||
uint8_t data[6];
|
|
||||||
int16_t ax, ay, az;
|
|
||||||
enum mgos_mpu9250_accelerometer_range acc_range;
|
|
||||||
uint16_t divider;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_mpu9250_get_accelerometer_range(imu, &acc_range)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_i2c_read_reg_n(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_ACCEL_XOUT_H, 6, data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ax = (data[0] << 8) | (data[1]);
|
|
||||||
ay = (data[2] << 8) | (data[3]);
|
|
||||||
az = (data[4] << 8) | (data[5]);
|
|
||||||
// LOG(LL_DEBUG, ("ax=%d ay=%d az=%d", ax, ay, az));
|
|
||||||
|
|
||||||
switch (acc_range) {
|
|
||||||
case RANGE_16G: divider = 2048; break;
|
|
||||||
|
|
||||||
case RANGE_8G: divider = 4096; break;
|
|
||||||
|
|
||||||
case RANGE_4G: divider = 8192; break;
|
|
||||||
|
|
||||||
case RANGE_2G: divider = 16384; break;
|
|
||||||
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
*x = (float)ax / divider;
|
|
||||||
*y = (float)ay / divider;
|
|
||||||
*z = (float)az / divider;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_set_gyroscope_range(struct mgos_mpu9250 *imu, enum mgos_mpu9250_gyroscope_range range) {
|
|
||||||
int val;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((val = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_GYRO_CONFIG)) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
val &= 0xE7; // 11100111
|
|
||||||
val |= range << 3;
|
|
||||||
|
|
||||||
return mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_GYRO_CONFIG, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_gyroscope_range(struct mgos_mpu9250 *imu, enum mgos_mpu9250_gyroscope_range *range) {
|
|
||||||
int val;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((val = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_GYRO_CONFIG)) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
val &= 0x18; // 00011000
|
|
||||||
val >>= 3;
|
|
||||||
*range = val;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_gyroscope(struct mgos_mpu9250 *imu, float *x, float *y, float *z) {
|
|
||||||
uint8_t data[6];
|
|
||||||
int16_t gx, gy, gz;
|
|
||||||
enum mgos_mpu9250_gyroscope_range gyr_range;
|
|
||||||
float divider;
|
|
||||||
|
|
||||||
if (!imu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_mpu9250_get_gyroscope_range(imu, &gyr_range)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_i2c_read_reg_n(imu->i2c, imu->i2caddr, MGOS_MPU9250_REG_GYRO_XOUT_H, 6, data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
gx = (data[0] << 8) | (data[1]);
|
|
||||||
gy = (data[2] << 8) | (data[3]);
|
|
||||||
gz = (data[4] << 8) | (data[5]);
|
|
||||||
// LOG(LL_DEBUG, ("gx=%d gy=%d gz=%d", gx, gy, gz));
|
|
||||||
|
|
||||||
switch (gyr_range) {
|
|
||||||
case RANGE_GYRO_2000: divider = 16.4; break;
|
|
||||||
|
|
||||||
case RANGE_GYRO_1000: divider = 32.8; break;
|
|
||||||
|
|
||||||
case RANGE_GYRO_500: divider = 65.5; break;
|
|
||||||
|
|
||||||
case RANGE_GYRO_250: divider = 131.0; break;
|
|
||||||
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
*x = (float)gx / divider;
|
|
||||||
*y = (float)gy / divider;
|
|
||||||
*z = (float)gz / divider;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_set_magnetometer_scale(struct mgos_mpu9250 *imu, enum mgos_mpu9250_magnetometer_scale scale) {
|
|
||||||
int val;
|
|
||||||
|
|
||||||
if (!imu || !imu->mag_enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((val = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL)) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
val &= 0x06;
|
|
||||||
mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL, 0x00);
|
|
||||||
mgos_usleep(10000);
|
|
||||||
|
|
||||||
mgos_i2c_write_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL, (scale << 4) | val);
|
|
||||||
mgos_usleep(10000);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_magnetometer_scale(struct mgos_mpu9250 *imu, enum mgos_mpu9250_magnetometer_scale *scale) {
|
|
||||||
int val;
|
|
||||||
|
|
||||||
if (!imu || !imu->mag_enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((val = mgos_i2c_read_reg_b(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_CNTL)) < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*scale = (val >> 4) & 0x01;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_set_magnetometer_speed(struct mgos_mpu9250 *imu, enum mgos_mpu9250_magnetometer_speed speed) {
|
|
||||||
if (!imu || !imu->mag_enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
(void) speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_magnetometer_speed(struct mgos_mpu9250 *imu, enum mgos_mpu9250_magnetometer_speed *speed) {
|
|
||||||
if (!imu || !imu->mag_enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
(void) speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_get_magnetometer(struct mgos_mpu9250 *imu, float *x, float *y, float *z) {
|
|
||||||
uint8_t data[7];
|
|
||||||
int16_t mx, my, mz;
|
|
||||||
enum mgos_mpu9250_magnetometer_scale mag_scale;
|
|
||||||
float divider;
|
|
||||||
|
|
||||||
if (!imu || !imu->mag_enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_mpu9250_get_magnetometer_scale(imu, &mag_scale)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_i2c_read_reg_n(imu->i2c, imu->i2caddr_ak8963, MGOS_MPU9250_REG_AK8963_XOUT_L, 7, data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (data[6] & 0x08) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mx = (data[1] << 8) | (data[0]);
|
|
||||||
my = (data[3] << 8) | (data[2]);
|
|
||||||
mz = (data[5] << 8) | (data[4]);
|
|
||||||
// LOG(LL_DEBUG, ("mx=%d my=%d mz=%d", mx, my, mz));
|
|
||||||
|
|
||||||
switch (mag_scale) {
|
|
||||||
case SCALE_14_BITS: divider = 8190.0; break;
|
|
||||||
|
|
||||||
case SCALE_16_BITS: divider = 32760.0; break;
|
|
||||||
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
*x = (float)mx * 4912.0 * imu->mag_adj[0] / divider;
|
|
||||||
*y = (float)my * 4912.0 * imu->mag_adj[1] / divider;
|
|
||||||
*z = (float)mz * 4912.0 * imu->mag_adj[2] / divider;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mgos_mpu9250_i2c_init(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public functions end
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_mpu9250.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MGOS_MPU9250_DEFAULT_I2CADDR (0x68)
|
|
||||||
#define MGOS_AK8963_DEFAULT_I2CADDR (0x0C)
|
|
||||||
|
|
||||||
#define MGOS_MPU9250_DEVID_9250 (0x71)
|
|
||||||
#define MGOS_MPU9250_DEVID_9255 (0x73)
|
|
||||||
#define MGOS_MPU9250_DEVID_AK8963 (0x48)
|
|
||||||
|
|
||||||
// MPU9250 -- Accelerometer and Gyro registers
|
|
||||||
#define MGOS_MPU9250_REG_SMPLRT_DIV (0x19)
|
|
||||||
#define MGOS_MPU9250_REG_CONFIG (0x1A)
|
|
||||||
#define MGOS_MPU9250_REG_GYRO_CONFIG (0x1B)
|
|
||||||
#define MGOS_MPU9250_REG_ACCEL_CONFIG (0x1C)
|
|
||||||
#define MGOS_MPU9250_REG_ACCEL_CONFIG2 (0x1D)
|
|
||||||
#define MGOS_MPU9250_REG_INT_PIN_CFG (0x37)
|
|
||||||
#define MGOS_MPU9250_REG_INT_ENABLE (0x38)
|
|
||||||
#define MGOS_MPU9250_REG_INT_STATUS (0x3A)
|
|
||||||
#define MGOS_MPU9250_REG_ACCEL_XOUT_H (0x3B)
|
|
||||||
#define MGOS_MPU9250_REG_TEMP_OUT_H (0x41)
|
|
||||||
#define MGOS_MPU9250_REG_GYRO_XOUT_H (0x43)
|
|
||||||
#define MGOS_MPU9250_REG_PWR_MGMT_1 (0x6B)
|
|
||||||
#define MGOS_MPU9250_REG_PWR_MGMT_2 (0x6C)
|
|
||||||
#define MGOS_MPU9250_REG_WHO_AM_I (0x75)
|
|
||||||
|
|
||||||
// AK8963 Companion -- Magnetometer Registers
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_WHO_AM_I (0x00)
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_ST1 (0x02)
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_XOUT_L (0x03)
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_CNTL (0x0A)
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_ASAX (0x10)
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_ASAY (0x11)
|
|
||||||
#define MGOS_MPU9250_REG_AK8963_ASAZ (0x12)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mgos_mpu9250 {
|
|
||||||
struct mgos_i2c *i2c;
|
|
||||||
uint8_t i2caddr;
|
|
||||||
uint8_t i2caddr_ak8963;
|
|
||||||
|
|
||||||
bool mag_enabled;
|
|
||||||
float mag_adj[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
180
src/mgos_sht31.c
180
src/mgos_sht31.c
@ -1,180 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_sht31_internal.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
|
|
||||||
// Datasheet:
|
|
||||||
// https://cdn-shop.adafruit.com/product-files/2857/Sensirion_Humidity_SHT3x_Datasheet_digital-767294.pdf
|
|
||||||
|
|
||||||
// Private functions follow
|
|
||||||
static bool mgos_sht31_cmd(struct mgos_sht31 *sensor, uint16_t cmd) {
|
|
||||||
uint8_t data[2];
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
data[0]=cmd>>8;
|
|
||||||
data[1]=cmd&0xFF;
|
|
||||||
if (!mgos_i2c_write(sensor->i2c, sensor->i2caddr, data, 2, true)) {
|
|
||||||
LOG(LL_ERROR, ("I2C=0x%02x cmd=%u (0x%04x) write error", sensor->i2caddr, cmd, cmd));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG(LL_DEBUG, ("I2C=0x%02x cmd=%u (0x%04x) write success", sensor->i2caddr, cmd, cmd));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t crc8(const uint8_t *data, int len) {
|
|
||||||
const uint8_t poly=0x31;
|
|
||||||
uint8_t crc=0xFF;
|
|
||||||
|
|
||||||
for (int j=len; j; --j) {
|
|
||||||
crc^=*data++;
|
|
||||||
for (int i=8; i; --i)
|
|
||||||
crc=(crc & 0x80) ? (crc << 1) ^ poly : (crc << 1);
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint16_t mgos_sht31_status(struct mgos_sht31 *sensor) {
|
|
||||||
uint8_t data[3];
|
|
||||||
uint16_t value;
|
|
||||||
|
|
||||||
mgos_sht31_cmd(sensor, MGOS_SHT31_READSTATUS);
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 3, true))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Check CRC8 checksums
|
|
||||||
if ((data[2]!=crc8(data, 2)))
|
|
||||||
return 0;
|
|
||||||
value=(data[0]<<8) + data[1];
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Private functions end
|
|
||||||
|
|
||||||
// Public functions follow
|
|
||||||
struct mgos_sht31 *mgos_sht31_create(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
struct mgos_sht31 *sensor;
|
|
||||||
uint16_t status0, status1, status2;
|
|
||||||
|
|
||||||
if (!i2c) return NULL;
|
|
||||||
|
|
||||||
sensor=calloc(1, sizeof(struct mgos_sht31));
|
|
||||||
if (!sensor) return NULL;
|
|
||||||
sensor->i2caddr=i2caddr;
|
|
||||||
sensor->i2c=i2c;
|
|
||||||
sensor->last_read_time=0;
|
|
||||||
|
|
||||||
mgos_sht31_cmd(sensor, MGOS_SHT31_SOFTRESET);
|
|
||||||
|
|
||||||
// Toggle heater on and off, which shows up in status register bit 13 (0=Off, 1=On)
|
|
||||||
// heater is off, bit13 is 0
|
|
||||||
status0=mgos_sht31_status(sensor);
|
|
||||||
mgos_sht31_cmd(sensor, MGOS_SHT31_HEATEREN);
|
|
||||||
// heater is on, bit13 is 1
|
|
||||||
status1=mgos_sht31_status(sensor);
|
|
||||||
mgos_sht31_cmd(sensor, MGOS_SHT31_HEATERDIS);
|
|
||||||
// heater is off, bit13 is 0
|
|
||||||
status2=mgos_sht31_status(sensor);
|
|
||||||
|
|
||||||
if (((status0 & 0x2000) == 0) && ((status1 & 0x2000) != 0) && ((status2 & 0x2000) == 0)) {
|
|
||||||
LOG(LL_INFO, ("SHT31 created at I2C 0x%02x", i2caddr));
|
|
||||||
return sensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(LL_ERROR, ("Failed to create SHT31 at I2C 0x%02x", i2caddr));
|
|
||||||
free(sensor);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mgos_sht31_destroy(struct mgos_sht31 **sensor) {
|
|
||||||
if (!*sensor) return;
|
|
||||||
free (*sensor);
|
|
||||||
*sensor=NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_sht31_read(struct mgos_sht31 *sensor) {
|
|
||||||
double now = mg_time();
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (now - sensor->last_read_time < MGOS_SHT31_READ_DELAY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Read out sensor data here
|
|
||||||
//
|
|
||||||
uint8_t data[6];
|
|
||||||
float humidity, temperature;
|
|
||||||
|
|
||||||
mgos_sht31_cmd(sensor, MGOS_SHT31_MEAS_HIGHREP);
|
|
||||||
|
|
||||||
mgos_usleep(500);
|
|
||||||
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 6, true))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check CRC8 checksums
|
|
||||||
if ((data[2]!=crc8(data, 2)) || (data[5]!=crc8(data+3, 2)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
temperature = data[0]*256 + data[1];
|
|
||||||
temperature *= 175;
|
|
||||||
temperature /= 0xffff;
|
|
||||||
temperature -= 45;
|
|
||||||
sensor->temperature = temperature;
|
|
||||||
|
|
||||||
humidity = data[3] * 256 + data[4];
|
|
||||||
humidity *= 100;
|
|
||||||
humidity /= 0xFFFF;
|
|
||||||
sensor->humidity = humidity;
|
|
||||||
|
|
||||||
LOG(LL_DEBUG, ("temperature=%.2fC humidity=%.1f%%", sensor->temperature, sensor->humidity));
|
|
||||||
sensor->last_read_time=now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_sht31_getTemperature(struct mgos_sht31 *sensor) {
|
|
||||||
if (!mgos_sht31_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_sht31_getHumidity(struct mgos_sht31 *sensor) {
|
|
||||||
if (!mgos_sht31_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->humidity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_sht31_i2c_init(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Public functions end
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_sht31.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MGOS_SHT31_DEFAULT_I2CADDR (0x44)
|
|
||||||
|
|
||||||
#define MGOS_SHT31_MEAS_HIGHREP_STRETCH (0x2C06)
|
|
||||||
#define MGOS_SHT31_MEAS_MEDREP_STRETCH (0x2C0D)
|
|
||||||
#define MGOS_SHT31_MEAS_LOWREP_STRETCH (0x2C10)
|
|
||||||
#define MGOS_SHT31_MEAS_HIGHREP (0x2400)
|
|
||||||
#define MGOS_SHT31_MEAS_MEDREP (0x240B)
|
|
||||||
#define MGOS_SHT31_MEAS_LOWREP (0x2416)
|
|
||||||
#define MGOS_SHT31_READSTATUS (0xF32D)
|
|
||||||
#define MGOS_SHT31_CLEARSTATUS (0x3041)
|
|
||||||
#define MGOS_SHT31_SOFTRESET (0x30A2)
|
|
||||||
#define MGOS_SHT31_HEATEREN (0x306D)
|
|
||||||
#define MGOS_SHT31_HEATERDIS (0x3066)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mgos_sht31
|
|
||||||
{
|
|
||||||
struct mgos_i2c *i2c;
|
|
||||||
uint8_t i2caddr;
|
|
||||||
double last_read_time;
|
|
||||||
|
|
||||||
float humidity, temperature;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_si7021_internal.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
|
|
||||||
// Datasheet:
|
|
||||||
// https://cdn-learn.adafruit.com/assets/assets/000/035/931/original/Support_Documents_TechnicalDocs_Si7021-A20.pdf
|
|
||||||
|
|
||||||
// Private functions follow
|
|
||||||
static bool mgos_si7021_cmd(struct mgos_si7021 *sensor, uint8_t cmd) {
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!mgos_i2c_write(sensor->i2c, sensor->i2caddr, &cmd, 1, true)) {
|
|
||||||
LOG(LL_ERROR, ("I2C=0x%02x cmd=%u (0x%02x) write error", sensor->i2caddr, cmd, cmd));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG(LL_DEBUG, ("I2C=0x%02x cmd=%u (0x%02x) write success", sensor->i2caddr, cmd, cmd));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t crc8(const uint8_t *data, int len) {
|
|
||||||
const uint8_t poly=0x31;
|
|
||||||
uint8_t crc=0x00;
|
|
||||||
|
|
||||||
for (int j=len; j; --j) {
|
|
||||||
crc^=*data++;
|
|
||||||
for (int i=8; i; --i)
|
|
||||||
crc=(crc & 0x80) ? (crc << 1) ^ poly : (crc << 1);
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Private functions end
|
|
||||||
|
|
||||||
// Public functions follow
|
|
||||||
struct mgos_si7021 *mgos_si7021_create(struct mgos_i2c *i2c, uint8_t i2caddr) {
|
|
||||||
struct mgos_si7021 *sensor;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!i2c) return NULL;
|
|
||||||
|
|
||||||
// Reset and query register
|
|
||||||
ret=mgos_i2c_read_reg_b(i2c, i2caddr, MGOS_SI7021_READRHT_REG_CMD);
|
|
||||||
if (ret!=0x3A) {
|
|
||||||
LOG(LL_ERROR, ("Chip ID register invalid, expected 0x3A got 0x%02x (%d)", ret, ret));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sensor=calloc(1, sizeof(struct mgos_si7021));
|
|
||||||
if (!sensor) return NULL;
|
|
||||||
sensor->i2caddr=i2caddr;
|
|
||||||
sensor->i2c=i2c;
|
|
||||||
sensor->last_read_time=0;
|
|
||||||
|
|
||||||
LOG(LL_INFO, ("SI7021 created at I2C 0x%02x", i2caddr));
|
|
||||||
return sensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mgos_si7021_destroy(struct mgos_si7021 **sensor) {
|
|
||||||
if (!*sensor) return;
|
|
||||||
free (*sensor);
|
|
||||||
*sensor=NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_si7021_read(struct mgos_si7021 *sensor) {
|
|
||||||
double now = mg_time();
|
|
||||||
|
|
||||||
if (!sensor || !sensor->i2c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (now - sensor->last_read_time < MGOS_SI7021_READ_DELAY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Read out sensor data here
|
|
||||||
//
|
|
||||||
uint8_t data[3];
|
|
||||||
|
|
||||||
if (!mgos_si7021_cmd(sensor, MGOS_SI7021_MEASRH_NOHOLD_CMD)) {
|
|
||||||
LOG(LL_ERROR, ("Could not write command"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 3, false)) {
|
|
||||||
LOG(LL_ERROR, ("Could not read command"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (data[2] != crc8(&data[0], 2)) {
|
|
||||||
LOG(LL_ERROR, ("CRC error on humidity data"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t hum = (data[0]<<8)+data[1];
|
|
||||||
float humidity = hum;
|
|
||||||
humidity *= 125;
|
|
||||||
humidity /= 65536;
|
|
||||||
humidity -= 6;
|
|
||||||
sensor->humidity = humidity;
|
|
||||||
|
|
||||||
if (!mgos_si7021_cmd(sensor, MGOS_SI7021_MEASTEMP_NOHOLD_CMD)) {
|
|
||||||
LOG(LL_ERROR, ("Could not write command"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!mgos_i2c_read(sensor->i2c, sensor->i2caddr, data, 3, false)) {
|
|
||||||
LOG(LL_ERROR, ("Could not read command"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (data[2] != crc8(&data[0], 2)) {
|
|
||||||
LOG(LL_ERROR, ("CRC error on temperature data"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t temp = (data[0]<<8)+data[1];
|
|
||||||
float temperature = temp;
|
|
||||||
temperature *= 175.72;
|
|
||||||
temperature /= 65536;
|
|
||||||
temperature -= 46.85;
|
|
||||||
sensor->temperature = temperature;
|
|
||||||
|
|
||||||
LOG(LL_DEBUG, ("temperature=%.2fC humidity=%.1f%%", sensor->temperature, sensor->humidity));
|
|
||||||
sensor->last_read_time=now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_si7021_getTemperature(struct mgos_si7021 *sensor) {
|
|
||||||
if (!mgos_si7021_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float mgos_si7021_getHumidity(struct mgos_si7021 *sensor) {
|
|
||||||
if (!mgos_si7021_read(sensor)) return NAN;
|
|
||||||
|
|
||||||
return sensor->humidity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool mgos_si7021_i2c_init(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Public functions end
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mgos.h"
|
|
||||||
#include "mgos_i2c.h"
|
|
||||||
#include "mgos_si7021.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#define MGOS_SI7021_DEFAULT_I2CADDR (0x40)
|
|
||||||
|
|
||||||
#define MGOS_SI7021_MEASRH_HOLD_CMD (0xE5)
|
|
||||||
#define MGOS_SI7021_MEASRH_NOHOLD_CMD (0xF5)
|
|
||||||
#define MGOS_SI7021_MEASTEMP_HOLD_CMD (0xE3)
|
|
||||||
#define MGOS_SI7021_MEASTEMP_NOHOLD_CMD (0xF3)
|
|
||||||
#define MGOS_SI7021_READPREVTEMP_CMD (0xE0)
|
|
||||||
#define MGOS_SI7021_RESET_CMD (0xFE)
|
|
||||||
#define MGOS_SI7021_WRITERHT_REG_CMD (0xE6)
|
|
||||||
#define MGOS_SI7021_READRHT_REG_CMD (0xE7)
|
|
||||||
#define MGOS_SI7021_WRITEHEATER_REG_CMD (0x51)
|
|
||||||
#define MGOS_SI7021_READHEATER_REG_CMD (0x11)
|
|
||||||
#define MGOS_SI7021_ID1_CMD (0xFA0F)
|
|
||||||
#define MGOS_SI7021_ID2_CMD (0xFCC9)
|
|
||||||
#define MGOS_SI7021_FIRMVERS_CMD (0x84B8)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mgos_si7021
|
|
||||||
{
|
|
||||||
struct mgos_i2c *i2c;
|
|
||||||
uint8_t i2caddr;
|
|
||||||
double last_read_time;
|
|
||||||
|
|
||||||
float humidity, temperature;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
Reference in New Issue
Block a user