Loading sht30/CMakeLists.txt +9 −6 Original line number Diff line number Diff line Loading @@ -7,16 +7,19 @@ project(sht30 C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) pico_sdk_init() list(APPEND PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}) set(PICO_CYW43_SUPPORTED "1") pico_register_common_scope_var(PICO_CYW43_SUPPORTED) pico_sdk_init() add_executable(${PROJECT_NAME} main.c) file(GLOB SRC *.cpp) add_executable(${PROJECT_NAME} ${SRC}) pico_enable_stdio_usb(${PROJECT_NAME} 1) pico_enable_stdio_uart(${PROJECT_NAME} 1) pico_add_extra_outputs(${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) target_link_libraries(${PROJECT_NAME} pico_cyw43_arch_lwip_threadsafe_background pico_stdlib hardware_i2c) pico_add_extra_outputs(${PROJECT_NAME}) No newline at end of file sht30/grove_shield.h→sht30/grove_pico_w.h +21 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ // no PICO_DEFAULT_WS2812_PIN // --- I2C --- #ifndef PICO_DEFAULT_I2C #define PICO_DEFAULT_I2C 0 #endif Loading @@ -43,6 +44,26 @@ #define PICO_DEFAULT_I2C_SCL_PIN 9 #endif #ifndef PICO_I2C0 #define PICO_I2C0 0 #endif #ifndef PICO_I2C0_SDA_PIN #define PICO_I2C0_SDA_PIN 8 #endif #ifndef PICO_I2C0_SCL_PIN #define PICO_I2C0_SCL_PIN 9 #endif #ifndef PICO_I2C1 #define PICO_I2C1 1 #endif #ifndef PICO_I2C1_SDA_PIN #define PICO_I2C1_SDA_PIN 6 #endif #ifndef PICO_I2C1_SCL_PIN #define PICO_I2C1_SCL_PIN 7 #endif // --- SPI --- #ifndef PICO_DEFAULT_SPI #define PICO_DEFAULT_SPI 0 Loading sht30/main.cdeleted 100644 → 0 +0 −77 Original line number Diff line number Diff line /** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // Sweep through all 7-bit I2C addresses, to see if any slaves are present on // the I2C bus. Print out a table that looks like this: // // I2C Bus Scan // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 // 1 @ // 2 // 3 @ // 4 // 5 // 6 // 7 // // E.g. if slave addresses 0x12 and 0x34 were acknowledged. #include <stdio.h> #include "pico/stdlib.h" #include "pico/binary_info.h" #include "hardware/i2c.h" // I2C reserves some addresses for special purposes. We exclude these from the scan. // These are any addresses of the form 000 0xxx or 111 1xxx bool reserved_addr(uint8_t addr) { return (addr & 0x78) == 0 || (addr & 0x78) == 0x78; } int main() { // Enable UART so we can print status output stdio_init_all(); #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) #warning i2c/bus_scan example requires a board with I2C pins puts("Default I2C pins were not defined"); #else // This example will use I2C0 on the default SDA and SCL pins (GP4, GP5 on a Pico) i2c_init(i2c_default, 100 * 1000); gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN); gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); printf("\nI2C Bus Scan\n"); printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); for (int addr = 0; addr < (1 << 7); ++addr) { if (addr % 16 == 0) { printf("%02x ", addr); } // Perform a 1-byte dummy read from the probe address. If a slave // acknowledges this address, the function returns the number of bytes // transferred. If the address byte is ignored, the function returns // -1. // Skip over any reserved addresses. int ret; uint8_t rxdata; if (reserved_addr(addr)) ret = PICO_ERROR_GENERIC; else ret = i2c_read_blocking(i2c_default, addr, &rxdata, 1, false); printf(ret < 0 ? "." : "@"); printf(addr % 16 == 15 ? "\n" : " "); } printf("Done.\n"); return 0; #endif } sht30/main.cpp 0 → 100644 +91 −0 Original line number Diff line number Diff line /** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // Sweep through all 7-bit I2C addresses, to see if any slaves are present on // the I2C bus. Print out a table that looks like this: // // I2C Bus Scan // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 // 1 @ // 2 // 3 @ // 4 // 5 // 6 // 7 // // E.g. if slave addresses 0x12 and 0x34 were acknowledged. #include <stdio.h> #include "pico/stdlib.h" #include "pico/cyw43_arch.h" #include "pico/binary_info.h" #include "hardware/i2c.h" #include "sht30.h" volatile bool error = false; bool callback(struct repeating_timer *t) { SHT30 *sht = (SHT30*)t->user_data; SHT30::measure mes = {0, 0}; if (!sht->single_measure(SHT30::HIGH, mes)) { error = true; return false; } printf("Temperature: %f, Humidity: %f %\n", mes.temperature, mes.humidity); return true; } int main() { struct repeating_timer timer; SHT30::measure mes = {0, 0}; stdio_init_all(); printf("\n*** Init SHT30 sensor ***\n"); SHT30 sht; if (!sht.is_connected()) { printf("Error: SHT30 is not connected\n"); return -1; } printf("SHT30 connected\n"); if (!sht.read_serial_number()) goto end; // if (!sht.single_measure(SHT30::HIGH, mes)) // goto end; // printf("Temperature: %f, Humidity: %f %\n", mes.temperature, mes.humidity); // add_repeating_timer_ms(2000, callback, &sht, &timer); while(!error) { if (!sht.single_measure(SHT30::HIGH, mes)) goto end; printf("Temperature: %f, Humidity: %f %\n", mes.temperature, mes.humidity); sleep_ms(1000); } printf("An error occurred stopping\n"); uint16_t status_reg; if (sht.read_status_reg(status_reg)) printf("Status reg: 0x%04X\n", status_reg); end: printf("Exiting\n"); return 1; } No newline at end of file sht30/sht30.cpp 0 → 100644 +371 −0 Original line number Diff line number Diff line #include <stdio.h> #include <string.h> #include "pico/stdlib.h" #include "pico/binary_info.h" #include "pico/time.h" #include "pico.h" #include "sht30.h" #define I2C_DEFAULT_BAUD 100*1000 // 100 kHz #define MILLI_SEC 1000 // us #define I2C_DEFAULT_TIMEOUT 25 * MILLI_SEC // SHT30 #define SHT30_DEFAULT_ADDR 0x44 // SHT30 Commands #define SHT30_CMD_LEN 2 #define SHT30_DATA_LEN 6 #define SHT30_READ_SERIAL_NUMBER 0x3780 #define SHT30_SOFT_RESET 0x30A2 #define SHT30_READ_STATUS_REG 0xF32D #define SHT30_CLEAR_STATUS_REG 0x3041 #define SHT30_ENABLE_HEATER 0x306D #define SHT30_DISABLE_HEATER 0x3066 #define SHT30_NO_SLEEP 0x303E #define SHT30_SINGLE_STRETCH 0x2C #define SHT30_SINGLE_NO_STRETCH 0x24 // SHT30 Error codes #define SHT30_CRC_ERROR (PICO_ERROR_INSUFFICIENT_RESOURCES - 1) // SHT30 conversion #define SHT30_DIVIDER ((0x01 << 16) - 1) #define SHT30_NEG_CELSIUS -45.0 #define SHT30_MULTI_CELSIUS 175.0 #define SHT30_MULTI_HUMIDITY 100.0 union status_reg_u { uint16_t val; struct { uint8_t write_data_crc_status : 1; uint8_t command_status : 1; uint8_t : 2; // Reserved uint8_t reset_detected : 1; uint8_t : 3; // Reserved uint8_t : 2; // Reserved uint8_t t_tracking_alert : 1; uint8_t rh_tracking_alert : 1; uint8_t : 1; // Reserved uint8_t heater_status : 1; uint8_t : 1; // Reserved uint8_t alert_status : 1; }; }; // Single measure clock stretching enabled const uint8_t single_stretch [][2] = { {SHT30_SINGLE_STRETCH, 0x10}, // Low {SHT30_SINGLE_STRETCH, 0x0D}, // Medium {SHT30_SINGLE_STRETCH, 0x06} // High }; // Single measure clock stretching disabled const uint8_t single_no_stretch [][2] = { {SHT30_SINGLE_NO_STRETCH, 0x16}, // Low {SHT30_SINGLE_NO_STRETCH, 0X0B}, // Medium {SHT30_SINGLE_NO_STRETCH, 0x00}, // High }; const uint8_t crc8x_table[] = { 0x00,0x31,0x62,0x53,0xC4,0xF5,0xA6,0x97,0xB9,0x88,0xDB,0xEA,0x7D,0x4C,0x1F,0x2E, 0x43,0x72,0x21,0x10,0x87,0xB6,0xE5,0xD4,0xFA,0xCB,0x98,0xA9,0x3E,0x0F,0x5C,0x6D, 0x86,0xB7,0xE4,0xD5,0x42,0x73,0x20,0x11,0x3F,0x0E,0x5D,0x6C,0xFB,0xCA,0x99,0xA8, 0xC5,0xF4,0xA7,0x96,0x01,0x30,0x63,0x52,0x7C,0x4D,0x1E,0x2F,0xB8,0x89,0xDA,0xEB, 0x3D,0x0C,0x5F,0x6E,0xF9,0xC8,0x9B,0xAA,0x84,0xB5,0xE6,0xD7,0x40,0x71,0x22,0x13, 0x7E,0x4F,0x1C,0x2D,0xBA,0x8B,0xD8,0xE9,0xC7,0xF6,0xA5,0x94,0x03,0x32,0x61,0x50, 0xBB,0x8A,0xD9,0xE8,0x7F,0x4E,0x1D,0x2C,0x02,0x33,0x60,0x51,0xC6,0xF7,0xA4,0x95, 0xF8,0xC9,0x9A,0xAB,0x3C,0x0D,0x5E,0x6F,0x41,0x70,0x23,0x12,0x85,0xB4,0xE7,0xD6, 0x7A,0x4B,0x18,0x29,0xBE,0x8F,0xDC,0xED,0xC3,0xF2,0xA1,0x90,0x07,0x36,0x65,0x54, 0x39,0x08,0x5B,0x6A,0xFD,0xCC,0x9F,0xAE,0x80,0xB1,0xE2,0xD3,0x44,0x75,0x26,0x17, 0xFC,0xCD,0x9E,0xAF,0x38,0x09,0x5A,0x6B,0x45,0x74,0x27,0x16,0x81,0xB0,0xE3,0xD2, 0xBF,0x8E,0xDD,0xEC,0x7B,0x4A,0x19,0x28,0x06,0x37,0x64,0x55,0xC2,0xF3,0xA0,0x91, 0x47,0x76,0x25,0x14,0x83,0xB2,0xE1,0xD0,0xFE,0xCF,0x9C,0xAD,0x3A,0x0B,0x58,0x69, 0x04,0x35,0x66,0x57,0xC0,0xF1,0xA2,0x93,0xBD,0x8C,0xDF,0xEE,0x79,0x48,0x1B,0x2A, 0xC1,0xF0,0xA3,0x92,0x05,0x34,0x67,0x56,0x78,0x49,0x1A,0x2B,0xBC,0x8D,0xDE,0xEF, 0x82,0xB3,0xE0,0xD1,0x46,0x77,0x24,0x15,0x3B,0x0A,0x59,0x68,0xFF,0xCE,0x9D,0xAC }; void print_status_reg(status_reg_u status) { printf("\nStatus reg value: 0x%04X\n", status.val); printf("Alert: %d\n", status.alert_status); printf("Heater: %d\n", status.heater_status); printf("RH tracking: %d\n", status.rh_tracking_alert); printf("T tracking: %d\n", status.t_tracking_alert); printf("Reset: %d\n", status.reset_detected); printf("CMD status: %d\n", status.command_status); printf("CRC status: %d\n\n", status.write_data_crc_status); } inline uint8_t calculate_crc8x(uint8_t *data, size_t len) { uint8_t crc = 0xFF; for (size_t i = 0; i < len; i++) { crc = crc8x_table[data[i] ^ crc]; } return crc; } int check_crc(uint8_t *data, size_t data_len) { size_t len = data_len - 1; uint8_t crc = data[len]; return calculate_crc8x(data, len) == crc ? 0 : SHT30_CRC_ERROR; } const char* which_error(int error_code) { switch (error_code) { case PICO_ERROR_GENERIC: return "Address not acknowledged or device not present"; case PICO_ERROR_TIMEOUT: return "Timeout reached"; case SHT30_CRC_ERROR: return "CRC don't match"; default: return "OK"; } } float convert_temperature_celsius(uint16_t temp) { float temperature = SHT30_NEG_CELSIUS + (SHT30_MULTI_CELSIUS * (float)temp) / SHT30_DIVIDER; return temperature; } float convert_humidity(uint16_t hum) { float humidity = SHT30_MULTI_HUMIDITY * ((float) hum) / SHT30_DIVIDER; return humidity; } SHT30::SHT30(unsigned int i2c_channel) : i2c_channel_(i2c_channel), connected_(false) { status_reg_u status; switch(i2c_channel_) { case 0: i2c_inst_ = i2c0; i2c_scl_ = PICO_I2C0_SCL_PIN; i2c_sda_ = PICO_I2C0_SDA_PIN; break; case 1: i2c_inst_ = i2c1; i2c_scl_ = PICO_I2C1_SCL_PIN; i2c_sda_ = PICO_I2C1_SDA_PIN; break; default: printf("Error: I2C channel %d not supported\n", i2c_channel_); return; } i2c_init(i2c_inst_, I2C_DEFAULT_BAUD); gpio_set_function(i2c_sda_, GPIO_FUNC_I2C); gpio_set_function(i2c_scl_, GPIO_FUNC_I2C); gpio_pull_up(i2c_scl_); gpio_pull_up(i2c_sda_); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(i2c_sda_, i2c_scl_, GPIO_FUNC_I2C)); if (!reset()) return; if (!write_command(SHT30_NO_SLEEP)) return; if (read_status_reg(status.val)) { if (status.val == 0x8010) connected_ = true; else print_status_reg(status); } } bool SHT30::read_serial_number() { int ret = 0; uint16_t serial_number = 0; if (!write_command(SHT30_READ_SERIAL_NUMBER)) { goto error; } if (!read_response(&serial_number, 1)) goto error; printf("SHT30 serial number: 0x%04X\n", serial_number); return true; error: printf("Error: Failed to read serial number\n"); return false; } bool SHT30::reset() { if(!write_command(SHT30_SOFT_RESET)) { printf("Error: Failed to reset SHT30 device\n"); return false; } return true; } bool SHT30::is_connected() { return connected_; } bool SHT30::write_command(uint8_t *cmd, size_t len) { int ret = 0; if ((ret = i2c_write_blocking(i2c_inst_, SHT30_DEFAULT_ADDR, cmd, len, false)) != len) { printf("Error [%d]: I2C write command 0x", ret); for (size_t i = 0; i < len; i++) { printf("%02X", cmd[i]); } printf(" failed. %s\n", which_error(ret)); return false; } // Since the device needs approx 1 ms before a new command can be sent we sleep. sleep_ms(1); return true; } bool SHT30::write_command(uint16_t cmd) { uint8_t tx_data[] = { (uint8_t)(cmd >> 8), (uint8_t)(cmd & 0xFF) }; return write_command(tx_data, SHT30_CMD_LEN); } bool SHT30::read_response(uint16_t *buf, size_t len) { int ret = 0; size_t rx_data_len = 2 * len + len; uint8_t rx_data[rx_data_len] = {0}; // Read 3 bytes at the time and check the CRC if ((ret = i2c_read_blocking(i2c_inst_, SHT30_DEFAULT_ADDR, rx_data, rx_data_len, false)) != rx_data_len) { goto error; } for (size_t i = 0; i < len; i++) { if ((ret = check_crc(&rx_data[i * 3], 3)) < 0) { goto error; } buf[i] = (rx_data[i * 3] << 8) | rx_data[i * 3 + 1]; } if ((ret = i2c_get_read_available(i2c_inst_)) != 0) printf("Warning at least %d more bytes are available\n", ret); return true; error: printf("Error [%d]: Failed to read. %s\n", ret, which_error(ret)); return false; } bool SHT30::read_status_reg(uint16_t ®) { int ret = 0; uint16_t buf; if (!write_command(SHT30_READ_STATUS_REG)) goto error; if (!read_response(&buf, 1)) goto error; reg = buf; return true; error: printf("Failed to read status register\n"); return false; } bool SHT30::heater_enable() { status_reg_u status; if (!write_command(SHT30_ENABLE_HEATER)) return false; if (!read_status_reg(status.val)) return false; if (!status.heater_status) { printf("Error: Failed to enable heater\n"); print_status_reg(status); return false; } return true; } bool SHT30::heater_disable() { status_reg_u status; if (!write_command(SHT30_DISABLE_HEATER)) return false; if (!read_status_reg(status.val)) return false; if (status.heater_status) { printf("Error: Failed to disable heater\n"); print_status_reg(status); return false; } return true; } bool SHT30::single_measure(Repeatability repeatability, struct SHT30::measure &measure, bool clock_stretching) { uint8_t cmd[SHT30_CMD_LEN] = {0}; uint16_t buf[2] = {0}; int ret = 0; if (clock_stretching) memcpy(cmd, single_stretch[repeatability], 2); else memcpy(cmd, single_no_stretch[repeatability], 2); if(!write_command(cmd, SHT30_CMD_LEN)) goto error; if (!read_response(buf, sizeof(buf))) goto error; measure.temperature = convert_temperature_celsius(buf[0]); measure.humidity = convert_humidity(buf[1]); return true; error: printf("Failed to get measure\n"); return false; } SHT30::~SHT30() { i2c_deinit(i2c_inst_); } No newline at end of file Loading
sht30/CMakeLists.txt +9 −6 Original line number Diff line number Diff line Loading @@ -7,16 +7,19 @@ project(sht30 C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) pico_sdk_init() list(APPEND PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}) set(PICO_CYW43_SUPPORTED "1") pico_register_common_scope_var(PICO_CYW43_SUPPORTED) pico_sdk_init() add_executable(${PROJECT_NAME} main.c) file(GLOB SRC *.cpp) add_executable(${PROJECT_NAME} ${SRC}) pico_enable_stdio_usb(${PROJECT_NAME} 1) pico_enable_stdio_uart(${PROJECT_NAME} 1) pico_add_extra_outputs(${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) target_link_libraries(${PROJECT_NAME} pico_cyw43_arch_lwip_threadsafe_background pico_stdlib hardware_i2c) pico_add_extra_outputs(${PROJECT_NAME}) No newline at end of file
sht30/grove_shield.h→sht30/grove_pico_w.h +21 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ // no PICO_DEFAULT_WS2812_PIN // --- I2C --- #ifndef PICO_DEFAULT_I2C #define PICO_DEFAULT_I2C 0 #endif Loading @@ -43,6 +44,26 @@ #define PICO_DEFAULT_I2C_SCL_PIN 9 #endif #ifndef PICO_I2C0 #define PICO_I2C0 0 #endif #ifndef PICO_I2C0_SDA_PIN #define PICO_I2C0_SDA_PIN 8 #endif #ifndef PICO_I2C0_SCL_PIN #define PICO_I2C0_SCL_PIN 9 #endif #ifndef PICO_I2C1 #define PICO_I2C1 1 #endif #ifndef PICO_I2C1_SDA_PIN #define PICO_I2C1_SDA_PIN 6 #endif #ifndef PICO_I2C1_SCL_PIN #define PICO_I2C1_SCL_PIN 7 #endif // --- SPI --- #ifndef PICO_DEFAULT_SPI #define PICO_DEFAULT_SPI 0 Loading
sht30/main.cdeleted 100644 → 0 +0 −77 Original line number Diff line number Diff line /** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // Sweep through all 7-bit I2C addresses, to see if any slaves are present on // the I2C bus. Print out a table that looks like this: // // I2C Bus Scan // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 // 1 @ // 2 // 3 @ // 4 // 5 // 6 // 7 // // E.g. if slave addresses 0x12 and 0x34 were acknowledged. #include <stdio.h> #include "pico/stdlib.h" #include "pico/binary_info.h" #include "hardware/i2c.h" // I2C reserves some addresses for special purposes. We exclude these from the scan. // These are any addresses of the form 000 0xxx or 111 1xxx bool reserved_addr(uint8_t addr) { return (addr & 0x78) == 0 || (addr & 0x78) == 0x78; } int main() { // Enable UART so we can print status output stdio_init_all(); #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) #warning i2c/bus_scan example requires a board with I2C pins puts("Default I2C pins were not defined"); #else // This example will use I2C0 on the default SDA and SCL pins (GP4, GP5 on a Pico) i2c_init(i2c_default, 100 * 1000); gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN); gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); printf("\nI2C Bus Scan\n"); printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); for (int addr = 0; addr < (1 << 7); ++addr) { if (addr % 16 == 0) { printf("%02x ", addr); } // Perform a 1-byte dummy read from the probe address. If a slave // acknowledges this address, the function returns the number of bytes // transferred. If the address byte is ignored, the function returns // -1. // Skip over any reserved addresses. int ret; uint8_t rxdata; if (reserved_addr(addr)) ret = PICO_ERROR_GENERIC; else ret = i2c_read_blocking(i2c_default, addr, &rxdata, 1, false); printf(ret < 0 ? "." : "@"); printf(addr % 16 == 15 ? "\n" : " "); } printf("Done.\n"); return 0; #endif }
sht30/main.cpp 0 → 100644 +91 −0 Original line number Diff line number Diff line /** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // Sweep through all 7-bit I2C addresses, to see if any slaves are present on // the I2C bus. Print out a table that looks like this: // // I2C Bus Scan // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 // 1 @ // 2 // 3 @ // 4 // 5 // 6 // 7 // // E.g. if slave addresses 0x12 and 0x34 were acknowledged. #include <stdio.h> #include "pico/stdlib.h" #include "pico/cyw43_arch.h" #include "pico/binary_info.h" #include "hardware/i2c.h" #include "sht30.h" volatile bool error = false; bool callback(struct repeating_timer *t) { SHT30 *sht = (SHT30*)t->user_data; SHT30::measure mes = {0, 0}; if (!sht->single_measure(SHT30::HIGH, mes)) { error = true; return false; } printf("Temperature: %f, Humidity: %f %\n", mes.temperature, mes.humidity); return true; } int main() { struct repeating_timer timer; SHT30::measure mes = {0, 0}; stdio_init_all(); printf("\n*** Init SHT30 sensor ***\n"); SHT30 sht; if (!sht.is_connected()) { printf("Error: SHT30 is not connected\n"); return -1; } printf("SHT30 connected\n"); if (!sht.read_serial_number()) goto end; // if (!sht.single_measure(SHT30::HIGH, mes)) // goto end; // printf("Temperature: %f, Humidity: %f %\n", mes.temperature, mes.humidity); // add_repeating_timer_ms(2000, callback, &sht, &timer); while(!error) { if (!sht.single_measure(SHT30::HIGH, mes)) goto end; printf("Temperature: %f, Humidity: %f %\n", mes.temperature, mes.humidity); sleep_ms(1000); } printf("An error occurred stopping\n"); uint16_t status_reg; if (sht.read_status_reg(status_reg)) printf("Status reg: 0x%04X\n", status_reg); end: printf("Exiting\n"); return 1; } No newline at end of file
sht30/sht30.cpp 0 → 100644 +371 −0 Original line number Diff line number Diff line #include <stdio.h> #include <string.h> #include "pico/stdlib.h" #include "pico/binary_info.h" #include "pico/time.h" #include "pico.h" #include "sht30.h" #define I2C_DEFAULT_BAUD 100*1000 // 100 kHz #define MILLI_SEC 1000 // us #define I2C_DEFAULT_TIMEOUT 25 * MILLI_SEC // SHT30 #define SHT30_DEFAULT_ADDR 0x44 // SHT30 Commands #define SHT30_CMD_LEN 2 #define SHT30_DATA_LEN 6 #define SHT30_READ_SERIAL_NUMBER 0x3780 #define SHT30_SOFT_RESET 0x30A2 #define SHT30_READ_STATUS_REG 0xF32D #define SHT30_CLEAR_STATUS_REG 0x3041 #define SHT30_ENABLE_HEATER 0x306D #define SHT30_DISABLE_HEATER 0x3066 #define SHT30_NO_SLEEP 0x303E #define SHT30_SINGLE_STRETCH 0x2C #define SHT30_SINGLE_NO_STRETCH 0x24 // SHT30 Error codes #define SHT30_CRC_ERROR (PICO_ERROR_INSUFFICIENT_RESOURCES - 1) // SHT30 conversion #define SHT30_DIVIDER ((0x01 << 16) - 1) #define SHT30_NEG_CELSIUS -45.0 #define SHT30_MULTI_CELSIUS 175.0 #define SHT30_MULTI_HUMIDITY 100.0 union status_reg_u { uint16_t val; struct { uint8_t write_data_crc_status : 1; uint8_t command_status : 1; uint8_t : 2; // Reserved uint8_t reset_detected : 1; uint8_t : 3; // Reserved uint8_t : 2; // Reserved uint8_t t_tracking_alert : 1; uint8_t rh_tracking_alert : 1; uint8_t : 1; // Reserved uint8_t heater_status : 1; uint8_t : 1; // Reserved uint8_t alert_status : 1; }; }; // Single measure clock stretching enabled const uint8_t single_stretch [][2] = { {SHT30_SINGLE_STRETCH, 0x10}, // Low {SHT30_SINGLE_STRETCH, 0x0D}, // Medium {SHT30_SINGLE_STRETCH, 0x06} // High }; // Single measure clock stretching disabled const uint8_t single_no_stretch [][2] = { {SHT30_SINGLE_NO_STRETCH, 0x16}, // Low {SHT30_SINGLE_NO_STRETCH, 0X0B}, // Medium {SHT30_SINGLE_NO_STRETCH, 0x00}, // High }; const uint8_t crc8x_table[] = { 0x00,0x31,0x62,0x53,0xC4,0xF5,0xA6,0x97,0xB9,0x88,0xDB,0xEA,0x7D,0x4C,0x1F,0x2E, 0x43,0x72,0x21,0x10,0x87,0xB6,0xE5,0xD4,0xFA,0xCB,0x98,0xA9,0x3E,0x0F,0x5C,0x6D, 0x86,0xB7,0xE4,0xD5,0x42,0x73,0x20,0x11,0x3F,0x0E,0x5D,0x6C,0xFB,0xCA,0x99,0xA8, 0xC5,0xF4,0xA7,0x96,0x01,0x30,0x63,0x52,0x7C,0x4D,0x1E,0x2F,0xB8,0x89,0xDA,0xEB, 0x3D,0x0C,0x5F,0x6E,0xF9,0xC8,0x9B,0xAA,0x84,0xB5,0xE6,0xD7,0x40,0x71,0x22,0x13, 0x7E,0x4F,0x1C,0x2D,0xBA,0x8B,0xD8,0xE9,0xC7,0xF6,0xA5,0x94,0x03,0x32,0x61,0x50, 0xBB,0x8A,0xD9,0xE8,0x7F,0x4E,0x1D,0x2C,0x02,0x33,0x60,0x51,0xC6,0xF7,0xA4,0x95, 0xF8,0xC9,0x9A,0xAB,0x3C,0x0D,0x5E,0x6F,0x41,0x70,0x23,0x12,0x85,0xB4,0xE7,0xD6, 0x7A,0x4B,0x18,0x29,0xBE,0x8F,0xDC,0xED,0xC3,0xF2,0xA1,0x90,0x07,0x36,0x65,0x54, 0x39,0x08,0x5B,0x6A,0xFD,0xCC,0x9F,0xAE,0x80,0xB1,0xE2,0xD3,0x44,0x75,0x26,0x17, 0xFC,0xCD,0x9E,0xAF,0x38,0x09,0x5A,0x6B,0x45,0x74,0x27,0x16,0x81,0xB0,0xE3,0xD2, 0xBF,0x8E,0xDD,0xEC,0x7B,0x4A,0x19,0x28,0x06,0x37,0x64,0x55,0xC2,0xF3,0xA0,0x91, 0x47,0x76,0x25,0x14,0x83,0xB2,0xE1,0xD0,0xFE,0xCF,0x9C,0xAD,0x3A,0x0B,0x58,0x69, 0x04,0x35,0x66,0x57,0xC0,0xF1,0xA2,0x93,0xBD,0x8C,0xDF,0xEE,0x79,0x48,0x1B,0x2A, 0xC1,0xF0,0xA3,0x92,0x05,0x34,0x67,0x56,0x78,0x49,0x1A,0x2B,0xBC,0x8D,0xDE,0xEF, 0x82,0xB3,0xE0,0xD1,0x46,0x77,0x24,0x15,0x3B,0x0A,0x59,0x68,0xFF,0xCE,0x9D,0xAC }; void print_status_reg(status_reg_u status) { printf("\nStatus reg value: 0x%04X\n", status.val); printf("Alert: %d\n", status.alert_status); printf("Heater: %d\n", status.heater_status); printf("RH tracking: %d\n", status.rh_tracking_alert); printf("T tracking: %d\n", status.t_tracking_alert); printf("Reset: %d\n", status.reset_detected); printf("CMD status: %d\n", status.command_status); printf("CRC status: %d\n\n", status.write_data_crc_status); } inline uint8_t calculate_crc8x(uint8_t *data, size_t len) { uint8_t crc = 0xFF; for (size_t i = 0; i < len; i++) { crc = crc8x_table[data[i] ^ crc]; } return crc; } int check_crc(uint8_t *data, size_t data_len) { size_t len = data_len - 1; uint8_t crc = data[len]; return calculate_crc8x(data, len) == crc ? 0 : SHT30_CRC_ERROR; } const char* which_error(int error_code) { switch (error_code) { case PICO_ERROR_GENERIC: return "Address not acknowledged or device not present"; case PICO_ERROR_TIMEOUT: return "Timeout reached"; case SHT30_CRC_ERROR: return "CRC don't match"; default: return "OK"; } } float convert_temperature_celsius(uint16_t temp) { float temperature = SHT30_NEG_CELSIUS + (SHT30_MULTI_CELSIUS * (float)temp) / SHT30_DIVIDER; return temperature; } float convert_humidity(uint16_t hum) { float humidity = SHT30_MULTI_HUMIDITY * ((float) hum) / SHT30_DIVIDER; return humidity; } SHT30::SHT30(unsigned int i2c_channel) : i2c_channel_(i2c_channel), connected_(false) { status_reg_u status; switch(i2c_channel_) { case 0: i2c_inst_ = i2c0; i2c_scl_ = PICO_I2C0_SCL_PIN; i2c_sda_ = PICO_I2C0_SDA_PIN; break; case 1: i2c_inst_ = i2c1; i2c_scl_ = PICO_I2C1_SCL_PIN; i2c_sda_ = PICO_I2C1_SDA_PIN; break; default: printf("Error: I2C channel %d not supported\n", i2c_channel_); return; } i2c_init(i2c_inst_, I2C_DEFAULT_BAUD); gpio_set_function(i2c_sda_, GPIO_FUNC_I2C); gpio_set_function(i2c_scl_, GPIO_FUNC_I2C); gpio_pull_up(i2c_scl_); gpio_pull_up(i2c_sda_); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(i2c_sda_, i2c_scl_, GPIO_FUNC_I2C)); if (!reset()) return; if (!write_command(SHT30_NO_SLEEP)) return; if (read_status_reg(status.val)) { if (status.val == 0x8010) connected_ = true; else print_status_reg(status); } } bool SHT30::read_serial_number() { int ret = 0; uint16_t serial_number = 0; if (!write_command(SHT30_READ_SERIAL_NUMBER)) { goto error; } if (!read_response(&serial_number, 1)) goto error; printf("SHT30 serial number: 0x%04X\n", serial_number); return true; error: printf("Error: Failed to read serial number\n"); return false; } bool SHT30::reset() { if(!write_command(SHT30_SOFT_RESET)) { printf("Error: Failed to reset SHT30 device\n"); return false; } return true; } bool SHT30::is_connected() { return connected_; } bool SHT30::write_command(uint8_t *cmd, size_t len) { int ret = 0; if ((ret = i2c_write_blocking(i2c_inst_, SHT30_DEFAULT_ADDR, cmd, len, false)) != len) { printf("Error [%d]: I2C write command 0x", ret); for (size_t i = 0; i < len; i++) { printf("%02X", cmd[i]); } printf(" failed. %s\n", which_error(ret)); return false; } // Since the device needs approx 1 ms before a new command can be sent we sleep. sleep_ms(1); return true; } bool SHT30::write_command(uint16_t cmd) { uint8_t tx_data[] = { (uint8_t)(cmd >> 8), (uint8_t)(cmd & 0xFF) }; return write_command(tx_data, SHT30_CMD_LEN); } bool SHT30::read_response(uint16_t *buf, size_t len) { int ret = 0; size_t rx_data_len = 2 * len + len; uint8_t rx_data[rx_data_len] = {0}; // Read 3 bytes at the time and check the CRC if ((ret = i2c_read_blocking(i2c_inst_, SHT30_DEFAULT_ADDR, rx_data, rx_data_len, false)) != rx_data_len) { goto error; } for (size_t i = 0; i < len; i++) { if ((ret = check_crc(&rx_data[i * 3], 3)) < 0) { goto error; } buf[i] = (rx_data[i * 3] << 8) | rx_data[i * 3 + 1]; } if ((ret = i2c_get_read_available(i2c_inst_)) != 0) printf("Warning at least %d more bytes are available\n", ret); return true; error: printf("Error [%d]: Failed to read. %s\n", ret, which_error(ret)); return false; } bool SHT30::read_status_reg(uint16_t ®) { int ret = 0; uint16_t buf; if (!write_command(SHT30_READ_STATUS_REG)) goto error; if (!read_response(&buf, 1)) goto error; reg = buf; return true; error: printf("Failed to read status register\n"); return false; } bool SHT30::heater_enable() { status_reg_u status; if (!write_command(SHT30_ENABLE_HEATER)) return false; if (!read_status_reg(status.val)) return false; if (!status.heater_status) { printf("Error: Failed to enable heater\n"); print_status_reg(status); return false; } return true; } bool SHT30::heater_disable() { status_reg_u status; if (!write_command(SHT30_DISABLE_HEATER)) return false; if (!read_status_reg(status.val)) return false; if (status.heater_status) { printf("Error: Failed to disable heater\n"); print_status_reg(status); return false; } return true; } bool SHT30::single_measure(Repeatability repeatability, struct SHT30::measure &measure, bool clock_stretching) { uint8_t cmd[SHT30_CMD_LEN] = {0}; uint16_t buf[2] = {0}; int ret = 0; if (clock_stretching) memcpy(cmd, single_stretch[repeatability], 2); else memcpy(cmd, single_no_stretch[repeatability], 2); if(!write_command(cmd, SHT30_CMD_LEN)) goto error; if (!read_response(buf, sizeof(buf))) goto error; measure.temperature = convert_temperature_celsius(buf[0]); measure.humidity = convert_humidity(buf[1]); return true; error: printf("Failed to get measure\n"); return false; } SHT30::~SHT30() { i2c_deinit(i2c_inst_); } No newline at end of file