diff --git a/sample-implementations/RaspberryPi_Pico/cMakeLists.txt b/sample-implementations/RaspberryPi_Pico/CMakeLists.txt similarity index 68% rename from sample-implementations/RaspberryPi_Pico/cMakeLists.txt rename to sample-implementations/RaspberryPi_Pico/CMakeLists.txt index 4a9c75a..f103ec6 100644 --- a/sample-implementations/RaspberryPi_Pico/cMakeLists.txt +++ b/sample-implementations/RaspberryPi_Pico/CMakeLists.txt @@ -6,20 +6,21 @@ pico_sdk_init() set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) +add_compile_options(-Wall -Wextra -Werror) add_executable(main main.c - sensirion_i2c.c - sensirion_i2c.hal.c - scd4x_i2c.c - sensirion_common.c) + ../../sensirion_i2c.c + sensirion_i2c_hal.c + ../../scd4x_i2c.c + ../../sensirion_common.c) # pull in common dependencies and additional i2c hardware support target_link_libraries(main pico_stdlib hardware_i2c) +# use USB for stdin/stdout, rather than using UART pico_enable_stdio_usb(main 1) - pico_enable_stdio_uart(main 0) # create map/bin/hex file etc. pico_add_extra_outputs(main) - + diff --git a/sample-implementations/RaspberryPi_Pico/README.md b/sample-implementations/RaspberryPi_Pico/README.md new file mode 100644 index 0000000..3cd2fbd --- /dev/null +++ b/sample-implementations/RaspberryPi_Pico/README.md @@ -0,0 +1,39 @@ +SCD4x on the Raspberry Pi Pico +============================== +Pico W with an SCD41 connected + +In this example, an SCD41 is connected to a Raspberry Pi Pico W on GPIO pins 12 +(I2C0 SDA) and 13 (I2C0 SCL). The same code will work for any variant of the +Pico. + +The following shell session is from an Ubuntu machine that had the Pico plugged +in to USB in boot select mode. + +```console +$ ls +CMakeLists.txt hardware.jpg main.c README.md sensirion_i2c_hal.c + +$ mkdir build + +$ cd build + +$ cmake -DPICO_SDK_PATH=$HOME/src/pico-sdk .. +[...] + +$ make -j +[...] + +$ ls +CMakeCache.txt cmake_install.cmake generated main.dis main.elf.map main.uf2 pico-sdk +CMakeFiles elf2uf2 main.bin main.elf main.hex Makefile pioasm + +$ cp main.uf2 /media/$USER/RPI-RP2 + +$ screen /dev/ttyACM0 +The I2C baudrate is 399361 Hz +Sensor serial number is: 0x8a7e 0xbb07 0x3ba0 +CO2: 1111 ppm, Temperature: 28.4 C (83.1 F), Humidity: 60.4% +CO2: 1080 ppm, Temperature: 28.5 C (83.2 F), Humidity: 61.1% +CO2: 1070 ppm, Temperature: 28.4 C (83.1 F), Humidity: 61.7% +[...] +``` diff --git a/sample-implementations/RaspberryPi_Pico/hardware.jpg b/sample-implementations/RaspberryPi_Pico/hardware.jpg new file mode 100644 index 0000000..263effe Binary files /dev/null and b/sample-implementations/RaspberryPi_Pico/hardware.jpg differ diff --git a/sample-implementations/RaspberryPi_Pico/main.c b/sample-implementations/RaspberryPi_Pico/main.c index 195febe..33a16af 100644 --- a/sample-implementations/RaspberryPi_Pico/main.c +++ b/sample-implementations/RaspberryPi_Pico/main.c @@ -1,77 +1,89 @@ +#include "../../scd4x_i2c.h" +#include +#include +#include -#include "hardware/i2c.h" -#include "pico/binary_info.h" -#include "pico/stdlib.h" -#include "scd4x_i2c.h" #include -/// I2C address -static int addr = 0x62; - -// I2C Pins -static uint sda_pin = 16; -static uint scl_pin = 17; - -// This is the main entry for your c application. U -// is -int main() { - +int main(void) { stdio_init_all(); - // Setup I2c using pins 16 & 17 - i2c_init(i2c_default, 400 * 1000); - gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); - gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); + // Give us a few seconds to start viewing the output if we're plugged into + // the computer over USB. + sleep_ms(3000); - // This variable will hold the return status of the function calls. - // You can separate each function call result into their own variable or re - // - use this. + // Setup I2C using GPIO pins 12 & 13. + const uint desired_clock_hz = 400 * 1000; + const uint actual_baudrate = i2c_init(i2c_default, desired_clock_hz); + printf("The I2C baudrate is %u Hz\n", actual_baudrate); + const uint sda_pin = 12; + const uint scl_pin = 13; + gpio_set_function(sda_pin, GPIO_FUNC_I2C); + gpio_set_function(scl_pin, GPIO_FUNC_I2C); + gpio_pull_up(sda_pin); + gpio_pull_up(scl_pin); - int status = 0; + // Initialize driver with i2c address + scd4x_init(SCD41_I2C_ADDR_62); - // Stop any readings if occuring - status = scd4x_stop_periodic_measurement(); + int status; - // Perform self test - uint16_t* selfTest = 0; - scd4x_perform_self_test(selfTest); - - // Get Serial number 3 parts - uint16_t one; - uint16_t two; - uint16_t three; + // Stop any ongoing measurement. + status = scd4x_stop_periodic_measurement(); + if (status) { + printf("Unable to stop measurement. Error: %d\n", status); + return status; + } - scd4x_get_serial_number(&one, &two, &three); + // Get serial number. + uint16_t serial_number[3] = {0}; + status = scd4x_get_serial_number(serial_number, 3); + if (status) { + printf("Unable to get sensor serial number. Error: %d\n", status); + return status; + } + printf("Sensor serial number is: 0x%x 0x%x 0x%x\n", (int)serial_number[0], + (int)serial_number[1], (int)serial_number[2]); // Start the readings. - status1 = scd4x_start_periodic_measurement(); - - while (1) { + status = scd4x_start_periodic_measurement(); + if (status) { + printf("Unable to start periodic measurement. Error %d\n", status); + return status; + } - // Check if data is ready to read + for (;;) { + // Wait for the measurement to complete. + sleep_ms(5000 - 10); bool dataReady; - while (dataReady == false) { - - status1 = scd4x_get_data_ready_flag(&dataReady); + do { + sleep_ms(10); + status = scd4x_get_data_ready_status(&dataReady); + if (status) { + printf("Unable to get sensor readiness status. Error %d.\n", + status); + return status; + } + } while (!dataReady); + + // Read the measurement data and convert it to common units. + uint16_t co2Raw; // ppm + int32_t temperatureRaw; // millicelsius + int32_t humidityRaw; // millipercent + status = scd4x_read_measurement(&co2Raw, &temperatureRaw, &humidityRaw); + if (status) { + printf("Unable to read measurement data. Error: %d\n", status); + return status; } - // Get the ticks. The scd4x_read_measurement function is giving - // incorrect data due to the arthimetic - uint16_t co2; - uint16_t temp; - uint16_t humidity; - status1 = scd4x_read_measurement_ticks(&co2, &temp, &humidity); - - // Arithemtic to change raw data into information - int tempInCelsius = -45 + 175 * temp / 65536; - int tempInFarenheit = tempInCelsius * 1.8 + 32; - int humidityPercent = 100 * humidity / 65536; - - // Print results to terminal (output) - printf("C:%d,T:%d,H:%d", co2, tempInFarenheit, humidityPercent); + const int co2Ppm = co2Raw; + const float temperatureCelsius = temperatureRaw / 1000.0f; + const float temperatureFahrenheit = temperatureCelsius * 1.8f + 32; + const float humidityPercent = humidityRaw / 1000.0f; - // Sleep for 5 seconds. - sleep_ms(5000); + printf("CO2: %d ppm, Temperature: %.1f C (%.1f F), Humidity: %.1f%%\n", + co2Ppm, temperatureCelsius, temperatureFahrenheit, + humidityPercent); } } diff --git a/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c b/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c index d7299ba..ae3510f 100644 --- a/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c +++ b/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c @@ -1,4 +1,3 @@ -#include /* * Copyright (c) 2018, Sensirion AG * All rights reserved. @@ -30,49 +29,12 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "sensirion_common.h" -#include "sensirion_config.h" -#include "sensirion_i2c_hal.h" - -/* - * INSTRUCTIONS - * ============ - * - * Implement all functions where they are marked as IMPLEMENT. - * Follow the function specification in the comments. - */ - -/** - * Select the current i2c bus by index. - * All following i2c operations will be directed at that bus. - * - * THE IMPLEMENTATION IS OPTIONAL ON SINGLE-BUS SETUPS (all sensors on the same - * bus) - * - * @param bus_idx Bus index to select - * @returns 0 on success, an error code otherwise - */ -int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx) { - /* TODO:IMPLEMENT or leave empty if all sensors are located on one single - * bus - */ - return NOT_IMPLEMENTED_ERROR; -} - -/** - * Initialize all hard- and software components that are needed for the I2C - * communication. - */ -void sensirion_i2c_hal_init(void) { - /* TODO:IMPLEMENT */ -} +#include "../../sensirion_i2c_hal.h" +#include "../../sensirion_common.h" +#include "../../sensirion_config.h" -/** - * Release all resources initialized by sensirion_i2c_hal_init(). - */ -void sensirion_i2c_hal_free(void) { - /* TODO:IMPLEMENT or leave empty if no resources need to be freed */ -} +#include +#include /** * Execute one read transaction on the I2C bus, reading a given number of bytes. @@ -84,12 +46,10 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { - int status = i2c_read_blocking(i2c_default, address, data, count, false); - if (status == 0) - return 1; - else - return 0; +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { + const bool nostop = + true; // master retains control of the bus after the read + return !i2c_read_blocking(i2c_default, address, data, count, nostop); } /** @@ -104,14 +64,10 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { - // I2C Default is used (I2C0). - int status = i2c_write_blocking(i2c_default, address, data, count, true); - - if (status == 0) - return 1; - else - return 0; + uint8_t count) { + const bool nostop = + true; // master retains control of the bus after the write + return !i2c_write_blocking(i2c_default, address, data, count, nostop); } /** @@ -120,8 +76,8 @@ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, * * Despite the unit, a <10 millisecond precision is sufficient. * - * @param useconds the sleep time in microseconds + * @param microseconds the sleep time in microseconds */ -void sensirion_i2c_hal_sleep_usec(uint32_t useconds) { - sleep_ms(useconds / 1000); +void sensirion_i2c_hal_sleep_usec(uint32_t microseconds) { + sleep_us(microseconds); }