Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Nochkin 2017-12-05 22:04:02 -05:00
commit b76f0cb760
80 changed files with 5483 additions and 938 deletions

View file

@ -83,6 +83,11 @@ int timer_set_frequency(const timer_frc_t frc, uint32_t freq)
uint32_t counts = 0;
timer_clkdiv_t div = timer_freq_to_div(freq);
if(freq == 0) //can't divide by 0
{
return -EINVAL;
}
counts = timer_freq_to_count(frc, freq, div);
if(counts == 0)
{

View file

@ -186,7 +186,7 @@ void spi_set_frequency_div(uint8_t bus, uint32_t divider);
inline uint32_t spi_get_frequency_div(uint8_t bus)
{
return (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) |
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
}
/**
* \brief Get SPI bus frequency in Hz
@ -196,8 +196,8 @@ inline uint32_t spi_get_frequency_div(uint8_t bus)
inline uint32_t spi_get_frequency_hz(uint8_t bus)
{
return APB_CLK_FREQ /
(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
}
/**
@ -231,7 +231,7 @@ inline spi_endianness_t spi_get_endianness(uint8_t bus)
{
return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
? SPI_BIG_ENDIAN
: SPI_LITTLE_ENDIAN;
: SPI_LITTLE_ENDIAN;
}
/**
@ -278,25 +278,26 @@ size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len
* \brief Add permanent command bits when transfert data over SPI
* Example:
*
* spi_set_command(1,1,0x01); // Set one command bit to 1
* for (uint8_t i = 0 ; i < x ; i++ ) {
* spi_transfer_8(1,0x55); // Send 1 bit command + 8 bits data x times
* spi_set_command(1, 1, 0x01); // Set one command bit to 1
* for (uint8_t i = 0; i < x; i++ ) {
* spi_transfer_8(1, 0x55); // Send 1 bit command + 8 bits data x times
* }
* spi_clear_command(1); // Clear command
* spi_transfer_8(1,0x55); // Send 8 bits data
* spi_transfer_8(1, 0x55); // Send 8 bits data
*
* \param bus Bus ID: 0 - system, 1 - user
* \param bits Number of bits (max: 16).
* \param data Command to send for each transfert.
*/
static inline void spi_set_command(uint8_t bus,uint8_t bits, uint16_t data)
static inline void spi_set_command(uint8_t bus, uint8_t bits, uint16_t data)
{
if(!bits) return ;
SPI(bus).USER0 |= SPI_USER0_COMMAND ; //enable COMMAND function in SPI module
uint16_t command = data << (16-bits); //align command data to high bits
command = ((command>>8)&0xff) | ((command<<8)&0xff00); //swap byte order
SPI(bus).USER2 = SET_FIELD(SPI(bus).USER2, SPI_USER2_COMMAND_BITLEN, --bits);
SPI(bus).USER2 = SET_FIELD(SPI(bus).USER2, SPI_USER2_COMMAND_VALUE, command);
if (!bits) return;
SPI(bus).USER0 |= SPI_USER0_COMMAND; //enable COMMAND function in SPI module
uint16_t command = data << (16 - bits); //align command data to high bits
command = ((command >> 8) & 0xff) | ((command << 8) & 0xff00); //swap byte order
SPI(bus).USER2 = SET_FIELD(SPI(bus).USER2, SPI_USER2_COMMAND_BITLEN, --bits);
SPI(bus).USER2 = SET_FIELD(SPI(bus).USER2, SPI_USER2_COMMAND_VALUE, command);
}
/**
@ -314,11 +315,12 @@ static inline void spi_set_command(uint8_t bus,uint8_t bits, uint16_t data)
* \param bits Number of bits (max: 32).
* \param data Address to send for each transfert.
*/
static inline void spi_set_address(uint8_t bus,uint8_t bits, uint32_t data)
static inline void spi_set_address(uint8_t bus, uint8_t bits, uint32_t data)
{
if(!bits) return ;
SPI(bus).USER0 |= SPI_USER0_ADDR ; //enable ADDRess function in SPI module
SPI(bus).ADDR = data<<(32-bits) ; //align address data to high bits
if (!bits) return;
SPI(bus).USER0 |= SPI_USER0_ADDR; //enable ADDRess function in SPI module
SPI(bus).ADDR = data << (32 - bits); //align address data to high bits
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_ADDR_BITLEN, --bits);
}
@ -326,12 +328,12 @@ static inline void spi_set_address(uint8_t bus,uint8_t bits, uint32_t data)
* \brief Add permanent dummy bits when transfert data over SPI
* Example:
*
* spi_set_dummy_bits(1,4,false); // Set 4 dummy bit before Dout
* for (uint8_t i = 0 ; i < x ; i++ ) {
* spi_transfer_16(1,0xC584); // Send 4 bits dummy + 16 bits Dout x times
* spi_set_dummy_bits(1, 4, false); // Set 4 dummy bit before Dout
* for (uint8_t i = 0; i < x; i++ ) {
* spi_transfer_16(1, 0xC584); // Send 4 bits dummy + 16 bits Dout x times
* }
* spi_set_dummy_bits(1,4,true); // Set 4 dummy bit between Dout and Din
* spi_transfer_8(1,0x55); // Send 8 bits Dout + 4 bits dummy + 8 bits Din
* spi_set_dummy_bits(1, 4, true); // Set 4 dummy bit between Dout and Din
* spi_transfer_8(1, 0x55); // Send 8 bits Dout + 4 bits dummy + 8 bits Din
*
* \param bus Bus ID: 0 - system, 1 - user
* \param bits Number of bits
@ -339,8 +341,9 @@ static inline void spi_set_address(uint8_t bus,uint8_t bits, uint32_t data)
*/
static inline void spi_set_dummy_bits(uint8_t bus, uint8_t bits, bool pos)
{
if(!bits) return ;
if(pos) { SPI(bus).USER0 |= SPI_USER0_MISO; } // Dummy bit will be between Dout and Din data if set
if (!bits) return;
if (pos)
SPI(bus).USER0 |= SPI_USER0_MISO; // Dummy bit will be between Dout and Din data if set
SPI(bus).USER0 |= SPI_USER0_DUMMY; //enable dummy bits
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_DUMMY_CYCLELEN, --bits);
}
@ -351,7 +354,7 @@ static inline void spi_set_dummy_bits(uint8_t bus, uint8_t bits, bool pos)
*/
static inline void spi_clear_address(uint8_t bus)
{
SPI(bus).USER0 &= ~(SPI_USER0_ADDR) ;
SPI(bus).USER0 &= ~(SPI_USER0_ADDR);
}
/**
@ -361,7 +364,7 @@ static inline void spi_clear_address(uint8_t bus)
static inline void spi_clear_command(uint8_t bus)
{
SPI(bus).USER0 &= ~(SPI_USER0_COMMAND) ;
SPI(bus).USER0 &= ~(SPI_USER0_COMMAND);
}
/**

View file

@ -14,6 +14,21 @@
#include <stdlib.h>
#include <stdout_redirect.h>
#include <sys/time.h>
#include <lwip/sockets.h>
/*
* The file descriptor index space is allocated in blocks. The first block of 3
* is for newlib I/O the stdin stdout and stderr. The next block of
* MEMP_NUM_NETCONN is allocated for lwip sockets, and the remainer to file
* system descriptors. The newlib default FD_SETSIZE is 64.
*/
#if LWIP_SOCKET_OFFSET < 3
#error Expecting a LWIP_SOCKET_OFFSET >= 3, to allow room for the standard I/O descriptors.
#endif
#define FILE_DESCRIPTOR_OFFSET (LWIP_SOCKET_OFFSET + MEMP_NUM_NETCONN)
#if FILE_DESCRIPTOR_OFFSET > FD_SETSIZE
#error Too many lwip sockets for the FD_SETSIZE.
#endif
extern void *xPortSupervisorStackPointer;
@ -81,10 +96,17 @@ __attribute__((weak)) long _write_filesystem_r(struct _reent *r, int fd, const c
__attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, int len )
{
if(fd != r->_stdout->_file) {
if (fd >= FILE_DESCRIPTOR_OFFSET) {
return _write_filesystem_r(r, fd, ptr, len);
}
return current_stdout_write_r(r, fd, ptr, len);
if (fd >= LWIP_SOCKET_OFFSET) {
return lwip_write(fd, ptr, len);
}
if (fd == r->_stdout->_file) {
return current_stdout_write_r(r, fd, ptr, len);
}
r->_errno = EBADF;
return -1;
}
/* syscall implementation for stdio read from UART */
@ -109,10 +131,36 @@ __attribute__((weak)) long _read_filesystem_r( struct _reent *r, int fd, char *p
__attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len )
{
if(fd != r->_stdin->_file) {
if (fd >= FILE_DESCRIPTOR_OFFSET) {
return _read_filesystem_r(r, fd, ptr, len);
}
return _read_stdin_r(r, fd, ptr, len);
if (fd >= LWIP_SOCKET_OFFSET) {
return lwip_read(fd, ptr, len);
}
if (fd == r->_stdin->_file) {
return _read_stdin_r(r, fd, ptr, len);
}
r->_errno = EBADF;
return -1;
}
/* default implementation, replace in a filesystem */
__attribute__((weak)) int _close_filesystem_r(struct _reent *r, int fd)
{
r->_errno = EBADF;
return -1;
}
__attribute__((weak)) int _close_r(struct _reent *r, int fd)
{
if (fd >= FILE_DESCRIPTOR_OFFSET) {
return _close_filesystem_r(r, fd);
}
if (fd >= LWIP_SOCKET_OFFSET) {
return lwip_close(fd);
}
r->_errno = EBADF;
return -1;
}
/* Stub syscall implementations follow, to allow compiling newlib functions that
@ -121,9 +169,6 @@ __attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len
__attribute__((weak, alias("syscall_returns_enosys")))
int _open_r(struct _reent *r, const char *pathname, int flags, int mode);
__attribute__((weak, alias("syscall_returns_enosys")))
int _close_r(struct _reent *r, int fd);
__attribute__((weak, alias("syscall_returns_enosys")))
int _unlink_r(struct _reent *r, const char *path);

View file

@ -1,11 +1,11 @@
/* Very basic example showing usage of access point mode and the DHCP server.
The ESP in the example runs a telnet server on 172.16.0.1 (port 23) that
outputs some status information if you connect to it, then closes
the connection.
This example code is in the public domain.
*/
/**
* Very basic example showing usage of access point mode and the DHCP server.
* The ESP in the example runs a telnet server on 172.16.0.1 (port 23) that
* outputs some status information if you connect to it, then closes
* the connection.
*
* This example code is in the public domain.
*/
#include <string.h>
#include <espressif/esp_common.h>
@ -36,61 +36,55 @@ void user_init(void)
IP4_ADDR(&ap_ip.netmask, 255, 255, 0, 0);
sdk_wifi_set_ip_info(1, &ap_ip);
struct sdk_softap_config ap_config = {
.ssid = AP_SSID,
.ssid_hidden = 0,
.channel = 3,
.ssid_len = strlen(AP_SSID),
.authmode = AUTH_WPA_WPA2_PSK,
.password = AP_PSK,
.max_connection = 3,
.beacon_interval = 100,
};
struct sdk_softap_config ap_config = { .ssid = AP_SSID, .ssid_hidden = 0, .channel = 3, .ssid_len = strlen(AP_SSID), .authmode =
AUTH_WPA_WPA2_PSK, .password = AP_PSK, .max_connection = 3, .beacon_interval = 100, };
sdk_wifi_softap_set_config(&ap_config);
ip4_addr_t first_client_ip;
IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
dhcpserver_start(&first_client_ip, 4);
xTaskCreate(telnetTask, "telnetTask", 512, NULL, 2, NULL);
}
/* Telnet task listens on port 23, returns some status information and then closes
the connection if you connect to it.
*/
the connection if you connect to it.
*/
static void telnetTask(void *pvParameters)
{
struct netconn *nc = netconn_new (NETCONN_TCP);
if(!nc) {
printf("Status monitor: Failed to allocate socket.\r\n");
return;
}
netconn_bind(nc, IP_ANY_TYPE, TELNET_PORT);
netconn_listen(nc);
ip_addr_t first_client_ip;
IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
dhcpserver_start(&first_client_ip, 4);
while(1) {
struct netconn *client = NULL;
err_t err = netconn_accept(nc, &client);
if (err != ERR_OK) {
if(client)
netconn_delete(client);
continue;
struct netconn *nc = netconn_new(NETCONN_TCP);
if (!nc)
{
printf("Status monitor: Failed to allocate socket.\r\n");
return;
}
netconn_bind(nc, IP_ANY_TYPE, TELNET_PORT);
netconn_listen(nc);
ip_addr_t client_addr;
uint16_t port_ignore;
netconn_peer(client, &client_addr, &port_ignore);
while (1)
{
struct netconn *client = NULL;
err_t err = netconn_accept(nc, &client);
char buf[80];
snprintf(buf, sizeof(buf), "Uptime %d seconds\r\n",
xTaskGetTickCount()*portTICK_PERIOD_MS/1000);
netconn_write(client, buf, strlen(buf), NETCONN_COPY);
snprintf(buf, sizeof(buf), "Free heap %d bytes\r\n", (int)xPortGetFreeHeapSize());
netconn_write(client, buf, strlen(buf), NETCONN_COPY);
char abuf[40];
snprintf(buf, sizeof(buf), "Your address is %s\r\n\r\n", ipaddr_ntoa_r(&client_addr, abuf, sizeof(abuf)));
netconn_write(client, buf, strlen(buf), NETCONN_COPY);
netconn_delete(client);
}
if (err != ERR_OK)
{
if (client)
netconn_delete(client);
continue;
}
ip_addr_t client_addr;
uint16_t port_ignore;
netconn_peer(client, &client_addr, &port_ignore);
char buf[80];
snprintf(buf, sizeof(buf), "Uptime %d seconds\r\n", xTaskGetTickCount() * portTICK_PERIOD_MS / 1000);
netconn_write(client, buf, strlen(buf), NETCONN_COPY);
snprintf(buf, sizeof(buf), "Free heap %d bytes\r\n", (int) xPortGetFreeHeapSize());
netconn_write(client, buf, strlen(buf), NETCONN_COPY);
char abuf[40];
snprintf(buf, sizeof(buf), "Your address is %s\r\n\r\n", ipaddr_ntoa_r(&client_addr, abuf, sizeof(abuf)));
netconn_write(client, buf, strlen(buf), NETCONN_COPY);
netconn_delete(client);
}
}

46
examples/bme680/README.md Normal file
View file

@ -0,0 +1,46 @@
# BME680 Driver Examples
These examples demonstrate the usage of the BME680 driver with only one and multiple BME680 sensors.
## Hardware setup
There are examples that are using either I2C or SPI with one or two sensors.
For examples using BME680 sensor as I2C slave, just use GPIO5 (SCL) and GPIO4 (SDA) to connect to the BME680 sensor's I2C interface.
```
+-------------------------+ +--------+
| ESP8266 Bus 0 | | BME680 |
| GPIO 5 (SCL) +---->+ SCL |
| GPIO 4 (SDA) +-----+ SDA |
| | +--------+
+-------------------------+
```
For examples that are using SPI, BME680 sensor has to be connected to SPI bus 1. Since GPIO15 used as default CS signal of SPI bus 1 does not work correctly together with BME680, you have to connect CS to another GPIO pin, e.g., GPIO2.
```
+-------------------------+ +----------+
| ESP8266 Bus 1 | | BME680 |
| GPIO 12 (MISO) <-----< SDO |
| GPIO 13 (MOSI) >-----> SDI |
| GPIO 14 (SCK) >-----> SCK |
| GPIO 2 (CS) >-----> CS |
+-------------------------+ +----------+
```
The example with two sensors use the combination of I2C and SPI.
## Example description
__*bme680_one_sensor*__
This simple example uses only **one sensor** connected either to **I2C** or to **SPI**. Which of these interfaces is used is defined by constant **SPI_USED**. The user task triggers a measurement every second and uses function ```vTaskDelay``` to wait for the measurement results.
__*bme680_two_sensors*__
This example uses **two sensors**. One sensor is connected to **I2C** bus 0 and one sensor is connected to **SPI**. It defines two different user tasks, one for each sensor. It demonstrate the possible approaches to wait for measurement results, active busy waiting using ```bme680_is_measuring``` and passive waiting using *vTaskDelay*.
__*bme680_heating_profiles*__
This simple example uses one **only sensor** connected to **I2C** bus 0 and a sequence of heating profiles. The heating profile is changed with each cycle.

View file

@ -0,0 +1,3 @@
PROGRAM=BME680_heating_profiles
EXTRA_COMPONENTS = extras/i2c extras/bme680
include ../../../common.mk

View file

@ -0,0 +1,125 @@
/**
* Simple example with one sensor connected to I2C bus 0 and a sequence of
* heating profiles. The heating profile is changed with each cycle.
*
* Harware configuration:
*
* I2C +-------------------------+ +----------+
* | ESP8266 Bus 0 | | BME680 |
* | GPIO 5 (SCL) ------> SCL |
* | GPIO 4 (SDA) ------- SDA |
* +-------------------------+ +----------+
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
// include communication interface driver
#include "i2c/i2c.h"
// include BME680 driver
#include "bme680/bme680.h"
// define I2C interface for BME680 sensors
#define I2C_BUS 0
#define I2C_SCL_PIN 5
#define I2C_SDA_PIN 4
static bme680_sensor_t* sensor;
/*
* User task that triggers measurements of sensor every seconds. It uses
* function *vTaskDelay* to wait for measurement results and changes the
* heating profile in each cycle.
*/
void user_task(void *pvParameters)
{
bme680_values_float_t values;
TickType_t last_wakeup = xTaskGetTickCount();
uint32_t count = 0;
while (1)
{
if (count++ < 60)
// disable gas measurement for cycle counter < 60
bme680_use_heater_profile (sensor, BME680_HEATER_NOT_USED);
else
// change heating profile in each cycle
switch (count % 5)
{
case 0: bme680_use_heater_profile (sensor, 0); break;
case 1: bme680_use_heater_profile (sensor, 1); break;
case 2: bme680_use_heater_profile (sensor, 2); break;
case 3: bme680_use_heater_profile (sensor, 3); break;
case 4: bme680_use_heater_profile (sensor, 4); break;
}
// measurement duration changes in each cycle
uint32_t duration = bme680_get_measurement_duration(sensor);
// trigger the sensor to start one TPHG measurement cycle
if (bme680_force_measurement (sensor))
{
// passive waiting until measurement results are available
vTaskDelay (duration);
// get the results and do something with them
if (bme680_get_results_float (sensor, &values))
printf("%.3f BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n",
(double)sdk_system_get_time()*1e-3,
values.temperature, values.humidity,
values.pressure, values.gas_resistance);
}
// passive waiting until 1 second is over
vTaskDelayUntil(&last_wakeup, 1000 / portTICK_PERIOD_MS);
}
}
void user_init(void)
{
// Set UART Parameter
uart_set_baud(0, 115200);
// Give the UART some time to settle
sdk_os_delay_us(500);
/** -- MANDATORY PART -- */
#ifdef SPI_USED
// Init the sensor connected either to SPI.
sensor = bme680_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
#else
// Init all I2C bus interfaces at which BME680 sensors are connected
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Init the sensor connected either to I2C.
sensor = bme680_init_sensor (I2C_BUS, BME680_I2C_ADDRESS_2, 0);
#endif
if (sensor)
{
// Create a task that uses the sensor
xTaskCreate(user_task, "user_task", 256, NULL, 2, NULL);
/** -- OPTIONAL PART -- */
// Changes the oversampling rates to 4x oversampling for temperature
// and 2x oversampling for humidity. Pressure measurement is skipped.
bme680_set_oversampling_rates(sensor, osr_4x, osr_none, osr_2x);
// Change the IIR filter size for temperature and pressure to 7.
bme680_set_filter_size(sensor, iir_size_7);
// Define a number of different heating profiles
bme680_set_heater_profile (sensor, 0, 200, 100);
bme680_set_heater_profile (sensor, 1, 250, 120);
bme680_set_heater_profile (sensor, 2, 300, 140);
bme680_set_heater_profile (sensor, 3, 350, 160);
bme680_set_heater_profile (sensor, 4, 400, 180);
}
}

View file

@ -0,0 +1,3 @@
PROGRAM=BME680_One_Sensor
EXTRA_COMPONENTS = extras/i2c extras/bme680
include ../../../common.mk

View file

@ -0,0 +1,131 @@
/**
* Simple example with one sensor connected either to I2C bus 0 or
* SPI bus 1.
*
* Harware configuration:
*
* I2C +-------------------------+ +----------+
* | ESP8266 Bus 0 | | BME680 |
* | GPIO 5 (SCL) ------> SCL |
* | GPIO 4 (SDA) ------- SDA |
* +-------------------------+ +----------+
*
* SPI +-------------------------+ +----------+
* | ESP8266 Bus 1 | | BME680 |
* | GPIO 12 (MISO) <-----< SDO |
* | GPIO 13 (MOSI) >-----> SDI |
* | GPIO 14 (SCK) >-----> SCK |
* | GPIO 2 (CS) >-----> CS |
* +-------------------------+ +----------+
*/
// Uncomment to use SPI
// #define SPI_USED
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
// include communication interface driver
#include "esp/spi.h"
#include "i2c/i2c.h"
// include BME680 driver
#include "bme680/bme680.h"
#ifdef SPI_USED
// define SPI interface for BME680 sensors
#define SPI_BUS 1
#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used
#else
// define I2C interface for BME680 sensors
#define I2C_BUS 0
#define I2C_SCL_PIN 5
#define I2C_SDA_PIN 4
#endif
static bme680_sensor_t* sensor;
/*
* User task that triggers measurements of sensor every seconds. It uses
* function *vTaskDelay* to wait for measurement results. Busy wating
* alternative is shown in comments
*/
void user_task(void *pvParameters)
{
bme680_values_float_t values;
TickType_t last_wakeup = xTaskGetTickCount();
// as long as sensor configuration isn't changed, duration is constant
uint32_t duration = bme680_get_measurement_duration(sensor);
while (1)
{
// trigger the sensor to start one TPHG measurement cycle
if (bme680_force_measurement (sensor))
{
// passive waiting until measurement results are available
vTaskDelay (duration);
// alternatively: busy waiting until measurement results are available
// while (bme680_is_measuring (sensor)) ;
// get the results and do something with them
if (bme680_get_results_float (sensor, &values))
printf("%.3f BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n",
(double)sdk_system_get_time()*1e-3,
values.temperature, values.humidity,
values.pressure, values.gas_resistance);
}
// passive waiting until 1 second is over
vTaskDelayUntil(&last_wakeup, 1000 / portTICK_PERIOD_MS);
}
}
void user_init(void)
{
// Set UART Parameter
uart_set_baud(0, 115200);
// Give the UART some time to settle
sdk_os_delay_us(500);
/** -- MANDATORY PART -- */
#ifdef SPI_USED
// Init the sensor connected either to SPI.
sensor = bme680_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
#else
// Init all I2C bus interfaces at which BME680 sensors are connected
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Init the sensor connected either to I2C.
sensor = bme680_init_sensor (I2C_BUS, BME680_I2C_ADDRESS_2, 0);
#endif
if (sensor)
{
// Create a task that uses the sensor
xTaskCreate(user_task, "user_task", 256, NULL, 2, NULL);
/** -- OPTIONAL PART -- */
// Changes the oversampling rates to 4x oversampling for temperature
// and 2x oversampling for humidity. Pressure measurement is skipped.
bme680_set_oversampling_rates(sensor, osr_4x, osr_none, osr_2x);
// Change the IIR filter size for temperature and pressure to 7.
bme680_set_filter_size(sensor, iir_size_7);
// Change the heater profile 0 to 200 degree Celcius for 100 ms.
bme680_set_heater_profile (sensor, 0, 200, 100);
bme680_use_heater_profile (sensor, 0);
// Set ambient temperature to 10 degree Celsius
bme680_set_ambient_temperature (sensor, 10);
}
}

View file

@ -0,0 +1,3 @@
PROGRAM=BME680_Tow_Sensors
EXTRA_COMPONENTS = extras/i2c extras/bme680
include ../../../common.mk

View file

@ -0,0 +1,155 @@
/**
* Simple example with two sensors, one sensor connected to I2C bus 0 and
* one sensor connected to SPI. It defines two different user tasks, one for
* each sensor. It demonstrate the possible approaches to wait for measurement
* results, active busy waiting using ```bme680_is_measuring``` and passive
* waiting using *vTaskDelay*.
*
* Harware configuration:
*
* +-------------------------+ +----------+
* | ESP8266 I2C Bus 0 | | BME680_1 |
* | GPIO 5 (SCL) ------> SCL |
* | GPIO 4 (SDA) ------- SDA |
* | | +----------+
* | SPI Bus 1 | | BME680_2 |
* | GPIO 12 (MISO) <------ SDO |
* | GPIO 13 (MOSI) >-----> SDI |
* | GPIO 14 (SCK) >-----> SCK |
* | GPIO 2 (CS) >-----> CS |
* +-------------------------+ +----------+
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
// include communication interface driver
#include "esp/spi.h"
#include "i2c/i2c.h"
// include BME680 driver
#include "bme680/bme680.h"
// define I2C interface for BME680 sensor 1
#define SPI_BUS 1
#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used
// define SPI interface for BME680 sensor 2
#define I2C_BUS 0
#define I2C_SCL_PIN 5
#define I2C_SDA_PIN 4
static bme680_sensor_t* sensor1;
static bme680_sensor_t* sensor2;
/*
* User task that triggers measurements of sensor1 every 5 seconds and
* uses *vTaskDelay* to wait for measurement results.
*/
void user_task_sensor1(void *pvParameters)
{
bme680_values_float_t values;
TickType_t last_wakeup = xTaskGetTickCount();
uint32_t duration = bme680_get_measurement_duration (sensor1);
while (1)
{
// trigger the sensor to start one TPHG measurement cycle
if (bme680_force_measurement (sensor1))
{
// passive waiting until measurement results are available
vTaskDelay (duration);
// get the results and so something with them
if (bme680_get_results_float (sensor1, &values))
printf("%.3f BME680 Sensor1: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n",
(double)sdk_system_get_time()*1e-3,
values.temperature, values.humidity,
values.pressure, values.gas_resistance);
}
// passive waiting until 5 seconds are over
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
}
}
/*
* User task that triggers measurements of sensor1 every 2 seconds and
* uses *bme680_is_measuring* to wait for measurement results.
*/
void user_task_sensor2(void *pvParameters)
{
bme680_values_float_t values;
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// trigger the sensor to start one TPHG measurement cycle
if (bme680_force_measurement (sensor2))
{
// busy waiting until measurement results are available
while (bme680_is_measuring (sensor2)) ;
// get the results and so something with them
if (bme680_get_results_float (sensor2, &values))
printf("%.3f BME680 Sensor2: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n",
(double)sdk_system_get_time()*1e-3,
values.temperature, values.humidity,
values.pressure, values.gas_resistance);
}
// passive waiting until 2 seconds are over
vTaskDelayUntil(&last_wakeup, 2000 / portTICK_PERIOD_MS);
}
}
void user_init(void)
{
// Set UART Parameter
uart_set_baud(0, 115200);
// Give the UART some time to settle
sdk_os_delay_us(500);
/** -- MANDATORY PART -- */
// Init all I2C bus interfaces at which BME680 sensors are connected
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Init the sensors connected to different I2C buses with same address
sensor1 = bme680_init_sensor (I2C_BUS, BME680_I2C_ADDRESS_2, 0);
sensor2 = bme680_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
if (sensor1 && sensor2)
{
// Create the tasks that use the sensors
xTaskCreate(user_task_sensor1, "user_task_sensor1", 256, NULL, 2, 0);
xTaskCreate(user_task_sensor2, "user_task_sensor2", 256, NULL, 2, 0);
// That's it.
/** -- OPTIONAL PART -- */
// Changes the oversampling rates for both sensor to different values
bme680_set_oversampling_rates(sensor1, osr_4x, osr_2x, osr_1x);
bme680_set_oversampling_rates(sensor2, osr_8x, osr_8x, osr_8x);
// Change the IIR filter size for temperature and and pressure to 7.
bme680_set_filter_size(sensor1, iir_size_7);
bme680_set_filter_size(sensor2, iir_size_7);
// Change the heater profile 0 to 200 degree Celcius for 150 ms.
bme680_set_heater_profile (sensor1, 0, 200, 150);
bme680_set_heater_profile (sensor2, 0, 200, 150);
// Activate the heater profile 0
bme680_use_heater_profile (sensor1, 0);
bme680_use_heater_profile (sensor2, 0);
}
}

View file

@ -41,7 +41,7 @@ void user_init(void)
printf("SDK version : %s\n", sdk_system_get_sdk_version());
printf("GIT version : %s\n", GITSHORTREV);
i2c_init(0,scl,sda,I2C_FREQ_400K);
i2c_init(0, scl, sda, I2C_FREQ_400K);
xTaskCreate(task1, "tsk1", 256, NULL, 2, NULL);
}

View file

@ -7,6 +7,7 @@
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include <unistd.h>
#include <string.h>
#include "FreeRTOS.h"

View file

@ -13,6 +13,7 @@
#include "esp/uart.h"
#include "esp/hwrand.h"
#include <unistd.h>
#include <string.h>
#include "FreeRTOS.h"

View file

@ -90,7 +90,7 @@ void ina_measure(void *pvParameters)
if(dev.mask.wf&(1<<(3-WARNING_CHANNEL)))
warning = true ;
#endif
for (uint8_t i = 0 ; i < BUS_NUMBER ; i++)
for (uint8_t i = 0 ; i < INA3221_BUS_NUMBER ; i++)
{
if(ina3221_getBusVoltage(&dev, i, &bus_voltage)) // Get voltage in V
goto error_loop;

View file

@ -20,7 +20,7 @@
#define ADDR MCP4725A0_ADDR0
#define VDD 3.3
inline static void wait_for_eeprom(i2c_dev_t* dev)
inline static void wait_for_eeprom(i2c_dev_t *dev)
{
while (mcp4725_eeprom_busy(dev))
{

View file

@ -23,7 +23,7 @@ static void measure(void *pvParameters)
while (1)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
printf("Value: %d\n", pcf8591_read(&dev, 0x03));
printf("Value: %d\n", pcf8591_read(&dev, PCF8591_IC_4_SINGLES, 3));
}
}

View file

@ -35,7 +35,7 @@ void user_init(void)
printf("pwm_init(1, [14])\n");
pins[0] = 14;
pwm_init(1, pins);
pwm_init(1, pins, false);
printf("pwm_set_freq(1000) # 1 kHz\n");
pwm_set_freq(1000);

3
examples/sht3x/Makefile Normal file
View file

@ -0,0 +1,3 @@
PROGRAM=SHT3x
EXTRA_COMPONENTS = extras/i2c extras/sht3x
include ../../common.mk

36
examples/sht3x/README.md Normal file
View file

@ -0,0 +1,36 @@
# SHT3x Driver Examples
These examples demonstrate the usage of the SHT3x driver with only one and multiple SHT3x sensors.
## Hardware setup
There are examples for only one sensor and examples for two sensors.
To run examples with **one sensor**, just use GPIO5 (SCL) and GPIO4 (SDA) to connect to the SHT3x sensor's I2C interface.
```
+------------------------+ +--------+
| ESP8266 Bus 0 | | SHT3x |
| GPIO 5 (SCL) >---- > SCL |
| GPIO 4 (SDA) ------- SDA |
| | +--------+
+------------------------+
```
If you want to run examples with **two sensors**, you could do this with only one bus and different I2C addresses or with two buses and the same or different I2C addresses. In later case, use GPIO14 (SCL) and GPIO12 (SDA) for the second bus to connect to the second SHT3x sensor's I2C interface.
```
+------------------------+ +----------+
| ESP8266 Bus 0 | | SHT3x_1 |
| GPIO 5 (SCL) >-----> SCL |
| GPIO 4 (SDA) ------- SDA |
| | +----------+
| Bus 1 | | SHT3x_2 |
| GPIO 14 (SCL) >-----> SCL |
| GPIO 12 (SDA) ------- SDA |
+------------------------+ +----------+
```
## Example description
It shows different user task implementations in *single shot mode* and *periodic mode*. In *single shot* mode either low level or high level functions are used. Constants SINGLE_SHOT_LOW_LEVEL and SINGLE_SHOT_HIGH_LEVEL controls which task implementation is used.

153
examples/sht3x/sht3x.c Normal file
View file

@ -0,0 +1,153 @@
/**
* Simple example with SHT3x sensor.
*
* It shows different user task implementations in *single shot mode* and
* *periodic mode*. In *single shot* mode either low level or high level
* functions are used.
*
* Constants SINGLE_SHOT_LOW_LEVEL and SINGLE_SHOT_HIGH_LEVEL controls which
* task implementation is used.
*
* Harware configuration:
*
* +------------------------+ +----------+
* | ESP8266 Bus 0 | | SHT3x |
* | GPIO 5 (SCL) ------> SCL |
* | GPIO 4 (SDA) ------- SDA |
* +------------------------+ +----------+
*/
// #define SINGLE_SHOT_LOW_LEVEL
// #define SINGLE_SHOT_HIGH_LEVEL
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
// include SHT3x driver
#include "sht3x/sht3x.h"
// define I2C interfaces at which SHTx3 sensors are connected
#define I2C_BUS 0
#define I2C_SCL_PIN GPIO_ID_PIN((5))
#define I2C_SDA_PIN GPIO_ID_PIN((4))
static sht3x_sensor_t* sensor; // sensor device data structure
#if defined(SINGLE_SHOT_HIGH_LEVEL)
/*
* User task that triggers a measurement every 5 seconds. Due to power
* efficiency reasons it uses *single shot* mode. In this example it uses the
* high level function *sht3x_measure* to perform one measurement in each cycle.
*/
void user_task (void *pvParameters)
{
float temperature;
float humidity;
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// perform one measurement and do something with the results
if (sht3x_measure (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// wait until 5 seconds are over
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
}
}
#elif defined(SINGLE_SHOT_LOW_LEVEL)
/*
* User task that triggers a measurement every 5 seconds. Due to power
* efficiency reasons it uses *single shot* mode. In this example it starts the
* measurement, waits for the results and fetches the results using separate
* functions
*/
void user_task (void *pvParameters)
{
float temperature;
float humidity;
TickType_t last_wakeup = xTaskGetTickCount();
// get the measurement duration for high repeatability;
uint8_t duration = sht3x_get_measurement_duration(sht3x_high);
while (1)
{
// Trigger one measurement in single shot mode with high repeatability.
sht3x_start_measurement (sensor, sht3x_single_shot, sht3x_high);
// Wait until measurement is ready (constant time of at least 30 ms
// or the duration returned from *sht3x_get_measurement_duration*).
vTaskDelay (duration);
// retrieve the values and do something with them
if (sht3x_get_results (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// wait until 5 seconds are over
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
}
}
#else // PERIODIC MODE
/*
* User task that fetches latest measurement results of sensor every 2
* seconds. It starts the SHT3x in periodic mode with 1 measurements per
* second (*sht3x_periodic_1mps*).
*/
void user_task (void *pvParameters)
{
float temperature;
float humidity;
// Start periodic measurements with 1 measurement per second.
sht3x_start_measurement (sensor, sht3x_periodic_1mps, sht3x_high);
// Wait until first measurement is ready (constant time of at least 30 ms
// or the duration returned from *sht3x_get_measurement_duration*).
vTaskDelay (sht3x_get_measurement_duration(sht3x_high));
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// Get the values and do something with them.
if (sht3x_get_results (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// Wait until 2 seconds (cycle time) are over.
vTaskDelayUntil(&last_wakeup, 2000 / portTICK_PERIOD_MS);
}
}
#endif
void user_init(void)
{
// Set UART Parameter.
uart_set_baud(0, 115200);
// Give the UART some time to settle.
sdk_os_delay_us(500);
// Init I2C bus interfaces at which SHT3x sensors are connected
// (different busses are possible).
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Create the sensors, multiple sensors are possible.
if ((sensor = sht3x_init_sensor (I2C_BUS, SHT3x_ADDR_2)))
{
// Create a user task that uses the sensors.
xTaskCreate(user_task, "user_task", 256, NULL, 2, 0);
}
// That's it.
}

View file

@ -94,18 +94,23 @@ static void gpiomon()
int i = 0;
printf("\n\n\nWelcome to gpiomon. Type 'help<enter>' for, well, help\n");
printf("%% ");
fflush(stdout); // stdout is line buffered
while(1) {
if (read(0, (void*)&ch, 1)) { // 0 is stdin
printf("%c", ch);
fflush(stdout);
if (ch == '\n' || ch == '\r') {
cmd[i] = 0;
i = 0;
printf("\n");
handle_command((char*) cmd);
printf("%% ");
fflush(stdout);
} else {
if (i < sizeof(cmd)) cmd[i++] = ch;
}
} else {
printf("You will never see this print as read(...) is blocking\n");
}
}
}

View file

@ -19,6 +19,7 @@
#include "esp/uart.h"
#include "esp/hwrand.h"
#include <unistd.h>
#include <string.h>
#include "FreeRTOS.h"

View file

@ -41,7 +41,7 @@ static int fix_index(int index)
static ws2812_pixel_t next_colour()
{
ws2812_pixel_t colour = {0, 0, 0, 0};
ws2812_pixel_t colour = { {0, 0, 0, 0} };
colour.red = rand() % 256;
colour.green = rand() % 256;
colour.blue = rand() % 256;

View file

@ -51,7 +51,7 @@ const float ads111x_gain_values[] = {
[ADS111X_GAIN_0V256_3] = 0.256
};
static uint16_t read_reg(i2c_dev_t* dev, uint8_t reg)
static uint16_t read_reg(i2c_dev_t *dev, uint8_t reg)
{
uint16_t res = 0;
if (i2c_slave_read(dev->bus, dev->addr, &reg, (uint8_t *)&res, 2))
@ -60,7 +60,7 @@ static uint16_t read_reg(i2c_dev_t* dev, uint8_t reg)
return res;
}
static void write_reg(i2c_dev_t* dev, uint8_t reg, uint16_t val)
static void write_reg(i2c_dev_t *dev, uint8_t reg, uint16_t val)
{
//debug("Write %d: 0x%04x", reg, val);
uint8_t buf[2] = { val >> 8, val};
@ -68,127 +68,127 @@ static void write_reg(i2c_dev_t* dev, uint8_t reg, uint16_t val)
debug("Could not write 0x%04x to register %d", val, reg);
}
static uint16_t read_conf_bits(i2c_dev_t* dev, uint8_t offs, uint16_t mask)
static uint16_t read_conf_bits(i2c_dev_t *dev, uint8_t offs, uint16_t mask)
{
return (read_reg(dev, REG_CONFIG) >> offs) & mask;
}
static void write_conf_bits(i2c_dev_t* dev, uint16_t val, uint8_t offs, uint16_t mask)
static void write_conf_bits(i2c_dev_t *dev, uint16_t val, uint8_t offs, uint16_t mask)
{
write_reg(dev, REG_CONFIG, (read_reg(dev, REG_CONFIG) & ~(mask << offs)) | (val << offs));
}
bool ads111x_busy(i2c_dev_t* dev)
bool ads111x_busy(i2c_dev_t *dev)
{
return read_conf_bits(dev, OS_OFFSET, OS_MASK);
}
void ads111x_start_conversion(i2c_dev_t* dev)
void ads111x_start_conversion(i2c_dev_t *dev)
{
write_conf_bits(dev, 1, OS_OFFSET, OS_MASK);
}
int16_t ads111x_get_value(i2c_dev_t* dev)
int16_t ads111x_get_value(i2c_dev_t *dev)
{
return read_reg(dev, REG_CONVERSION);
}
ads111x_gain_t ads111x_get_gain(i2c_dev_t* dev)
ads111x_gain_t ads111x_get_gain(i2c_dev_t *dev)
{
return read_conf_bits(dev, PGA_OFFSET, PGA_MASK);
}
void ads111x_set_gain(i2c_dev_t* dev, ads111x_gain_t gain)
void ads111x_set_gain(i2c_dev_t *dev, ads111x_gain_t gain)
{
write_conf_bits(dev, gain, PGA_OFFSET, PGA_MASK);
}
ads111x_mux_t ads111x_get_input_mux(i2c_dev_t* dev)
ads111x_mux_t ads111x_get_input_mux(i2c_dev_t *dev)
{
return read_conf_bits(dev, MUX_OFFSET, MUX_MASK);
}
void ads111x_set_input_mux(i2c_dev_t* dev, ads111x_mux_t mux)
void ads111x_set_input_mux(i2c_dev_t *dev, ads111x_mux_t mux)
{
write_conf_bits(dev, mux, MUX_OFFSET, MUX_MASK);
}
ads111x_mode_t ads111x_get_mode(i2c_dev_t* dev)
ads111x_mode_t ads111x_get_mode(i2c_dev_t *dev)
{
return read_conf_bits(dev, MODE_OFFSET, MODE_MASK);
}
void ads111x_set_mode(i2c_dev_t* dev, ads111x_mode_t mode)
void ads111x_set_mode(i2c_dev_t *dev, ads111x_mode_t mode)
{
write_conf_bits(dev, mode, MODE_OFFSET, MODE_MASK);
}
ads111x_data_rate_t ads111x_get_data_rate(i2c_dev_t* dev)
ads111x_data_rate_t ads111x_get_data_rate(i2c_dev_t *dev)
{
return read_conf_bits(dev, DR_OFFSET, DR_MASK);
}
void ads111x_set_data_rate(i2c_dev_t* dev, ads111x_data_rate_t rate)
void ads111x_set_data_rate(i2c_dev_t *dev, ads111x_data_rate_t rate)
{
write_conf_bits(dev, rate, DR_OFFSET, DR_MASK);
}
ads111x_comp_mode_t ads111x_get_comp_mode(i2c_dev_t* dev)
ads111x_comp_mode_t ads111x_get_comp_mode(i2c_dev_t *dev)
{
return read_conf_bits(dev, COMP_MODE_OFFSET, COMP_MODE_MASK);
}
void ads111x_set_comp_mode(i2c_dev_t* dev, ads111x_comp_mode_t mode)
void ads111x_set_comp_mode(i2c_dev_t *dev, ads111x_comp_mode_t mode)
{
write_conf_bits(dev, mode, COMP_MODE_OFFSET, COMP_MODE_MASK);
}
ads111x_comp_polarity_t ads111x_get_comp_polarity(i2c_dev_t* dev)
ads111x_comp_polarity_t ads111x_get_comp_polarity(i2c_dev_t *dev)
{
return read_conf_bits(dev, COMP_POL_OFFSET, COMP_POL_MASK);
}
void ads111x_set_comp_polarity(i2c_dev_t* dev, ads111x_comp_polarity_t polarity)
void ads111x_set_comp_polarity(i2c_dev_t *dev, ads111x_comp_polarity_t polarity)
{
write_conf_bits(dev, polarity, COMP_POL_OFFSET, COMP_POL_MASK);
}
ads111x_comp_latch_t ads111x_get_comp_latch(i2c_dev_t* dev)
ads111x_comp_latch_t ads111x_get_comp_latch(i2c_dev_t *dev)
{
return read_conf_bits(dev, COMP_LAT_OFFSET, COMP_LAT_MASK);
}
void ads111x_set_comp_latch(i2c_dev_t* dev, ads111x_comp_latch_t latch)
void ads111x_set_comp_latch(i2c_dev_t *dev, ads111x_comp_latch_t latch)
{
write_conf_bits(dev, latch, COMP_LAT_OFFSET, COMP_LAT_MASK);
}
ads111x_comp_queue_t ads111x_get_comp_queue(i2c_dev_t* dev)
ads111x_comp_queue_t ads111x_get_comp_queue(i2c_dev_t *dev)
{
return read_conf_bits(dev, COMP_QUE_OFFSET, COMP_QUE_MASK);
}
void ads111x_set_comp_queue(i2c_dev_t* dev, ads111x_comp_queue_t queue)
void ads111x_set_comp_queue(i2c_dev_t *dev, ads111x_comp_queue_t queue)
{
write_conf_bits(dev, queue, COMP_QUE_OFFSET, COMP_QUE_MASK);
}
int16_t ads111x_get_comp_low_thresh(i2c_dev_t* dev)
int16_t ads111x_get_comp_low_thresh(i2c_dev_t *dev)
{
return read_reg(dev, REG_THRESH_L);
}
void ads111x_set_comp_low_thresh(i2c_dev_t* dev, int16_t thresh)
void ads111x_set_comp_low_thresh(i2c_dev_t *dev, int16_t thresh)
{
write_reg(dev, REG_THRESH_L, thresh);
}
int16_t ads111x_get_comp_high_thresh(i2c_dev_t* dev)
int16_t ads111x_get_comp_high_thresh(i2c_dev_t *dev)
{
return read_reg(dev, REG_THRESH_H);
}
void ads111x_set_comp_high_thresh(i2c_dev_t* dev, int16_t thresh)
void ads111x_set_comp_high_thresh(i2c_dev_t *dev, int16_t thresh)
{
write_reg(dev, REG_THRESH_H, thresh);
}

View file

@ -125,20 +125,20 @@ typedef enum
* @param addr Deivce address
* @return true when device performing conversion
*/
bool ads111x_busy(i2c_dev_t* dev);
bool ads111x_busy(i2c_dev_t *dev);
/**
* Begin a single conversion (when in single-shot mode)
* @param addr Deivce address
*/
void ads111x_start_conversion(i2c_dev_t* dev);
void ads111x_start_conversion(i2c_dev_t *dev);
/**
* Read last conversion result
* @param addr
* @return Last conversion result
*/
int16_t ads111x_get_value(i2c_dev_t* dev);
int16_t ads111x_get_value(i2c_dev_t *dev);
/**
* Read the programmable gain amplifier configuration
@ -146,70 +146,70 @@ int16_t ads111x_get_value(i2c_dev_t* dev);
* @param addr Deivce address
* @return Gain value
*/
ads111x_gain_t ads111x_get_gain(i2c_dev_t* dev);
ads111x_gain_t ads111x_get_gain(i2c_dev_t *dev);
/**
* Configure the programmable gain amplifier (ADS1114 and ADS1115 only)
* @param addr Deivce address
* @param gain Gain value
*/
void ads111x_set_gain(i2c_dev_t* dev, ads111x_gain_t gain);
void ads111x_set_gain(i2c_dev_t *dev, ads111x_gain_t gain);
/**
* Read the input multiplexer configuration (ADS1115 only)
* @param addr Deivce address
* @return Input multiplexer configuration
*/
ads111x_mux_t ads111x_get_input_mux(i2c_dev_t* dev);
ads111x_mux_t ads111x_get_input_mux(i2c_dev_t *dev);
/**
* Configure the input multiplexer configuration (ADS1115 only)
* @param addr Deivce address
* @param mux Input multiplexer configuration
*/
void ads111x_set_input_mux(i2c_dev_t* dev, ads111x_mux_t mux);
void ads111x_set_input_mux(i2c_dev_t *dev, ads111x_mux_t mux);
/**
* Read the device operating mode
* @param addr Deivce address
* @return Device operating mode
*/
ads111x_mode_t ads111x_get_mode(i2c_dev_t* dev);
ads111x_mode_t ads111x_get_mode(i2c_dev_t *dev);
/**
* Set the device operating mode
* @param addr Deivce address
* @param mode Device operating mode
*/
void ads111x_set_mode(i2c_dev_t* dev, ads111x_mode_t mode);
void ads111x_set_mode(i2c_dev_t *dev, ads111x_mode_t mode);
/**
* Read the data rate
* @param addr Deivce address
* @return Data rate
*/
ads111x_data_rate_t ads111x_get_data_rate(i2c_dev_t* dev);
ads111x_data_rate_t ads111x_get_data_rate(i2c_dev_t *dev);
/**
* Configure the data rate
* @param addr Deivce address
* @param rate Data rate
*/
void ads111x_set_data_rate(i2c_dev_t* dev, ads111x_data_rate_t rate);
void ads111x_set_data_rate(i2c_dev_t *dev, ads111x_data_rate_t rate);
/**
* Get comparator mode (ADS1114 and ADS1115 only)
* @param addr Deivce address
* @return Comparator mode
*/
ads111x_comp_mode_t ads111x_get_comp_mode(i2c_dev_t* dev);
ads111x_comp_mode_t ads111x_get_comp_mode(i2c_dev_t *dev);
/**
* Set comparator mode (ADS1114 and ADS1115 only)
* @param addr Deivce address
* @param mode Comparator mode
*/
void ads111x_set_comp_mode(i2c_dev_t* dev, ads111x_comp_mode_t mode);
void ads111x_set_comp_mode(i2c_dev_t *dev, ads111x_comp_mode_t mode);
/**
* Get polarity of the comparator output pin ALERT/RDY
@ -217,7 +217,7 @@ void ads111x_set_comp_mode(i2c_dev_t* dev, ads111x_comp_mode_t mode);
* @param addr Deivce address
* @return Comparator output pin polarity
*/
ads111x_comp_polarity_t ads111x_get_comp_polarity(i2c_dev_t* dev);
ads111x_comp_polarity_t ads111x_get_comp_polarity(i2c_dev_t *dev);
/**
* Set polarity of the comparator output pin ALERT/RDY
@ -225,7 +225,7 @@ ads111x_comp_polarity_t ads111x_get_comp_polarity(i2c_dev_t* dev);
* @param addr Deivce address
* @param polarity Comparator output pin polarity
*/
void ads111x_set_comp_polarity(i2c_dev_t* dev, ads111x_comp_polarity_t polarity);
void ads111x_set_comp_polarity(i2c_dev_t *dev, ads111x_comp_polarity_t polarity);
/**
* Get comparator output latch mode, see datasheet.
@ -233,14 +233,14 @@ void ads111x_set_comp_polarity(i2c_dev_t* dev, ads111x_comp_polarity_t polarity)
* @param addr Deivce address
* @return Comparator output latch mode
*/
ads111x_comp_latch_t ads111x_get_comp_latch(i2c_dev_t* dev);
ads111x_comp_latch_t ads111x_get_comp_latch(i2c_dev_t *dev);
/**
* Set comparator output latch mode (ADS1114 and ADS1115 only)
* @param addr Deivce address
* @param latch Comparator output latch mode
*/
void ads111x_set_comp_latch(i2c_dev_t* dev, ads111x_comp_latch_t latch);
void ads111x_set_comp_latch(i2c_dev_t *dev, ads111x_comp_latch_t latch);
/**
* Set number of the comparator conversions before pin ALERT/RDY
@ -248,7 +248,7 @@ void ads111x_set_comp_latch(i2c_dev_t* dev, ads111x_comp_latch_t latch);
* @param addr Deivce address
* @return Number of the comparator conversions
*/
ads111x_comp_queue_t ads111x_get_comp_queue(i2c_dev_t* dev);
ads111x_comp_queue_t ads111x_get_comp_queue(i2c_dev_t *dev);
/**
* Get number of the comparator conversions before pin ALERT/RDY
@ -256,35 +256,35 @@ ads111x_comp_queue_t ads111x_get_comp_queue(i2c_dev_t* dev);
* @param addr Deivce address
* @param queue Number of the comparator conversions
*/
void ads111x_set_comp_queue(i2c_dev_t* dev, ads111x_comp_queue_t queue);
void ads111x_set_comp_queue(i2c_dev_t *dev, ads111x_comp_queue_t queue);
/**
* Get the lower threshold value used by comparator
* @param addr Deivce address
* @return Lower threshold value
*/
int16_t ads111x_get_comp_low_thresh(i2c_dev_t* dev);
int16_t ads111x_get_comp_low_thresh(i2c_dev_t *dev);
/**
* Set the lower threshold value used by comparator
* @param addr Deivce address
* @param thresh Lower threshold value
*/
void ads111x_set_comp_low_thresh(i2c_dev_t* dev, int16_t thresh);
void ads111x_set_comp_low_thresh(i2c_dev_t *dev, int16_t thresh);
/**
* Get the upper threshold value used by comparator
* @param addr Deivce address
* @return Upper threshold value
*/
int16_t ads111x_get_comp_high_thresh(i2c_dev_t* dev);
int16_t ads111x_get_comp_high_thresh(i2c_dev_t *dev);
/**
* Set the upper threshold value used by comparator
* @param addr Deivce address
* @param thresh Upper threshold value
*/
void ads111x_set_comp_high_thresh(i2c_dev_t* dev, int16_t thresh);
void ads111x_set_comp_high_thresh(i2c_dev_t *dev, int16_t thresh);
#ifdef __cplusplus
}

555
extras/bme680/README.md Normal file
View file

@ -0,0 +1,555 @@
# Driver for **BME680** digital **environmental sensor**
This driver is written for usage with the ESP8266 and FreeRTOS. It supports multiple BME680 sensors which are either connected to the SPI or to different I2C interfaces with different addresses.
## About the sensor
BME680 is an ultra-low-power environmental sensor that integrates temperature, pressure, humidity and gas sensors in only one unit.
## Communication interfaces
The BME680 sensor can be connected using I2C or SPI.
The I2C interface supports data rates up to 3.4 Mbps. It is possible to connect multiple BME680 sensors with different I2C slave addresses to the same I2C bus or to different I2C buses. Possible I2C slave addresses are 0x76 and 0x77.
The SPI interface allows clock rates up to 10 MHz and the SPI modes '00' (CPOL=CPHA=0) and '11' (CPOL=CPHA=1).
Interface selection is done automatically by the sensor using the SPI CS signal. As long as the CS signal keeps high after power-on reset, the I2C interface is used. Once the CS signal has been pulled down, SPI interface is used until next power-on reset.
## Measurement process
Once the BME680 has been initialized, it can be used for measurements. The BME680 operates in two different modes, the **sleep mode** and the **forced mode**.
The sensor starts after power-up automatically in the *sleep mode* where it does not perform any measurement and consumes only 0.15 μA. Measurements are only done in *forced mode*.
**Please note:** There are two further undocumented modes, the *parallel* and the *sequential* mode. They can't be supported by the driver, since it is not clear what they do and how to use them.
#### Measurement cylce
To perform measurements, the BME680 sensor has to be triggered to switch to the **forced mode**. In this mode, it performs exactly one measurement of temperature, pressure, humidity, and gas in that order, the so-called **TPHG measurement cycle**. After the execution of this TPHG measurement cycle, **raw sensor data** become available and the sensor returns automatically back to sleep mode.
Each of the individual measurements can be configured or skipped separately via the sensor settings, see section **Measurement settings**. Dependent on the configuration, the **duration of a TPHG measurement cycle** can vary from some milliseconds up to about 4.5 seconds, especially if gas measurement is enabled.
To avoid the blocking of the user task during measurements, the measurement process is therefore separated into the following steps:
1. Trigger the sensor with function ```bme680_force_measurement``` to switch to *forced mode* in which it performs exactly one THPG measurement cycle.
2. Wait the measurement duration using function ```vTaskDelay``` and the value returned from function ```bme680_get_measurement_duration``` or wait as long as function ```bme680_is_measuring``` returns true.
3. Fetch the results as fixed point values with function ```bme680_get_results_fixed``` or as floating point values with function ```bme680_get_results_float```.
```
...
// as long as sensor configuration isn't changed, the duration can be considered as constant
uint32_t duration = bme680_get_measurement_duration(sensor);
...
if (bme680_force_measurement (sensor)) // STEP 1
{
// STEP 2: passive waiting until measurement results are available
vTaskDelay (duration);
// STEP 3: get the results and do something with them
if (bme680_get_results_float (sensor, &values))
...
}
...
```
Alternatively, busy waiting can be realized using function ```bme680_is_measuring```.
```
...
if (bme680_force_measurement (sensor)) // STEP 1
{
// STEP 2: busy waiting until measurement results are available
while (bme680_is_measuring (sensor)) ;
// STEP 3: get the results and do something with them
if (bme680_get_results_float (sensor, &values))
...
}
...
```
For convenience, it is also possible to use the high-level functions ```bme680_measure_float``` or ```bme680_measure_fixed```. These functions combine all 3 steps above within a single function and are therefore very easy to use. **Please note** that these functions must not be used when they are called from a software timer callback function since the calling task is delayed using function *vTaskDelay*.
```
...
// ONE STEP: measure, wait, get the results and do something with them
if (bme680_measure_float (sensor, &values))
...
...
```
#### Measurement results
Once the sensor has finished the measurement raw data are available at the sensor. Either function ```bme680_get_results_fixed``` or function ```bme680_get_results_float``` can be used to fetch the results. Both functions read raw data from the sensor and converts them into utilizable fixed point or floating point sensor values.
**Please note:** Conversion of raw sensor data into the final sensor values is based on very complex calculations that use a large number of calibration parameters. Therefore, the driver does not provide functions that only return the raw sensor data.
Dependent on sensor value representation, measurement results contain different dimensions:
| Value | Fixed Point | Floating Point | Conversion
| ----------- | ------------- | -------- |-----------
| temperature | 1/100 °C | °C | float = fixed / 100
| pressure | Pascal | hPascal | float = fixed / 100
| humidity | 1/1000 % | % | float = fixed / 1000
| gas_resistance | Ohm | Ohm | float = fixed
The gas resistance value in Ohm represents the resistance of sensor's gas sensitive layer.
If the TPHG measurement cycle or fetching the results fails, invalid sensor values are returned:
| Invalid Value | Fixed Point | Floating Point |
| ----------- | ------------- | -------- |
| temperature | INT16_MIN | -327.68 |
| pressure | 0 | 0.0 |
| humidity | 0 | 0.0 |
| gas_resistance | 0 | 0.0 |
## Measurement settings
The sensor allows to change a lot of measurement parameters.
#### Oversampling rates
To increase the resolution of raw sensor data, the sensor supports oversampling for temperature, pressure, and humidity measurements. Using function ```bme680_set_oversampling_rates```, individual **oversampling rates** can be defined for these measurements. With an oversampling rate *osr*, the resolution of the according raw sensor data can be increased from 16 bit to 16+ld(*osr*) bit.
Possible oversampling rates are 1x (default by the driver) 2x, 4x, 8x and 16x. It is also possible to define an oversampling rate of 0. This **deactivates** the corresponding measurement and the output values become invalid.
```
...
// Changes the oversampling rate for temperature to 4x and for pressure to 2x. Humidity measurement is skipped.
bme680_set_oversampling_rates(sensor, osr_4x, osr_2x, osr_none);
...
```
#### IIR Filter
The sensor also integrates an internal IIR filter (low pass filter) to reduce short-term changes in sensor output values caused by external disturbances. It effectively reduces the bandwidth of the sensor output values.
The filter can optionally be used for pressure and temperature data that are subject to many short-term changes. With the IIR filter the resolution of pressure and temperature data increases to 20 bit. Humidity and gas inside the sensor does not fluctuate rapidly and does not require such a low pass filtering.
Using function ```bme680_set_filter_size```, the user task can change the **size of the filter**. The default size is 3. If the size of the filter becomes 0, the filter is **not used**.
```
...
// Change the IIR filter size for temperature and pressure to 7.
bme680_set_filter_size(sensor, iir_size_7);
...
// Don't use IIR filter
bme680_set_filter_size(sensor, iir_size_0);
...
```
#### Heater profile
For the gas measurement, the sensor integrates a heater. Parameters for this heater are defined by **heater profiles**. The sensor supports up to 10 such heater profiles, which are numbered from 0 to 9. Each profile consists of a temperature set-point (the target temperature) and a heating duration. By default, only the heater profile 0 with 320 degree Celsius as target temperature and 150 ms heating duration is defined.
**Please note:** According to the data sheet, target temperatures between 200 and 400 degrees Celsius are typical and about 20 to 30 ms are necessary for the heater to reach the desired target temperature.
Function ```bme680_set_heater_profile``` can be used to set the parameters for one of the heater profiles 0 ... 9. Once the parameters of a heater profile are defined, the gas measurement can be activated with that heater profile using function ```bme680_use_heater_profile```. If -1 or ```BME680_HEATER_NOT_USED``` is used as heater profile, gas measurement is deactivated completely.
```
...
// Change the heater profile 1 to 300 degree Celsius for 100 ms and activate it
bme680_set_heater_profile (sensor, 1, 300, 100);
bme680_use_heater_profile (sensor, 1);
...
// Deactivate gas measurement completely
bme680_use_heater_profile (sensor, BME680_HEATER_NOT_USED);
...
```
If several heater profiles have been defined with function ```bme680_set_heater_profile```, a sequence of gas measurements with different heater parameters can be realized by a sequence of activations of different heater profiles for successive TPHG measurements using function ```bme680_use_heater_profile```.
For example, if there were 5 heater profiles defined with following code during the setup
```
bme680_set_heater_profile (sensor, 0, 200, 100);
bme680_set_heater_profile (sensor, 1, 250, 120);
bme680_set_heater_profile (sensor, 2, 300, 140);
bme680_set_heater_profile (sensor, 3, 350, 160);
bme680_set_heater_profile (sensor, 4, 400, 180);
```
the user task could use them as a sequence like following:
```
...
while (1)
{
switch (count++ % 5)
{
case 0: bme680_use_heater_profile (sensor, 0); break;
case 1: bme680_use_heater_profile (sensor, 1); break;
case 2: bme680_use_heater_profile (sensor, 2); break;
case 3: bme680_use_heater_profile (sensor, 3); break;
case 4: bme680_use_heater_profile (sensor, 4); break;
}
// measurement duration changes in each cycle
uint32_t duration = bme680_get_measurement_duration(sensor);
// trigger the sensor to start one TPHG measurement cycle
if (bme680_force_measurement (sensor))
{
vTaskDelay (duration);
// get the results and do something with them
if (bme680_get_results_float (sensor, &values))
...
}
...
}
...
```
#### Ambient temperature
The heater resistance calculation algorithm takes into account the ambient temperature of the sensor. Using function ```bme680_set_ambient_temperature```, the ambient temperature either determined from the sensor itself or from another temperature sensor can be set.
```
...
bme680_set_ambient_temperature (sensor, ambient);
...
```
## Error Handling
Most driver functions return a simple boolean value to indicate whether its execution was successful or an error happened. In the latter case, the member ```error_code``` of the sensor device data structure is set which indicates what error happened.
There are two different error levels that are ORed into one single *error_code*, errors in the I2C or SPI communication and errors of the BME680 sensor itself. To test for a certain error, first you can AND the *error_code* with one of the error masks, ```BME680_INT_ERROR_MASK``` for I2C or SPI errors and ```BME680_DRV_ERROR_MASK``` for other errors. Then you can test the result for a certain error code.
For example, error handling for ```bme680_get_results_float``` could look like:
```
if (bme680_get_results_float (sensor, &values))
{
// no error happened
...
}
else
{
// error happened
switch (sensor->error_code & BME680_INT_ERROR_MASK)
{
case BME680_INT_BUSY: ...
case BME680_INT_READ_FAILED: ...
...
}
switch (sensor->error_code & BME680_DRV_ERROR_MASK)
{
case BME680_MEAS_STILL_RUNNING: ...
case BME680_NO_NEW_DATA: ...
...
}
}
```
## Usage
First, the hardware configuration has to be established. This can differ dependent on the communication interface and the number of sensors used.
### Hardware configurations
The driver supports multiple BME680 sensors at the same time that are connected either to I2C or SPI. Following figures show some possible hardware configurations.
First figure shows the configuration with only one sensor at I2C bus 0.
```
+-------------------------+ +--------+
| ESP8266 Bus 0 | | BME680 |
| GPIO 5 (SCL) +---->+ SCL |
| GPIO 4 (SDA) +-----+ SDA |
| | +--------+
+-------------------------+
```
Next figure shows the configuration with only one sensor at SPI bus using GPIO2 as CS signal.
```
+-------------------------+ +--------+
| Bus 1 | | BME680 |
| GPIO 12 (MISO) <------ SDO |
| GPIO 13 (MOSI) >-----> SDI |
| GPIO 14 (SCK) >-----> SCK |
| GPIO 2 (CS) >-----> CS |
+-------------------------+ +--------+
```
**Please note:**
1. Since the system flash memory is connected to SPI bus 0, the sensor has to be connected to SPI bus 1.
2. GPIO15 which is used as CS signal of SPI bus 1 does not work correctly together with the BME680. Therefore, the user has to specify another GPIO pin as CS signal, e.g., GPIO2.
Next figure shows a possible configuration with two I2C buses. In that case, the sensors can have same or different I2C slave addresses.
```
+-------------------------+ +----------+
| ESP8266 Bus 0 | | BME680_1 |
| GPIO 5 (SCL) ------> SCL |
| GPIO 4 (SDA) ------- SDA |
| | +----------+
| Bus 1 | | BME680_2 |
| GPIO 14 (SCL) ------> SCL |
| GPIO 12 (SDA) ------- SDA |
+-------------------------+ +----------+
```
Last figure shows a possible configuration using I2C bus 0 and SPI bus 1 at the same time.
```
+-------------------------+ +----------+
| ESP8266 Bus 0 | | BME680_1 |
| GPIO 5 (SCL) ------> SCL |
| GPIO 4 (SDA) ------- SDA |
| | +----------+
| Bus 1 | | BME680_2 |
| GPIO 12 (MISO) <------ SDO |
| GPIO 13 (MOSI) >-----> SDI |
| GPIO 14 (SCK) >-----> SCK |
| GPIO 2 (CS) >-----> CS |
+-------------------------+ +----------+
```
Further configurations are possible, e.g., two sensors that are connected at the same I2C bus with different slave addresses.
### Communication interface settings
Dependent on the hardware configuration, the communication interface settings have to be defined.
```
// define SPI interface for BME680 sensors
#define SPI_BUS 1
#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used
#else
// define I2C interface for BME680 sensors
#define I2C_BUS 0
#define I2C_SCL_PIN 5
#define I2C_SDA_PIN 4
#endif
```
### Main program
If I2C interfaces are used, they have to be initialized first.
```
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K))
```
SPI interface has not to be initialized explicitly.
Once the I2C interfaces are initialized, function ```bme680_init_sensor``` has to be called for each BME680 sensor in order to initialize the sensor and to check its availability as well as its error state. This function returns a pointer to a sensor device data structure or NULL in case of error.
The parameter *bus* specifies the ID of the I2C or SPI bus to which the sensor is connected.
```
static bme680_sensor_t* sensor;
```
For sensors connected to an I2C interface, a valid I2C slave address has to be defined as parameter *addr*. In that case parameter *cs* is ignored.
```
sensor = bme680_init_sensor (I2C_BUS, BME680_I2C_ADDRESS_2, 0);
```
If parameter *addr* is 0, the sensor is connected to a SPI bus. In that case, parameter *cs* defines the GPIO used as CS signal.
```
sensor = bme680_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
```
The remaining part of the program is independent on the communication interface.
Optionally, you could wish to set some measurement parameters. For details see the section **Measurement settings** above, the header file of the driver ```bme680.h```, and of course the data sheet of the sensor.
```
if (sensor)
{
// Create a task that uses the sensor
xTaskCreate(user_task, "user_task", 256, NULL, 2, NULL);
/** -- OPTIONAL PART -- */
// Changes the oversampling rates to 4x oversampling for temperature
// and 2x oversampling for humidity. Pressure measurement is skipped.
bme680_set_oversampling_rates(sensor, osr_4x, osr_none, osr_2x);
// Change the IIR filter size for temperature and pressure to 7.
bme680_set_filter_size(sensor, iir_size_7);
// Change the heater profile 0 to 200 degree Celsius for 100 ms.
bme680_set_heater_profile (sensor, 0, 200, 100);
bme680_use_heater_profile (sensor, 0);
...
}
```
Last, the user task that uses the sensor has to be created.
### User task
BME680 supports only the *forced mode* that performs exactly one measurement. Therefore, the measurement has to be triggered in each cycle. The waiting for measurement results is also required in each cycle, before the results can be fetched.
Thus the user task could look like the following:
```
void user_task(void *pvParameters)
{
bme680_values_float_t values;
TickType_t last_wakeup = xTaskGetTickCount();
// as long as sensor configuration isn't changed, duration is constant
uint32_t duration = bme680_get_measurement_duration(sensor);
while (1)
{
// trigger the sensor to start one TPHG measurement cycle
bme680_force_measurement (sensor);
// passive waiting until measurement results are available
vTaskDelay (duration);
// alternatively: busy waiting until measurement results are available
// while (bme680_is_measuring (sensor)) ;
// get the results and do something with them
if (bme680_get_results_float (sensor, &values))
printf("%.3f BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n",
(double)sdk_system_get_time()*1e-3,
values.temperature, values.humidity,
values.pressure, values.gas_resistance);
// passive waiting until 1 second is over
vTaskDelayUntil(&last_wakeup, 1000 / portTICK_PERIOD_MS);
}
}
```
Function ```bme680_force_measurement``` is called inside the task loop to perform exactly one measurement in each cycle.
The task is then delayed using function ```vTaskDelay``` and the value returned from function ```bme680_get_measurement_duration``` or as long as function ```bme680_is_measuring``` returns true.
Since the measurement duration only depends on the current sensor configuration, it changes only when sensor configuration is changed. Therefore, it can be considered as constant as long as the sensor configuration isn't changed and can be determined with function ```bme680_get_measurement_duration``` outside the task loop. If the sensor configuration changes, the function has to be executed again.
Once the measurement results are available, they can be fetched as fixed point oder floating point sensor values using function ```bme680_get_results_float``` and ```bme680_get_results_fixed```, respectively.
## Full Example
```
// Uncomment to use SPI
// #define SPI_USED
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
// include communication interface driver
#include "esp/spi.h"
#include "i2c/i2c.h"
// include BME680 driver
#include "bme680/bme680.h"
#ifdef SPI_USED
// define SPI interface for BME680 sensors
#define SPI_BUS 1
#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used
#else
// define I2C interface for BME680 sensors
#define I2C_BUS 0
#define I2C_SCL_PIN 5
#define I2C_SDA_PIN 4
#endif
static bme680_sensor_t* sensor;
/*
* User task that triggers measurements of sensor every seconds. It uses
* function *vTaskDelay* to wait for measurement results. Busy waiting
* alternative is shown in comments
*/
void user_task(void *pvParameters)
{
bme680_values_float_t values;
TickType_t last_wakeup = xTaskGetTickCount();
// as long as sensor configuration isn't changed, duration is constant
uint32_t duration = bme680_get_measurement_duration(sensor);
while (1)
{
// trigger the sensor to start one TPHG measurement cycle
bme680_force_measurement (sensor);
// passive waiting until measurement results are available
vTaskDelay (duration);
// alternatively: busy waiting until measurement results are available
// while (bme680_is_measuring (sensor)) ;
// get the results and do something with them
if (bme680_get_results_float (sensor, &values))
printf("%.3f BME680 Sensor: %.2f °C, %.2f %%, %.2f hPa, %.2f Ohm\n",
(double)sdk_system_get_time()*1e-3,
values.temperature, values.humidity,
values.pressure, values.gas_resistance);
// passive waiting until 1 second is over
vTaskDelayUntil(&last_wakeup, 1000 / portTICK_PERIOD_MS);
}
}
void user_init(void)
{
// Set UART Parameter
uart_set_baud(0, 115200);
// Give the UART some time to settle
sdk_os_delay_us(500);
/** -- MANDATORY PART -- */
#ifdef SPI_USED
// Init the sensor connected either to SPI.
sensor = bme680_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
#else
// Init all I2C bus interfaces at which BME680 sensors are connected
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Init the sensor connected either to I2C.
sensor = bme680_init_sensor (I2C_BUS, BME680_I2C_ADDRESS_2, 0);
#endif
if (sensor)
{
// Create a task that uses the sensor
xTaskCreate(user_task, "user_task", 256, NULL, 2, NULL);
/** -- OPTIONAL PART -- */
// Changes the oversampling rates to 4x oversampling for temperature
// and 2x oversampling for humidity. Pressure measurement is skipped.
bme680_set_oversampling_rates(sensor, osr_4x, osr_none, osr_2x);
// Change the IIR filter size for temperature and pressure to 7.
bme680_set_filter_size(sensor, iir_size_7);
// Change the heater profile 0 to 200 degree Celcius for 100 ms.
bme680_set_heater_profile (sensor, 0, 200, 100);
bme680_use_heater_profile (sensor, 0);
}
}
```

1475
extras/bme680/bme680.c Normal file

File diff suppressed because it is too large Load diff

384
extras/bme680/bme680.h Normal file
View file

@ -0,0 +1,384 @@
/*
* Driver for Bosch Sensortec BME680 digital temperature, humidity, pressure and
* gas sensor connected to I2C or SPI
*
* Part of esp-open-rtos [https://github.com/SuperHouse/esp-open-rtos]
*
* ---------------------------------------------------------------------------
*
* The BSD License (3-clause license)
*
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __BME680_H__
#define __BME680_H__
#include "bme680/bme680_types.h"
// Uncomment one of the following defines to enable debug output
// #define BME680_DEBUG_LEVEL_1 // only error messages
// #define BME680_DEBUG_LEVEL_2 // debug and error messages
// BME680 addresses
#define BME680_I2C_ADDRESS_1 0x76 // SDO pin is low
#define BME680_I2C_ADDRESS_2 0x77 // SDO pin is high
// BME680 chip id
#define BME680_CHIP_ID 0x61 // BME680_REG_ID<7:0>
// Definition of error codes
#define BME680_OK 0
#define BME680_NOK -1
#define BME680_INT_ERROR_MASK 0x000f
#define BME680_DRV_ERROR_MASK 0xfff0
// Error codes for I2C and SPI interfaces ORed with BME680 driver error codes
#define BME680_I2C_READ_FAILED 1
#define BME680_I2C_WRITE_FAILED 2
#define BME680_I2C_BUSY 3
#define BME680_SPI_WRITE_FAILED 4
#define BME680_SPI_READ_FAILED 5
#define BME680_SPI_BUFFER_OVERFLOW 6
#define BME680_SPI_SET_PAGE_FAILED 7
// BME680 driver error codes ORed with error codes for I2C and SPI interfaces
#define BME680_RESET_CMD_FAILED ( 1 << 8)
#define BME680_WRONG_CHIP_ID ( 2 << 8)
#define BME680_READ_CALIB_DATA_FAILED ( 3 << 8)
#define BME680_MEAS_ALREADY_RUNNING ( 4 << 8)
#define BME680_MEAS_NOT_RUNNING ( 5 << 8)
#define BME680_MEAS_STILL_RUNNING ( 6 << 8)
#define BME680_FORCE_MODE_FAILED ( 7 << 8)
#define BME680_NO_NEW_DATA ( 8 << 8)
#define BME680_WRONG_HEAT_PROFILE ( 9 << 8)
#define BME680_MEAS_GAS_NOT_VALID (10 << 8)
#define BME680_HEATER_NOT_STABLE (11 << 8)
// Driver range definitions
#define BME680_HEATER_TEMP_MIN 200 // min. 200 degree Celsius
#define BME680_HEATER_TEMP_MAX 400 // max. 200 degree Celsius
#define BME680_HEATER_PROFILES 10 // max. 10 heater profiles 0 ... 9
#define BME680_HEATER_NOT_USED -1 // heater not used profile
#ifdef __cplusplus
extern "C"
{
#endif
/** --------------------------------------------------------------------------
*
* Functional Description of the BME680 sensor
*
* The BME680 sensor only support two modes, the sleep mode and the forced
* mode in which measurements are done. After power-up sequence, the sensor
* automatically starts in sleep mode. To start a measurement, the sensor has
* to switch in the forced mode. In this mode it performs exactly one
* measurement of temperature, pressure, humidity, and gas in that order,
* the so-called TPHG measurement cycle. After the execution of this TPHG
* measurement cycle, raw sensor data are available and the sensor returns
* automatically back to sleep mode.
*
* Using the BME680 consists of the following steps
*
* 1. Trigger the sensor to switch into forced mode to perform one THPG cycle
* 2. Wait until the THPG cycle has been finished (measurement duration)
* 3. Fetch raw sensor data, compensate and convert them to sensor values
*
* ---------------------------------------------------------------------------
*/
/**
* @brief Initialize a BME680 sensor
*
* The function initializes the sensor device data structure, probes the
* sensor, soft resets the sensor, and configures the sensor with the
* the following default settings:
*
* - Oversampling rate for temperature, pressure, humidity is osr_1x
* - Filter size for pressure and temperature is iir_size 3
* - Heater profile 0 with 320 degree C and 150 ms duration
*
* The sensor can be connected either to an I2C or a SPI bus. In both cases,
* the parameter *bus* specifies the ID of the corresponding bus. Please note
* that in case of SPI, bus 1 has to be used since bus 0 is used for system
* flash memory.
*
* If parameter *addr* is greater than 0, it defines a valid I2C slave address
* and the sensor is connected to an I2C bus. In that case parameter *cs* is
* ignored.
*
* If parameter *addr* is 0, the sensor is connected to a SPI bus. In that
* case, parameter *cs* defines the GPIO used as CS signal
*
* @param bus I2C or SPI bus at which BME680 sensor is connected
* @param addr I2C addr of the BME680 sensor, 0 for SPI
* @param cs SPI CS GPIO, ignored for I2C
* @return pointer to sensor data structure, or NULL on error
*/
bme680_sensor_t* bme680_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs_pin);
/**
* @brief Force one single TPHG measurement
*
* The function triggers the sensor to start one THPG measurement cycle.
* Parameters for the measurement like oversampling rates, IIR filter sizes
* and heater profile can be configured before.
*
* Once the TPHG measurement is started, the user task has to wait for the
* results. The duration of the TPHG measurement can be determined with
* function *bme680_get_measurement_duration*.
*
* @param dev pointer to the sensor device data structure
* @return true on success, false on error
*/
bool bme680_force_measurement (bme680_sensor_t* dev);
/**
* @brief Get estimated duration of a TPHG measurement
*
* The function returns an estimated duration of the TPHG measurement cycle
* in RTOS ticks for the current configuration of the sensor.
*
* This duration is the time required by the sensor for one TPHG measurement
* until the results are available. It strongly depends on which measurements
* are performed in the THPG measurement cycle and what configuration
* parameters were set. It can vary from 1 RTOS (10 ms) tick up to 4500 RTOS
* ticks (4.5 seconds).
*
* If the measurement configuration is not changed, the duration can be
* considered as constant.
*
* @param dev pointer to the sensor device data structure
* @return duration of TPHG measurement cycle in ticks or 0 on error
*/
uint32_t bme680_get_measurement_duration (const bme680_sensor_t *dev);
/**
* @brief Get the measurement status
*
* The function can be used to test whether a measurement that was started
* before is still running.
*
* @param dev pointer to the sensor device data structure
* @return true if measurement is still running or false otherwise
*/
bool bme680_is_measuring (bme680_sensor_t* dev);
/**
* @brief Get results of a measurement in fixed point representation
*
* The function returns the results of a TPHG measurement that has been
* started before. If the measurement is still running, the function fails
* and returns invalid values (see type declaration).
*
* @param dev pointer to the sensor device data structure
* @param results pointer to a data structure that is filled with results
* @return true on success, false on error
*/
bool bme680_get_results_fixed (bme680_sensor_t* dev,
bme680_values_fixed_t* results);
/**
* @brief Get results of a measurement in floating point representation
*
* The function returns the results of a TPHG measurement that has been
* started before. If the measurement is still running, the function fails
* and returns invalid values (see type declaration).
*
* @param dev pointer to the sensor device data structure
* @param results pointer to a data structure that is filled with results
* @return true on success, false on error
*/
bool bme680_get_results_float (bme680_sensor_t* dev,
bme680_values_float_t* results);
/**
* @brief Start a measurement, wait and return the results (fixed point)
*
* This function is a combination of functions above. For convenience it
* starts a TPHG measurement using *bme680_force_measurement*, then it waits
* the measurement duration for the results using *vTaskDelay* and finally it
* returns the results using function *bme680_get_results_fixed*.
*
* Note: Since the calling task is delayed using function *vTaskDelay*, this
* function must not be used when it is called from a software timer callback
* function.
*
* @param dev pointer to the sensor device data structure
* @param results pointer to a data structure that is filled with results
* @return true on success, false on error
*/
bool bme680_measure_fixed (bme680_sensor_t* dev,
bme680_values_fixed_t* results);
/**
* @brief Start a measurement, wait and return the results (floating point)
*
* This function is a combination of functions above. For convenience it
* starts a TPHG measurement using *bme680_force_measurement*, then it waits
* the measurement duration for the results using *vTaskDelay* and finally it
* returns the results using function *bme680_get_results_float*.
*
* Note: Since the calling task is delayed using function *vTaskDelay*, this
* function must not be used when it is called from a software timer callback
* function.
*
* @param dev pointer to the sensor device data structure
* @param results pointer to a data structure that is filled with results
* @return true on success, false on error
*/
bool bme680_measure_float (bme680_sensor_t* dev,
bme680_values_float_t* results);
/**
* @brief Set the oversampling rates for measurements
*
* The BME680 sensor allows to define individual oversampling rates for
* the measurements of temperature, pressure and humidity. Using an
* oversampling rate of *osr*, the resolution of raw sensor data can be
* increased by ld(*osr*) bits.
*
* Possible oversampling rates are 1x (default), 2x, 4x, 8x, 16x, see type
* *bme680_oversampling_rate_t*. The default oversampling rate is 1.
*
* Please note: Use *osr_none* to skip the corresponding measurement.
*
* @param dev pointer to the sensor device data structure
* @param ost oversampling rate for temperature measurements
* @param osp oversampling rate for pressure measurements
* @param osh oversampling rate for humidity measurements
* @return true on success, false on error
*/
bool bme680_set_oversampling_rates (bme680_sensor_t* dev,
bme680_oversampling_rate_t osr_t,
bme680_oversampling_rate_t osr_p,
bme680_oversampling_rate_t osr_h);
/**
* @brief Set the size of the IIR filter
*
* The sensor integrates an internal IIR filter (low pass filter) to reduce
* short-term changes in sensor output values caused by external disturbances.
* It effectively reduces the bandwidth of the sensor output values.
*
* The filter can optionally be used for pressure and temperature data that
* are subject to many short-term changes. Using the IIR filter, increases the
* resolution of pressure and temperature data to 20 bit. Humidity and gas
* inside the sensor does not fluctuate rapidly and does not require such a
* low pass filtering.
*
* The default filter size is 3 (*iir_size_3*).
*
* Please note: If the size of the filter is 0, the filter is not used.
*
* @param dev pointer to the sensor device data structure
* @param size IIR filter size
* @return true on success, false on error
*/
bool bme680_set_filter_size(bme680_sensor_t* dev, bme680_filter_size_t size);
/**
* @brief Set a heater profile for gas measurements
*
* The sensor integrates a heater for the gas measurement. Parameters for this
* heater are defined by so called heater profiles. The sensor supports up to
* 10 heater profiles, which are numbered from 0 to 9. Each profile consists of
* a temperature set-point (the target temperature) and a heating duration.
*
* This function sets the parameters for one of the heater profiles 0 ... 9.
* To activate the gas measurement with this profile, use function
* *bme680_use_heater_profile*, see below.
*
* Please note: According to the data sheet, a target temperatures of between
* 200 and 400 degrees Celsius are typical and about 20 to 30 ms are necessary
* for the heater to reach the desired target temperature.
*
* @param dev pointer to the sensor device data structure
* @param profile heater profile 0 ... 9
* @param temperature target temperature in degree Celsius
* @param duration heating duration in milliseconds
* @return true on success, false on error
*/
bool bme680_set_heater_profile (bme680_sensor_t* dev,
uint8_t profile,
uint16_t temperature,
uint16_t duration);
/**
* @brief Activate gas measurement with a given heater profile
*
* The function activates the gas measurement with one of the heater
* profiles 0 ... 9 or deactivates the gas measurement completely when
* -1 or BME680_HEATER_NOT_USED is used as heater profile.
*
* Parameters of the activated heater profile have to be set before with
* function *bme680_set_heater_profile* otherwise the function fails.
*
* If several heater profiles have been defined with function
* *bme680_set_heater_profile*, a sequence of gas measurements with different
* heater parameters can be realized by a sequence of activations of different
* heater profiles for successive TPHG measurements using this function.
*
* @param dev pointer to the sensor device data structure0 *
* @param profile 0 ... 9 to activate or -1 to deactivate gas measure
* @return true on success, false on error
*/
bool bme680_use_heater_profile (bme680_sensor_t* dev, int8_t profile);
/**
* @brief Set ambient temperature
*
* The heater resistance calculation algorithm takes into account the ambient
* temperature of the sensor. This function can be used to set this ambient
* temperature. Either values determined from the sensor itself or from
* another temperature sensor can be used. The default ambient temperature
* is 25 degree Celsius.
*
* @param dev pointer to the sensor device data structure
* @param temperature ambient temperature in degree Celsius
* @return true on success, false on error
*/
bool bme680_set_ambient_temperature (bme680_sensor_t* dev,
int16_t temperature);
#ifdef __cplusplus
}
#endif /* End of CPP guard */
#endif /* __BME680_H__ */

View file

@ -0,0 +1,192 @@
/*
* Driver for Bosch Sensortec BME680 digital temperature, humity, pressure and
* gas sensor connected to I2C or SPI
*
* Part of esp-open-rtos [https://github.com/SuperHouse/esp-open-rtos]
*
* ---------------------------------------------------------------------------
*
* The BSD License (3-clause license)
*
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht]
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __BME680_TYPES_H__
#define __BME680_TYPES_H__
#include "stdint.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Fixed point sensor values (fixed THPG values)
*/
typedef struct { // invalid value
int16_t temperature; // temperature in degree C * 100 (INT16_MIN)
uint32_t pressure; // barometric pressure in Pascal (0)
uint32_t humidity; // relative humidity in % * 1000 (0)
uint32_t gas_resistance; // gas resistance in Ohm (0)
} bme680_values_fixed_t;
/**
* @brief Floating point sensor values (real THPG values)
*/
typedef struct { // invalid value
float temperature; // temperature in degree C (-327.68)
float pressure; // barometric pressure in hPascal (0.0)
float humidity; // relative humidity in % (0.0)
float gas_resistance; // gas resistance in Ohm (0.0)
} bme680_values_float_t;
/**
* @brief Oversampling rates
*/
typedef enum {
osr_none = 0, // measurement is skipped, output values are invalid
osr_1x = 1, // default oversampling rates
osr_2x = 2,
osr_4x = 3,
osr_8x = 4,
osr_16x = 5
} bme680_oversampling_rate_t;
/**
* @brief Filter sizes
*/
typedef enum {
iir_size_0 = 0, // filter is not used
iir_size_1 = 1,
iir_size_3 = 2,
iir_size_7 = 3,
iir_size_15 = 4,
iir_size_31 = 5,
iir_size_63 = 6,
iir_size_127 = 7
} bme680_filter_size_t;
/**
* @brief Sensor parameters that configure the TPHG measurement cycle
*
* T - temperature measurement
* P - pressure measurement
* H - humidity measurement
* G - gas measurement
*/
typedef struct {
uint8_t osr_temperature; // T oversampling rate (default osr_1x)
uint8_t osr_pressure; // P oversampling rate (default osr_1x)
uint8_t osr_humidity; // H oversampling rate (default osr_1x)
uint8_t filter_size; // IIR filter size (default iir_size_3)
int8_t heater_profile; // Heater profile used (default 0)
uint16_t heater_temperature[10]; // Heater temperature for G (default 320)
uint16_t heater_duration[10]; // Heater duration for G (default 150)
int8_t ambient_temperature; // Ambient temperature for G (default 25)
} bme680_settings_t;
/**
* @brief Data structure for calibration parameters
*
* These calibration parameters are used in compensation algorithms to convert
* raw sensor data to measurement results.
*/
typedef struct {
uint16_t par_t1; // calibration data for temperature compensation
int16_t par_t2;
int8_t par_t3;
uint16_t par_p1; // calibration data for pressure compensation
int16_t par_p2;
int8_t par_p3;
int16_t par_p4;
int16_t par_p5;
int8_t par_p7;
int8_t par_p6;
int16_t par_p8;
int16_t par_p9;
uint8_t par_p10;
uint16_t par_h1; // calibration data for humidity compensation
uint16_t par_h2;
int8_t par_h3;
int8_t par_h4;
int8_t par_h5;
uint8_t par_h6;
int8_t par_h7;
int8_t par_gh1; // calibration data for gas compensation
int16_t par_gh2;
int8_t par_gh3;
int32_t t_fine; // temperatur correction factor for P and G
uint8_t res_heat_range;
int8_t res_heat_val;
int8_t range_sw_err;
} bme680_calib_data_t;
/**
* @brief BME680 sensor device data structure type
*/
typedef struct {
int error_code; // contains the error code of last operation
uint8_t bus; // I2C = x, SPI = 1
uint8_t addr; // I2C = slave address, SPI = 0
uint8_t spi_cs_pin; // GPIO used as SPI CS
bool meas_started; // indicates whether measurement started
uint32_t meas_start_tick; // measurement start time in RTOS ticks
uint8_t meas_status; // last sensor status (for internal use only)
bme680_settings_t settings; // sensor settings
bme680_calib_data_t calib_data; // calibration data of the sensor
} bme680_sensor_t;
#ifdef __cplusplus
}
#endif /* End of CPP guard */
#endif /* __BME680_TYPES_H__ */

View file

@ -0,0 +1,9 @@
# Component makefile for extras/bme60
# expected anyone using bme680 driver includes it as 'bme680/bme680.h'
INC_DIRS += $(bme680_ROOT)..
# args for passing into compile rule generation
bme680_SRC_DIR = $(bme680_ROOT)
$(eval $(call component_compile_rules,bme680))

View file

@ -38,7 +38,7 @@
static int bmp180_readRegister16(i2c_dev_t *dev, uint8_t reg, int16_t *r)
{
uint8_t d[] = { 0, 0 };
int error ;
int error;
if ((error = i2c_slave_read(dev->bus, dev->addr, &reg, d, 2)))
return error;
@ -49,7 +49,7 @@ static int bmp180_readRegister16(i2c_dev_t *dev, uint8_t reg, int16_t *r)
static int bmp180_start_Messurement(i2c_dev_t *dev, uint8_t cmd)
{
uint8_t reg = BMP180_CONTROL_REG ;
uint8_t reg = BMP180_CONTROL_REG;
return i2c_slave_write(dev->bus, dev->addr, &reg, &cmd, 1);
}
@ -216,8 +216,8 @@ typedef struct
} bmp180_command_t;
// Just works due to the fact that QueueHandle_t is a "void *"
static QueueHandle_t bmp180_rx_queue[MAX_I2C_BUS] = { NULL };
static TaskHandle_t bmp180_task_handle[MAX_I2C_BUS] = { NULL };
static QueueHandle_t bmp180_rx_queue[I2C_MAX_BUS] = { NULL };
static TaskHandle_t bmp180_task_handle[I2C_MAX_BUS] = { NULL };
//
// Forward declarations

View file

@ -21,9 +21,10 @@ from it.
Connect BMP280 or BME280 module to you ESP8266 module and initialize the I2C SCL and SDA pins:
```
const uint8_t bus = 0;
const uint8_t scl_pin = 0;
const uint8_t sda_pin = 2;
i2c_init(scl_pin, sda_pin);
i2c_init(bus, scl_pin, sda_pin, I2C_FREQ_100K);
```

View file

@ -60,12 +60,12 @@ void bmp280_init_default_params(bmp280_params_t *params)
params->mode = BMP280_MODE_NORMAL;
params->filter = BMP280_FILTER_OFF;
params->oversampling_pressure = BMP280_STANDARD;
params->oversampling_temperature = BMP280_STANDARD ;
params->oversampling_temperature = BMP280_STANDARD;
params->oversampling_humidity = BMP280_STANDARD;
params->standby = BMP280_STANDBY_250;
}
static bool read_register16(i2c_dev_t* dev, uint8_t addr, uint16_t *value)
static bool read_register16(i2c_dev_t *dev, uint8_t addr, uint16_t *value)
{
uint8_t d[] = {0, 0};
if (!i2c_slave_read(dev->bus, dev->addr, &addr, d, sizeof(d))) {
@ -75,7 +75,7 @@ static bool read_register16(i2c_dev_t* dev, uint8_t addr, uint16_t *value)
return false;
}
static inline int read_data(i2c_dev_t* dev, uint8_t addr, uint8_t *value, uint8_t len)
static inline int read_data(i2c_dev_t *dev, uint8_t addr, uint8_t *value, uint8_t len)
{
return i2c_slave_read(dev->bus, dev->addr, &addr, value, len);
}
@ -141,7 +141,7 @@ static bool read_hum_calibration_data(bmp280_t *dev)
return false;
}
static int write_register8(i2c_dev_t* dev, uint8_t addr, uint8_t value)
static int write_register8(i2c_dev_t *dev, uint8_t addr, uint8_t value)
{
return i2c_slave_write(dev->bus, dev->addr, &addr, &value, 1);
}

View file

@ -28,12 +28,6 @@
#define debug(s, ...)
#endif
#if (DHCP_DEBUG == LWIP_DBG_ON)
#define debug(s, ...) printf("%s: " s "\n", "DHCP", ## __VA_ARGS__)
#else
#define debug(s, ...)
#endif
/* Grow the size of the lwip dhcp_msg struct's options field, as LWIP
defaults to a 68 octet options field for its DHCP client, and most
full-sized clients send us more than this. */
@ -216,6 +210,7 @@ static void dhcpserver_task(void *pxParameter)
break;
case DHCP_RELEASE:
handle_dhcp_release(&received);
break;
default:
debug("DHCP Server Error: Unsupported message type %d", *message_type);
break;

View file

@ -3,7 +3,7 @@
*
* Part of esp-open-rtos
* Copyright (C) 2016 Ruslan V. Uss <unclerus@gmail.com>,
* Pavel Merlyakov <merzlyakovpavel@gmail.com>
* Pavel Merzlyakov <merzlyakovpavel@gmail.com>
* BSD Licensed as described in the file LICENSE
*/
#ifndef EXTRAS_DS1302_H_

View file

@ -38,36 +38,36 @@ static uint8_t dec2bcd(uint8_t val)
return ((val / 10) << 4) + (val % 10);
}
static uint8_t read_register(i2c_dev_t* dev, uint8_t reg)
static uint8_t read_register(i2c_dev_t *dev, uint8_t reg)
{
uint8_t val;
i2c_slave_read(dev->bus, dev->addr, &reg, &val, 1);
return val;
}
static void update_register(i2c_dev_t* dev, uint8_t reg, uint8_t mask, uint8_t val)
static void update_register(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val)
{
uint8_t buf = (read_register(dev,reg) & mask) | val;
uint8_t buf = (read_register(dev, reg) & mask) | val;
i2c_slave_write(dev->bus, dev->addr, &reg, &buf, 1);
}
void ds1307_start(i2c_dev_t* dev, bool start)
void ds1307_start(i2c_dev_t *dev, bool start)
{
update_register(dev, TIME_REG, CH_MASK, start ? 0 : CH_BIT);
}
bool ds1307_is_running(i2c_dev_t* dev)
bool ds1307_is_running(i2c_dev_t *dev)
{
return !(read_register(dev, TIME_REG) & CH_BIT);
}
void ds1307_get_time(i2c_dev_t* dev, struct tm *time)
void ds1307_get_time(i2c_dev_t *dev, struct tm *time)
{
uint8_t buf[7];
uint8_t reg = TIME_REG ;
uint8_t reg = TIME_REG;
i2c_slave_read(dev->bus, dev->addr, &reg , buf, 7);
i2c_slave_read(dev->bus, dev->addr, &reg, buf, 7);
time->tm_sec = bcd2dec(buf[0] & SECONDS_MASK);
time->tm_min = bcd2dec(buf[1]);
@ -78,14 +78,15 @@ void ds1307_get_time(i2c_dev_t* dev, struct tm *time)
if (buf[2] & PM_BIT)
time->tm_hour += 12;
}
else time->tm_hour = bcd2dec(buf[2] & HOUR24_MASK);
else
time->tm_hour = bcd2dec(buf[2] & HOUR24_MASK);
time->tm_wday = bcd2dec(buf[3]) - 1;
time->tm_mday = bcd2dec(buf[4]);
time->tm_mon = bcd2dec(buf[5]) - 1;
time->tm_mon = bcd2dec(buf[5]) - 1;
time->tm_year = bcd2dec(buf[6]) + 2000;
}
void ds1307_set_time(i2c_dev_t* dev, const struct tm *time)
void ds1307_set_time(i2c_dev_t *dev, const struct tm *time)
{
uint8_t buf[8];
buf[0] = TIME_REG;
@ -97,51 +98,55 @@ void ds1307_set_time(i2c_dev_t* dev, const struct tm *time)
buf[6] = dec2bcd(time->tm_mon + 1);
buf[7] = dec2bcd(time->tm_year - 2000);
i2c_slave_write(dev->bus, dev->addr, &buf[0], &buf[1] , 7);
i2c_slave_write(dev->bus, dev->addr, &buf[0], &buf[1], 7);
}
void ds1307_enable_squarewave(i2c_dev_t* dev, bool enable)
void ds1307_enable_squarewave(i2c_dev_t *dev, bool enable)
{
update_register(dev, CONTROL_REG, SQWE_MASK, enable ? SQWE_BIT : 0);
}
bool ds1307_is_squarewave_enabled(i2c_dev_t* dev)
bool ds1307_is_squarewave_enabled(i2c_dev_t *dev)
{
return read_register(dev, CONTROL_REG) & SQWE_BIT;
}
void ds1307_set_squarewave_freq(i2c_dev_t* dev, ds1307_squarewave_freq_t freq)
void ds1307_set_squarewave_freq(i2c_dev_t *dev, ds1307_squarewave_freq_t freq)
{
update_register(dev, CONTROL_REG, SQWEF_MASK, (uint8_t)freq);
}
ds1307_squarewave_freq_t ds1307_get_squarewave_freq(i2c_dev_t* dev)
ds1307_squarewave_freq_t ds1307_get_squarewave_freq(i2c_dev_t *dev)
{
return (ds1307_squarewave_freq_t)(read_register(dev, CONTROL_REG) & SQWEF_MASK);
}
bool ds1307_get_output(i2c_dev_t* dev)
bool ds1307_get_output(i2c_dev_t *dev)
{
return read_register(dev, CONTROL_REG) & OUT_BIT;
}
void ds1307_set_output(i2c_dev_t* dev, bool value)
void ds1307_set_output(i2c_dev_t *dev, bool value)
{
update_register(dev, CONTROL_REG, OUT_MASK, value ? OUT_BIT : 0);
}
int ds1307_read_ram(i2c_dev_t* dev, uint8_t offset, uint8_t *buf, uint8_t len)
int ds1307_read_ram(i2c_dev_t *dev, uint8_t offset, uint8_t *buf, uint8_t len)
{
if (offset + len > RAM_SIZE) return false;
uint8_t reg = RAM_REG + offset ;
if (offset + len > RAM_SIZE)
return -EINVAL;
uint8_t reg = RAM_REG + offset;
return i2c_slave_read(dev->bus, dev->addr, &reg, buf, len);
}
int ds1307_write_ram(i2c_dev_t* dev, uint8_t offset, uint8_t *buf, uint8_t len)
int ds1307_write_ram(i2c_dev_t *dev, uint8_t offset, uint8_t *buf, uint8_t len)
{
if (offset + len > RAM_SIZE) return false;
uint8_t reg = RAM_REG + offset ;
if (offset + len > RAM_SIZE)
return -EINVAL;
uint8_t reg = RAM_REG + offset;
return i2c_slave_write(dev->bus, dev->addr, &reg, buf, len);
}

View file

@ -17,11 +17,12 @@
extern "C" {
#endif
#define DS1307_ADDR 0x68
#define DS1307_ADDR 0x68
/**
* Squarewave frequency
*/
typedef enum _ds1307_squarewave_freq_t
typedef enum
{
DS1307_1HZ = 0, //!< 1 Hz
DS1307_4096HZ, //!< 4096 Hz
@ -33,62 +34,62 @@ typedef enum _ds1307_squarewave_freq_t
* \brief Start/stop clock
* \param start Start clock if true
*/
void ds1307_start(i2c_dev_t* dev, bool start);
void ds1307_start(i2c_dev_t *dev, bool start);
/**
* \brief Get current clock state
* \return true if clock running
*/
bool ds1307_is_running(i2c_dev_t* dev);
bool ds1307_is_running(i2c_dev_t *dev);
/**
* \brief Get current time
* \param time Pointer to the time struct to fill
*/
void ds1307_get_time(i2c_dev_t* dev, struct tm *time);
void ds1307_get_time(i2c_dev_t *dev, struct tm *time);
/**
* \brief Set time to RTC
* \param time Pointer to the time struct
*/
void ds1307_set_time(i2c_dev_t* dev, const struct tm *time);
void ds1307_set_time(i2c_dev_t *dev, const struct tm *time);
/**
* \brief Enable or disable square-wave oscillator output
* \param enable Enable oscillator if true
*/
void ds1307_enable_squarewave(i2c_dev_t* dev, bool enable);
void ds1307_enable_squarewave(i2c_dev_t *dev, bool enable);
/**
* \brief Get square-wave oscillator output
* \return true if square-wave oscillator enabled
*/
bool ds1307_is_squarewave_enabled(i2c_dev_t* dev);
bool ds1307_is_squarewave_enabled(i2c_dev_t *dev);
/**
* \brief Set square-wave oscillator frequency
* \param freq Frequency
*/
void ds1307_set_squarewave_freq(i2c_dev_t* dev, ds1307_squarewave_freq_t freq);
void ds1307_set_squarewave_freq(i2c_dev_t *dev, ds1307_squarewave_freq_t freq);
/**
* \brief Get current square-wave oscillator frequency
* \return Frequency
*/
ds1307_squarewave_freq_t ds1307_get_squarewave_freq(i2c_dev_t* dev);
ds1307_squarewave_freq_t ds1307_get_squarewave_freq(i2c_dev_t *dev);
/**
* \brief Get current output level of the SQW/OUT pin
* \return true if high
*/
bool ds1307_get_output(i2c_dev_t* dev);
bool ds1307_get_output(i2c_dev_t *dev);
/**
* \brief Set output level of the SQW/OUT pin
* Set output level if square-wave output is disabled
* \param value High level if true
*/
void ds1307_set_output(i2c_dev_t* dev, bool value);
void ds1307_set_output(i2c_dev_t *dev, bool value);
/**
* \brief Read RAM contents into the buffer
@ -97,7 +98,7 @@ void ds1307_set_output(i2c_dev_t* dev, bool value);
* \param len Bytes to read, 1..56
* \return Non-zero if error occured
*/
int ds1307_read_ram(i2c_dev_t* dev, uint8_t offset, uint8_t *buf, uint8_t len);
int ds1307_read_ram(i2c_dev_t *dev, uint8_t offset, uint8_t *buf, uint8_t len);
/**
* \brief Write buffer to RTC RAM
@ -106,7 +107,7 @@ int ds1307_read_ram(i2c_dev_t* dev, uint8_t offset, uint8_t *buf, uint8_t len);
* \param len Bytes to write, 1..56
* \return Non-zero if error occured
*/
int ds1307_write_ram(i2c_dev_t* dev, uint8_t offset, uint8_t *buf, uint8_t len);
int ds1307_write_ram(i2c_dev_t *dev, uint8_t offset, uint8_t *buf, uint8_t len);
#ifdef __cplusplus

View file

@ -104,7 +104,7 @@ hd44780_t lcd = {
hd44780_t lcd = {
.addr = ADDR,
.font = HD44780_FONT_5X8,
.lines = 2,
.lines = 4,
.pins = {
.rs = 0,
.e = 2,

View file

@ -52,24 +52,24 @@ static const float gain_values [] = {
static float current_gain;
static hmc5883l_operating_mode_t current_mode;
static inline void write_register(i2c_dev_t* dev, uint8_t reg, uint8_t val)
static inline void write_register(i2c_dev_t *dev, uint8_t reg, uint8_t val)
{
i2c_slave_write(dev->bus, dev->addr, &reg, &val, 1);
}
static inline uint8_t read_register(i2c_dev_t* dev, uint8_t reg)
static inline uint8_t read_register(i2c_dev_t *dev, uint8_t reg)
{
uint8_t res;
i2c_slave_read(dev->bus, dev->addr, &reg, &res, 1);
return res;
}
static inline void update_register(i2c_dev_t* dev, uint8_t reg, uint8_t mask, uint8_t val)
static inline void update_register(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val)
{
write_register(dev, reg, (read_register(dev, reg) & mask) | val);
}
bool hmc5883l_init(i2c_dev_t* dev)
bool hmc5883l_init(i2c_dev_t *dev)
{
if (hmc5883l_get_id(dev) != HMC5883L_ID)
return false;
@ -78,7 +78,7 @@ bool hmc5883l_init(i2c_dev_t* dev)
return true;
}
uint32_t hmc5883l_get_id(i2c_dev_t* dev)
uint32_t hmc5883l_get_id(i2c_dev_t *dev)
{
uint32_t res = 0;
uint8_t reg = REG_ID_A;
@ -86,70 +86,70 @@ uint32_t hmc5883l_get_id(i2c_dev_t* dev)
return res;
}
hmc5883l_operating_mode_t hmc5883l_get_operating_mode(i2c_dev_t* dev)
hmc5883l_operating_mode_t hmc5883l_get_operating_mode(i2c_dev_t *dev)
{
uint8_t res = read_register(dev, REG_MODE) & MASK_MD;
return res == 0 ? HMC5883L_MODE_CONTINUOUS : HMC5883L_MODE_SINGLE;
}
void hmc5883l_set_operating_mode(i2c_dev_t* dev, hmc5883l_operating_mode_t mode)
void hmc5883l_set_operating_mode(i2c_dev_t *dev, hmc5883l_operating_mode_t mode)
{
write_register(dev, REG_MODE, mode);
current_mode = mode;
}
hmc5883l_samples_averaged_t hmc5883l_get_samples_averaged(i2c_dev_t* dev)
hmc5883l_samples_averaged_t hmc5883l_get_samples_averaged(i2c_dev_t *dev)
{
return (read_register(dev, REG_CR_A) & MASK_MA) >> BIT_MA;
}
void hmc5883l_set_samples_averaged(i2c_dev_t* dev, hmc5883l_samples_averaged_t samples)
void hmc5883l_set_samples_averaged(i2c_dev_t *dev, hmc5883l_samples_averaged_t samples)
{
update_register(dev, REG_CR_A, MASK_MA, samples << BIT_MA);
}
hmc5883l_data_rate_t hmc5883l_get_data_rate(i2c_dev_t* dev)
hmc5883l_data_rate_t hmc5883l_get_data_rate(i2c_dev_t *dev)
{
return (read_register(dev, REG_CR_A) & MASK_DO) >> BIT_DO;
}
void hmc5883l_set_data_rate(i2c_dev_t* dev, hmc5883l_data_rate_t rate)
void hmc5883l_set_data_rate(i2c_dev_t *dev, hmc5883l_data_rate_t rate)
{
update_register(dev, REG_CR_A, MASK_DO, rate << BIT_DO);
}
hmc5883l_bias_t hmc5883l_get_bias(i2c_dev_t* dev)
hmc5883l_bias_t hmc5883l_get_bias(i2c_dev_t *dev)
{
return read_register(dev, REG_CR_A) & MASK_MS;
}
void hmc5883l_set_bias(i2c_dev_t* dev, hmc5883l_bias_t bias)
void hmc5883l_set_bias(i2c_dev_t *dev, hmc5883l_bias_t bias)
{
update_register(dev, REG_CR_A, MASK_MS, bias);
}
hmc5883l_gain_t hmc5883l_get_gain(i2c_dev_t* dev)
hmc5883l_gain_t hmc5883l_get_gain(i2c_dev_t *dev)
{
return read_register(dev, REG_CR_B) >> BIT_GN;
}
void hmc5883l_set_gain(i2c_dev_t* dev, hmc5883l_gain_t gain)
void hmc5883l_set_gain(i2c_dev_t *dev, hmc5883l_gain_t gain)
{
write_register(dev, REG_CR_B, gain << BIT_GN);
current_gain = gain_values[gain];
}
bool hmc5883l_data_is_locked(i2c_dev_t* dev)
bool hmc5883l_data_is_locked(i2c_dev_t *dev)
{
return read_register(dev, REG_STAT) & MASK_DL;
}
bool hmc5883l_data_is_ready(i2c_dev_t* dev)
bool hmc5883l_data_is_ready(i2c_dev_t *dev)
{
return read_register(dev, REG_STAT) & MASK_DR;
}
bool hmc5883l_get_raw_data(i2c_dev_t* dev, hmc5883l_raw_data_t *data)
bool hmc5883l_get_raw_data(i2c_dev_t *dev, hmc5883l_raw_data_t *data)
{
if (current_mode == HMC5883L_MODE_SINGLE)
{
@ -179,7 +179,7 @@ void hmc5883l_raw_to_mg(const hmc5883l_raw_data_t *raw, hmc5883l_data_t *mg)
mg->z = raw->z * current_gain;
}
bool hmc5883l_get_data(i2c_dev_t* dev, hmc5883l_data_t *data)
bool hmc5883l_get_data(i2c_dev_t *dev, hmc5883l_data_t *data)
{
hmc5883l_raw_data_t raw;

View file

@ -104,82 +104,82 @@ typedef struct
* \brief Init device
* \return false if error occured
*/
bool hmc5883l_init(i2c_dev_t* dev);
bool hmc5883l_init(i2c_dev_t *dev);
/**
* \brief Get device ID
* Always returns 0x00333448 if IC functioning properly.
* \return Device ID
*/
uint32_t hmc5883l_get_id(i2c_dev_t* dev);
uint32_t hmc5883l_get_id(i2c_dev_t *dev);
/**
* \brief Get operating mode
* \return Measurement mode
*/
hmc5883l_operating_mode_t hmc5883l_get_operating_mode(i2c_dev_t* dev);
hmc5883l_operating_mode_t hmc5883l_get_operating_mode(i2c_dev_t *dev);
/**
* \brief Set operating mode
* \param mode Measurement mode
*/
void hmc5883l_set_operating_mode(i2c_dev_t* dev, hmc5883l_operating_mode_t mode);
void hmc5883l_set_operating_mode(i2c_dev_t *dev, hmc5883l_operating_mode_t mode);
/**
* \brief Get number of samples averaged per measurement output
* \return Number of samples
*/
hmc5883l_samples_averaged_t hmc5883l_get_samples_averaged(i2c_dev_t* dev);
hmc5883l_samples_averaged_t hmc5883l_get_samples_averaged(i2c_dev_t *dev);
/**
* \brief Set number of samples averaged per measurement output
* \param samples Number of samples
*/
void hmc5883l_set_samples_averaged(i2c_dev_t* dev, hmc5883l_samples_averaged_t samples);
void hmc5883l_set_samples_averaged(i2c_dev_t *dev, hmc5883l_samples_averaged_t samples);
/**
* \brief Get data output rate in continuous measurement mode
* \return Data output rate
*/
hmc5883l_data_rate_t hmc5883l_get_data_rate(i2c_dev_t* dev);
hmc5883l_data_rate_t hmc5883l_get_data_rate(i2c_dev_t *dev);
/**
* \brief Set data output rate in continuous measurement mode
* \param rate Data output rate
*/
void hmc5883l_set_data_rate(i2c_dev_t* dev, hmc5883l_data_rate_t rate);
void hmc5883l_set_data_rate(i2c_dev_t *dev, hmc5883l_data_rate_t rate);
/**
* \brief Get measurement mode (bias of the axes)
* See datasheet for self test description
* \return Bias
*/
hmc5883l_bias_t hmc5883l_get_bias(i2c_dev_t* dev);
hmc5883l_bias_t hmc5883l_get_bias(i2c_dev_t *dev);
/**
* \brief Set measurement mode (bias of the axes)
* See datasheet for self test description
* \param bias Bias
*/
void hmc5883l_set_bias(i2c_dev_t* dev, hmc5883l_bias_t bias);
void hmc5883l_set_bias(i2c_dev_t *dev, hmc5883l_bias_t bias);
/**
* \brief Get device gain
* \return Current gain
*/
hmc5883l_gain_t hmc5883l_get_gain(i2c_dev_t* dev);
hmc5883l_gain_t hmc5883l_get_gain(i2c_dev_t *dev);
/**
* \brief Set device gain
* \param gain Gain
*/
void hmc5883l_set_gain(i2c_dev_t* dev, hmc5883l_gain_t gain);
void hmc5883l_set_gain(i2c_dev_t *dev, hmc5883l_gain_t gain);
/**
* \brief Get data state
* \return true when data is written to all six data registers
*/
bool hmc5883l_data_is_ready(i2c_dev_t* dev);
bool hmc5883l_data_is_ready(i2c_dev_t *dev);
/**
* \brief Get lock state.
@ -191,14 +191,14 @@ bool hmc5883l_data_is_ready(i2c_dev_t* dev);
* 4. power is reset.
* \return true when data registers is locked
*/
bool hmc5883l_data_is_locked(i2c_dev_t* dev);
bool hmc5883l_data_is_locked(i2c_dev_t *dev);
/**
* \brief Get raw magnetic data
* \param data Pointer to the struct to write raw data
* \return false if error occured in single measurement mode, always true in continuous mode
*/
bool hmc5883l_get_raw_data(i2c_dev_t* dev, hmc5883l_raw_data_t *data);
bool hmc5883l_get_raw_data(i2c_dev_t *dev, hmc5883l_raw_data_t *data);
/**
* \brief Convert raw magnetic data to milligausses
@ -212,7 +212,7 @@ void hmc5883l_raw_to_mg(const hmc5883l_raw_data_t *raw, hmc5883l_data_t *mg);
* \param data Pointer to the struct to write data
* \return false if error occured in single measurement mode, always true in continuous mode
*/
bool hmc5883l_get_data(i2c_dev_t* dev, hmc5883l_data_t *data);
bool hmc5883l_get_data(i2c_dev_t *dev, hmc5883l_data_t *data);
#ifdef __cplusplus
}

View file

@ -2,11 +2,12 @@
This time a driver for the excellent esp-open-rtos. This is a bit banging I2C driver based on the Wikipedia pesudo C code [1].
### Usage
### Basic usage
````
```C
#include <i2c.h>
#define BUS (0)
#define SCL_PIN (0)
#define SDA_PIN (2)
@ -14,19 +15,19 @@ uint8_t slave_addr = 0x20;
uint8_t reg_addr = 0x1f;
uint8_t reg_data;
i2c_init(SCL_PIN, SDA_PIN);
i2c_init(BUS, SCL_PIN, SDA_PIN, I2C_FREQ_400K);
// Write 1 byte to slave register
int err = i2c_slave_write(slave_addr, &reg_addr, &data, 1);
int err = i2c_slave_write(BUS, slave_addr, &reg_addr, &data, 1);
if (err != 0)
{
// do something with error
}
// Issue write to slave, sending reg_addr, followed by reading 1 byte
err = i2c_slave_read(slave_addr, &reg_addr, &reg_data, 1);
err = i2c_slave_read(BUS, slave_addr, &reg_addr, &reg_data, 1);
````
```
For details please see `extras/i2c/i2c.h`.

View file

@ -22,10 +22,13 @@
* THE SOFTWARE.
*/
#include "i2c.h"
#include <esp8266.h>
#include <espressif/esp_misc.h> // sdk_os_delay_us
#include <espressif/esp_system.h>
#include "i2c.h"
#include <FreeRTOS.h>
#include <task.h>
//#define I2C_DEBUG true
@ -35,23 +38,51 @@
#define debug(fmt, ...)
#endif
#define CLK_STRETCH (10)
//#define CLK_STRETCH (10)
static uint8_t freq ; // Store CPU frequency for optimisation speed in delay function ( Warning: Don't change CPU frequency during a transaction)
static i2c_bus_description_t i2c_bus[MAX_I2C_BUS];
// Following array contain delay values for different frequencies
// Warning: 1 is minimal, that mean at 80MHz clock, frequency max is 320kHz
const static uint8_t i2c_freq_array[][2] = {
[I2C_FREQ_80K] = {255, 35},
[I2C_FREQ_100K] = {100, 20},
[I2C_FREQ_400K] = {10, 1},
[I2C_FREQ_500K] = {6, 1}
};
static uint8_t freq; // Store CPU frequency for optimisation speed in delay function (Warning: Don't change CPU frequency during a transaction)
// Bus settings
typedef struct i2c_bus_description
{
uint8_t g_scl_pin; // SCL pin
uint8_t g_sda_pin; // SDA pin
i2c_freq_t frequency; // Frequency
bool started;
bool flag;
bool force;
uint32_t clk_stretch;
} i2c_bus_description_t;
static i2c_bus_description_t i2c_bus[I2C_MAX_BUS];
inline bool i2c_status(uint8_t bus)
{
return i2c_bus[bus].started;
}
void i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq)
int i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq)
{
if (bus >= I2C_MAX_BUS) {
debug("Invalid bus");
return -EINVAL;
}
i2c_bus[bus].started = false;
i2c_bus[bus].flag = false ;
i2c_bus[bus].flag = false;
i2c_bus[bus].g_scl_pin = scl_pin;
i2c_bus[bus].g_sda_pin = sda_pin;
i2c_bus[bus].frequency = freq ;
i2c_bus[bus].frequency = freq;
i2c_bus[bus].clk_stretch = I2C_DEFAULT_CLK_STRETCH;
// Just to prevent these pins floating too much if not connected.
gpio_set_pullup(i2c_bus[bus].g_scl_pin, 1, 1);
@ -66,14 +97,22 @@ void i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq)
// Prevent user, if frequency is high
if (sdk_system_get_cpu_freq() == SYS_CPU_80MHZ)
if (i2c_freq_array[i2c_bus[bus].frequency][1] == 1)
if (i2c_freq_array[i2c_bus[bus].frequency][1] == 1) {
debug("Max frequency is 320Khz at 80MHz");
return -ENOTSUP;
}
return 0;
}
void i2c_frequency(uint8_t bus, i2c_freq_t freq)
void i2c_set_frequency(uint8_t bus, i2c_freq_t freq)
{
i2c_bus[bus].frequency = freq ;
i2c_bus[bus].frequency = freq;
}
void i2c_set_clock_stretch(uint8_t bus, uint32_t clk_stretch)
{
i2c_bus[bus].clk_stretch = clk_stretch;
}
static inline void i2c_delay(uint8_t bus)
@ -135,14 +174,15 @@ void i2c_start(uint8_t bus)
// Set SDA to 1
(void) read_sda(bus);
i2c_delay(bus);
uint32_t clk_stretch = CLK_STRETCH;
while (read_scl(bus) == 0 && clk_stretch--) ;
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
while (read_scl(bus) == 0 && clk_stretch--)
;
// Repeated start setup time, minimum 4.7us
i2c_delay(bus);
}
i2c_bus[bus].started = true;
if (read_sda(bus) == 0) {
debug("arbitration lost in i2c_start from bus %u",bus);
debug("arbitration lost in i2c_start from bus %u", bus);
}
// SCL is high, set SDA from 1 to 0.
clear_sda(bus);
@ -153,22 +193,23 @@ void i2c_start(uint8_t bus)
// Output stop condition
bool i2c_stop(uint8_t bus)
{
uint32_t clk_stretch = CLK_STRETCH;
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
// Set SDA to 0
clear_sda(bus);
i2c_delay(bus);
// Clock stretching
while (read_scl(bus) == 0 && clk_stretch--) ;
while (read_scl(bus) == 0 && clk_stretch--)
;
// Stop bit setup time, minimum 4us
i2c_delay(bus);
// SCL is high, set SDA from 0 to 1
if (read_sda(bus) == 0) {
debug("arbitration lost in i2c_stop from bus %u",bus);
debug("arbitration lost in i2c_stop from bus %u", bus);
}
i2c_delay(bus);
if (!i2c_bus[bus].started) {
debug("bus %u link was break!",bus);
return false ; //If bus was stop in other way, the current transmission Failed
debug("bus %u link was break!", bus);
return false; // If bus was stop in other way, the current transmission Failed
}
i2c_bus[bus].started = false;
return true;
@ -177,7 +218,7 @@ bool i2c_stop(uint8_t bus)
// Write a bit to I2C bus
static void i2c_write_bit(uint8_t bus, bool bit)
{
uint32_t clk_stretch = CLK_STRETCH;
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
if (bit) {
(void) read_sda(bus);
} else {
@ -185,11 +226,12 @@ static void i2c_write_bit(uint8_t bus, bool bit)
}
i2c_delay(bus);
// Clock stretching
while (read_scl(bus) == 0 && clk_stretch--) ;
while (read_scl(bus) == 0 && clk_stretch--)
;
// SCL is high, now data is valid
// If SDA is high, check that nobody else is driving SDA
if (bit && read_sda(bus) == 0) {
debug("arbitration lost in i2c_write_bit from bus %u",bus);
debug("arbitration lost in i2c_write_bit from bus %u", bus);
}
i2c_delay(bus);
clear_scl(bus);
@ -198,13 +240,14 @@ static void i2c_write_bit(uint8_t bus, bool bit)
// Read a bit from I2C bus
static bool i2c_read_bit(uint8_t bus)
{
uint32_t clk_stretch = CLK_STRETCH;
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
bool bit;
// Let the slave drive data
(void) read_sda(bus);
i2c_delay(bus);
// Clock stretching
while (read_scl(bus) == 0 && clk_stretch--) ;
while (read_scl(bus) == 0 && clk_stretch--)
;
// SCL is high, now data is valid
bit = read_sda(bus);
i2c_delay(bus);
@ -217,7 +260,7 @@ bool i2c_write(uint8_t bus, uint8_t byte)
bool nack;
uint8_t bit;
for (bit = 0; bit < 8; bit++) {
i2c_write_bit(bus,(byte & 0x80) != 0);
i2c_write_bit(bus, (byte & 0x80) != 0);
byte <<= 1;
}
nack = i2c_read_bit(bus);
@ -231,99 +274,93 @@ uint8_t i2c_read(uint8_t bus, bool ack)
for (bit = 0; bit < 8; bit++) {
byte = ((byte << 1)) | (i2c_read_bit(bus));
}
i2c_write_bit(bus,ack);
i2c_write_bit(bus, ack);
return byte;
}
void i2c_force_bus(uint8_t bus, bool state)
{
i2c_bus[bus].force = state ;
i2c_bus[bus].force = state;
}
static int i2c_bus_test(uint8_t bus)
{
taskENTER_CRITICAL(); // To prevent task swaping after checking flag and before set it!
bool status = i2c_bus[bus].flag ; // get current status
if(i2c_bus[bus].force)
{
i2c_bus[bus].flag = true ; // force bus on
bool status = i2c_bus[bus].flag; // get current status
if (i2c_bus[bus].force) {
i2c_bus[bus].flag = true; // force bus on
taskEXIT_CRITICAL();
if(status)
if (status)
i2c_stop(bus); //Bus was busy, stop it.
}
else
{
if (status)
{
else {
if (status) {
taskEXIT_CRITICAL();
debug("busy");
taskYIELD(); // If bus busy, change task to try finish last com.
return -EBUSY ; // If bus busy, inform user
return -EBUSY; // If bus busy, inform user
}
else
{
i2c_bus[bus].flag = true ; // Set Bus busy
else {
i2c_bus[bus].flag = true; // Set Bus busy
taskEXIT_CRITICAL();
}
}
return 0 ;
return 0;
}
int i2c_slave_write(uint8_t bus, uint8_t slave_addr, const uint8_t *data, const uint8_t *buf, uint32_t len)
{
if(i2c_bus_test(bus))
return -EBUSY ;
if (i2c_bus_test(bus))
return -EBUSY;
i2c_start(bus);
if (!i2c_write(bus, slave_addr << 1))
goto error;
if(data != NULL)
if (!i2c_write(bus,*data))
if (data != NULL)
if (!i2c_write(bus, *data))
goto error;
while (len--) {
if (!i2c_write(bus,*buf++))
if (!i2c_write(bus, *buf++))
goto error;
}
if (!i2c_stop(bus))
goto error;
i2c_bus[bus].flag = false ; // Bus free
i2c_bus[bus].flag = false; // Bus free
return 0;
error:
debug("Bus %u Write Error",bus);
error:
debug("Bus %u Write Error", bus);
i2c_stop(bus);
i2c_bus[bus].flag = false ; // Bus free
i2c_bus[bus].flag = false; // Bus free
return -EIO;
}
int i2c_slave_read(uint8_t bus, uint8_t slave_addr, const uint8_t *data, uint8_t *buf, uint32_t len)
{
if(i2c_bus_test(bus))
return -EBUSY ;
if(data != NULL) {
if (i2c_bus_test(bus))
return -EBUSY;
if (data != NULL) {
i2c_start(bus);
if (!i2c_write(bus,slave_addr << 1))
if (!i2c_write(bus, slave_addr << 1))
goto error;
if (!i2c_write(bus,*data))
goto error;
if (!i2c_stop(bus))
if (!i2c_write(bus, *data))
goto error;
}
i2c_start(bus);
if (!i2c_write(bus,slave_addr << 1 | 1)) // Slave address + read
if (!i2c_write(bus, slave_addr << 1 | 1)) // Slave address + read
goto error;
while(len) {
*buf = i2c_read(bus,len == 1);
*buf = i2c_read(bus, len == 1);
buf++;
len--;
}
if (!i2c_stop(bus))
goto error;
i2c_bus[bus].flag = false ; // Bus free
i2c_bus[bus].flag = false; // Bus free
return 0;
error:
error:
debug("Read Error");
i2c_stop(bus);
i2c_bus[bus].flag = false ; // Bus free
i2c_bus[bus].flag = false; // Bus free
return -EIO;
}

View file

@ -21,6 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* I2C driver for ESP8266 written for use with esp-open-rtos
* Based on https://en.wikipedia.org/wiki/I²C#Example_of_bit-banging_the_I.C2.B2C_Master_protocol
*/
#ifndef __I2C_H__
#define __I2C_H__
@ -28,62 +32,38 @@
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <FreeRTOS.h>
#include <task.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Define i2c bus max number
/**
* Define i2c bus max number
*/
#define MAX_I2C_BUS 2
#ifndef I2C_MAX_BUS
#define I2C_MAX_BUS 2
#endif
#define I2C_DEFAULT_CLK_STRETCH (10)
/*
* following array contain value for different frequency
* Warning : 1 is minimal, that mean at 80MHz clock, frequency max is 320kHz
* Array format is { {160MHz, 80MHz} , {160MHz, 80MHz} , ... }
*/
#define NB_FREQ_AVAILABLE 4
typedef enum {
I2C_FREQ_80K = 0,
I2C_FREQ_100K,
I2C_FREQ_400K,
I2C_FREQ_500K,
typedef enum
{
I2C_FREQ_80K = 0,//!< I2C_FREQ_80K
I2C_FREQ_100K, //!< I2C_FREQ_100K
I2C_FREQ_400K, //!< I2C_FREQ_400K
I2C_FREQ_500K, //!< I2C_FREQ_500K
} i2c_freq_t;
const static uint8_t i2c_freq_array[NB_FREQ_AVAILABLE][2] = { {255,35}, {100,20}, {10,1}, {6,1} } ;
/**
* Device descriptor
*/
typedef struct i2c_dev {
uint8_t bus ;
uint8_t addr ;
} i2c_dev_t ;
typedef struct i2c_dev
{
uint8_t bus;
uint8_t addr;
} i2c_dev_t;
/**
* Bus settings
*/
typedef struct i2c_bus_description {
uint8_t g_scl_pin; // Scl pin
uint8_t g_sda_pin; // Sda pin
uint8_t frequency; // frequency selection
bool started;
bool flag;
bool force;
} i2c_bus_description_t ;
// I2C driver for ESP8266 written for use with esp-open-rtos
// Based on https://en.wikipedia.org/wiki/I²C#Example_of_bit-banging_the_I.C2.B2C_Master_protocol
// With calling overhead, we end up at ~320kbit/s
//Level 0 API
/// Level 0 API
/**
* Init bitbanging I2C driver on given pins
@ -91,15 +71,24 @@ typedef struct i2c_bus_description {
* @param scl_pin SCL pin for I2C
* @param sda_pin SDA pin for I2C
* @param freq frequency of bus (ex : I2C_FREQ_400K)
* @param clk_stretch I2C clock stretch. I2C_DEFAULT_CLK_STRETCH would be good in most cases
* @return Non-zero if error occured
*/
void i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq);
int i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq);
/**
* Change bus frequency
* @param bus Bus i2c selection
* @param freq frequency of bus (ex : I2C_FREQ_400K)
*/
void i2c_frequency(uint8_t bus, i2c_freq_t freq);
void i2c_set_frequency(uint8_t bus, i2c_freq_t freq);
/**
* Change clock stretch
* @param bus I2C bus
* @param clk_stretch I2C clock stretch. I2C_DEFAULT_CLK_STRETCH by default
*/
void i2c_set_clock_stretch(uint8_t bus, uint32_t clk_stretch);
/**
* Write a byte to I2C bus.
@ -137,7 +126,7 @@ bool i2c_stop(uint8_t bus);
*/
bool i2c_status(uint8_t bus);
//Level 1 API (Don't need functions above)
/// Level 1 API (Don't need functions above)
/**
* This function will allow you to force a transmission I2C, cancel current transmission.

View file

@ -18,20 +18,20 @@
#define debug(fmt, ...)
#endif
static int _wireWriteRegister (const i2c_dev_t* dev, uint8_t reg, uint16_t value)
static int _wireWriteRegister(const i2c_dev_t *dev, uint8_t reg, uint16_t value)
{
uint8_t d[2] = { 0 , 0 };
d[1] = value & 0x00FF;
uint8_t d[2] = { 0, 0 };
d[1] = value & 0x00FF;
d[0] = (value >> 8) & 0x00FF;
debug("Data write to bus %u at %02X : %02X+%04X\n",dev->bus, dev->addr, reg, value);
debug("Data write to bus %u at %02X : %02X+%04X\n", dev->bus, dev->addr, reg, value);
return i2c_slave_write(dev->bus, dev->addr, &reg, d, sizeof(d));
}
static int _wireReadRegister(const i2c_dev_t* dev, uint8_t reg, uint16_t *value)
static int _wireReadRegister(const i2c_dev_t *dev, uint8_t reg, uint16_t *value)
{
uint8_t d[] = {0, 0};
int error = i2c_slave_read(dev->bus, dev->addr, &reg, d, sizeof(d))
debug("Data read from bus %u at %02X: %02X+%04X\n",dev->bus, dev->addr, reg, *value);
uint8_t d[] = { 0, 0 };
int error = i2c_slave_read(dev->bus, dev->addr, &reg, d, sizeof(d));
debug("Data read from bus %u at %02X: %02X+%04X\n", dev->bus, dev->addr, reg, *value);
*value = d[1] | (d[0] << 8);
return error;
}
@ -53,21 +53,23 @@ int ina3221_sync(ina3221_t *dev)
//////////////////////// Sync config register
if ((err = _wireReadRegister(&dev->i2c_dev, INA3221_REG_CONFIG, &ptr_data))) // Read config
return err;
if( ptr_data != dev->config.config_register) {
if (ptr_data != dev->config.config_register)
{
if ((err = _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register))) // Update config
return err;
}
//////////////////////// Sync mask register config
if ((err = _wireReadRegister(&dev->i2c_dev, INA3221_REG_MASK, &ptr_data))) // Read mask
return err;
if( (ptr_data & INA3221_MASK_CONFIG) != (dev->mask.mask_register & INA3221_MASK_CONFIG)) {
if ((ptr_data & INA3221_MASK_CONFIG) != (dev->mask.mask_register & INA3221_MASK_CONFIG))
{
if ((err = _wireWriteRegister(&dev->i2c_dev, INA3221_REG_MASK, dev->mask.mask_register & INA3221_MASK_CONFIG))) // Update config
return err;
}
return 0;
}
int ina3221_setting(ina3221_t *dev ,bool mode, bool bus, bool shunt)
int ina3221_setting(ina3221_t *dev, bool mode, bool bus, bool shunt)
{
dev->config.mode = mode;
dev->config.ebus = bus;
@ -75,7 +77,7 @@ int ina3221_setting(ina3221_t *dev ,bool mode, bool bus, bool shunt)
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register);
}
int ina3221_enableChannel(ina3221_t *dev ,bool ch1, bool ch2, bool ch3)
int ina3221_enableChannel(ina3221_t *dev, bool ch1, bool ch2, bool ch3)
{
dev->config.ch1 = ch1;
dev->config.ch2 = ch2;
@ -83,7 +85,7 @@ int ina3221_enableChannel(ina3221_t *dev ,bool ch1, bool ch2, bool ch3)
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register);
}
int ina3221_enableChannelSum(ina3221_t *dev ,bool ch1, bool ch2, bool ch3)
int ina3221_enableChannelSum(ina3221_t *dev, bool ch1, bool ch2, bool ch3)
{
dev->mask.scc1 = ch1;
dev->mask.scc2 = ch2;
@ -91,7 +93,7 @@ int ina3221_enableChannelSum(ina3221_t *dev ,bool ch1, bool ch2, bool ch3)
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_MASK, dev->mask.mask_register & INA3221_MASK_CONFIG);
}
int ina3221_enableLatchPin(ina3221_t *dev ,bool warning, bool critical)
int ina3221_enableLatchPin(ina3221_t *dev, bool warning, bool critical)
{
dev->mask.wen = warning;
dev->mask.cen = critical;
@ -104,13 +106,13 @@ int ina3221_setAverage(ina3221_t *dev, ina3221_avg_t avg)
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register);
}
int ina3221_setBusConversionTime(ina3221_t *dev,ina3221_ct_t ct)
int ina3221_setBusConversionTime(ina3221_t *dev, ina3221_ct_t ct)
{
dev->config.vbus = ct;
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register);
}
int ina3221_setShuntConversionTime(ina3221_t *dev,ina3221_ct_t ct)
int ina3221_setShuntConversionTime(ina3221_t *dev, ina3221_ct_t ct)
{
dev->config.vsht = ct;
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register);
@ -118,9 +120,9 @@ int ina3221_setShuntConversionTime(ina3221_t *dev,ina3221_ct_t ct)
int ina3221_reset(ina3221_t *dev)
{
dev->config.config_register = INA3221_DEFAULT_CONFIG ; //dev reset
dev->mask.mask_register = INA3221_DEFAULT_CONFIG ; //dev reset
dev->config.rst = 1 ;
dev->config.config_register = INA3221_DEFAULT_CONFIG; //dev reset
dev->mask.mask_register = INA3221_DEFAULT_CONFIG; //dev reset
dev->config.rst = 1;
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CONFIG, dev->config.config_register); // send reset to device
}
@ -128,74 +130,78 @@ int ina3221_getBusVoltage(ina3221_t *dev, ina3221_channel_t channel, float *volt
{
int16_t raw_value;
int err = 0;
if ((err = _wireReadRegister(&dev->i2c_dev,INA3221_REG_BUSVOLTAGE_1+channel*2, (uint16_t*)&raw_value)))
if ((err = _wireReadRegister(&dev->i2c_dev, INA3221_REG_BUSVOLTAGE_1 + channel * 2, (uint16_t*)&raw_value)))
return err;
*voltage = raw_value*0.001 ; //V 8mV step
return 0;
if (voltage)
*voltage = raw_value * 0.001; //V 8mV step
return 0;
}
int ina3221_getShuntValue(ina3221_t *dev, ina3221_channel_t channel, float *voltage, float *current)
{
int16_t raw_value;
int err = 0;
int err = 0;
if ((err = _wireReadRegister(&dev->i2c_dev,INA3221_REG_SHUNTVOLTAGE_1+channel*2, (uint16_t*)&raw_value)))
return err;
*voltage = raw_value*0.005; //mV 40uV step
float compute = raw_value*0.005; //mV 40uV step
if (voltage)
*voltage = compute;
if(!dev->shunt[channel])
{
debug("No shunt configured for channel %u. Dev:%u:%X\n",channel+1, dev->bus, dev->addr);
debug("No shunt configured for channel %u. Dev:%u:%X\n", channel+1, dev->bus, dev->addr);
return -EINVAL;
}
*current = (*voltage*1000.0)/dev->shunt[channel] ; //mA
return 0;
if (current)
*current = (compute*1000.0)/dev->shunt[channel]; //mA
return 0;
}
int ina3221_getSumShuntValue(ina3221_t *dev, float *voltage)
{
int16_t raw_value;
int err = 0;
if ((err = _wireReadRegister(&dev->i2c_dev,INA3221_REG_SHUNT_VOLTAGE_SUM, (uint16_t*)&raw_value)))
if ((err = _wireReadRegister(&dev->i2c_dev, INA3221_REG_SHUNT_VOLTAGE_SUM, (uint16_t*)&raw_value)))
return err;
*voltage = raw_value*0.02; //uV 40uV step
*voltage = raw_value * 0.02; //uV 40uV step
return 0;
}
int ina3221_setCriticalAlert(ina3221_t *dev, ina3221_channel_t channel, float current)
{
int16_t raw_value = current*dev->shunt[channel]*0.2; // format
return _wireWriteRegister(&dev->i2c_dev,INA3221_REG_CRITICAL_ALERT_1+channel*2, *(uint16_t*)&raw_value);
int16_t raw_value = current * dev->shunt[channel] * 0.2; // format
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_CRITICAL_ALERT_1 + channel * 2, *(uint16_t*)&raw_value);
}
int ina3221_setWarningAlert(ina3221_t *dev, ina3221_channel_t channel, float current)
{
int16_t raw_value = current*dev->shunt[channel]*0.2 ; // format
return _wireWriteRegister(&dev->i2c_dev,INA3221_REG_WARNING_ALERT_1+channel*2, *(uint16_t*)&raw_value);
int16_t raw_value = current * dev->shunt[channel] * 0.2; // format
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_WARNING_ALERT_1 + channel * 2, *(uint16_t*)&raw_value);
}
int ina3221_setSumWarningAlert(ina3221_t *dev, float voltage)
{
int16_t raw_value = voltage*50.0 ; // format
return _wireWriteRegister(&dev->i2c_dev,INA3221_REG_SHUNT_VOLTAGE_SUM_LIMIT, *(uint16_t*)&raw_value);
int16_t raw_value = voltage * 50.0; // format
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_SHUNT_VOLTAGE_SUM_LIMIT, *(uint16_t*)&raw_value);
}
int ina3221_setPowerValidUpperLimit(ina3221_t *dev, float voltage)
{
if(!dev->config.ebus)
if (!dev->config.ebus)
{
debug("Bus not enable. Dev:%u:%X\n", dev->bus, dev->addr);
return -ENOTSUP;
}
int16_t raw_value = voltage*1000.0; //format
return _wireWriteRegister(&dev->i2c_dev,INA3221_REG_VALID_POWER_UPPER_LIMIT, *(uint16_t*)&raw_value);
int16_t raw_value = voltage * 1000.0; //format
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_VALID_POWER_UPPER_LIMIT, *(uint16_t*)&raw_value);
}
int ina3221_setPowerValidLowerLimit(ina3221_t *dev, float voltage)
{
if(!dev->config.ebus)
if (!dev->config.ebus)
{
debug("Bus not enable. Dev:%u:%X\n", dev->bus, dev->addr);
return -ENOTSUP;
}
int16_t raw_value = voltage*1000.0; // round and format
return _wireWriteRegister(&dev->i2c_dev,INA3221_REG_VALID_POWER_LOWER_LIMIT, *(uint16_t*)&raw_value);
int16_t raw_value = voltage * 1000.0; // round and format
return _wireWriteRegister(&dev->i2c_dev, INA3221_REG_VALID_POWER_LOWER_LIMIT, *(uint16_t*)&raw_value);
}

View file

@ -19,14 +19,14 @@
extern "C" {
#endif
#include "i2c/i2c.h"
#include <i2c/i2c.h>
#define INA3221_ADDR_0 (0x40) // A0 to GND
#define INA3221_ADDR_1 (0x41) // A0 to Vs+
#define INA3221_ADDR_2 (0x42) // A0 to SDA
#define INA3221_ADDR_3 (0x43) // A0 to SCL
#define INA3221_ADDR_0 (0x40) ///< A0 to GND
#define INA3221_ADDR_1 (0x41) ///< A0 to Vs+
#define INA3221_ADDR_2 (0x42) ///< A0 to SDA
#define INA3221_ADDR_3 (0x43) ///< A0 to SCL
#define BUS_NUMBER 3 //Number of shunt available
#define INA3221_BUS_NUMBER 3 ///< Number of shunt available
#define INA3221_REG_CONFIG (0x00)
#define INA3221_REG_SHUNTVOLTAGE_1 (0x01)
@ -39,7 +39,7 @@ extern "C" {
#define INA3221_REG_VALID_POWER_UPPER_LIMIT (0x10)
#define INA3221_REG_VALID_POWER_LOWER_LIMIT (0x11)
/*
/**
* Default register after reset
*/
#define INA3221_DEFAULT_CONFIG (0x7127)
@ -48,11 +48,12 @@ extern "C" {
#define INA3221_DEFAULT_POWER_LOWER_LIMIT (0x2328) //9V
#define INA3221_MASK_CONFIG (0x7C00)
/*
* Numbrer of samples
/**
* Number of samples
*/
typedef enum {
INA3221_AVG_1 = 0, //Default
INA3221_AVG_1 = 0, ///< Default
INA3221_AVG_4,
INA3221_AVG_16,
INA3221_AVG_64,
@ -62,7 +63,7 @@ typedef enum {
INA3221_AVG_1024,
} ina3221_avg_t;
/*
/**
* Channel selection list
*/
typedef enum {
@ -71,7 +72,7 @@ typedef enum {
CHANNEL_3,
} ina3221_channel_t;
/*
/**
* Conversion time in us
*/
typedef enum {
@ -79,63 +80,63 @@ typedef enum {
INA3221_CT_204,
INA3221_CT_332,
INA3221_CT_588,
INA3221_CT_1100, //Default
INA3221_CT_1100, ///< Default
INA3221_CT_2116,
INA3221_CT_4156,
INA3221_CT_8244,
} ina3221_ct_t ;
} ina3221_ct_t;
/*
/**
* Config description register
*/
typedef union
{
struct {
uint16_t esht : 1; // Enable/Disable shunt measure // LSB
uint16_t ebus : 1; // Enable/Disable bus measure
uint16_t mode : 1; // Single shot measure or continious mode
uint16_t vsht : 3; // Shunt voltage conversion time
uint16_t vbus : 3; // Bus voltage conversion time
uint16_t avg : 3; // number of sample collected and averaged together
uint16_t ch3 : 1; // Enable/Disable channel 3
uint16_t ch2 : 1; // Enable/Disable channel 2
uint16_t ch1 : 1; // Enable/Disable channel 1
uint16_t rst : 1; //Set this bit to 1 to reset device // MSB
uint16_t esht : 1; ///< Enable/Disable shunt measure // LSB
uint16_t ebus : 1; ///< Enable/Disable bus measure
uint16_t mode : 1; ///< Single shot measure or continious mode
uint16_t vsht : 3; ///< Shunt voltage conversion time
uint16_t vbus : 3; ///< Bus voltage conversion time
uint16_t avg : 3; ///< number of sample collected and averaged together
uint16_t ch3 : 1; ///< Enable/Disable channel 3
uint16_t ch2 : 1; ///< Enable/Disable channel 2
uint16_t ch1 : 1; ///< Enable/Disable channel 1
uint16_t rst : 1; ///< Set this bit to 1 to reset device // MSB
};
uint16_t config_register;
} ina3221_config_t;
/*
/**
* Mask/enable description register
*/
typedef union
{
struct {
uint16_t cvrf : 1 ; // Conversion ready flag (1: ready) // LSB
uint16_t tcf : 1 ; // Timing control flag
uint16_t pvf : 1 ; // Power valid flag
uint16_t wf : 3 ; // Warning alert flag (Read mask to clear) (order : Channel1:channel2:channel3)
uint16_t sf : 1 ; // Sum alert flag (Read mask to clear)
uint16_t cf : 3 ; // Critical alert flag (Read mask to clear) (order : Channel1:channel2:channel3)
uint16_t cen : 1 ; // Critical alert latch (1:enable)
uint16_t wen : 1 ; // Warning alert latch (1:enable)
uint16_t scc3 : 1 ; // channel 3 sum (1:enable)
uint16_t scc2 : 1 ; // channel 2 sum (1:enable)
uint16_t scc1 : 1 ; // channel 1 sum (1:enable)
uint16_t : 1 ; //Reserved //MSB
uint16_t cvrf : 1; ///< Conversion ready flag (1: ready) // LSB
uint16_t tcf : 1; ///< Timing control flag
uint16_t pvf : 1; ///< Power valid flag
uint16_t wf : 3; ///< Warning alert flag (Read mask to clear) (order : Channel1:channel2:channel3)
uint16_t sf : 1; ///< Sum alert flag (Read mask to clear)
uint16_t cf : 3; ///< Critical alert flag (Read mask to clear) (order : Channel1:channel2:channel3)
uint16_t cen : 1; ///< Critical alert latch (1:enable)
uint16_t wen : 1; ///< Warning alert latch (1:enable)
uint16_t scc3 : 1; ///< channel 3 sum (1:enable)
uint16_t scc2 : 1; ///< channel 2 sum (1:enable)
uint16_t scc1 : 1; ///< channel 1 sum (1:enable)
uint16_t : 1; ///< Reserved //MSB
};
uint16_t mask_register;
} ina3221_mask_t;
/*
/**
* Device description
*/
typedef struct {
const i2c_dev_t i2c_dev; // ina3221 I2C address
const uint16_t shunt[BUS_NUMBER]; //Memory of shunt value (mOhm)
ina3221_config_t config; //Memory of ina3221 config
ina3221_mask_t mask; //Memory of mask_config
const i2c_dev_t i2c_dev; ///< ina3221 I2C address
const uint16_t shunt[INA3221_BUS_NUMBER]; ///< Memory of shunt value (mOhm)
ina3221_config_t config; ///< Memory of ina3221 config
ina3221_mask_t mask; ///< Memory of mask_config
} ina3221_t;
/**

View file

@ -21,6 +21,7 @@
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#include <fcntl.h>
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else

View file

@ -11,12 +11,12 @@
#define CMD_EEPROM 0x60
#define BIT_READY 0x80
static void read_data(i2c_dev_t* dev, uint8_t *buf, uint8_t size)
static void read_data(i2c_dev_t *dev, uint8_t *buf, uint8_t size)
{
i2c_slave_read(dev->bus, dev->addr , NULL, buf, size);
i2c_slave_read(dev->bus, dev->addr, NULL, buf, size);
}
bool mcp4725_eeprom_busy(i2c_dev_t* dev)
bool mcp4725_eeprom_busy(i2c_dev_t *dev)
{
uint8_t res;
read_data(dev, &res, 1);
@ -24,7 +24,7 @@ bool mcp4725_eeprom_busy(i2c_dev_t* dev)
return !(res & BIT_READY);
}
mcp4725_power_mode_t mcp4725_get_power_mode(i2c_dev_t* dev, bool eeprom)
mcp4725_power_mode_t mcp4725_get_power_mode(i2c_dev_t *dev, bool eeprom)
{
uint8_t buf[4];
read_data(dev, buf, eeprom ? 4 : 1);
@ -32,7 +32,7 @@ mcp4725_power_mode_t mcp4725_get_power_mode(i2c_dev_t* dev, bool eeprom)
return (eeprom ? buf[3] >> 5 : buf[0] >> 1) & 0x03;
}
void mcp4725_set_power_mode(i2c_dev_t* dev, mcp4725_power_mode_t mode, bool eeprom)
void mcp4725_set_power_mode(i2c_dev_t *dev, mcp4725_power_mode_t mode, bool eeprom)
{
uint16_t value = mcp4725_get_raw_output(dev, eeprom);
uint8_t data[] = {
@ -43,7 +43,7 @@ void mcp4725_set_power_mode(i2c_dev_t* dev, mcp4725_power_mode_t mode, bool eepr
i2c_slave_write(dev->bus, dev->addr, &data[0], &data[1], 2);
}
uint16_t mcp4725_get_raw_output(i2c_dev_t* dev, bool eeprom)
uint16_t mcp4725_get_raw_output(i2c_dev_t *dev, bool eeprom)
{
uint8_t buf[5];
read_data(dev, buf, eeprom ? 5 : 3);
@ -53,7 +53,7 @@ uint16_t mcp4725_get_raw_output(i2c_dev_t* dev, bool eeprom)
: ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
}
void mcp4725_set_raw_output(i2c_dev_t* dev, uint16_t value, bool eeprom)
void mcp4725_set_raw_output(i2c_dev_t *dev, uint16_t value, bool eeprom)
{
uint8_t data[] = {
(eeprom ? CMD_EEPROM : CMD_DAC),

View file

@ -42,7 +42,7 @@ typedef enum
* @param addr Device address
* @return true when EEPROM is busy
*/
bool mcp4725_eeprom_busy(i2c_dev_t* dev);
bool mcp4725_eeprom_busy(i2c_dev_t *dev);
/**
* Get power mode
@ -50,7 +50,7 @@ bool mcp4725_eeprom_busy(i2c_dev_t* dev);
* @param eeprom Read power mode from EEPROM if true
* @return Power mode
*/
mcp4725_power_mode_t mcp4725_get_power_mode(i2c_dev_t* dev, bool eeprom);
mcp4725_power_mode_t mcp4725_get_power_mode(i2c_dev_t *dev, bool eeprom);
/**
* Set power mode
@ -58,7 +58,7 @@ mcp4725_power_mode_t mcp4725_get_power_mode(i2c_dev_t* dev, bool eeprom);
* @param mode Power mode
* @param eeprom Store mode to device EEPROM if true
*/
void mcp4725_set_power_mode(i2c_dev_t* dev, mcp4725_power_mode_t mode, bool eeprom);
void mcp4725_set_power_mode(i2c_dev_t *dev, mcp4725_power_mode_t mode, bool eeprom);
/**
* Get current DAC value
@ -66,7 +66,7 @@ void mcp4725_set_power_mode(i2c_dev_t* dev, mcp4725_power_mode_t mode, bool eepr
* @param eeprom Read value from device EEPROM if true
* @return Raw output value, 0..4095
*/
uint16_t mcp4725_get_raw_output(i2c_dev_t* dev, bool eeprom);
uint16_t mcp4725_get_raw_output(i2c_dev_t *dev, bool eeprom);
/**
* Set DAC output value
@ -74,7 +74,7 @@ uint16_t mcp4725_get_raw_output(i2c_dev_t* dev, bool eeprom);
* @param value Raw output value, 0..4095
* @param eeprom Store value to device EEPROM if true
*/
void mcp4725_set_raw_output(i2c_dev_t* dev, uint16_t value, bool eeprom);
void mcp4725_set_raw_output(i2c_dev_t *dev, uint16_t value, bool eeprom);
/**
* Get current DAC output voltage
@ -83,7 +83,7 @@ void mcp4725_set_raw_output(i2c_dev_t* dev, uint16_t value, bool eeprom);
* @param eeprom Read voltage from device EEPROM if true
* @return Current output voltage, volts
*/
inline float mcp4725_get_voltage(i2c_dev_t* dev, float vdd, bool eeprom)
inline float mcp4725_get_voltage(i2c_dev_t *dev, float vdd, bool eeprom)
{
return vdd / MCP4725_MAX_VALUE * mcp4725_get_raw_output(dev, eeprom);
}
@ -95,7 +95,7 @@ inline float mcp4725_get_voltage(i2c_dev_t* dev, float vdd, bool eeprom)
* @param value Output value, volts
* @param eeprom Store value to device EEPROM if true
*/
inline void mcp4725_set_voltage(i2c_dev_t* dev, float vdd, float value, bool eeprom)
inline void mcp4725_set_voltage(i2c_dev_t *dev, float vdd, float value, bool eeprom)
{
mcp4725_set_raw_output(dev, MCP4725_MAX_VALUE / vdd * value, eeprom);
}

View file

@ -25,6 +25,7 @@
#include <lwip/sys.h>
#include <lwip/netdb.h>
#include <lwip/dns.h>
#include <lwip/prot/dns.h>
#include <lwip/udp.h>
#include <lwip/igmp.h>
#include <lwip/netif.h>
@ -39,6 +40,7 @@
#define DNS_MULTICAST_ADDRESS "224.0.0.251" // RFC 6762
#define DNS_MDNS_PORT 5353 // RFC 6762
#define DNS_MSG_SIZE 512
//-------------------------------------------------------------------
@ -100,7 +102,7 @@ PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
#define vTaskDelayMs(ms) vTaskDelay((ms)/portTICK_PERIOD_MS)
#define UNUSED_ARG(x) (void)x
#define kDummyDataSize 8 // arbitrary, dynamically resized
@ -145,7 +147,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
#define DNS_FLAG2_RESMASK 0x0F
static char qstr[12];
static char* mdns_qrtype(uint16_t typ)
{
switch(typ) {
@ -161,9 +163,9 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
sprintf(qstr,"type %d",typ);
return qstr;
}
#ifdef qLogAllTraffic
static void mdns_printhex(u8_t* p, int n)
{
int i;
@ -178,14 +180,14 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
{
int i, n;
char* cp;
n = *p++;
cp = (char*)p;
for (i=0; i<n; i++) putchar(*cp++);
}
static char cstr[16];
static char* mdns_qclass(uint16_t cls)
{
switch(cls) {
@ -202,7 +204,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
{
char* cp = (char*)p;
int i, n;
do {
n = *cp++;
if ((n & 0xC0) == 0xC0) {
@ -222,7 +224,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
return (u8_t*)cp;
}
static u8_t* mdns_print_header(struct mdns_hdr* hdr)
{
if (hdr->flags1 & DNS_FLAG1_RESP) {
@ -235,7 +237,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
}
if (hdr->flags1 & DNS_FLAG1_RD) printf("RD ");
if (hdr->flags1 & DNS_FLAG1_TRUNC) printf("[TRUNC] ");
printf(": %d questions", htons(hdr->numquestions) );
if (hdr->numanswers != 0)
printf(", %d answers",htons(hdr->numanswers));
@ -252,7 +254,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
{
struct mdns_query q;
uint16_t c;
memcpy(&q,p,SIZEOF_DNS_QUERY);
c = htons(q.class);
printf(" %s %s", mdns_qrtype(htons(q.type)), mdns_qclass(c & 0x7FFF) );
@ -266,7 +268,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
{
struct mdns_answer ans;
u16_t rrlen, atype, rrClass;;
memcpy(&ans,p,SIZEOF_DNS_ANSWER);
atype = htons(ans.type);
rrlen = htons(ans.len);
@ -305,7 +307,7 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
u8_t* tp;
u8_t* limP = msgP + msgLen;
struct mdns_hdr* hdr;
hdr = (struct mdns_hdr*) msgP;
tp = mdns_print_header(hdr);
for (i=0; i<htons(hdr->numquestions); i++) {
@ -314,21 +316,21 @@ static mdns_rsrc* gDictP = NULL; // RR database, linked list
tp = mdns_print_query(tp);
if (tp > limP) return 0;
}
for (i=0; i<htons(hdr->numanswers); i++) {
printf(" A%d: ",i+1);
tp = mdns_print_name(tp,hdr);
tp = mdns_print_answer(tp,hdr);
if (tp > limP) return 0;
}
for (i=0; i<htons(hdr->numauthrr); i++) {
printf(" AuRR%d: ",i+1);
tp = mdns_print_name(tp,hdr);
tp = mdns_print_answer(tp,hdr);
if (tp > limP) return 0;
}
for (i=0; i<htons(hdr->numextrarr); i++) {
printf(" ExRR%d: ",i+1);
tp = mdns_print_name(tp,hdr);
@ -348,7 +350,7 @@ static u8_t* mdns_labels2str(u8_t* hdrP, u8_t* p, char* qStr)
// Handles compression
{
int i, n;
do {
n = *p++;
if ((n & 0xC0) == 0xC0) {
@ -374,7 +376,7 @@ static int mdns_str2labels(const char* name, u8_t* lseq, int max)
{
int i,n,sdx,idx = 0;
int lc = 0;
do {
sdx = idx;
while (name[idx] != '.' && name[idx] != 0) idx++;
@ -399,7 +401,7 @@ static u8_t* mdns_get_question(u8_t* hdrP, u8_t* qp, char* qStr, uint16_t* qClas
{
struct mdns_query qr;
uint16_t cls;
qp = mdns_labels2str(hdrP, qp, qStr);
memcpy(&qr,qp,SIZEOF_DNS_QUERY);
*qType = htons(qr.type);
@ -417,7 +419,7 @@ static void mdns_add_response(const char* vKey, u16_t vType, u32_t ttl, const vo
{
mdns_rsrc* rsrcP;
int keyLen, recSize;
keyLen = strlen(vKey) + 1;
recSize = sizeof(mdns_rsrc) - kDummyDataSize + keyLen + vDataSize;
rsrcP = (mdns_rsrc*)malloc(recSize);
@ -442,7 +444,7 @@ void mdns_add_PTR(const char* rKey, u32_t ttl, const char* nmStr)
{
int nl;
u8_t lBuff[kMaxNameSize];
nl = mdns_str2labels(nmStr,lBuff,sizeof(lBuff));
if (nl>0)
mdns_add_response(rKey, DNS_RRTYPE_PTR, ttl, lBuff, nl);
@ -454,10 +456,10 @@ void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targName
struct mdns_rr_srv srvRR;
u8_t lBuff[kMaxNameSize];
} __attribute__((packed)) SrvRec;
int nl;
SrvRec temp;
temp.srvRR.prio = 0;
temp.srvRR.weight = 0;
temp.srvRR.port = htons(rPort);
@ -480,46 +482,45 @@ void mdns_add_TXT(const char* rKey, u32_t ttl, const char* txStr)
}
}
void mdns_add_A(const char* rKey, u32_t ttl, struct ip_addr addr)
void mdns_add_A(const char* rKey, u32_t ttl, ip_addr_t addr)
{
mdns_add_response(rKey, DNS_RRTYPE_A, ttl, &addr, sizeof(addr));
}
void mdns_add_facility( const char* instanceName, // Friendly name, need not be unique
const char* serviceName, // Must be _name
const char* serviceName, // Must be "name", e.g. "hap" or "http"
const char* addText, // Must be <key>=<value>
mdns_flags flags, // TCP or UDP
u16_t onPort, // port number
u32_t ttl // seconds
mdns_flags flags, // TCP or UDP
u16_t onPort, // port number
u32_t ttl // seconds
)
{
char key[64];
char fullName[128];
char devName[96];
struct ip_info ipInfo;
#ifdef qDebugLog
printf("\nmDNS advertising instance %s protocol %s text %s on port %d %s TTL %d secs\n",
instanceName,serviceName,addText,onPort,(flags & mdns_UDP) ? "UDP" : "TCP", ttl);
instanceName, serviceName, addText, onPort, (flags & mdns_UDP) ? "UDP" : "TCP", ttl);
#endif
snprintf(key,sizeof(key),"%s.%s.local.",serviceName,(flags & mdns_UDP) ? "_udp" :"_tcp");
snprintf(fullName,sizeof(fullName),"%s.%s",instanceName,key);
snprintf(devName,sizeof(devName),"%s.local.",instanceName);
snprintf(key, sizeof(key), "%s.%s.local.", serviceName, (flags & mdns_UDP) ? "_udp" :"_tcp");
snprintf(fullName, sizeof(fullName), "%s.%s", instanceName, key);
snprintf(devName, sizeof(devName), "%s.local.", instanceName);
if (!sdk_wifi_get_ip_info(STATION_IF,&ipInfo))
ipInfo.ip.addr = IPADDR_NONE;
// Order has significance for extraRR feature
mdns_add_TXT(fullName,ttl,addText);
mdns_add_A(devName,ttl,ipInfo.ip);
mdns_add_SRV(fullName,ttl,onPort,devName);
mdns_add_PTR(key,ttl,fullName);
mdns_add_TXT(fullName, ttl, addText);
mdns_add_A(devName, ttl, ipInfo.ip);
mdns_add_SRV(fullName, ttl, onPort, devName);
mdns_add_PTR(key, ttl, fullName);
// Optional, makes us browsable
if (flags & mdns_Browsable)
mdns_add_PTR("_services._dns-sd._udp.local.",ttl,key);
}
static void mdns_update_ipaddr(struct ip_info* ipInfo)
@ -573,7 +574,7 @@ static int mdns_add_to_answer(mdns_rsrc* rsrcP, u8_t* resp, int respLen)
// Data for this key
memcpy(&resp[respLen], &rsrcP->rData[rsrcP->rKeySize], rsrcP->rDataSize);
respLen += rsrcP->rDataSize;
return respLen;
}
@ -599,7 +600,7 @@ static void mdns_send_mcast(u8_t* msgP, int nBytes)
} else
printf(">>> mdns_send: alloc failed[%d]\n",nBytes);
}
static void mdns_reply(struct mdns_hdr* hdrP)
// Message has passed tests, may want to send an answer
{
@ -609,13 +610,13 @@ static void mdns_reply(struct mdns_hdr* hdrP)
u8_t* qBase = (u8_t*)hdrP;
u8_t* qp;
u8_t* mdns_response;
mdns_response = malloc(DNS_MSG_SIZE);
if (mdns_response==NULL) {
printf(">>> mdns_reply could not alloc %d\n",DNS_MSG_SIZE);
return;
}
// Build response header
rHdr = (struct mdns_hdr*) mdns_response;
rHdr->id = hdrP->id;
@ -630,28 +631,28 @@ static void mdns_reply(struct mdns_hdr* hdrP)
extra = NULL;
qp = qBase + SIZEOF_DNS_HDR;
nquestions = htons(hdrP->numquestions);
for (i=0; i<nquestions; i++) {
char qStr[kMaxQStr];
u16_t qClass, qType;
u8_t qUnicast;
mdns_rsrc* rsrcP;
qp = mdns_get_question(qBase, qp, qStr, &qClass, &qType, &qUnicast);
if (qClass==DNS_RRCLASS_IN || qClass==DNS_RRCLASS_ANY) {
rsrcP = mdns_match(qStr,qType);
rsrcP = mdns_match(qStr, qType);
if (rsrcP) {
respLen = mdns_add_to_answer(rsrcP, mdns_response, respLen);
rHdr->numanswers = htons( htons(rHdr->numanswers) + 1 );
// Extra RR logic: if SRV follows PTR, or A follows SRV, volunteer it in extraRR
// Not required, but could do more here, see RFC6763 s12
if (qType==DNS_RRTYPE_PTR) {
if (rsrcP->rNext && rsrcP->rNext->rType==DNS_RRTYPE_SRV)
if (rsrcP->rNext && rsrcP->rNext->rType==DNS_RRTYPE_SRV)
extra = rsrcP->rNext;
} else if (qType==DNS_RRTYPE_SRV) {
if (rsrcP->rNext && rsrcP->rNext->rType==DNS_RRTYPE_A)
if (rsrcP->rNext && rsrcP->rNext->rType==DNS_RRTYPE_A)
extra = rsrcP->rNext;
}
}
}
}
} // for nQuestions
@ -661,20 +662,20 @@ static void mdns_reply(struct mdns_hdr* hdrP)
respLen = mdns_add_to_answer(extra, mdns_response, respLen);
rHdr->numextrarr = htons( htons(rHdr->numextrarr) + 1 );
}
mdns_send_mcast(mdns_response,respLen);
mdns_send_mcast(mdns_response, respLen);
}
free(mdns_response);
}
static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
// Callback from udp_recv
{
UNUSED_ARG(pcb);
UNUSED_ARG(port);
u8_t* mdns_payload;
int plen;
// Sanity checks on size
plen = p->tot_len;
if (plen > DNS_MSG_SIZE) {
@ -691,12 +692,12 @@ static void mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_
else {
if (pbuf_copy_partial(p, mdns_payload, plen, 0) == plen) {
struct mdns_hdr* hdrP = (struct mdns_hdr*) mdns_payload;
#ifdef qLogAllTraffic
mdns_print_msg(mdns_payload, plen);
#endif
if ( (hdrP->flags1 & (DNS_FLAG1_RESP + DNS_FLAG1_OPMASK + DNS_FLAG1_TRUNC) ) == 0
if ( (hdrP->flags1 & (DNS_FLAG1_RESP + DNS_FLAG1_OPMASK + DNS_FLAG1_TRUNC) ) == 0
&& hdrP->numquestions > 0 )
mdns_reply(hdrP);
}
@ -711,19 +712,19 @@ static void mdns_start()
{
struct ip_info ipInfo;
err_t err;
if (sdk_wifi_get_opmode() != STATION_MODE) {
printf(">>> mDNS_start: wifi opmode not station\n");
return;
}
if (!sdk_wifi_get_ip_info(STATION_IF,&ipInfo)) {
printf(">>> mDNS_start: no IP addr\n");
return;
}
mdns_update_ipaddr(&ipInfo);
// Start IGMP on the netif for our interface: this isn't done for us
struct netif* nfp = netif_list;
while (nfp!=NULL) {
@ -739,23 +740,23 @@ static void mdns_start()
}
nfp = nfp->next;
}
gMDNS_pcb = udp_new();
if (!gMDNS_pcb) {
printf(">>> mDNS_start: udp_new failed\n");
return;
}
if ((err=igmp_joingroup(&ipInfo.ip, &gMulticastAddr)) != ERR_OK) {
printf(">>> mDNS_start: igmp_join failed %d\n",err);
return;
}
if ((err=udp_bind(gMDNS_pcb, IP_ADDR_ANY, DNS_MDNS_PORT)) != ERR_OK) {
printf(">>> mDNS_start: udp_bind failed %d\n",err);
return;
}
udp_recv(gMDNS_pcb, mdns_recv, NULL);
}

View file

@ -1,9 +1,11 @@
#ifndef __MDNSRESPONDER_H__
#define __MDNSRESPONDER_H__
#include <lwip/ip_addr.h>
/*
* Basic multicast DNS responder
*
*
* Advertises the IP address, port, and characteristics of a service to other devices using multicast DNS on the same LAN,
* so they can find devices with addresses dynamically allocated by DHCP. See avahi, Bonjour, etc
* See RFC6762, RFC6763
@ -26,7 +28,7 @@ typedef enum {
mdns_UDP,
mdns_Browsable // see RFC6763:11 - adds a standard record that lets browsers find the service without needing to know its name
} mdns_flags;
void mdns_add_facility( const char* instanceName, // Short user-friendly instance name, should NOT include serial number/MAC/etc
const char* serviceName, // Must be registered, _name, (see RFC6335 5.1 & 5.2)
const char* addText, // Should be <key>=<value>, or "" if unused (see RFC6763 6.3)
@ -40,13 +42,13 @@ void mdns_add_facility( const char* instanceName, // Short user-friendly insta
void mdns_add_PTR(const char* rKey, u32_t ttl, const char* nameStr);
void mdns_add_SRV(const char* rKey, u32_t ttl, u16_t rPort, const char* targname);
void mdns_add_TXT(const char* rKey, u32_t ttl, const char* txtStr);
void mdns_add_A (const char* rKey, u32_t ttl, struct ip_addr addr);
void mdns_add_A (const char* rKey, u32_t ttl, ip_addr_t addr);
/* Sample usage, advertising a secure web service
mdns_init();
mdns_add_facility("Fluffy", "_https", "Zoom=1", mdns_TCP+mdns_Browsable, 443, 600);
*/
#endif

View file

@ -25,7 +25,7 @@
*/
#define CONVERSION_TIME 20 / portTICK_PERIOD_MS // milliseconds
static inline int reset(i2c_dev_t* i2c_dev)
static inline int reset(i2c_dev_t *i2c_dev)
{
uint8_t buf[1] = { RESET };
return i2c_slave_write(i2c_dev->bus, i2c_dev->addr, NULL, buf, 1);
@ -34,33 +34,33 @@ static inline int reset(i2c_dev_t* i2c_dev)
static inline bool read_prom(ms561101ba03_t *dev)
{
uint8_t tmp[2] = { 0, 0 };
uint8_t reg = 0xA2 ;
uint8_t reg = 0xA2;
if (i2c_slave_read(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, tmp, 2))
return false;
dev->config_data.sens = tmp[0] << 8 | tmp[1];
reg = 0xA4 ;
reg = 0xA4;
if (i2c_slave_read(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, tmp, 2))
return false;
dev->config_data.off = tmp[0] << 8 | tmp[1];
reg = 0xA6 ;
reg = 0xA6;
if (i2c_slave_read(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, tmp, 2))
return false;
dev->config_data.tcs = tmp[0] << 8 | tmp[1];
reg = 0xA8 ;
reg = 0xA8;
if (i2c_slave_read(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, tmp, 2))
return false;
dev->config_data.tco = tmp[0] << 8 | tmp[1];
reg = 0xAA ;
reg = 0xAA;
if (i2c_slave_read(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, tmp, 2))
return false;
dev->config_data.t_ref = tmp[0] << 8 | tmp[1];
reg = 0xAC ;
reg = 0xAC;
if (i2c_slave_read(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, tmp, 2))
return false;
dev->config_data.tempsens = tmp[0] << 8 | tmp[1];
@ -80,11 +80,11 @@ static inline int start_temperature_conversion(ms561101ba03_t *dev) //D2
return i2c_slave_write(dev->i2c_dev.bus, dev->i2c_dev.addr, NULL, &buf, 1);
}
static inline bool read_adc(i2c_dev_t* i2c_dev, uint32_t *result)
static inline bool read_adc(i2c_dev_t *i2c_dev, uint32_t *result)
{
*result = 0;
uint8_t tmp[3];
uint8_t reg = 0x00 ;
uint8_t reg = 0x00;
if (i2c_slave_read(i2c_dev->bus, i2c_dev->addr, &reg, tmp, 3))
return false;

View file

@ -61,7 +61,7 @@ typedef struct
*/
typedef struct
{
i2c_dev_t i2c_dev; //!< I2C device settings
i2c_dev_t i2c_dev; //!< I2C device settings
ms561101ba03_osr_t osr; //!< Oversampling setting
ms561101ba03_config_data_t config_data; //!< Device configuration, filled upon initalize
ms561101ba03_result_t result; //!< Result, filled upon co

View file

@ -108,7 +108,7 @@ bool onewire_write(int pin, uint8_t v) {
bool onewire_write_bytes(int pin, const uint8_t *buf, size_t count) {
size_t i;
for (i = 0 ; i < count ; i++) {
for (i = 0; i < count; i++) {
if (!onewire_write(pin, buf[i])) {
return false;
}
@ -138,7 +138,7 @@ bool onewire_read_bytes(int pin, uint8_t *buf, size_t count) {
size_t i;
int b;
for (i = 0 ; i < count ; i++) {
for (i = 0; i < count; i++) {
b = onewire_read(pin);
if (b < 0) return false;
buf[i] = b;
@ -434,7 +434,7 @@ uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) {
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
uint16_t i;
for (i = 0 ; i < len ; i++) {
for (i = 0; i < len; i++) {
// Even though we're just copying a byte from the input,
// we'll be doing 16-bit computation with it.
uint16_t cdata = input[i];

View file

@ -20,6 +20,7 @@
*/
#include <espressif/esp_common.h>
#include <unistd.h>
#include <lwip/sockets.h>
#include <lwip/inet.h>
#include <lwip/netdb.h>

View file

@ -58,13 +58,13 @@ inline static uint32_t round_div(uint32_t x, uint32_t y)
return (x + y / 2) / y;
}
inline static void write_reg(i2c_dev_t* dev, uint8_t reg, uint8_t val)
inline static void write_reg(i2c_dev_t *dev, uint8_t reg, uint8_t val)
{
if (i2c_slave_write(dev->bus, dev->addr, &reg, &val, 1))
debug("Could not write 0x%02x to 0x%02x, bus %u, addr = 0x%02x", reg, val, dev->bus, dev->addr);
}
inline static uint8_t read_reg(i2c_dev_t* dev, uint8_t reg)
inline static uint8_t read_reg(i2c_dev_t *dev, uint8_t reg)
{
uint8_t res = 0;
if (i2c_slave_read(dev->bus, dev->addr, &reg, &res, 1))
@ -72,18 +72,18 @@ inline static uint8_t read_reg(i2c_dev_t* dev, uint8_t reg)
return res;
}
inline static void update_reg(i2c_dev_t* dev, uint8_t reg, uint8_t mask, uint8_t val)
inline static void update_reg(i2c_dev_t *dev, uint8_t reg, uint8_t mask, uint8_t val)
{
write_reg(dev, reg, (read_reg(dev, reg) & ~mask) | val);
}
void pca9685_init(i2c_dev_t* dev)
void pca9685_init(i2c_dev_t *dev)
{
// Enable autoincrement
update_reg(dev, REG_MODE1, MODE1_AI, MODE1_AI);
}
bool pca9685_set_subaddr(i2c_dev_t* dev, uint8_t num, uint8_t subaddr, bool enable)
bool pca9685_set_subaddr(i2c_dev_t *dev, uint8_t num, uint8_t subaddr, bool enable)
{
if (num > MAX_SUBADDR)
{
@ -99,19 +99,19 @@ bool pca9685_set_subaddr(i2c_dev_t* dev, uint8_t num, uint8_t subaddr, bool enab
return true;
}
bool pca9685_is_sleeping(i2c_dev_t* dev)
bool pca9685_is_sleeping(i2c_dev_t *dev)
{
return (read_reg(dev, REG_MODE1) & MODE1_SLEEP) != 0;
}
void pca9685_sleep(i2c_dev_t* dev, bool sleep)
void pca9685_sleep(i2c_dev_t *dev, bool sleep)
{
update_reg(dev, REG_MODE1, MODE1_SLEEP, sleep ? MODE1_SLEEP : 0);
if (!sleep)
sdk_os_delay_us(WAKEUP_DELAY_US);
}
void pca9685_restart(i2c_dev_t* dev)
void pca9685_restart(i2c_dev_t *dev)
{
uint8_t mode = read_reg(dev, REG_MODE1);
if (mode & MODE1_RESTART)
@ -122,32 +122,32 @@ void pca9685_restart(i2c_dev_t* dev)
write_reg(dev, REG_MODE1, (mode & ~MODE1_SLEEP) | MODE1_RESTART);
}
bool pca9685_is_output_inverted(i2c_dev_t* dev)
bool pca9685_is_output_inverted(i2c_dev_t *dev)
{
return (read_reg(dev, REG_MODE2) & MODE2_INVRT) != 0;
}
void pca9685_set_output_inverted(i2c_dev_t* dev, bool inverted)
void pca9685_set_output_inverted(i2c_dev_t *dev, bool inverted)
{
update_reg(dev, REG_MODE2, MODE2_INVRT, inverted ? MODE2_INVRT : 0);
}
bool pca9685_get_output_open_drain(i2c_dev_t* dev)
bool pca9685_get_output_open_drain(i2c_dev_t *dev)
{
return (read_reg(dev, REG_MODE2) & MODE2_OUTDRV) == 0;
}
void pca9685_set_output_open_drain(i2c_dev_t* dev, bool open_drain)
void pca9685_set_output_open_drain(i2c_dev_t *dev, bool open_drain)
{
update_reg(dev, REG_MODE2, MODE2_OUTDRV, open_drain ? 0 : MODE2_OUTDRV);
}
uint8_t pca9685_get_prescaler(i2c_dev_t* dev)
uint8_t pca9685_get_prescaler(i2c_dev_t *dev)
{
return read_reg(dev, REG_PRE_SCALE);
}
bool pca9685_set_prescaler(i2c_dev_t* dev, uint8_t prescaler)
bool pca9685_set_prescaler(i2c_dev_t *dev, uint8_t prescaler)
{
if (prescaler < MIN_PRESCALER)
{
@ -161,12 +161,12 @@ bool pca9685_set_prescaler(i2c_dev_t* dev, uint8_t prescaler)
return true;
}
uint16_t pca9685_get_pwm_frequency(i2c_dev_t* dev)
uint16_t pca9685_get_pwm_frequency(i2c_dev_t *dev)
{
return INTERNAL_FREQ / ((uint32_t)4096 * (read_reg(dev, REG_PRE_SCALE) + 1));
}
bool pca9685_set_pwm_frequency(i2c_dev_t* dev, uint16_t freq)
bool pca9685_set_pwm_frequency(i2c_dev_t *dev, uint16_t freq)
{
uint16_t prescaler = round_div(INTERNAL_FREQ, (uint32_t)4096 * freq) - 1;
if (prescaler < MIN_PRESCALER || prescaler > MAX_PRESCALER)
@ -178,7 +178,7 @@ bool pca9685_set_pwm_frequency(i2c_dev_t* dev, uint16_t freq)
return pca9685_set_prescaler(dev, prescaler);
}
void pca9685_set_pwm_value(i2c_dev_t* dev, uint8_t channel, uint16_t val)
void pca9685_set_pwm_value(i2c_dev_t *dev, uint8_t channel, uint16_t val)
{
uint8_t reg = channel > MAX_CHANNEL ? REG_ALL_LED : REG_LED_N(channel);
@ -200,7 +200,7 @@ void pca9685_set_pwm_value(i2c_dev_t* dev, uint8_t channel, uint16_t val)
}
}
bool pca9685_set_pwm_values(i2c_dev_t* dev, uint8_t first_ch, uint8_t channels, const uint16_t *values)
bool pca9685_set_pwm_values(i2c_dev_t *dev, uint8_t first_ch, uint8_t channels, const uint16_t *values)
{
if (channels == 0 || first_ch + channels - 1 > MAX_CHANNEL)
{

View file

@ -23,7 +23,7 @@ extern "C"
* Init device
* @param addr Device address
*/
void pca9685_init(i2c_dev_t* dev);
void pca9685_init(i2c_dev_t *dev);
/**
* Setup device subaddress (see section 7.3.6 if the datasheet)
@ -33,62 +33,62 @@ void pca9685_init(i2c_dev_t* dev);
* @param enable True to enable subaddress, false to disable
* @return False if error occured
*/
bool pca9685_set_subaddr(i2c_dev_t* dev, uint8_t num, uint8_t subaddr, bool enable);
bool pca9685_set_subaddr(i2c_dev_t *dev, uint8_t num, uint8_t subaddr, bool enable);
/**
* Restart device (see section 7.3.1.1 of the datasheet)
* @param addr Device address
*/
void pca9685_restart(i2c_dev_t* dev);
void pca9685_restart(i2c_dev_t *dev);
/**
* Check if device is in sleep mode
* @param addr Device address
* @return True if device is sleeping
*/
bool pca9685_is_sleeping(i2c_dev_t* dev);
bool pca9685_is_sleeping(i2c_dev_t *dev);
/**
* Switch device to low-power mode or wake it up.
* @param addr Device address
* @param sleep True for sleep mode, false for wake up
*/
void pca9685_sleep(i2c_dev_t* dev, bool sleep);
void pca9685_sleep(i2c_dev_t *dev, bool sleep);
/**
* Get logic inversion of the outputs
* @param addr Device address
* @return True if outputs are inverted, false otherwise
*/
bool pca9685_is_output_inverted(i2c_dev_t* dev);
bool pca9685_is_output_inverted(i2c_dev_t *dev);
/**
* Logically invert outputs (see section 7.7 of the datasheet)
* @param addr Device address
* @param inverted True for inverted outputs
*/
void pca9685_set_output_inverted(i2c_dev_t* dev, bool inverted);
void pca9685_set_output_inverted(i2c_dev_t *dev, bool inverted);
/**
* Get outputs mode
* @param addr Device address
* @return True if outputs are in open drain mode
*/
bool pca9685_get_output_open_drain(i2c_dev_t* dev);
bool pca9685_get_output_open_drain(i2c_dev_t *dev);
/**
* Set outputs mode
* @param addr Device address
* @param open_drain True to set open drain mode, false to normal mode
*/
void pca9685_set_output_open_drain(i2c_dev_t* dev, bool open_drain);
void pca9685_set_output_open_drain(i2c_dev_t *dev, bool open_drain);
/**
* Get current PWM frequency prescaler.
* @param addr Device address
* @return Frequency prescaler
*/
uint8_t pca9685_get_prescaler(i2c_dev_t* dev);
uint8_t pca9685_get_prescaler(i2c_dev_t *dev);
/**
* Set PWM frequency prescaler.
@ -96,14 +96,14 @@ uint8_t pca9685_get_prescaler(i2c_dev_t* dev);
* @param prescaler Prescaler value
* @return False if error occured
*/
bool pca9685_set_prescaler(i2c_dev_t* dev, uint8_t prescaler);
bool pca9685_set_prescaler(i2c_dev_t *dev, uint8_t prescaler);
/**
* Get current PWM frequency
* @param addr Device address
* @return PWM frequency, Hz
*/
uint16_t pca9685_get_pwm_frequency(i2c_dev_t* dev);
uint16_t pca9685_get_pwm_frequency(i2c_dev_t *dev);
/**
* Set PWM frequency
@ -111,7 +111,7 @@ uint16_t pca9685_get_pwm_frequency(i2c_dev_t* dev);
* @param freq PWM frequency, Hz
* @return False if error occured
*/
bool pca9685_set_pwm_frequency(i2c_dev_t* dev, uint16_t freq);
bool pca9685_set_pwm_frequency(i2c_dev_t *dev, uint16_t freq);
/**
* Set PWM value on output channel
@ -119,7 +119,7 @@ bool pca9685_set_pwm_frequency(i2c_dev_t* dev, uint16_t freq);
* @param channel Channel number, 0..15 or >15 for all channels
* @param val PWM value, 0..4096
*/
void pca9685_set_pwm_value(i2c_dev_t* dev, uint8_t channel, uint16_t val);
void pca9685_set_pwm_value(i2c_dev_t *dev, uint8_t channel, uint16_t val);
/**
* Set PWM values on output channels
@ -129,7 +129,7 @@ void pca9685_set_pwm_value(i2c_dev_t* dev, uint8_t channel, uint16_t val);
* @param values Array of the channel values, each 0..4096
* @return False if error occured
*/
bool pca9685_set_pwm_values(i2c_dev_t* dev, uint8_t first_ch, uint8_t channels, const uint16_t *values);
bool pca9685_set_pwm_values(i2c_dev_t *dev, uint8_t first_ch, uint8_t channels, const uint16_t *values);
#ifdef __cplusplus
}

View file

@ -1,14 +1,14 @@
#include "pcf8574.h"
uint8_t pcf8574_port_read(i2c_dev_t* dev)
uint8_t pcf8574_port_read(i2c_dev_t *dev)
{
uint8_t res;
if (i2c_slave_read(dev->bus, dev->addr, NULL, &res, 1))
return 0;
return 0;
return res;
}
size_t pcf8574_port_read_buf(i2c_dev_t* dev, void *buf, size_t len)
size_t pcf8574_port_read_buf(i2c_dev_t *dev, void *buf, size_t len)
{
if (!len || !buf) return 0;
uint8_t *_buf = (uint8_t *)buf;
@ -18,7 +18,7 @@ size_t pcf8574_port_read_buf(i2c_dev_t* dev, void *buf, size_t len)
return len;
}
size_t pcf8574_port_write_buf(const i2c_dev_t* dev, void *buf, size_t len)
size_t pcf8574_port_write_buf(const i2c_dev_t *dev, void *buf, size_t len)
{
if (!len || !buf) return 0;
uint8_t *_buf = (uint8_t *)buf;
@ -28,19 +28,19 @@ size_t pcf8574_port_write_buf(const i2c_dev_t* dev, void *buf, size_t len)
return len;
}
void pcf8574_port_write(const i2c_dev_t* dev, uint8_t value)
void pcf8574_port_write(const i2c_dev_t *dev, uint8_t value)
{
i2c_slave_write(dev->bus, dev->addr, NULL, &value, 1);
}
bool pcf8574_gpio_read(i2c_dev_t* dev, uint8_t num)
bool pcf8574_gpio_read(i2c_dev_t *dev, uint8_t num)
{
return (bool)((pcf8574_port_read(dev) >> num) & 1);
}
void pcf8574_gpio_write(i2c_dev_t* dev, uint8_t num, bool value)
void pcf8574_gpio_write(i2c_dev_t *dev, uint8_t num, bool value)
{
uint8_t bit = (uint8_t)value << num;
uint8_t mask = ~(1 << num);
pcf8574_port_write (dev, (pcf8574_port_read(dev) & mask) | bit);
pcf8574_port_write(dev, (pcf8574_port_read(dev) & mask) | bit);
}

View file

@ -20,7 +20,7 @@ extern "C"
* \param addr I2C register address (0b0100<A2><A1><A0> for PCF8574)
* \return 8-bit GPIO port value
*/
uint8_t pcf8574_port_read(i2c_dev_t* dev);
uint8_t pcf8574_port_read(i2c_dev_t *dev);
/**
* \brief Continiously read GPIO port values to buffer
@ -29,14 +29,14 @@ uint8_t pcf8574_port_read(i2c_dev_t* dev);
* @param len Buffer length
* @return Number of bytes read
*/
size_t pcf8574_port_read_buf(i2c_dev_t* dev, void *buf, size_t len);
size_t pcf8574_port_read_buf(i2c_dev_t *dev, void *buf, size_t len);
/**
* \brief Write value to GPIO port
* \param addr I2C register address (0b0100<A2><A1><A0> for PCF8574)
* \param value GPIO port value
*/
void pcf8574_port_write(const i2c_dev_t* dev, uint8_t value);
void pcf8574_port_write(const i2c_dev_t *dev, uint8_t value);
/**
* \brief Continiously write GPIO values to GPIO port
@ -45,7 +45,7 @@ void pcf8574_port_write(const i2c_dev_t* dev, uint8_t value);
* @param len Buffer length
* @return Number of bytes written
*/
size_t pcf8574_port_write_buf(const i2c_dev_t* dev, void *buf, size_t len);
size_t pcf8574_port_write_buf(const i2c_dev_t *dev, void *buf, size_t len);
/**
* \brief Read input value of a GPIO pin
@ -53,7 +53,7 @@ size_t pcf8574_port_write_buf(const i2c_dev_t* dev, void *buf, size_t len);
* \param num pin number (0..7)
* \return GPIO pin value
*/
bool pcf8574_gpio_read(i2c_dev_t* dev, uint8_t num);
bool pcf8574_gpio_read(i2c_dev_t *dev, uint8_t num);
/**
* \brief Set GPIO pin output
@ -63,7 +63,7 @@ bool pcf8574_gpio_read(i2c_dev_t* dev, uint8_t num);
* \param num pin number (0..7)
* \param value true for high level
*/
void pcf8574_gpio_write(i2c_dev_t* dev, uint8_t num, bool value);
void pcf8574_gpio_write(i2c_dev_t *dev, uint8_t num, bool value);
#ifdef __cplusplus
}

View file

@ -1,20 +1,41 @@
#include <stddef.h>
#include <stdint.h>
/**
* Driver for 8-bit analog-to-digital conversion and
* an 8-bit digital-to-analog conversion PCF8591
*
* Part of esp-open-rtos
* Copyright (C) 2017 Pham Ngoc Thanh <pnt239@gmail.com>
* 2017 Ruslan V. Uss <unclerus@gmail.com>
* BSD Licensed as described in the file LICENSE
*/
#include "pcf8591.h"
/**
* CAUTION: PLEASE SET LOW FREQUENCY
*/
#include <stddef.h>
#define PCF8591_CTRL_REG_READ 0x03
#define BV(x) (1 << (x))
uint8_t pcf8591_read(i2c_dev_t* dev, uint8_t analog_pin)
#define CTRL_AD_CH_MASK 0x03
#define CTRL_AD_IN_PRG 4
#define CTRL_AD_IN_PRG_MASK (0x03 << CTRL_AD_IN_PRG)
#define CTRL_DA_OUT_EN 6
uint8_t pcf8591_read(i2c_dev_t *dev, pcf8591_input_conf_t conf, uint8_t channel)
{
uint8_t res = 0;
uint8_t control_reg = PCF8591_CTRL_REG_READ & analog_pin;
uint8_t control_reg =
((conf << CTRL_AD_IN_PRG) & CTRL_AD_IN_PRG_MASK) |
(channel & CTRL_AD_CH_MASK) |
BV(CTRL_DA_OUT_EN);
i2c_slave_read(dev->bus, dev->addr, &control_reg, &res, 1);
return res;
}
void pcf8591_write(i2c_dev_t *dev, uint8_t value)
{
uint8_t buf[2] = { BV(CTRL_DA_OUT_EN), value };
i2c_slave_write(dev->bus, dev->addr, NULL, buf, 2);
}

View file

@ -1,14 +1,16 @@
/**
* Driver for 8-bit analog-to-digital conversion and
* Driver for 8-bit analog-to-digital conversion and
* an 8-bit digital-to-analog conversion PCF8591
*
* Part of esp-open-rtos
* Copyright (C) 2017 Pham Ngoc Thanh <pnt239@gmail.com>
* 2017 Ruslan V. Uss <unclerus@gmail.com>
* BSD Licensed as described in the file LICENSE
*/
#ifndef _EXTRAS_PCF8591_H_
#define _EXTRAS_PCF8591_H_
#include <stdint.h>
#include <i2c/i2c.h>
#ifdef __cplusplus
@ -16,25 +18,33 @@ extern "C"
{
#endif
/**
* CAUTION: PLEASE SET I2C_FREQUENCY_400K IS 'false' IN 'i2c.h' FILE
*/
#define PCF8591_DEFAULT_ADDRESS 0x48
void pcf8591_init(void); //FIXME : library incomplete ?
/**
* Analog inputs configuration, see datasheet
*/
typedef enum {
PCF8591_IC_4_SINGLES = 0, //!< Four single-ended inputs
PCF8591_IC_DIFF, //!< Three differential inputs
PCF8591_IC_2_SINGLES_DIFF, //!< Two single-ended and differnetial mixed
PCF8591_IC_2_DIFFS //!< Two differential inputs
} pcf8591_input_conf_t;
/**
* Read input value of an analog pin.
* @param[in] addr Pointer to device
* @param[in] analog_pin pin number:
* 0 - AIN0
* 1 - AIN1
* 2 - AIN2
* 3 - AIN3
* @param[in] dev Pointer to device
* @param[in] conf Analog inputs configuration
* @param[in] channel Analog channel
* @return analog value
*/
uint8_t pcf8591_read(i2c_dev_t* dev, uint8_t analog_pin);
uint8_t pcf8591_read(i2c_dev_t *dev, pcf8591_input_conf_t conf, uint8_t channel);
/**
* Write value to analog output
* @param[in] dev Pointer to device
* @param[in] value DAC value
*/
void pcf8591_write(i2c_dev_t *dev, uint8_t value);
#ifdef __cplusplus

View file

@ -12,6 +12,12 @@
#include <FreeRTOS.h>
#include <esp8266.h>
#ifdef PWM_DEBUG
#define debug(fmt, ...) printf("%s: " fmt "\n", "PWM", ## __VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
typedef struct PWMPinDefinition
{
uint8_t pin;
@ -27,6 +33,8 @@ typedef enum {
typedef struct pwmInfoDefinition
{
uint8_t running;
bool output;
bool reverse;
uint16_t freq;
uint16_t dutyCycle;
@ -43,7 +51,7 @@ typedef struct pwmInfoDefinition
static PWMInfo pwmInfo;
static void frc1_interrupt_handler(void *arg)
static void IRAM frc1_interrupt_handler(void *arg)
{
uint8_t i = 0;
bool out = true;
@ -59,19 +67,19 @@ static void frc1_interrupt_handler(void *arg)
for (; i < pwmInfo.usedPins; ++i)
{
gpio_write(pwmInfo.pins[i].pin, out);
gpio_write(pwmInfo.pins[i].pin, pwmInfo.reverse ? !out : out);
}
timer_set_load(FRC1, load);
pwmInfo._step = step;
}
void pwm_init(uint8_t npins, const uint8_t* pins)
void pwm_init(uint8_t npins, const uint8_t* pins, uint8_t reverse)
{
/* Assert number of pins is correct */
if (npins > MAX_PWM_PINS)
{
printf("Incorrect number of PWM pins (%d)\n", npins);
debug("Incorrect number of PWM pins (%d)\n", npins);
return;
}
@ -80,6 +88,7 @@ void pwm_init(uint8_t npins, const uint8_t* pins)
pwmInfo._onLoad = 0;
pwmInfo._offLoad = 0;
pwmInfo._step = PERIOD_ON;
pwmInfo.reverse = reverse;
/* Save pins information */
pwmInfo.usedPins = npins;
@ -101,12 +110,11 @@ void pwm_init(uint8_t npins, const uint8_t* pins)
/* Flag not running */
pwmInfo.running = 0;
debug("PWM Init");
}
void pwm_set_freq(uint16_t freq)
{
pwmInfo.freq = freq;
/* Stop now to avoid load being used */
if (pwmInfo.running)
{
@ -114,8 +122,13 @@ void pwm_set_freq(uint16_t freq)
pwmInfo.running = 1;
}
timer_set_frequency(FRC1, freq);
pwmInfo._maxLoad = timer_get_load(FRC1);
if (!timer_set_frequency(FRC1, freq))
{
pwmInfo._maxLoad = timer_get_load(FRC1);
pwmInfo.freq = freq;
debug("Frequency set at %u",pwmInfo.freq);
debug("MaxLoad is %u",pwmInfo._maxLoad);
}
if (pwmInfo.running)
{
@ -125,22 +138,13 @@ void pwm_set_freq(uint16_t freq)
void pwm_set_duty(uint16_t duty)
{
bool output;
pwmInfo.dutyCycle = duty;
if (duty > 0 && duty < UINT16_MAX) {
pwm_restart();
return;
}
// 0% and 100% duty cycle are special cases: constant output.
pwm_stop();
pwmInfo.running = 1;
output = (duty == UINT16_MAX);
for (uint8_t i = 0; i < pwmInfo.usedPins; ++i)
if (duty == 0 || duty == UINT16_MAX)
{
gpio_write(pwmInfo.pins[i].pin, output);
pwmInfo.output = (duty == UINT16_MAX);
}
debug("Duty set at %u",pwmInfo.dutyCycle);
pwm_restart();
}
void pwm_restart()
@ -158,18 +162,34 @@ void pwm_start()
pwmInfo._offLoad = pwmInfo._maxLoad - pwmInfo._onLoad;
pwmInfo._step = PERIOD_ON;
// Trigger ON
uint8_t i = 0;
for (; i < pwmInfo.usedPins; ++i)
if(!pwmInfo._onLoad)
{
gpio_write(pwmInfo.pins[i].pin, true);
debug("Can't set timer with low duty and frequency settings, put duty at 0");
pwmInfo.dutyCycle = 0;
}
timer_set_load(FRC1, pwmInfo._onLoad);
timer_set_reload(FRC1, false);
timer_set_interrupts(FRC1, true);
timer_set_run(FRC1, true);
// 0% and 100% duty cycle are special cases: constant output.
if (pwmInfo.dutyCycle > 0 && pwmInfo.dutyCycle < UINT16_MAX)
{
// Trigger ON
uint8_t i = 0;
for (; i < pwmInfo.usedPins; ++i)
{
gpio_write(pwmInfo.pins[i].pin, pwmInfo.reverse ? false : true);
}
timer_set_load(FRC1, pwmInfo._onLoad);
timer_set_reload(FRC1, false);
timer_set_interrupts(FRC1, true);
timer_set_run(FRC1, true);
}
else
{
for (uint8_t i = 0; i < pwmInfo.usedPins; ++i)
{
gpio_write(pwmInfo.pins[i].pin, pwmInfo.reverse ? !pwmInfo.output : pwmInfo.output );
}
}
debug("PWM started");
pwmInfo.running = 1;
}
@ -177,5 +197,10 @@ void pwm_stop()
{
timer_set_interrupts(FRC1, false);
timer_set_run(FRC1, false);
for (uint8_t i = 0; i < pwmInfo.usedPins; ++i)
{
gpio_write(pwmInfo.pins[i].pin, pwmInfo.reverse ? true : false);
}
debug("PWM stopped");
pwmInfo.running = 0;
}

View file

@ -16,12 +16,41 @@
extern "C" {
#endif
void pwm_init(uint8_t npins, const uint8_t* pins);
//Warning: Printf disturb pwm. You can use "uart_putc" instead.
/**
* Initialize pwm
* @param npins Number of pwm pin used
* @param pins Array pointer to the pins
* @param reverse If true, the pwm work in reverse mode
*/
void pwm_init(uint8_t npins, const uint8_t* pins, uint8_t reverse);
/**
* Set PWM frequency. If error, frequency not set
* @param freq PWM frequency value in Hertz
*/
void pwm_set_freq(uint16_t freq);
/**
* Set Duty between 0 and UINT16_MAX
* @param duty Duty value
*/
void pwm_set_duty(uint16_t duty);
/**
* Restart the pwm signal
*/
void pwm_restart();
/**
* Start the pwm signal
*/
void pwm_start();
/**
* Stop the pwm signal
*/
void pwm_stop();
#ifdef __cplusplus

309
extras/sht3x/README.md Normal file
View file

@ -0,0 +1,309 @@
# Driver for **SHT3x** digital **temperature and humidity sensor**
This driver is written for usage with the ESP8266 and FreeRTOS using the I2C interface driver. It supports multiple SHT3x sensors connected to the same or different I2C interfaces.
## About the sensor
SHT3x is a digital temperature and humidity sensor that uses an I2C interface with up to 1 MHz communication speed. It can operate with **three levels of repeatability** (low, medium and high) and in two different modes, the **single shot data acquisition mode** (or short **single shot mode**) and the **periodic data acquisition mode** (or short **periodic mode**).
## Measurement process
Once the SHT3x sensor is initialized, it can be used for measurements.
### Single shot mode
In **single shot mode**, a measurement command triggers the acquisition of **exactly one data pair**. Each data pair consists of temperature and humidity as 16-bit decimal values.
Due to the measurement duration of up to 15 ms, the measurement process is separated into steps to avoid blocking the user task during measurements:
1. Trigger the sensor with function ```sht3x_start_measurement``` to perform exactly one single measurement.
2. Wait the measurement duration using function ```vTaskDelay``` until the results are available . Use either a constant duration of at least 30 ms or the duration in RTOS ticks returned from function ```sht3x_get_measurement_duration``` to wait.
3. Fetch the results as floating point sensor values with function ```sht3x_get_results``` or as raw data with function ```sht3x_get_raw_data```.
In the *single shot mode*, the user task has to perform all steps every time new sensor values are needed.
For convenience a high level function ```sht3x_measure``` that comprises all three steps above in only one function to perform a measurement. This function is the easiest way to use the sensor. It is most suitable for users that don't want to have the control on sensor details.
The advantage of this mode is that the sensor can switch between successive measurements into the sleep mode, which is more energy-efficient. This is particularly useful when the measurement rate is less than 1 measurement per second.
### Periodic mode
In this mode, one issued measurement command yields a stream of data pairs. Each data pair consists again of temperature and humidity as 16-bit decimal values. As soon as the measurement command has been sent to the sensor, it automatically performs measurements **periodically at a rate of 0.5, 1, 2, 4 or 10 measurements per second (mps)**. The data pairs can be fetched with the same rate or a lower rate.
As in *single shot mode*, the measurement process is separated into the following steps:
1. Trigger the sensor with function ```sht3x_start_measurement``` with a given rate to start periodic measurements.
2. Wait the measurement duration using function ```vTaskDelay``` until the first results are available . Use either a constant duration of at least 30 ms or the duration in RTOS ticks returned from function ```sht3x_get_measurement_duration``` to wait.
3. Fetch the results as floating point sensor values with function ```sht3x_get_results``` or as raw data with function ```sht3x_get_raw_data```.
However, in contrast to the *single shot mode*, steps 1 and 2 have to be executed only once. Once the measurement is started, the user task hast can simply fetch data periodically.
**Please note:** The rate of fetching the measurement results must not be greater than the rate of periodic measurements of the sensor. Even more, it *should be less* to avoid conflicts caused by the timing tolerance of the sensor.
## Measurement results
Once new measurement results are available, either function ```sht3x_get_raw_data``` or function ```sht3x_get_results``` can be used to fetch the results.
Function ```_sht3x_get_raw_data``` fetches only raw sensor data in 16-decimal format, checks the CRC checksums and stores them in an byte array of type ```sht3x_raw_data_t```. The user task then can use them directly or to call function ```sht3x_compute_values``` to compute floating point sensor values from them.
Function ```sht3x_get_results``` combines function ```sht3x_read_raw_data``` and function
```sht3x_compute_values``` to get the sensor values. This is the preferred approach to get sensor values by the user task.
## Error Handling
Most driver functions return a simple boolean value to indicate whether its execution was successful or an error happened. In the latter case, the member ```error_code``` of the sensor device data structure is set which indicates what error happened.
There are two different error levels that are ORed into one single ```error_code```, errors in the I2C communication and errors with the SHT3x sensor itself. To test for a certain error you can AND the *error_code* with one of the error masks, ```SHT3x_I2C_ERROR_MASK``` for I2C errors and ```SHT3x_DRV_ERROR_MASK``` for other errors and then test for a certain error code.
## Repeatability
The SHT3x sensor supports **three levels of repeatability** (low, medium and high). Repeatability is the variation in measurement results taken by the sensor under the same conditions, and in a short period of time. It is a measure for the noise on the physical sensor output. The higher the repeatability the smaller are changes in the output of subsequent measurements.
The repeatability settings influences the measurement duration as well as the power consumption of the sensor. The measurement takes 3 ms with low repeatability, 5 ms with medium repeatability and 13.5 ms with high repeatability. That is, the measurement produces a noticeable delay in execution.
While the sensor measures at the lowest repeatability, the average current consumption is 800 μA. That is, the higher the repeatability level, the longer the measurement takes and the higher the power consumption. The sensor consumes only 0.2 μA in standby mode.
The repeatability used for a measurement is specified as parameter of function ```sht3x_start_measurement```.
## Usage
Before using the SHT3x driver, function ```i2c_init``` needs to be called for each I2C interface to setup them.
```
#include "sht3x/sht3x.h"
...
#define I2C_BUS 0
#define I2C_SCL_PIN GPIO_ID_PIN((5))
#define I2C_SDA_PIN GPIO_ID_PIN((4))
...
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K)
...
```
Once I2C interfaces to be used are initialized, function ```sht3x_init_sensor``` has to be called for each SHT3x sensor to initialize the sensor and to check its availability as well as its error state. The parameters specify the I2C bus to which it is connected and its I2C slave address.
```
static sht3x_sensor_t* sensor; // pointer to sensor device data structure
...
if ((sensor = sht3x_init_sensor (I2C_BUS, SHT3x_ADDR_2)))
{
...
}
```
Function ```sht3x_init_sensor``` returns a pointer to the sensor device data structure or NULL in case of error.
Last, the user task that uses the sensor has to be created.
```
xTaskCreate(user_task, "user_task", 256, NULL, 2, 0);
```
In **periodic mode**, the user task has to start the periodic measurement only once at the beginning of the task. After that, it has only to wait for the results of the first measurement. In the task loop itself, it simply fetches the next measurement results in each cycle.
Thus, in this mode the user task could look like the following:
```
void user_task (void *pvParameters)
{
float temperature;
float humidity;
// Start periodic measurements with 1 measurement per second.
sht3x_start_measurement (sensor, sht3x_periodic_1mps, sht3x_high);
// Wait until first measurement is ready (constant time of at least 30 ms
// or the duration returned from *sht3x_get_measurement_duration*).
vTaskDelay (sht3x_get_measurement_duration(sht3x_high));
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// Get the values and do something with them.
if (sht3x_get_results (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// Wait until 2 seconds (cycle time) are over.
vTaskDelayUntil(&last_wakeup, 2000 / portTICK_PERIOD_MS);
}
}
```
At the beginning of the task, the periodic measurement is started with function ```sht3x_start_measurement``` at high repeatability level and a rate of 1 measurement per second. The task is then delayed with function ```vTaskDelay``` to wait for first measurement results. The duration can be either a constant time of at least 30 ms or the duration returned by ```sht3x_get_measurement_duration```, as in the example. Inside the task loop, simply the measurement results are fetched periodically using function ```sht3x_get_results``` every 2 seconds.
**Please note:** The rate of fetching the measurement results must be not greater than the rate of periodic measurements of the sensor, however, it *should be less* to avoid conflicts caused by the timing tolerance of the sensor.
In **single shot mode**, the measurement has to be triggered
in each cycle. Also the waiting for measurement results is required in each cylce, before the results can be fetched.
Thus the user task could look like the following:
```
void user_task (void *pvParameters)
{
float temperature;
float humidity;
TickType_t last_wakeup = xTaskGetTickCount();
uint8_t duration = sht3x_get_measurement_duration(sht3x_high);
while (1)
{
// Trigger one measurement in single shot mode with high repeatability.
sht3x_start_measurement (sensor, sht3x_single_shot, sht3x_high);
// Wait until measurement is ready (constant time of at least 30 ms
// or the duration returned from *sht3x_get_measurement_duration*).
vTaskDelay (duration);
// retrieve the values and do something with them
if (sht3x_get_results (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// wait until 5 seconds are over
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
}
}
```
In contrast to the *periodic mode*, the function ```sht3x_start_measurement``` is called inside the task loop to start exactly one measurement in each cycle. The task is then also delayed every time using function ```vTaskDelay``` before the results are fetched with function ```sht3x_get_results```.
Alternatively, user task can use the high level function ```sht3x_measure``` that comprises these steps in only one function. This would simplify the user task that would then look like the following:
```
void user_task (void *pvParameters)
{
float temperature;
float humidity;
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// perform one measurement and do something with the results
if (sht3x_measure (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// wait until 5 seconds are over
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
}
}
```
The code could be extended by an error handling. In the event of an error, most driver functions set the ```error_code``` element of the sensor device data structure. This indicates which error has occurred. Error codes are a combination of I2C communication error codes and SHT3x sensor error codes. To test for a particular error, the *error code* has to be ANDed with one of the error masks ```SHT3x_I2C_ERROR_MASK``` or ```SHT3x_DRV_ERROR_MASK``` and then tested for a certain value.
For example, error handling for ```sht3x_get_results``` could look like:
```
if (!sht3x_get_results (sensor, &values))
{
// error happened
switch (sensor->error_code & SHT3x_I2C_ERROR_MASK)
{
case SHT3x_I2C_BUSY: ...
case SHT3x_I2C_READ_FAILED: ...
...
}
switch (sensor->error_code & SHT3x_DRV_ERROR_MASK)
{
case SHT3x_MEAS_NOT_RUNNING: ...
case SHT3x_READ_RAW_DATA_FAILED: ...
case SHT3x_WRONG_CRC_TEMPERATURE: ...
...
}
}
```
## Full Example
```
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
// include SHT3x driver
#include "sht3x/sht3x.h"
// define I2C interfaces at which SHTx3 sensors are connected
#define I2C_BUS 0
#define I2C_SCL_PIN GPIO_ID_PIN((5))
#define I2C_SDA_PIN GPIO_ID_PIN((4))
static sht3x_sensor_t* sensor; // sensor device data structure
/*
* User task that triggers a measurement every 5 seconds. Due to
* power efficiency reasons, it uses the SHT3x *sht3x_single_shot*.
*/
void user_task (void *pvParameters)
{
float temperature;
float humidity;
TickType_t last_wakeup = xTaskGetTickCount();
// get the measurement duration for high repeatability;
uint8_t duration = sht3x_get_measurement_duration(sht3x_high);
while (1)
{
// Trigger one measurement in single shot mode with high repeatability.
sht3x_start_measurement (sensor, sht3x_single_shot, sht3x_high);
// Wait until measurement is ready (constant time of at least 30 ms
// or the duration returned from *sht3x_get_measurement_duration*).
vTaskDelay (duration);
// retrieve the values and do something with them
if (sht3x_get_results (sensor, &temperature, &humidity))
printf("%.3f SHT3x Sensor: %.2f °C, %.2f %%\n",
(double)sdk_system_get_time()*1e-3, temperature, humidity);
// wait until 5 seconds are over
vTaskDelayUntil(&last_wakeup, 5000 / portTICK_PERIOD_MS);
}
}
void user_init(void)
{
// Set UART Parameter.
uart_set_baud(0, 115200);
// Give the UART some time to settle.
sdk_os_delay_us(500);
// Init I2C bus interfaces at which SHT3x sensors are connected
// (different busses are possible).
i2c_init(I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ_100K);
// Create the sensors.
if ((sensor = sht3x_init_sensor (I2C_BUS, SHT3x_ADDR_2)))
{
// Create a user task that uses the sensors.
xTaskCreate(user_task, "user_task", 256, NULL, 2, 0);
}
// That's it.
}
```
## Further Examples
See also the examples in the examples directory [examples directory](../../examples/sht3x/README.md).

View file

@ -0,0 +1,9 @@
# Component makefile for extras/sht3x
# expected anyone using SHT3x driver includes it as 'sht3x/sht3x.h'
INC_DIRS += $(sht3x_ROOT)..
# args for passing into compile rule generation
sht3x_SRC_DIR = $(sht3x_ROOT)
$(eval $(call component_compile_rules,sht3x))

447
extras/sht3x/sht3x.c Normal file
View file

@ -0,0 +1,447 @@
/*
* Driver for Sensirion SHT3x digital temperature and humidity sensor
* connected to I2C
*
* Part of esp-open-rtos
*
* ----------------------------------------------------------------
*
* The BSD License (3-clause license)
*
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Driver for Sensirion SHT3x digital temperature and humity sensor
* connected to I2C
*
* Part of esp-open-rtos
*/
#include <string.h>
#include "sht3x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "espressif/esp_common.h"
#include "espressif/sdk_private.h"
#define SHT3x_STATUS_CMD 0xF32D
#define SHT3x_CLEAR_STATUS_CMD 0x3041
#define SHT3x_RESET_CMD 0x30A2
#define SHT3x_FETCH_DATA_CMD 0xE000
#define SHT3x_HEATER_OFF_CMD 0x3066
const uint16_t SHT3x_MEASURE_CMD[6][3] = {
{0x2400,0x240b,0x2416}, // [SINGLE_SHOT][H,M,L] without clock stretching
{0x2032,0x2024,0x202f}, // [PERIODIC_05][H,M,L]
{0x2130,0x2126,0x212d}, // [PERIODIC_1 ][H,M,L]
{0x2236,0x2220,0x222b}, // [PERIODIC_2 ][H,M,L]
{0x2234,0x2322,0x2329}, // [PERIODIC_4 ][H,M,L]
{0x2737,0x2721,0x272a} }; // [PERIODIC_10][H,M,L]
// due to the fact that ticks can be smaller than portTICK_PERIOD_MS, one and
// a half tick period added to the duration to be sure that waiting time for
// the results is long enough
#define TIME_TO_TICKS(ms) (1 + ((ms) + (portTICK_PERIOD_MS-1) + portTICK_PERIOD_MS/2 ) / portTICK_PERIOD_MS)
#define SHT3x_MEAS_DURATION_REP_HIGH 15
#define SHT3x_MEAS_DURATION_REP_MEDIUM 6
#define SHT3x_MEAS_DURATION_REP_LOW 4
// measurement durations in us
const uint16_t SHT3x_MEAS_DURATION_US[3] = { SHT3x_MEAS_DURATION_REP_HIGH * 1000,
SHT3x_MEAS_DURATION_REP_MEDIUM * 1000,
SHT3x_MEAS_DURATION_REP_LOW * 1000 };
// measurement durations in RTOS ticks
const uint8_t SHT3x_MEAS_DURATION_TICKS[3] = { TIME_TO_TICKS(SHT3x_MEAS_DURATION_REP_HIGH),
TIME_TO_TICKS(SHT3x_MEAS_DURATION_REP_MEDIUM),
TIME_TO_TICKS(SHT3x_MEAS_DURATION_REP_LOW) };
#if defined(SHT3x_DEBUG_LEVEL_2)
#define debug(s, f, ...) printf("%s %s: " s "\n", "SHT3x", f, ## __VA_ARGS__)
#define debug_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "SHT3x", f, d->bus, d->addr, ## __VA_ARGS__)
#else
#define debug(s, f, ...)
#define debug_dev(s, f, d, ...)
#endif
#if defined(SHT3x_DEBUG_LEVEL_1) || defined(SHT3x_DEBUG_LEVEL_2)
#define error(s, f, ...) printf("%s %s: " s "\n", "SHT3x", f, ## __VA_ARGS__)
#define error_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "SHT3x", f, d->bus, d->addr, ## __VA_ARGS__)
#else
#define error(s, f, ...)
#define error_dev(s, f, d, ...)
#endif
/** Forward declaration of function for internal use */
static bool sht3x_is_measuring (sht3x_sensor_t*);
static bool sht3x_send_command (sht3x_sensor_t*, uint16_t);
static bool sht3x_read_data (sht3x_sensor_t*, uint8_t*, uint32_t);
static bool sht3x_get_status (sht3x_sensor_t*, uint16_t*);
static bool sht3x_reset (sht3x_sensor_t*);
static uint8_t crc8 (uint8_t data[], int len);
/** ------------------------------------------------ */
bool sht3x_init_driver()
{
return true;
}
sht3x_sensor_t* sht3x_init_sensor(uint8_t bus, uint8_t addr)
{
sht3x_sensor_t* dev;
if ((dev = malloc (sizeof(sht3x_sensor_t))) == NULL)
return NULL;
// inititalize sensor data structure
dev->bus = bus;
dev->addr = addr;
dev->mode = sht3x_single_shot;
dev->meas_start_time = 0;
dev->meas_started = false;
dev->meas_first = false;
uint16_t status;
// reset the sensor
if (!sht3x_reset(dev))
{
error_dev ("could not reset the sensor", __FUNCTION__, dev);
free(dev);
return NULL;
}
// check again the status after clear status command
if (!sht3x_get_status(dev, &status))
{
error_dev ("could not get sensor status", __FUNCTION__, dev);
free(dev);
return NULL;
}
debug_dev ("sensor initialized", __FUNCTION__, dev);
return dev;
}
bool sht3x_measure (sht3x_sensor_t* dev, float* temperature, float* humidity)
{
if (!dev || (!temperature && !humidity)) return false;
if (!sht3x_start_measurement (dev, sht3x_single_shot, sht3x_high))
return false;
vTaskDelay (SHT3x_MEAS_DURATION_TICKS[sht3x_high]);
sht3x_raw_data_t raw_data;
if (!sht3x_get_raw_data (dev, raw_data))
return false;
return sht3x_compute_values (raw_data, temperature, humidity);
}
bool sht3x_start_measurement (sht3x_sensor_t* dev, sht3x_mode_t mode, sht3x_repeat_t repeat)
{
if (!dev) return false;
dev->error_code = SHT3x_OK;
dev->mode = mode;
dev->repeatability = repeat;
// start measurement according to selected mode and return an duration estimate
if (!sht3x_send_command(dev, SHT3x_MEASURE_CMD[mode][repeat]))
{
error_dev ("could not send start measurment command", __FUNCTION__, dev);
dev->error_code |= SHT3x_SEND_MEAS_CMD_FAILED;
return false;
}
dev->meas_start_time = sdk_system_get_time ();
dev->meas_started = true;
dev->meas_first = true;
return true;
}
uint8_t sht3x_get_measurement_duration (sht3x_repeat_t repeat)
{
return SHT3x_MEAS_DURATION_TICKS[repeat]; // in RTOS ticks
}
bool sht3x_get_raw_data(sht3x_sensor_t* dev, sht3x_raw_data_t raw_data)
{
if (!dev || !raw_data) return false;
dev->error_code = SHT3x_OK;
if (!dev->meas_started)
{
debug_dev ("measurement is not started", __FUNCTION__, dev);
dev->error_code = SHT3x_MEAS_NOT_STARTED;
return sht3x_is_measuring (dev);
}
if (sht3x_is_measuring(dev))
{
error_dev ("measurement is still running", __FUNCTION__, dev);
dev->error_code = SHT3x_MEAS_STILL_RUNNING;
return false;
}
// send fetch command in any periodic mode (mode > 0) before read raw data
if (dev->mode && !sht3x_send_command(dev, SHT3x_FETCH_DATA_CMD))
{
debug_dev ("send fetch command failed", __FUNCTION__, dev);
dev->error_code |= SHT3x_SEND_FETCH_CMD_FAILED;
return false;
}
// read raw data
if (!sht3x_read_data(dev, raw_data, sizeof(sht3x_raw_data_t)))
{
error_dev ("read raw data failed", __FUNCTION__, dev);
dev->error_code |= SHT3x_READ_RAW_DATA_FAILED;
return false;
}
// reset first measurement flag
dev->meas_first = false;
// reset measurement started flag in single shot mode
if (dev->mode == sht3x_single_shot)
dev->meas_started = false;
// check temperature crc
if (crc8(raw_data,2) != raw_data[2])
{
error_dev ("CRC check for temperature data failed", __FUNCTION__, dev);
dev->error_code |= SHT3x_WRONG_CRC_TEMPERATURE;
return false;
}
// check humidity crc
if (crc8(raw_data+3,2) != raw_data[5])
{
error_dev ("CRC check for humidity data failed", __FUNCTION__, dev);
dev->error_code |= SHT3x_WRONG_CRC_HUMIDITY;
return false;
}
return true;
}
bool sht3x_compute_values (sht3x_raw_data_t raw_data, float* temperature, float* humidity)
{
if (!raw_data) return false;
if (temperature)
*temperature = ((((raw_data[0] * 256.0) + raw_data[1]) * 175) / 65535.0) - 45;
if (humidity)
*humidity = ((((raw_data[3] * 256.0) + raw_data[4]) * 100) / 65535.0);
return true;
}
bool sht3x_get_results (sht3x_sensor_t* dev, float* temperature, float* humidity)
{
if (!dev || (!temperature && !humidity)) return false;
sht3x_raw_data_t raw_data;
if (!sht3x_get_raw_data (dev, raw_data))
return false;
return sht3x_compute_values (raw_data, temperature, humidity);
}
/* Functions for internal use only */
static bool sht3x_is_measuring (sht3x_sensor_t* dev)
{
if (!dev) return false;
dev->error_code = SHT3x_OK;
// not running if measurement is not started at all or
// it is not the first measurement in periodic mode
if (!dev->meas_started || !dev->meas_first)
return false;
// not running if time elapsed is greater than duration
uint32_t elapsed = sdk_system_get_time() - dev->meas_start_time;
return elapsed < SHT3x_MEAS_DURATION_US[dev->repeatability];
}
static bool sht3x_send_command(sht3x_sensor_t* dev, uint16_t cmd)
{
if (!dev) return false;
uint8_t data[2] = { cmd >> 8, cmd & 0xff };
debug_dev ("send command MSB=%02x LSB=%02x", __FUNCTION__, dev, data[0], data[1]);
int err;
int count = 10;
// in case i2c is busy, try to write up to ten ticks (normally 100 ms)
// tested with a task that is disturbing by using i2c bus almost all the time
while ((err=i2c_slave_write(dev->bus, dev->addr, 0, data, 2)) == -EBUSY && count--)
vTaskDelay (1);
if (err)
{
dev->error_code |= (err == -EBUSY) ? SHT3x_I2C_BUSY : SHT3x_I2C_SEND_CMD_FAILED;
error_dev ("i2c error %d on write command %02x", __FUNCTION__, dev, err, cmd);
return false;
}
return true;
}
static bool sht3x_read_data(sht3x_sensor_t* dev, uint8_t *data, uint32_t len)
{
if (!dev) return false;
int err;
int count = 10;
// in case i2c is busy, try to read up to ten ticks (normally 100 ms)
while ((err=i2c_slave_read(dev->bus, dev->addr, 0, data, len)) == -EBUSY && count--)
vTaskDelay (1);
if (err)
{
dev->error_code |= (err == -EBUSY) ? SHT3x_I2C_BUSY : SHT3x_I2C_READ_FAILED;
error_dev ("error %d on read %d byte", __FUNCTION__, dev, err, len);
return false;
}
# ifdef SHT3x_DEBUG_LEVEL_2
printf("SHT3x %s: bus %d, addr %02x - read following bytes: ",
__FUNCTION__, dev->bus, dev->addr);
for (int i=0; i < len; i++)
printf("%02x ", data[i]);
printf("\n");
# endif
return true;
}
static bool sht3x_reset (sht3x_sensor_t* dev)
{
if (!dev) return false;
debug_dev ("soft-reset triggered", __FUNCTION__, dev);
dev->error_code = SHT3x_OK;
// send reset command
if (!sht3x_send_command(dev, SHT3x_RESET_CMD))
{
dev->error_code |= SHT3x_SEND_RESET_CMD_FAILED;
return false;
}
// wait for small amount of time needed (according to datasheet 0.5ms)
vTaskDelay (100 / portTICK_PERIOD_MS);
uint16_t status;
// check the status after reset
if (!sht3x_get_status(dev, &status))
return false;
return true;
}
static bool sht3x_get_status (sht3x_sensor_t* dev, uint16_t* status)
{
if (!dev || !status) return false;
dev->error_code = SHT3x_OK;
uint8_t data[3];
if (!sht3x_send_command(dev, SHT3x_STATUS_CMD) || !sht3x_read_data(dev, data, 3))
{
dev->error_code |= SHT3x_SEND_STATUS_CMD_FAILED;
return false;
}
*status = data[0] << 8 | data[1];
debug_dev ("status=%02x", __FUNCTION__, dev, *status);
return true;
}
const uint8_t g_polynom = 0x31;
static uint8_t crc8 (uint8_t data[], int len)
{
// initialization value
uint8_t crc = 0xff;
// iterate over all bytes
for (int i=0; i < len; i++)
{
crc ^= data[i];
for (int i = 0; i < 8; i++)
{
bool xor = crc & 0x80;
crc = crc << 1;
crc = xor ? crc ^ g_polynom : crc;
}
}
return crc;
}

279
extras/sht3x/sht3x.h Normal file
View file

@ -0,0 +1,279 @@
/*
* Driver for Sensirion SHT3x digital temperature and humidity sensor
* connected to I2C
*
* Part of esp-open-rtos
*
* ----------------------------------------------------------------
*
* The BSD License (3-clause license)
*
* Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DRIVER_SHT3x_H_
#define DRIVER_SHT3x_H_
#include "stdint.h"
#include "stdbool.h"
#include "FreeRTOS.h"
#include "i2c/i2c.h"
// Uncomment to enable debug output
// #define SHT3x_DEBUG_LEVEL_1 // only error messages
// #define SHT3x_DEBUG_LEVEL_2 // error and debug messages
#ifdef __cplusplus
extern "C" {
#endif
// definition of possible I2C slave addresses
#define SHT3x_ADDR_1 0x44 // ADDR pin connected to GND/VSS (default)
#define SHT3x_ADDR_2 0x45 // ADDR pin connected to VDD
// definition of error codes
#define SHT3x_OK 0
#define SHT3x_NOK -1
#define SHT3x_I2C_ERROR_MASK 0x000f
#define SHT3x_DRV_ERROR_MASK 0xfff0
// error codes for I2C interface ORed with SHT3x error codes
#define SHT3x_I2C_READ_FAILED 1
#define SHT3x_I2C_SEND_CMD_FAILED 2
#define SHT3x_I2C_BUSY 3
// SHT3x driver error codes OR ed with error codes for I2C interface
#define SHT3x_MEAS_NOT_STARTED (1 << 8)
#define SHT3x_MEAS_ALREADY_RUNNING (2 << 8)
#define SHT3x_MEAS_STILL_RUNNING (3 << 8)
#define SHT3x_READ_RAW_DATA_FAILED (4 << 8)
#define SHT3x_SEND_MEAS_CMD_FAILED (5 << 8)
#define SHT3x_SEND_RESET_CMD_FAILED (6 << 8)
#define SHT3x_SEND_STATUS_CMD_FAILED (7 << 8)
#define SHT3x_SEND_FETCH_CMD_FAILED (8 << 8)
#define SHT3x_WRONG_CRC_TEMPERATURE (9 << 8)
#define SHT3x_WRONG_CRC_HUMIDITY (10 << 8)
#define SHT3x_RAW_DATA_SIZE 6
/**
* @brief raw data type
*/
typedef uint8_t sht3x_raw_data_t [SHT3x_RAW_DATA_SIZE];
/**
* @brief possible measurement modes
*/
typedef enum {
sht3x_single_shot = 0, // one single measurement
sht3x_periodic_05mps, // periodic with 0.5 measurements per second (mps)
sht3x_periodic_1mps, // periodic with 1 measurements per second (mps)
sht3x_periodic_2mps, // periodic with 2 measurements per second (mps)
sht3x_periodic_4mps, // periodic with 4 measurements per second (mps)
sht3x_periodic_10mps // periodic with 10 measurements per second (mps)
} sht3x_mode_t;
/**
* @brief possible repeatability modes
*/
typedef enum {
sht3x_high = 0,
sht3x_medium,
sht3x_low
} sht3x_repeat_t;
/**
* @brief SHT3x sensor device data structure type
*/
typedef struct {
uint32_t error_code; // combined error codes
uint8_t bus; // I2C bus at which sensor is connected
uint8_t addr; // I2C slave address of the sensor
sht3x_mode_t mode; // used measurement mode
sht3x_repeat_t repeatability; // used repeatability
bool meas_started; // indicates whether measurement started
uint32_t meas_start_time; // measurement start time in us
bool meas_first; // first measurement in periodic mode
} sht3x_sensor_t;
/**
* @brief Initialize a SHT3x sensor
*
* The function creates a data structure describing the sensor and
* initializes the sensor device.
*
* @param bus I2C bus at which the sensor is connected
* @param addr I2C slave address of the sensor
* @return pointer to sensor data structure, or NULL on error
*/
sht3x_sensor_t* sht3x_init_sensor (uint8_t bus, uint8_t addr);
/**
* @brief High level measurement function
*
* For convenience this function comprises all three steps to perform
* one measurement in only one function:
*
* 1. Starts a measurement in single shot mode with high reliability
* 2. Waits using *vTaskDelay* until measurement results are available
* 3. Returns the results in kind of floating point sensor values
*
* This function is the easiest way to use the sensor. It is most suitable
* for users that don't want to have the control on sensor details.
*
* Please note: The function delays the calling task up to 30 ms to wait for
* the the measurement results. This might lead to problems when the function
* is called from a software timer callback function.
*
* @param dev pointer to sensor device data structure
* @param temperature returns temperature in degree Celsius
* @param humidity returns humidity in percent
* @return true on success, false on error
*/
bool sht3x_measure (sht3x_sensor_t* dev, float* temperature, float* humidity);
/**
* @brief Start the measurement in single shot or periodic mode
*
* The function starts the measurement either in *single shot mode*
* (exactly one measurement) or *periodic mode* (periodic measurements)
* with given repeatabilty.
*
* In the *single shot mode*, this function has to be called for each
* measurement. The measurement duration has to be waited every time
* before the results can be fetched.
*
* In the *periodic mode*, this function has to be called only once. Also
* the measurement duration has to be waited only once until the first
* results are available. After this first measurement, the sensor then
* automatically performs all subsequent measurements. The rate of periodic
* measurements can be 10, 4, 2, 1 or 0.5 measurements per second (mps).
*
* Please note: Due to inaccuracies in timing of the sensor, the user task
* should fetch the results at a lower rate. The rate of the periodic
* measurements is defined by the parameter *mode*.
*
* @param dev pointer to sensor device data structure
* @param mode measurement mode, see type *sht3x_mode_t*
* @param repeat repeatability, see type *sht3x_repeat_t*
* @return true on success, false on error
*/
bool sht3x_start_measurement (sht3x_sensor_t* dev, sht3x_mode_t mode,
sht3x_repeat_t repeat);
/**
* @brief Get the duration of a measurement in RTOS ticks.
*
* The function returns the duration in RTOS ticks required by the sensor to
* perform a measurement for the given repeatability. Once a measurement is
* started with function *sht3x_start_measurement* the user task can use this
* duration in RTOS ticks directly to wait with function *vTaskDelay* until
* the measurement results can be fetched.
*
* Please note: The duration only depends on repeatability level. Therefore,
* it can be considered as constant for a repeatibility.
*
* @param repeat repeatability, see type *sht3x_repeat_t*
* @return measurement duration given in RTOS ticks
*/
uint8_t sht3x_get_measurement_duration (sht3x_repeat_t repeat);
/**
* @brief Read measurement results from sensor as raw data
*
* The function read measurement results from the sensor, checks the CRC
* checksum and stores them in the byte array as following.
*
* data[0] = Temperature MSB
* data[1] = Temperature LSB
* data[2] = Temperature CRC
* data[3] = Pressure MSB
* data[4] = Pressure LSB
* data[2] = Pressure CRC
*
* In case that there are no new data that can be read, the function fails.
*
* @param dev pointer to sensor device data structure
* @param raw_data byte array in which raw data are stored
* @return true on success, false on error
*/
bool sht3x_get_raw_data(sht3x_sensor_t* dev, sht3x_raw_data_t raw_data);
/**
* @brief Computes sensor values from raw data
*
* @param raw_data byte array that contains raw data
* @param temperature returns temperature in degree Celsius
* @param humidity returns humidity in percent
* @return true on success, false on error
*/
bool sht3x_compute_values (sht3x_raw_data_t raw_data,
float* temperature, float* humidity);
/**
* @brief Get measurement results in form of sensor values
*
* The function combines function *sht3x_read_raw_data* and function
* *sht3x_compute_values* to get the measurement results.
*
* In case that there are no results that can be read, the function fails.
*
* @param dev pointer to sensor device data structure
* @param temperature returns temperature in degree Celsius
* @param humidity returns humidity in percent
* @return true on success, false on error
*/
bool sht3x_get_results (sht3x_sensor_t* dev,
float* temperature, float* humidity);
#ifdef __cplusplus
}
#endif
#endif /* DRIVER_SHT3x_H_ */

View file

@ -90,7 +90,7 @@ void esp_spiffs_init(uint32_t addr, uint32_t size)
config.hal_write_f = esp_spiffs_write;
config.hal_erase_f = esp_spiffs_erase;
config.fh_ix_offset = 3;
config.fh_ix_offset = SPIFFS_FILEHDL_OFFSET;
}
@ -149,7 +149,8 @@ int _open_r(struct _reent *r, const char *pathname, int flags, int mode)
return SPIFFS_open(&fs, pathname, spiffs_flags, mode);
}
int _close_r(struct _reent *r, int fd)
// This implementation replaces implementation in core/newlib_syscals.c
int _close_filesystem_r(struct _reent *r, int fd)
{
return SPIFFS_close(&fs, (spiffs_file)fd);
}

View file

@ -198,7 +198,15 @@ typedef unsigned char u8_t;
// NB: This adds config field fh_ix_offset in the configuration struct when
// mounting, which must be defined.
#ifndef SPIFFS_FILEHDL_OFFSET
#define SPIFFS_FILEHDL_OFFSET 1
#define SPIFFS_FILEHDL_OFFSET 17
// Not ideal having to use a literal above, which is necessary because this file
// is also included by tools that run on the host, but at least do some checks
// when building the target code.
#ifdef LWIP_SOCKET_OFFSET
#if SPIFFS_FILEHDL_OFFSET < (LWIP_SOCKET_OFFSET + MEMP_NUM_NETCONN)
#error SPIFFS_FILEHDL_OFFSET clashes with lwip sockets range.
#endif
#endif
#endif
// Enable this to compile a read only version of spiffs.

View file

@ -1,4 +1,4 @@
# Driver for SSD1306 OLED LCD
# Driver for SSD1306/SH1106 OLED LCD
This driver is written for usage with the ESP8266 and FreeRTOS ([esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos)).
@ -11,34 +11,38 @@ This driver is written for usage with the ESP8266 and FreeRTOS ([esp-open-rtos](
## Supported connection interfaces
Currently supported two of them: I2C and SPI4.
I2C, SPI3 and SPI4.
## Usage
If Reset pin is accesible in your display module, connect it to the RESET pin of ESP8266.
If you don't do this, display RAM may be glitchy after the power lost/restore.
If you don't, display RAM could be glitchy after the power cycle.
### I2C protocol
Before using the SSD1306 LCD module the function `i2c_init(SCL_PIN, SDA_PIN)` needs to be
called to setup the I2C interface and then you must call `ssd1306_init()`.
Before using the OLED module you need to call the function `i2c_init(BUS, SCL_PIN, SDA_PIN, I2C_FREQ_400K)`
to configure the I2C interface and then you should call `ssd1306_init()`.
#### Example
```C
#define SCL_PIN 5
#define SDA_PIN 4
#define I2C_BUS 0
...
static const ssd1306_t device = {
.protocol = SSD1306_PROTO_I2C,
.width = 128,
.height = 64
.protocol = SSD1306_PROTO_I2C,
.screen = SSD1306_SCREEN, // or SH1106_SCREEN
.i2c_dev.bus = I2C_BUS,
.i2c_dev.addr = SSD1306_I2C_ADDR_0,
.width = 128,
.height = 64
};
...
i2c_init(SCL_PIN, SDA_PIN);
i2c_init(I2C_BUS, SCL_PIN, SDA_PIN, I2C_FREQ_400K);
if (ssd1306_init(&device)) {
// An error occured, while performing SSD1306 init (E.g device not found etc.)
@ -47,14 +51,15 @@ if (ssd1306_init(&device)) {
// rest of the code
```
### SPI4 protocol
### SPI3 and SPI4 protocols
This protocol MUCH faster than I2C but uses 2 additional GPIO pins (beside of HSPI CLK
and HSPI MOSI): Data/Command pin and Chip Select pin.
These protocols are MUCH faster than I2C, but use 2 additional GPIO pins
(besides the **HSPI CLK** and **HSPI MOSI**): **Chip Select** and **Data/Command** (in case of SPI4).
No additional function calls are required before `ssd1306_init()`.
#### Example
#### SPI4 Example
```C
#define CS_PIN 5
@ -64,10 +69,35 @@ No additional function calls are required before `ssd1306_init()`.
static const ssd1306_t device = {
.protocol = SSD1306_PROTO_SPI4,
.cs_pin = CS_PIN,
.dc_pin = DC_PIN,
.width = 128,
.height = 64
.screen = SSD1306_SCREEN,
.cs_pin = CS_PIN,
.dc_pin = DC_PIN,
.width = 128,
.height = 64
};
...
if (ssd1306_init(&device)) {
// An error occured, while performing SSD1306 init
}
// rest of the code
```
#### SPI3 example
```C
#define CS_PIN 5
...
static const ssd1306_t device = {
.protocol = SSD1306_PROTO_SPI3,
.screen = SSD1306_SCREEN,
.cs_pin = CS_PIN,
.width = 128,
.height = 64
};
...

View file

@ -85,11 +85,10 @@
#define abs(x) ((x)<0 ? -(x) : (x))
#define swap(x, y) do { typeof(x) temp##x##y = x; x = y; y = temp##x##y; } while (0)
#if (SSD1306_I2C_SUPPORT)
static int inline i2c_send(const ssd1306_t *dev, uint8_t reg, uint8_t* data, uint8_t len)
{
return i2c_slave_write(dev->i2c_dev.bus, dev->i2c_dev.addr , &reg, data, len);
return i2c_slave_write(dev->i2c_dev.bus, dev->i2c_dev.addr, &reg, data, len);
}
#endif
@ -120,7 +119,7 @@ int ssd1306_command(const ssd1306_t *dev, uint8_t cmd)
#if (SSD1306_SPI3_SUPPORT)
case SSD1306_PROTO_SPI3:
gpio_write(dev->cs_pin, false);
spi_set_command(SPI_BUS,1,0); // command mode
spi_set_command(SPI_BUS, 1, 0); // command mode
spi_transfer_8(SPI_BUS, cmd);
spi_clear_command(SPI_BUS);
gpio_write(dev->cs_pin, true);
@ -135,7 +134,7 @@ int ssd1306_command(const ssd1306_t *dev, uint8_t cmd)
}
/* Perform default init routine according
* to SSD1306 datasheet from adafruit.com */
* to SSD1306 datasheet from adafruit.com */
int ssd1306_init(const ssd1306_t *dev)
{
uint8_t pin_cfg;
@ -221,10 +220,12 @@ int ssd1306_init(const ssd1306_t *dev)
return -EIO;
}
static int sh1106_go_coordinate(const ssd1306_t *dev, uint8_t x, uint8_t y) {
if (x >= dev->width || y >= (dev->height/8)) return -EINVAL;
static int sh1106_go_coordinate(const ssd1306_t *dev, uint8_t x, uint8_t y)
{
if (x >= dev->width || y >= (dev->height / 8))
return -EINVAL;
int err = 0;
x+=2 ; //offset : panel is 128 ; RAM is 132 for sh1106
x += 2; //offset : panel is 128 ; RAM is 132 for sh1106
if ((err = ssd1306_command(dev, SH1106_SET_PAGE_ADDRESS + y))) // Set row
return err;
if ((err = ssd1306_command(dev, SH1106_SET_LOW_COL_ADDR | (x & 0xf)))) // Set lower column address
@ -237,11 +238,10 @@ int ssd1306_load_frame_buffer(const ssd1306_t *dev, uint8_t buf[])
uint16_t i;
uint8_t j;
#if (SSD1306_I2C_SUPPORT)
uint8_t tab[16] = { 0 } ;
uint8_t tab[16] = { 0 };
#endif
size_t len = dev->width * dev->height / 8;
if(dev->screen == SSD1306_SCREEN)
{
if (dev->screen == SSD1306_SCREEN) {
ssd1306_set_column_addr(dev, 0, dev->width - 1);
ssd1306_set_page_addr(dev, 0, dev->height / 8 - 1);
}
@ -249,35 +249,33 @@ int ssd1306_load_frame_buffer(const ssd1306_t *dev, uint8_t buf[])
switch (dev->protocol) {
#if (SSD1306_I2C_SUPPORT)
case SSD1306_PROTO_I2C:
for (i = 0; i < len; i++)
{
if(dev->screen == SH1106_SCREEN && i%dev->width == 0) sh1106_go_coordinate(dev,0,i/dev->width);
for (i = 0; i < len; i++) {
if (dev->screen == SH1106_SCREEN && i % dev->width == 0)
sh1106_go_coordinate(dev, 0, i / dev->width);
i2c_send(dev, 0x40, buf ? &buf[i] : tab, 16);
i+=15 ;
i += 15;
}
break;
#endif
#if (SSD1306_SPI4_SUPPORT)
case SSD1306_PROTO_SPI4:
gpio_write(dev->cs_pin, false);
if(dev->screen == SSD1306_SCREEN)
{
if (dev->screen == SSD1306_SCREEN) {
gpio_write(dev->dc_pin, true); // data mode
if (buf)
spi_transfer(SPI_BUS, buf, NULL, len, SPI_8BIT);
else
spi_repeat_send_8(SPI_BUS,0,len);
spi_repeat_send_8(SPI_BUS, 0, len);
}
else
{
for (i = 0 ; i < (dev->height/8) ; i++) {
sh1106_go_coordinate(dev,0,i);
else {
for (i = 0; i < (dev->height / 8); i++) {
sh1106_go_coordinate(dev, 0, i);
gpio_write(dev->dc_pin, true); // data mode
gpio_write(dev->cs_pin, false);
if (buf)
spi_transfer(SPI_BUS, &buf[dev->width*i], NULL, dev->width, SPI_8BIT);
spi_transfer(SPI_BUS, &buf[dev->width * i], NULL, dev->width, SPI_8BIT);
else
spi_repeat_send_8(SPI_BUS,0,dev->width);
spi_repeat_send_8(SPI_BUS, 0, dev->width);
}
}
gpio_write(dev->cs_pin, true);
@ -286,36 +284,30 @@ int ssd1306_load_frame_buffer(const ssd1306_t *dev, uint8_t buf[])
#if (SSD1306_SPI3_SUPPORT)
case SSD1306_PROTO_SPI3:
gpio_write(dev->cs_pin, false);
if(dev->screen == SSD1306_SCREEN)
{
spi_set_command(SPI_BUS,1,1); // data mode
if (buf)
{
for (i = 0; i < len; i++)
{
if (dev->screen == SSD1306_SCREEN) {
spi_set_command(SPI_BUS, 1, 1); // data mode
if (buf) {
for (i = 0; i < len; i++) {
spi_transfer(SPI_BUS, &buf[i], NULL, 1, SPI_8BIT);
}
}
else
{
for (i = 0; i < len; i++)
{
else {
for (i = 0; i < len; i++) {
spi_transfer_8(SPI_BUS, 0);
}
}
}
else
{
for (i = 0 ; i < (dev->height/8) ; i++) {
sh1106_go_coordinate(dev,0,i);
spi_set_command(SPI_BUS,1,1); // data mode
else {
for (i = 0; i < (dev->height / 8); i++) {
sh1106_go_coordinate(dev, 0, i);
spi_set_command(SPI_BUS, 1, 1); // data mode
gpio_write(dev->cs_pin, false);
if (buf)
for (j = 0 ; j < dev->width ; j++)
spi_transfer_8(SPI_BUS, buf[dev->width*i+j]);
for (j = 0; j < dev->width; j++)
spi_transfer_8(SPI_BUS, buf[dev->width * i + j]);
else
for (j = 0 ; j < dev->width ; j++)
spi_transfer_8(SPI_BUS, buf[dev->width*i+j]);
for (j = 0; j < dev->width; j++)
spi_transfer_8(SPI_BUS, buf[dev->width * i + j]);
}
}
spi_clear_command(SPI_BUS);
@ -357,41 +349,38 @@ int ssd1306_set_display_offset(const ssd1306_t *dev, uint8_t offset)
int sh1106_set_charge_pump_voltage(const ssd1306_t *dev, sh1106_voltage_t select)
{
if (dev->screen == SSD1306_SCREEN)
{
if (dev->screen == SSD1306_SCREEN) {
debug("Unsupported screen type");
return -ENOTSUP ;
return -ENOTSUP;
}
return ssd1306_command(dev, select | SH1106_CHARGE_PUMP_VALUE);
}
int ssd1306_set_charge_pump_enabled(const ssd1306_t *dev, bool enabled)
{
int err = 0;
switch (dev->screen) {
case SH1106_SCREEN:
if ((err = ssd1306_command(dev, SH1106_SET_CHARGE_PUMP)))
return err;
return ssd1306_command(dev, enabled ? SH1106_CHARGE_PUMP_EN : SH1106_CHARGE_PUMP_DIS);
break;
case SSD1306_SCREEN:
if ((err = ssd1306_command(dev, SSD1306_SET_CHARGE_PUMP)))
return err;
return ssd1306_command(dev, enabled ? SSD1306_CHARGE_PUMP_EN : SSD1306_CHARGE_PUMP_DIS);
break;
default:
debug("Unsupported screen type");
return -ENOTSUP;
case SH1106_SCREEN:
if ((err = ssd1306_command(dev, SH1106_SET_CHARGE_PUMP)))
return err;
return ssd1306_command(dev, enabled ? SH1106_CHARGE_PUMP_EN : SH1106_CHARGE_PUMP_DIS);
break;
case SSD1306_SCREEN:
if ((err = ssd1306_command(dev, SSD1306_SET_CHARGE_PUMP)))
return err;
return ssd1306_command(dev, enabled ? SSD1306_CHARGE_PUMP_EN : SSD1306_CHARGE_PUMP_DIS);
break;
default:
debug("Unsupported screen type");
return -ENOTSUP;
}
}
int ssd1306_set_mem_addr_mode(const ssd1306_t *dev, ssd1306_mem_addr_mode_t mode)
{
if (dev->screen == SH1106_SCREEN)
{
if (dev->screen == SH1106_SCREEN) {
debug("Unsupported screen type");
return -ENOTSUP ;
return -ENOTSUP;
}
int err = 0;
if ((err = ssd1306_command(dev, SSD1306_SET_MEM_ADDR_MODE)))
@ -498,7 +487,7 @@ int ssd1306_set_deseltct_lvl(const ssd1306_t *dev, uint8_t lvl)
int ssd1306_set_whole_display_lighting(const ssd1306_t *dev, bool light)
{
return ssd1306_command(dev, light ? SSD1306_SET_ENTIRE_DISP_ON : SSD1306_SET_ENTIRE_DISP_OFF);
return ssd1306_command(dev, light ? SSD1306_SET_ENTIRE_DISP_ON : SSD1306_SET_ENTIRE_DISP_OFF);
}
/* one byte of xbm - 8 dots in line of picture source
@ -510,9 +499,9 @@ int ssd1306_load_xbm(const ssd1306_t *dev, uint8_t *xbm, uint8_t *fb)
int row = 0;
int column = 0;
for (row = 0; row < dev->height; row ++) {
for (row = 0; row < dev->height; row++) {
for (column = 0; column < dev->width / 8; column++) {
uint16_t xbm_offset = row * 16 + column;
uint16_t xbm_offset = row * 16 + column;
for (bit = 0; bit < 8; bit++) {
if (*(xbm + xbm_offset) & 1 << bit) {
*(fb + dev->width * (row / 8) + column * 8 + bit) |= 1 << row % 8;
@ -532,19 +521,18 @@ int ssd1306_draw_pixel(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y, ss
return -EINVAL;
index = x + (y / 8) * dev->width;
switch (color)
{
case OLED_COLOR_WHITE:
fb[index] |= (1 << (y & 7));
break;
case OLED_COLOR_BLACK:
fb[index] &= ~(1 << (y & 7));
break;
case OLED_COLOR_INVERT:
fb[index] ^= (1 << (y & 7));
break;
default:
break;
switch (color) {
case OLED_COLOR_WHITE:
fb[index] |= (1 << (y & 7));
break;
case OLED_COLOR_BLACK:
fb[index] &= ~(1 << (y & 7));
break;
case OLED_COLOR_INVERT:
fb[index] ^= (1 << (y & 7));
break;
default:
break;
}
return 0;
}
@ -565,32 +553,28 @@ int ssd1306_draw_hline(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y, ui
t = w;
index = x + (y / 8) * dev->width;
mask = 1 << (y & 7);
switch (color)
{
case OLED_COLOR_WHITE:
while (t--)
{
fb[index] |= mask;
++index;
}
break;
case OLED_COLOR_BLACK:
mask = ~mask;
while (t--)
{
fb[index] &= mask;
++index;
}
break;
case OLED_COLOR_INVERT:
while (t--)
{
fb[index] ^= mask;
++index;
}
break;
default:
break;
switch (color) {
case OLED_COLOR_WHITE:
while (t--) {
fb[index] |= mask;
++index;
}
break;
case OLED_COLOR_BLACK:
mask = ~mask;
while (t--) {
fb[index] &= mask;
++index;
}
break;
case OLED_COLOR_INVERT:
while (t--) {
fb[index] ^= mask;
++index;
}
break;
default:
break;
}
return 0;
}
@ -619,19 +603,18 @@ int ssd1306_draw_vline(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y, ui
mask = premask[mod];
if (t < mod)
mask &= (0xFF >> (mod - t));
switch (color)
{
case OLED_COLOR_WHITE:
fb[index] |= mask;
break;
case OLED_COLOR_BLACK:
fb[index] &= ~mask;
break;
case OLED_COLOR_INVERT:
fb[index] ^= mask;
break;
default:
break;
switch (color) {
case OLED_COLOR_WHITE:
fb[index] |= mask;
break;
case OLED_COLOR_BLACK:
fb[index] &= ~mask;
break;
case OLED_COLOR_INVERT:
fb[index] ^= mask;
break;
default:
break;
}
if (t < mod)
@ -640,35 +623,31 @@ int ssd1306_draw_vline(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y, ui
index += dev->width;
}
if (t >= 8) // byte aligned line at middle
{
switch (color)
{
case OLED_COLOR_WHITE:
do
{
fb[index] = 0xff;
index += dev->width;
t -= 8;
} while (t >= 8);
break;
case OLED_COLOR_BLACK:
do
{
fb[index] = 0x00;
index += dev->width;
t -= 8;
} while (t >= 8);
break;
case OLED_COLOR_INVERT:
do
{
fb[index] = ~fb[index];
index += dev->width;
t -= 8;
} while (t >= 8);
break;
default:
break;
switch (color) {
case OLED_COLOR_WHITE:
do {
fb[index] = 0xff;
index += dev->width;
t -= 8;
} while (t >= 8);
break;
case OLED_COLOR_BLACK:
do {
fb[index] = 0x00;
index += dev->width;
t -= 8;
} while (t >= 8);
break;
case OLED_COLOR_INVERT:
do {
fb[index] = ~fb[index];
index += dev->width;
t -= 8;
} while (t >= 8);
break;
default:
break;
}
}
if (t) // partial line at bottom
@ -676,19 +655,18 @@ int ssd1306_draw_vline(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y, ui
mod = t & 7;
static const uint8_t postmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
mask = postmask[mod];
switch (color)
{
case OLED_COLOR_WHITE:
fb[index] |= mask;
break;
case OLED_COLOR_BLACK:
fb[index] &= ~mask;
break;
case OLED_COLOR_INVERT:
fb[index] ^= mask;
break;
default:
break;
switch (color) {
case OLED_COLOR_WHITE:
fb[index] |= mask;
break;
case OLED_COLOR_BLACK:
fb[index] &= ~mask;
break;
case OLED_COLOR_INVERT:
fb[index] ^= mask;
break;
default:
break;
}
}
return 0;
@ -706,7 +684,6 @@ int ssd1306_draw_rectangle(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y
return ssd1306_draw_vline(dev, fb, x + w - 1, y, h, color);
}
int ssd1306_fill_rectangle(const ssd1306_t *dev, uint8_t *fb, int8_t x, int8_t y, uint8_t w, uint8_t h, ssd1306_color_t color)
{
// Can be optimized?
@ -738,8 +715,7 @@ int ssd1306_draw_circle(const ssd1306_t *dev, uint8_t *fb, int8_t x0, int8_t y0,
if ((err = ssd1306_draw_pixel(dev, fb, x0, y0 + r, color)))
return err;
while (x >= y)
{
while (x >= y) {
if ((err = ssd1306_draw_pixel(dev, fb, x0 + x, y0 + y, color)))
return err;
if ((err = ssd1306_draw_pixel(dev, fb, x0 - x, y0 + y, color)))
@ -748,8 +724,7 @@ int ssd1306_draw_circle(const ssd1306_t *dev, uint8_t *fb, int8_t x0, int8_t y0,
return err;
if ((err = ssd1306_draw_pixel(dev, fb, x0 - x, y0 - y, color)))
return err;
if (x != y)
{
if (x != y) {
/* Otherwise the 4 drawings below are the same as above, causing
* problem when color is INVERT
*/
@ -788,14 +763,12 @@ int ssd1306_fill_circle(const ssd1306_t *dev, uint8_t *fb, int8_t x0, int8_t y0,
if ((err = ssd1306_draw_vline(dev, fb, x0, y0 - r, 2 * r + 1, color))) // Center vertical line
return err;
while (y >= x)
{
while (y >= x) {
if ((err = ssd1306_draw_vline(dev, fb, x0 - x, y0 - y, 2 * y + 1, color)))
return err;
if ((err = ssd1306_draw_vline(dev, fb, x0 + x, y0 - y, 2 * y + 1, color)))
return err;
if (color != OLED_COLOR_INVERT)
{
if (color != OLED_COLOR_INVERT) {
if ((err = ssd1306_draw_vline(dev, fb, x0 - y, y0 - x, 2 * x + 1, color)))
return err;
if ((err = ssd1306_draw_vline(dev, fb, x0 + y, y0 - x, 2 * x + 1, color)))
@ -811,8 +784,7 @@ int ssd1306_fill_circle(const ssd1306_t *dev, uint8_t *fb, int8_t x0, int8_t y0,
}
}
if (color == OLED_COLOR_INVERT)
{
if (color == OLED_COLOR_INVERT) {
x1 = x; // Save where we stopped
y = 1;
@ -822,15 +794,14 @@ int ssd1306_fill_circle(const ssd1306_t *dev, uint8_t *fb, int8_t x0, int8_t y0,
return err;
if ((err = ssd1306_draw_hline(dev, fb, x0 - r, y0, r - x1 + 1, color)))
return err;
while (x >= y)
{
while (x >= y) {
if ((err = ssd1306_draw_hline(dev, fb, x0 + x1, y0 - y, x - x1 + 1, color)))
return err;
if ((err = ssd1306_draw_hline(dev, fb, x0 + x1, y0 + y, x - x1 + 1, color)))
return err;
if ((err = ssd1306_draw_hline(dev, fb, x0 - x, y0 - y, x - x1 + 1, color)))
if ((err = ssd1306_draw_hline(dev, fb, x0 - x, y0 - y, x - x1 + 1, color)))
return err;
if ((err = ssd1306_draw_hline(dev, fb, x0 - x, y0 + y, x - x1 + 1, color)))
if ((err = ssd1306_draw_hline(dev, fb, x0 - x, y0 + y, x - x1 + 1, color)))
return err;
++y;
if (radius_err < 0) {
@ -845,59 +816,59 @@ int ssd1306_fill_circle(const ssd1306_t *dev, uint8_t *fb, int8_t x0, int8_t y0,
return 0;
}
int ssd1306_draw_line(const ssd1306_t *dev, uint8_t *fb, int16_t x0, int16_t y0,
int16_t x1, int16_t y1, ssd1306_color_t color)
int ssd1306_draw_line(const ssd1306_t *dev, uint8_t *fb, int16_t x0, int16_t y0, int16_t x1, int16_t y1, ssd1306_color_t color)
{
if ((x0 >= dev->width) || (x0 < 0) || (y0 >= dev->height) || (y0 < 0))
return -EINVAL;
if ((x1 >= dev->width) || (x1 < 0) || (y1 >= dev->height) || (y1 < 0))
return -EINVAL;
int err;
bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
int err;
bool steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t errx = dx / 2;
int16_t ystep;
int16_t errx = dx / 2;
int16_t ystep;
if (y0 < y1) {
ystep = 1;
} else {
ystep = -1;
}
if (y0 < y1) {
ystep = 1;
}
else {
ystep = -1;
}
for (; x0 <= x1; x0++) {
if (steep) {
if ((err = ssd1306_draw_pixel(dev, fb, y0, x0, color)))
return err;
} else {
if ((err = ssd1306_draw_pixel(dev, fb, x0, y0, color)))
return err;
}
errx -= dy;
if (errx < 0) {
y0 += ystep;
errx += dx;
}
}
return 0;
if (steep) {
if ((err = ssd1306_draw_pixel(dev, fb, y0, x0, color)))
return err;
}
else {
if ((err = ssd1306_draw_pixel(dev, fb, x0, y0, color)))
return err;
}
errx -= dy;
if (errx < 0) {
y0 += ystep;
errx += dx;
}
}
return 0;
}
int ssd1306_draw_triangle(const ssd1306_t *dev, uint8_t *fb, int16_t x0,
int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2,
ssd1306_color_t color)
int ssd1306_draw_triangle(const ssd1306_t *dev, uint8_t *fb, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2,
ssd1306_color_t color)
{
int err;
if ((err = ssd1306_draw_line(dev, fb, x0, y0, x1, y1, color)))
@ -907,25 +878,24 @@ int ssd1306_draw_triangle(const ssd1306_t *dev, uint8_t *fb, int16_t x0,
return ssd1306_draw_line(dev, fb, x2, y2, x0, y0, color);
}
int ssd1306_fill_triangle(const ssd1306_t *dev, uint8_t *fb, int16_t x0,
int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2,
ssd1306_color_t color)
int ssd1306_fill_triangle(const ssd1306_t *dev, uint8_t *fb, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2,
ssd1306_color_t color)
{
int16_t a, b, y, last;
int err;
// Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1) {
swap(y0, y1); swap(x0, x1);
swap(y0, y1);swap(x0, x1);
}
if (y1 > y2) {
swap(y2, y1); swap(x2, x1);
swap(y2, y1);swap(x2, x1);
}
if (y0 > y1) {
swap(y0, y1); swap(x0, x1);
swap(y0, y1);swap(x0, x1);
}
if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
a = b = x0;
if (x1 < a) a = x1;
else if (x1 > b) b = x1;
@ -984,14 +954,13 @@ int ssd1306_fill_triangle(const ssd1306_t *dev, uint8_t *fb, int16_t x0,
*/
if (a > b) swap(a, b);
if ((err = ssd1306_draw_hline(dev, fb, a, y, b - a + 1, color)))
return err;
return err;
}
return 0;
}
int ssd1306_draw_char(const ssd1306_t *dev, uint8_t *fb,
const font_info_t *font, uint8_t x, uint8_t y, char c,
ssd1306_color_t foreground, ssd1306_color_t background)
int ssd1306_draw_char(const ssd1306_t *dev, uint8_t *fb, const font_info_t *font, uint8_t x, uint8_t y, char c, ssd1306_color_t foreground,
ssd1306_color_t background)
{
uint8_t i, j;
const uint8_t *bitmap;
@ -1015,30 +984,29 @@ int ssd1306_draw_char(const ssd1306_t *dev, uint8_t *fb,
err = ssd1306_draw_pixel(dev, fb, x + i, y + j, foreground);
}
else {
switch (background)
{
case OLED_COLOR_TRANSPARENT:
// Not drawing for transparent background
break;
case OLED_COLOR_WHITE:
case OLED_COLOR_BLACK:
err = ssd1306_draw_pixel(dev, fb, x + i, y + j, background);
break;
case OLED_COLOR_INVERT:
// I don't know why I need invert background
break;
switch (background) {
case OLED_COLOR_TRANSPARENT:
// Not drawing for transparent background
break;
case OLED_COLOR_WHITE:
case OLED_COLOR_BLACK:
err = ssd1306_draw_pixel(dev, fb, x + i, y + j, background);
break;
case OLED_COLOR_INVERT:
// I don't know why I need invert background
break;
}
}
if (err) return -ERANGE ;
if (err)
return -ERANGE;
line = line << 1;
}
}
return d->width;
}
int ssd1306_draw_string(const ssd1306_t *dev, uint8_t *fb,
const font_info_t *font, uint8_t x, uint8_t y, char *str,
ssd1306_color_t foreground, ssd1306_color_t background)
int ssd1306_draw_string(const ssd1306_t *dev, uint8_t *fb, const font_info_t *font, uint8_t x, uint8_t y, char *str,
ssd1306_color_t foreground, ssd1306_color_t background)
{
uint8_t t = x;
int err;
@ -1046,11 +1014,10 @@ int ssd1306_draw_string(const ssd1306_t *dev, uint8_t *fb,
if (font == NULL || str == NULL)
return 0;
while (*str)
{
if ((err = ssd1306_draw_char(dev, fb, font, x, y, *str, foreground, background)) < 0 )
while (*str) {
if ((err = ssd1306_draw_char(dev, fb, font, x, y, *str, foreground, background)) < 0)
return err;
x += err;
x += err;
++str;
if (*str)
x += font->c;
@ -1067,13 +1034,11 @@ int ssd1306_start_scroll_hori(const ssd1306_t *dev, bool way, uint8_t start, uin
{
int err;
if (way)
{
if (way) {
if ((err = ssd1306_command(dev, SSD1306_SCROLL_HOR_LEFT)))
return err;
}
else
{
else {
if ((err = ssd1306_command(dev, SSD1306_SCROLL_HOR_RIGHT)))
return err;
}
@ -1089,7 +1054,7 @@ int ssd1306_start_scroll_hori(const ssd1306_t *dev, bool way, uint8_t start, uin
return -EIO;
}
int ssd1306_start_scroll_hori_vert(const ssd1306_t *dev, bool way, uint8_t start, uint8_t stop, uint8_t dy, ssd1306_scroll_t frame)
int ssd1306_start_scroll_hori_vert(const ssd1306_t *dev, bool way, uint8_t start, uint8_t stop, uint8_t dy, ssd1306_scroll_t frame)
{
//this function dont work well if no vertical setting.
if ((!dy) || (dy > 63))
@ -1104,13 +1069,11 @@ int ssd1306_start_scroll_hori_vert(const ssd1306_t *dev, bool way, uint8_t star
if ((err = ssd1306_command(dev, dev->height)))
return err;
if (way)
{
if (way) {
if ((err = ssd1306_command(dev, SSD1306_SCROLL_HOR_VER_LEFT)))
return err;
}
else
{
else {
if ((err = ssd1306_command(dev, SSD1306_SCROLL_HOR_VER_RIGHT)))
return err;
}

View file

@ -65,13 +65,16 @@ typedef enum
typedef struct
{
ssd1306_protocol_t protocol;
ssd1306_screen_t screen ;
union {
ssd1306_screen_t screen;
union
{
#if (SSD1306_I2C_SUPPORT)
i2c_dev_t i2c_dev; //!< I2C devuce descriptor, used by SSD1306_PROTO_I2C
#endif
uint8_t cs_pin ; //!< Chip Select GPIO pin, used by SSD1306_PROTO_SPI3, SSD1306_PROTO_SPI4
} ;
#if (SSD1306_SPI4_SUPPORT) || (SSD1306_SPI3_SUPPORT)
uint8_t cs_pin; //!< Chip Select GPIO pin, used by SSD1306_PROTO_SPI3, SSD1306_PROTO_SPI4
#endif
};
#if (SSD1306_SPI4_SUPPORT)
uint8_t dc_pin; //!< Data/Command GPIO pin, used by SSD1306_PROTO_SPI4
#endif
@ -520,10 +523,9 @@ int ssd1306_start_scroll_hori(const ssd1306_t *dev, bool way, uint8_t start, uin
* @param frame Time interval between each scroll
* @return Non-zero if error occured
*/
int ssd1306_start_scroll_hori_vert(const ssd1306_t *dev, bool way, uint8_t start, uint8_t stop, uint8_t dy, ssd1306_scroll_t frame);
int ssd1306_start_scroll_hori_vert(const ssd1306_t *dev, bool way, uint8_t start, uint8_t stop, uint8_t dy, ssd1306_scroll_t frame);
#ifdef __cplusplus
extern "C"
}
#endif

View file

@ -95,26 +95,33 @@
#define B8C 0x0000 // 0.000 * 2^LUX_SCALE
#define M8C 0x0000 // 0.000 * 2^LUX_SCALE
static int write_register(i2c_dev_t* i2c_dev, uint8_t reg, uint8_t value)
#ifdef TSL2561_DEBUG
#include <stdio.h>
#define debug(fmt, ...) printf("%s: " fmt "\n", "TSL2561", ## __VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
static int write_register(i2c_dev_t *i2c_dev, uint8_t reg, uint8_t value)
{
reg = TSL2561_REG_COMMAND | reg;
return i2c_slave_write(i2c_dev->bus, i2c_dev->addr, &reg, &value, 1);
}
static uint8_t read_register(i2c_dev_t* i2c_dev, uint8_t reg)
static uint8_t read_register(i2c_dev_t *i2c_dev, uint8_t reg)
{
uint8_t data[1];
reg = TSL2561_REG_COMMAND | reg;
if (i2c_slave_read(i2c_dev->bus, i2c_dev->addr, &reg, data, 1))
{
printf("Error in tsl2561 read_register\n");
debug("Error in tsl2561 read_register\n");
}
return data[0];
}
static uint16_t read_register_16(i2c_dev_t* i2c_dev, uint8_t low_register_addr)
static uint16_t read_register_16(i2c_dev_t *i2c_dev, uint8_t low_register_addr)
{
uint16_t value = 0;
uint8_t data[2];
@ -122,7 +129,7 @@ static uint16_t read_register_16(i2c_dev_t* i2c_dev, uint8_t low_register_addr)
if (i2c_slave_read(i2c_dev->bus, i2c_dev->addr, &low_register_addr, data, 2))
{
printf("Error with i2c_slave_read in read_register_16\n");
debug("Error with i2c_slave_read in read_register_16\n");
}
value = ((uint16_t)data[1] << 8) | (data[0]);
@ -130,12 +137,12 @@ static uint16_t read_register_16(i2c_dev_t* i2c_dev, uint8_t low_register_addr)
return value;
}
static int enable(i2c_dev_t* i2c_dev)
static int enable(i2c_dev_t *i2c_dev)
{
return write_register(i2c_dev, TSL2561_REG_CONTROL, TSL2561_ON);
}
static int disable(i2c_dev_t* i2c_dev)
static int disable(i2c_dev_t *i2c_dev)
{
return write_register(i2c_dev, TSL2561_REG_CONTROL, TSL2561_OFF);
}
@ -144,14 +151,14 @@ void tsl2561_init(tsl2561_t *device)
{
if (enable(&device->i2c_dev))
{
printf("Error initializing tsl2561\n");
debug("Error initializing tsl2561\n");
}
uint8_t control_reg = (read_register(&device->i2c_dev, TSL2561_REG_CONTROL) & TSL2561_ON);
if (control_reg != TSL2561_ON)
{
printf("Error initializing tsl2561, control register wasn't set to ON\n");
debug("Error initializing tsl2561, control register wasn't set to ON\n");
}
// Fetch the package type
@ -348,7 +355,7 @@ bool tsl2561_read_lux(tsl2561_t *device, uint32_t *lux)
break;
default:
printf("Invalid package type in CalculateLux\n");
debug("Invalid package type in CalculateLux\n");
b = 0;
m = 0;
success = false;

View file

@ -26,13 +26,13 @@
#define TSL4531_INTEGRATION_TIME_200MS 240
#define TSL4531_INTEGRATION_TIME_400MS 480 // Default
static int write_register(i2c_dev_t* i2c_dev, uint8_t reg, uint8_t value)
static int write_register(i2c_dev_t *i2c_dev, uint8_t reg, uint8_t value)
{
reg = TSL4531_REG_COMMAND | reg;
return i2c_slave_write(i2c_dev->bus, i2c_dev->addr, &reg, &value, 1);
}
static uint8_t read_register(i2c_dev_t* i2c_dev, uint8_t reg)
static uint8_t read_register(i2c_dev_t *i2c_dev, uint8_t reg)
{
uint8_t data[1];
reg = TSL4531_REG_COMMAND | reg;
@ -45,7 +45,7 @@ static uint8_t read_register(i2c_dev_t* i2c_dev, uint8_t reg)
return data[0];
}
static uint16_t read_register_16(i2c_dev_t* i2c_dev, uint8_t low_register_addr)
static uint16_t read_register_16(i2c_dev_t *i2c_dev, uint8_t low_register_addr)
{
uint16_t value = 0;
uint8_t data[2];

View file

@ -21,6 +21,7 @@
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <espressif/esp_common.h>
#include <espressif/user_interface.h>
@ -1722,9 +1723,9 @@ static void dns_task(void *pvParameters)
const struct ifreq ifreq0 = { "en0" };
const struct ifreq ifreq1 = { "en1" };
lwip_setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
sdk_wifi_get_opmode() == STATIONAP_MODE ? &ifreq1 : &ifreq0,
sizeof(ifreq0));
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
sdk_wifi_get_opmode() == STATIONAP_MODE ? &ifreq1 : &ifreq0,
sizeof(ifreq0));
for (;;) {
char buffer[96];
@ -1846,7 +1847,7 @@ void wificfg_init(uint32_t port, const wificfg_dispatch *dispatch)
/* If the ssid and password are not valid then disable the AP interface. */
if (!wifi_ap_ssid || strlen(wifi_ap_ssid) < 1 || strlen(wifi_ap_ssid) >= 32 ||
!wifi_ap_password || strlen(wifi_ap_ssid) < 8 || strlen(wifi_ap_password) >= 64) {
!wifi_ap_password || strlen(wifi_ap_password) < 8 || strlen(wifi_ap_password) >= 64) {
wifi_ap_enable = 0;
}
}
@ -1856,7 +1857,7 @@ void wificfg_init(uint32_t port, const wificfg_dispatch *dispatch)
wifi_mode = STATIONAP_MODE;
else if (wifi_sta_enable)
wifi_mode = STATION_MODE;
else
else if (wifi_ap_enable)
wifi_mode = SOFTAP_MODE;
sdk_wifi_set_opmode(wifi_mode);
@ -2013,10 +2014,12 @@ void wificfg_init(uint32_t port, const wificfg_dispatch *dispatch)
if (wifi_ap_ssid) free(wifi_ap_ssid);
if (wifi_ap_password) free(wifi_ap_password);
server_params *params = malloc(sizeof(server_params));
params->port = port;
params->wificfg_dispatch = wificfg_dispatch_list;
params->dispatch = dispatch;
if (wifi_mode != NULL_MODE) {
server_params *params = malloc(sizeof(server_params));
params->port = port;
params->wificfg_dispatch = wificfg_dispatch_list;
params->dispatch = dispatch;
xTaskCreate(server_task, "WiFi Cfg HTTP", 464, params, 2, NULL);
xTaskCreate(server_task, "WiFi Cfg HTTP", 464, params, 2, NULL);
}
}

View file

@ -31,11 +31,14 @@
extern "C" {
#endif
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t white;
typedef union {
struct {
uint8_t blue; //LSB
uint8_t green;
uint8_t red;
uint8_t white;
};
uint32_t color; // 0xWWRRGGBB
} ws2812_pixel_t;
typedef enum {

View file

@ -39,6 +39,7 @@
#include <sys/time.h>
#include <sys/errno.h>
#include <esp/hwrand.h>
#include <fcntl.h>
struct ip4_addr;
struct esf_buf;

View file

@ -144,6 +144,15 @@
------------------------------------------------
*/
/**
* MEMP_NUM_NETCONN: the number of struct netconns.
* (only needed if you use the sequential API, like api_lib.c)
* This also sets the number of lwip socket descriptors.
*/
#ifndef MEMP_NUM_NETCONN
#define MEMP_NUM_NETCONN 12
#endif
/*
--------------------------------
---------- ARP options -------
@ -549,6 +558,25 @@
---------- Socket options ----------
------------------------------------
*/
/**
* LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names.
* Disable this option if you use a POSIX operating system that uses the same
* names (read, write & close). (only used if you use sockets.c)
*/
#define LWIP_POSIX_SOCKETS_IO_NAMES 0
/**
* LWIP_SOCKET_OFFSET==n: Increases the file descriptor number created by LwIP with n.
* This can be useful when there are multiple APIs which create file descriptors.
* When they all start with a different offset and you won't make them overlap you can
* re implement read/write/close/ioctl/fnctl to send the requested action to the right
* library (sharing select will need more work though).
*/
#ifndef LWIP_SOCKET_OFFSET
#define LWIP_SOCKET_OFFSET 3
#endif
/**
* LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
* SO_SNDTIMEO processing.

@ -1 +1 @@
Subproject commit 74676d46f0bc5ed82515f8e247008b7c45ec6cf6
Subproject commit f372198834caf642c4b2d29d98fc1e120e3ce2b2

View file

@ -40,6 +40,11 @@ DEFINE_TESTCASE(04_wifi_basic, DUAL)
static void server_task(void *pvParameters)
{
ip_addr_t first_client_ip;
IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
dhcpserver_start(&first_client_ip, 4);
char buf[BUF_SIZE];
struct netconn *nc = netconn_new(NETCONN_TCP);
TEST_ASSERT_TRUE_MESSAGE(nc != 0, "Failed to allocate socket");
@ -103,10 +108,6 @@ static void a_04_wifi_basic(void)
};
sdk_wifi_softap_set_config(&ap_config);
ip_addr_t first_client_ip;
IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
dhcpserver_start(&first_client_ip, 4);
xTaskCreate(server_task, "setver_task", 1024, NULL, 2, NULL);
}