Coder Social home page Coder Social logo

sensirion / embedded-sps Goto Github PK

View Code? Open in Web Editor NEW
45.0 12.0 15.0 93 KB

Embedded i2c Driver for Sensirion Particulate Matter Sensors - Download the Zip Package from the Release Page

Home Page: https://github.com/Sensirion/embedded-sps/releases

License: BSD 3-Clause "New" or "Revised" License

Makefile 9.63% C 62.39% C++ 21.33% BitBake 6.65%
driver i2c sensirion sensor particulate-matter particulates-sensors embedded sps30 sensirion-embedded-drivers

embedded-sps's Introduction

embedded-sps CircleCI GitHub license

This repository contains the embedded i2c driver sources for Sensirion's SPS product line.

If you just want to use the driver, it is recommended to download the release zip from https://github.com/Sensirion/embedded-sps/releases

The UART driver of the SPS is available in the embedded-uart-sps repository.

Arduino (i2c): https://github.com/Sensirion/arduino-sps

Clone this repository

git clone --recursive https://github.com/Sensirion/embedded-sps.git

Repository content

  • embedded-common submodule repository for common HAL
  • sps30-i2c SPS30 i2c driver

Hardware setup

  • Make sure that the SPS30's Pin 4 ("Interface select") is connected to GND, on power-up of the sensor, otherwise the sensor works in UART instead of i2c mode. Note that the interface-select configuration is read on every start of the sensor including after a soft-reset.

  • The i2c wires need appropriate pull-up resistors if they're not included or enabled in your microprocessor.

Collecting resources

make release

This will create a release folder with the necessary driver files in it, including a Makefile. That way, you have just ONE folder with all the sources ready to build your driver for your platform.

Files to adjust (from embedded-common)

To adapt the driver to your platform, you only need to touch the following

  • sensirion_arch_config.h (architecture specifics, you need to specify the integer sizes if your compiler toolchain does not provide stdint.h)

and depending on your i2c implementation either of the following:

  • embedded-common/hw_i2c/sensirion_hw_i2c_implementation.c functions for hardware i2c communication if your platform has a dedicated I2C controller.
  • embedded-common/sw_i2c/sensirion_sw_i2c_implementation.c functions for software i2c communication if your platform does not have a dedicated I2C controller and communication happens with GPIOs. In this case you need to make sure that appropriate I2C pull-up resistors are used.

Building the driver

  1. Adjust sensirion_arch_config.h if you don't have the <stdint.h> header file available.

  2. Either use one of the provided sample implementations implement necessary functions in one of the *_implementation.c files described above (either in the hw_i2c or sw_i2c folder).

    To reuse a provided sample implementation you can specify it in a custom build configuration. For this adapt the file user_config.inc in the driver:

    user_config.inc for hardware i2c with dedicated i2c controller:

    CONFIG_I2C_TYPE=hw_i2c
    hw_i2c_impl_src = ${embedded-common}/hw_i2c/sample-implementations/linux_user_space/sensirion_hw_i2c_implementation.c
    

    user_config.inc for software i2c with bit-banging on GPIOs:

    CONFIG_I2C_TYPE=sw_i2c
    sw_i2c_impl_src = ${embedded-common}/sw_i2c/sample-implementations/linux_user_space/sensirion_sw_i2c_implementation.c
    
  3. Run make

  4. Run the compiled example usage with ./sps30_example_usage. Note that hardware access permissions (e.g. sudo) might be needed.


Please check the embedded-common repository for further information and sample implementations.


embedded-sps's People

Contributors

abrauchli avatar getpastthemonkey avatar psachs avatar rnestler avatar zifzaf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

embedded-sps's Issues

Driver returning very strange readings.

Hi, I'm using the provided driver with pretty much the example usage. It works. I can read the firmware version (2.2), the device serial number. And I can apparantly read back the values. The problem is the values it reads are impossible (over 3000 um/m3 in a home). As others have said, the PM2.5 and PM10 values are identical.

I (3302767) SPS30 Particulate Matter Sensor Task: measured values:
        3032.95 pm1.0
        3207.23 pm2.5
        3207.23 pm4.0
        3207.23 pm10.0
        20713.03 nc0.5
        24136.81 nc1.0
        24205.48 nc2.5
        24213.31 nc4.5
        24217.41 nc10.0
        typical particle size = 0.28

This in fact leads me to believe it is working. Or at least, sort of. I'm also using the SCD30, SHT30, SGP40 devices. And they all seem to be fine. They are using the same common libraries to manipulate bytes to floats. I'm working on an ESP32, which is little endian, but the conversion seems fine for the other sensors.

I don't know what a 'sensible' result should look like. Anyone care to post one and offer an opinion as to what might be causing this? Thanks.

I did a back of the envelope, based on the assumption that particles under 0.5um are all 0.28um diameter. 20,000 particles, based on the density of human skin dust (1500kg/m3), it gives 194 ug/m3. Which is still horrifically high, so I don't think I can trust that either.

additional info
I had a look at the read mesurement command with a scope. The command is W69, 03, 00. All good! The Read back is as follows: R69, 45, 8B, EF, 0E, 23, 39, 45, 93, 15, 0B, a8, etc... To be extra careful, I checked the integrity of the bytes by checking the CRC.

The payload for the first chunk is 0x45, 0x8B, 0x0E, 0x23. I knocked up a little snippet to see what it should be:

#include <stdio.h>
#include <stdint.h>

float bytesToFloat(uint8_t *bytes, int big_endian);

int main()
{
    #define b3 0x45
    #define b2 0x8B
    #define b1 0x0E
    #define b0 0x23
    
    unsigned char byte_array[] = { b3, b2, b1, b0 };
    float output = bytesToFloat(byte_array, 1);
    printf("Your float is %f.", output);

    return 0;
}

float bytesToFloat(uint8_t *bytes, int big_endian) {
    float f;
    uint8_t *f_ptr = (uint8_t *) &f;
    if (big_endian) {
        f_ptr[3] = bytes[0];
        f_ptr[2] = bytes[1];
        f_ptr[1] = bytes[2];
        f_ptr[0] = bytes[3];
    } else {
        f_ptr[3] = bytes[3];
        f_ptr[2] = bytes[2];
        f_ptr[1] = bytes[1];
        f_ptr[0] = bytes[0];
    }
    return f;
}

The output is

Your float is 4449.767090.

I've had a look at the following resource: single precision float and I'm confident that the interpretation of the bytes is correct. And 4449 is what I actually measured with this library. So, it looks like either I'm sitting inside a volcano, or there is something else wrong that's causing the device to read back crazy values... Sensirion, help me out. This is very odd indeed.

Very last edit
I thought I should completely rule out any misinterpretation of the float format since there was a handy means available in the integer16 data format. I set that with :

#define SPS_CMD_START_MEASUREMENT_ARG 0x0500

and got the exact same results.

why so much error in compiling

My system is generating multiple errors, I am confused can any one guide me why. i.e. library has been installed. Below is description of error codes being generated, Pls guide why it is happening:

Arduino: 1.8.10 (Windows 10), Board: "Arduino/Genuino Uno"

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1\sps30.c: In function 'sps30_get_fan_auto_cleaning_interval_days':

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1\sps30.c:209:50: warning: integer overflow in expression [-Woverflow]

 *interval_days = interval_seconds / (24 * 60 * 60);

                                              ^

libraries\sps30-i2c-3.0.1\sps30_example_usage.c.o (symbol from plugin): In function `main':

(.text+0x0): multiple definition of `main'

sketch\sps30.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `sensirion_i2c_read_words_as_bytes':

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1/sensirion_common.c:94: undefined reference to `sensirion_i2c_read'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `main':

E:\ALPHASENSE\sps30\arduino-sps-0.0.2 (1)\arduino-sps-0.0.2\examples\sps30/sps30.ino:30: undefined reference to `sensirion_i2c_init'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `sensirion_i2c_delayed_read_cmd':

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1/sensirion_common.c:153: undefined reference to `sensirion_i2c_write'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `sensirion_i2c_write_cmd_with_args':

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1/sensirion_common.c:143: undefined reference to `sensirion_i2c_write'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `sps30_start_measurement':

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1/sps30.c:103: undefined reference to `sensirion_sleep_usec'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `main':

E:\ALPHASENSE\sps30\arduino-sps-0.0.2 (1)\arduino-sps-0.0.2\examples\sps30/sps30.ino:64: undefined reference to `sensirion_sleep_usec'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `sensirion_i2c_delayed_read_cmd':

C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1/sensirion_common.c:153: undefined reference to `sensirion_i2c_write'

C:\Users\Airshed\AppData\Local\Temp\ccClGeOx.ltrans0.ltrans.o: In function `main':

E:\ALPHASENSE\sps30\arduino-sps-0.0.2 (1)\arduino-sps-0.0.2\examples\sps30/sps30.ino:44: undefined reference to `sensirion_sleep_usec'

collect2.exe: error: ld returned 1 exit status

Multiple libraries were found for "sps30.h"
Used: C:\Users\Airshed\Documents\Arduino\libraries\sps30-i2c-3.0.1
Not used: C:\Users\Airshed\Documents\Arduino\libraries\arduino-sps-0.0.2
Not used: C:\Users\Airshed\Documents\Arduino\libraries\sensirion-sps
exit status 1
Error compiling for board Arduino/Genuino Uno.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Set pointer address does not work for locations greater than 0x0300

Hi, We are able to follow the datasheet to enable readings and set the address pointer to 0x0300 and read 32 bytes of measurement data (CRCs checkout and all that). However, we are using an Arduino Uno. On an Arduino the I2C buffer is limited to 32 bytes. So it is necessary to read 32 bytes of measured values, then move the pointer to 0x0320, then continue reading the measurement values. This fails. We can set the pointer to 0x0300 but any other address is read as 0xFF bytes with failed (0xFF) CRC bytes.

Is this a hardware limitation? If we are unable to set the address pointer then your Arduino example will also be limited to mass concentrations and unable to report number concentrations.

I suspect this is an issue for this repo/driver for Arduino (example only prints PM2.5) but we're not technical enough to get the driver to compile under Arduino, so I can't test the theory.

Let me know:

  • If the address pointer is designed to be arbitrarily set (one would expect it to be)
  • What suggestions you have for compiling this driver for Arduino. I suspect many people will be asking for help ;) If you'd like, we can release a fully formed Arduino libray, we just need to get the address pointer to behave.

Thanks!
-Nathan

Device does not return measurements. Getting serial or device register works though...

Hello Team,

I've got the sps30 attached to my raspberry pi zero and I'm using I2C. The device is available and I'm able to read/write data through I2C such as getting the serial or reading the device register:

Reading device status: [0, 0, 0, 0, 0, 0]

But for some reason, I only retrieve 0 mesasurements:

[0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129, 0, 0, 129]

Is there any check I can do to see if this is a hardware issue?

Best regards,
Martin

SPS Sensor Probing Failed!

I implemented this software to connect to an SPS 30 PM sensor and it will not connect. I wrote my own drivers but only ever recieved back 0xff for bytes. I then implemented sps30-i2c-3.1.1 on a custom board using an STM32L071KZT6 MCU. The I2c bus has several sensors on it and they communicate well. I have three sps30 sensors and they all do the same. I modified the sensirion_hw_i2c_implementation for my i2c bus and should be correct. Its the same initialization as in my main.c, I am using STM32CubeIDE for this project. What are the chances i have three faulty sensors? I am not sure what i am doing wrong. I will paste my implementation below:

sensirion_hw_i2c_implementation:

#include <stm32l0xx_hal.h>

#include "sensirion_arch_config.h"
#include "sensirion_i2c.h"

/**

  • Create new I2C instance. You may also use a different interface, e.g. hi2c2,
  • depending on your CubeMX configuration
    */
    static I2C_HandleTypeDef hi2c1;

/**

  • Initialize all hard- and software components that are needed for the I2C

  • communication.
    /
    void sensirion_i2c_init(void) {
    // hi2c1.Instance = I2C1;
    //hi2c1.Init.ClockSpeed = 100000;
    // hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    // hi2c1.Init.OwnAddress1 = 0;
    // hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    // hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    // hi2c1.Init.OwnAddress2 = 0;
    // hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    // hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    /
    Enable the remapping of Pins 6/7 to 8/9 and the I2C clock before the

    • initialization of the GPIO Pins in HAL_I2C_Init(). This is a fix of the
    • code generated by CubeMX v4.16.0 */
      //__HAL_AFIO_REMAP_I2C1_ENABLE();
      // __HAL_RCC_I2C1_CLK_ENABLE();

    hi2c1.Instance = I2C1;
    hi2c1.Init.Timing = 0x00303D5B;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.OwnAddress2 = 0;
    hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

    HAL_I2C_Init(&hi2c1);
    }

/**

  • Release all resources initialized by sensirion_i2c_init().
    */
    void sensirion_i2c_release(void) {
    }

/**

  • Execute one read transaction on the I2C bus, reading a given number of bytes.
  • If the device does not acknowledge the read command, an error shall be
  • returned.
  • @param address 7-bit I2C address to read from
  • @param data pointer to the buffer where the data is to be stored
  • @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_read(uint8_t address, uint8_t
    data, uint16_t count) {
    return (int8_t)HAL_I2C_Master_Receive(&hi2c1, (uint16_t)(address << 1),
    data, count, 100);
    }

/**

  • Execute one write transaction on the I2C bus, sending a given number of
  • bytes. The bytes in the supplied buffer must be sent to the given address. If
  • the slave device does not acknowledge any of the bytes, an error shall be
  • returned.
  • @param address 7-bit I2C address to write to
  • @param data pointer to the buffer containing the data to write
  • @param count number of bytes to read from the buffer and send over I2C
  • @returns 0 on success, error code otherwise
    /
    int8_t sensirion_i2c_write(uint8_t address, const uint8_t
    data,
    uint16_t count) {
    return (int8_t)HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)(address << 1),
    (uint8_t*)data, count, 100);
    }

/**

  • Sleep for a given number of microseconds. The function should delay the

  • execution for at least the given time, but may also sleep longer.

  • @param useconds the sleep time in microseconds
    */
    void sensirion_sleep_usec(uint32_t useconds) {
    uint32_t msec = useconds / 1000;
    if (useconds % 1000 > 0) {
    msec++;
    }

    /*

    • Increment by 1 if STM32F1 driver version less than 1.1.1
    • Old firmwares of STM32F1 sleep 1ms shorter than specified in HAL_Delay.
    • This was fixed with firmware 1.6 (driver version 1.1.1), so we have to
    • fix it ourselves for older firmwares
      */
      if (HAL_GetHalVersion() < 0x01010100) {
      msec++;
      }

    HAL_Delay(msec);
    }

main.c:

/* USER CODE BEGIN Header /
/
*



  • @attention
  • Copyright (c) 2024 STMicroelectronics.
  • All rights reserved.
  • This software is licensed under terms that can be found in the LICENSE file
  • in the root directory of this software component.
  • If no LICENSE file comes with this software, it is provided AS-IS.

/
/
USER CODE END Header /
/
Includes ------------------------------------------------------------------*/
#include "main.h"
//#include "bme680.c"
#include "bme680.h"
#include "HDC2080.h"
#include "TDE5531.h"
#include "sps30pm.h"
#include "sps30.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Private includes ----------------------------------------------------------/
/
USER CODE BEGIN Includes /
//#include "ssd1306.c"
//#include "fonts.c"
/
USER CODE END Includes */

/* Private typedef -----------------------------------------------------------/
/
USER CODE BEGIN PTD /
#define DELAY_PERIOD_MS (15
1000) // 15 seconds
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------/
/
USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------/
/
USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;

CRC_HandleTypeDef hcrc;

I2C_HandleTypeDef hi2c1;
I2C_HandleTypeDef hi2c3;

RTC_HandleTypeDef hrtc;

SPI_HandleTypeDef hspi1;

UART_HandleTypeDef huart1;
UART_HandleTypeDef huart4;

/* USER CODE BEGIN PV */
uint8_t data[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
char serial_msg[10];
void serial_string10(char str[10]);
uint8_t ver_buf[3];
uint8_t id_buf[49];
uint8_t pim_buf[3];
uint8_t rdy;
uint8_t num_err;
char i2c_reading_buf[100];
//int8_t rslt = BME680_OK;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
static void MX_I2C1_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART4_UART_Init(void);
static void MX_CRC_Init(void);
static void MX_RTC_Init(void);
static void MX_I2C3_Init(void);
/
USER CODE BEGIN PFP */
uint8_t getPMversion();
int8_t bme680I2cRead(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len);
int8_t bme680I2cWrite(uint8_t dev_id, uint8_t reg_addr, uint8_t reg_data, uint16_t len);
uint8_t getSFA30_id();
uint8_t getPIM_id();
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
/
USER CODE END PFP */

/* Private user code ---------------------------------------------------------/
/
USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**

  • @brief The application entry point.
  • @RetVal int
    /
    int main(void)
    {
    /
    USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals /
MX_GPIO_Init();
MX_ADC_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
MX_USART4_UART_Init();
MX_CRC_Init();
MX_RTC_Init();
MX_I2C3_Init();
/
USER CODE BEGIN 2 */

struct bme680_dev bme_sensor;
bme_sensor.dev_id = BME680_I2C_ADDR_PRIMARY;
bme_sensor.intf = BME680_I2C_INTF;
bme_sensor.read = bme680I2cRead;
bme_sensor.write = bme680I2cWrite;
bme_sensor.delay_ms = HAL_Delay;
bme_sensor.amb_temp = 25;

int8_t rslt = BME680_OK;
rslt = bme680_init(&bme_sensor);

uint8_t set_required_settings;

/* Set the temperature, pressure and humidity settings */
bme_sensor.tph_sett.os_hum = BME680_OS_2X;
bme_sensor.tph_sett.os_pres = BME680_OS_4X;
bme_sensor.tph_sett.os_temp = BME680_OS_8X;
bme_sensor.tph_sett.filter = BME680_FILTER_SIZE_3;

/* Set the remaining gas sensor settings and link the heating profile /
bme_sensor.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
/
Create a ramp heat waveform in 3 steps /
bme_sensor.gas_sett.heatr_temp = 320; /
degree Celsius /
bme_sensor.gas_sett.heatr_dur = 150; /
milliseconds */

/* Select the power mode /
/
Must be set before writing the sensor configuration */
bme_sensor.power_mode = BME680_FORCED_MODE;

/* Set the required sensor settings needed */
set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL
| BME680_GAS_SENSOR_SEL;

/* Set the desired sensor configuration */
rslt = bme680_set_sensor_settings(set_required_settings,&bme_sensor);

/* Set the power mode */
rslt = bme680_set_sensor_mode(&bme_sensor);

HDC2080 hdc_sensor;

num_err = HDC2080_init(&hdc_sensor, &hi2c1);

TDE5531 CO2_sensor;

num_err = 0;

num_err = TDE5531_init(&CO2_sensor, &hi2c1);

//SPS30 pm_sensor;

//rdy = SPS30_init(&pm_sensor, &hi2c1);

/* Get the total measurement duration so as to sleep or wait till the
* measurement is complete */
uint16_t meas_period;
bme680_get_profile_dur(&meas_period, &bme_sensor);

  struct bme680_field_data bme_data;

  struct sps30_measurement m;
     int16_t ret;
     while (sps30_probe() != 0) {
            printf("SPS sensor probing failed\n");
            sensirion_sleep_usec(1000000); /* wait 1s */
        }
        printf("SPS sensor probing successful\n");

        uint8_t fw_major;
        uint8_t fw_minor;
        ret = sps30_read_firmware_version(&fw_major, &fw_minor);
        if (ret) {
            printf("error reading firmware version\n");
        } else {
            printf("FW: %u.%u\n", fw_major, fw_minor);
        }

        char serial_number[SPS30_MAX_SERIAL_LEN];
        ret = sps30_get_serial(serial_number);
        if (ret) {
            printf("error reading serial number\n");
        } else {
            printf("Serial Number: %s\n", serial_number);
        }

        ret = sps30_start_measurement();
        if (ret < 0)
            printf("error starting measurement\n");
        printf("measurements started\n");

// Init lcd using one of the stm32HAL i2c typedefs
// ssd1306_Init(&hi2c1);

/* USER CODE END 2 */

/* Infinite loop /
/
USER CODE BEGIN WHILE /
while (1)
{
/
USER CODE END WHILE */

//  HAL_Delay(meas_period); /* Delay till the measurement is ready */

 //         rslt = bme680_get_sensor_data(&bme_data, &bme_sensor);

  //       printf("T: %.2f degC, P: %.2f hPa, H %.2f %%rH ", bme_data.temperature / 100.0f,
  //            bme_data.pressure / 100.0f, bme_data.humidity / 1000.0f );
          /* Avoid using measurements from an unstable heating setup */
  //        if(bme_data.status & BME680_GASM_VALID_MSK)
  //            printf(", G: %d ohms", bme_data.gas_resistance);

  //        printf("\r\n");

          /* Trigger the next measurement if you would like to read data out continuously */
  //        if (bme_sensor.power_mode == BME680_FORCED_MODE) {
  //            rslt = bme680_set_sensor_mode(&bme_sensor);
  //       }

    //      HAL_Delay(5000);

  sensirion_sleep_usec(SPS30_MEASUREMENT_DURATION_USEC); /* wait 1s */
         ret = sps30_read_measurement(&m);
         if (ret < 0) {
             printf("error reading measurement\n");

         } else {
             printf("measured values:\n"
                    "\t%0.2f pm1.0\n"
                    "\t%0.2f pm2.5\n"
                    "\t%0.2f pm4.0\n"
                    "\t%0.2f pm10.0\n"
                    "\t%0.2f nc0.5\n"
                    "\t%0.2f nc1.0\n"
                    "\t%0.2f nc2.5\n"
                    "\t%0.2f nc4.5\n"
                    "\t%0.2f nc10.0\n"
                    "\t%0.2f typical particle size\n\n",
                    m.mc_1p0, m.mc_2p5, m.mc_4p0, m.mc_10p0, m.nc_0p5, m.nc_1p0,
                    m.nc_2p5, m.nc_4p0, m.nc_10p0, m.typical_particle_size);
         }

/* printf("Hello World\n\r");
HAL_Delay(1000);
HAL_UART_Transmit(&huart1, "hello\n", 6, 1000);
HAL_Delay(1000);
rdy = sps30_wake(&pm_sensor);
rdy = getPMversion();
rdy = getSFA30_id();
rdy = getPIM_id();
rdy = sps30_start_int(&pm_sensor);
// HAL_Delay(5000);
// HAL_Delay(5000);
// HAL_Delay(5000);
// HAL_Delay(5000);
rdy = sps30_getData(&pm_sensor);
rdy = sps30_getStatus(&pm_sensor);
rdy = 1;
// if (rdy == sps30_rdy(&pm_sensor)){
// rdy = sps30_stop(&pm_sensor);
// rdy = sps30_getData(&pm_sensor);
//}// end if
*/

/* USER CODE BEGIN 3 */

}
/* USER CODE END 3 */
}

/**

  • @brief System Clock Configuration
  • @RetVal None
    */
    void SystemClock_Config(void)
    {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

/** Initializes the RCC Oscillators according to the specified parameters

  • in the RCC_OscInitTypeDef structure.
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.LSIState = RCC_LSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
    Error_Handler();
    }

/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1
|RCC_PERIPHCLK_I2C3|RCC_PERIPHCLK_RTC;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
PeriphClkInit.I2c3ClockSelection = RCC_I2C3CLKSOURCE_PCLK1;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}

/**

  • @brief ADC Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_ADC_Init(void)
    {

/* USER CODE BEGIN ADC_Init 0 */

/* USER CODE END ADC_Init 0 */

ADC_ChannelConfTypeDef sConfig = {0};

/* USER CODE BEGIN ADC_Init 1 */

/* USER CODE END ADC_Init 1 */

/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}

/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}

/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_3;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}

/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_4;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}

/** Configure for the selected ADC regular channel to be converted.
/
sConfig.Channel = ADC_CHANNEL_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN ADC_Init 2 */

/* USER CODE END ADC_Init 2 */

}

/**

  • @brief CRC Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_CRC_Init(void)
    {

/* USER CODE BEGIN CRC_Init 0 */

/* USER CODE END CRC_Init 0 */

/* USER CODE BEGIN CRC_Init 1 */

/* USER CODE END CRC_Init 1 /
hcrc.Instance = CRC;
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
if (HAL_CRC_Init(&hcrc) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN CRC_Init 2 */

/* USER CODE END CRC_Init 2 */

}

/**

  • @brief I2C1 Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_I2C1_Init(void)
    {

/* USER CODE BEGIN I2C1_Init 0 */

/* USER CODE END I2C1_Init 0 */

/* USER CODE BEGIN I2C1_Init 1 */

/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00303D5B;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}

/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}

/** Configure Digital filter
/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN I2C1_Init 2 */

/* USER CODE END I2C1_Init 2 */

}

/**

  • @brief I2C3 Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_I2C3_Init(void)
    {

/* USER CODE BEGIN I2C3_Init 0 */

/* USER CODE END I2C3_Init 0 */

/* USER CODE BEGIN I2C3_Init 1 */

/* USER CODE END I2C3_Init 1 */
hi2c3.Instance = I2C3;
hi2c3.Init.Timing = 0x00303D5B;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
Error_Handler();
}

/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c3, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}

/** Configure Digital filter
/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c3, 0) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN I2C3_Init 2 */

/* USER CODE END I2C3_Init 2 */

}

/**

  • @brief RTC Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_RTC_Init(void)
    {

/* USER CODE BEGIN RTC_Init 0 */

/* USER CODE END RTC_Init 0 */

RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};

/* USER CODE BEGIN RTC_Init 1 */

/* USER CODE END RTC_Init 1 */

/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}

/* USER CODE BEGIN Check_RTC_BKUP */

/* USER CODE END Check_RTC_BKUP */

/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 0x1;
sDate.Year = 0x0;

if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */

/* USER CODE END RTC_Init 2 */

}

/**

  • @brief SPI1 Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_SPI1_Init(void)
    {

/* USER CODE BEGIN SPI1_Init 0 */

/* USER CODE END SPI1_Init 0 */

/* USER CODE BEGIN SPI1_Init 1 */

/* USER CODE END SPI1_Init 1 /
/
SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */

/* USER CODE END SPI1_Init 2 */

}

/**

  • @brief USART1 Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_USART1_UART_Init(void)
    {

/* USER CODE BEGIN USART1_Init 0 */

/* USER CODE END USART1_Init 0 */

/* USER CODE BEGIN USART1_Init 1 */

/* USER CODE END USART1_Init 1 /
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN USART1_Init 2 */

/* USER CODE END USART1_Init 2 */

}

/**

  • @brief USART4 Initialization Function
  • @param None
  • @RetVal None
    */
    static void MX_USART4_UART_Init(void)
    {

/* USER CODE BEGIN USART4_Init 0 */

/* USER CODE END USART4_Init 0 */

/* USER CODE BEGIN USART4_Init 1 */

/* USER CODE END USART4_Init 1 /
huart4.Instance = USART4;
huart4.Init.BaudRate = 115200;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN USART4_Init 2 */

/* USER CODE END USART4_Init 2 */

}

/**

  • @brief GPIO Initialization Function
  • @param None
  • @RetVal None
    /
    static void MX_GPIO_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /
    USER CODE BEGIN MX_GPIO_Init_1 /
    /
    USER CODE END MX_GPIO_Init_1 */

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, ERVH_Pin|ERVL_Pin, GPIO_PIN_RESET);

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);

/*Configure GPIO pins : CO2Ready_Pin TempHumReady_Pin */
GPIO_InitStruct.Pin = CO2Ready_Pin|TempHumReady_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pins : ERVH_Pin ERVL_Pin */
GPIO_InitStruct.Pin = ERVH_Pin|ERVL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

/*Configure GPIO pin : PA12 */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pin : INTPIM_Pin */
GPIO_InitStruct.Pin = INTPIM_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(INTPIM_GPIO_Port, &GPIO_InitStruct);

/* USER CODE BEGIN MX_GPIO_Init_2 /
/
USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
void serial_string10(char temp[10]){

for(int i = 0; i < 10; i++){

	data[i] = temp[i];

}// end for

}//end serial strin10

uint8_t getPMversion(){
HAL_StatusTypeDef status;

//uint8_t pmAddrPtr[2]= {0xD1,  0x00};
status = HAL_I2C_Mem_Read(&hi2c1, 0x69 << 1, 0xD100, 2, ver_buf, 3, 1000);

if (status == HAL_OK){
		serial_string10("version: \n");
		HAL_UART_Transmit(&huart1, data, 10, 1000);
		HAL_Delay(1000);
		HAL_UART_Transmit(&huart1, ver_buf, 3, 1000);
		HAL_Delay(1000);
		return 1;
}// end if
else return 0;

}///end getpmversion

int8_t bme680I2cRead(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) {
int8_t result;

if (HAL_I2C_Master_Transmit(&hi2c1, (dev_id << 1), &reg_addr, 1, HAL_MAX_DELAY) != HAL_OK) {
result = -1;
} else if (HAL_I2C_Master_Receive (&hi2c1, (dev_id << 1) | 0x01, reg_data, len, HAL_MAX_DELAY) != HAL_OK) {
result = -1;
} else {
result = 0;
}

return result;
}/// end bme read

int8_t bme680I2cWrite(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) {
int8_t result;
int8_t *buf;

// Allocate and load I2C transmit buffer
buf = malloc(len + 1);
buf[0] = reg_addr;
memcpy(buf + 1, reg_data, len);

if (HAL_I2C_Master_Transmit(&hi2c1, (dev_id << 1), (uint8_t *) buf, len + 1, HAL_MAX_DELAY) != HAL_OK) {
result = -1;
} else {
result = 0;
}

free(buf);
return result;
}// end bme write

uint8_t getSFA30_id(){
HAL_StatusTypeDef status;
id_buf[48] = '\n';

	//uint8_t pmAddrPtr[2]= {0xD1,  0x00};
	status = HAL_I2C_Mem_Read(&hi2c1, 0x5D << 1, 0xD060, 2, id_buf, 48, 1000);

	if (status == HAL_OK){
			serial_string10("version: \n");
			HAL_UART_Transmit(&huart1, data, 10, 1000);
			HAL_Delay(1000);
			HAL_UART_Transmit(&huart1, id_buf, 49, 1000);
			HAL_Delay(1000);
			return 1;
	}// end if
	return 0;

}//end getSFA30_id

uint8_t getPIM_id(){
HAL_StatusTypeDef status;
pim_buf[2] = '\n';

		//uint8_t pmAddrPtr[2]= {0xD1,  0x00};
		status = HAL_I2C_Mem_Read(&hi2c1, 0x18 << 1, 0xFBFA, 2, pim_buf, 2, 1000);

		if (status == HAL_OK){
				serial_string10("version: \n");
				HAL_UART_Transmit(&huart1, data, 10, 1000);
				HAL_Delay(1000);
				HAL_UART_Transmit(&huart1, pim_buf, 2, 1000);
				HAL_Delay(1000);
				return 1;
		}// end if
		return 0;

}// end getPIM_id

/**

  • @brief Retargets the C library printf function to the USART.
  • @param None
  • @RetVal None
    /
    PUTCHAR_PROTOTYPE
    {
    /
    Place your implementation of fputc here /
    /
    e.g. write a character to the USART1 and Loop until the end of transmission */
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);

return ch;
}

/* USER CODE END 4 */

/**

  • @brief This function is executed in case of error occurrence.
  • @RetVal None
    /
    void Error_Handler(void)
    {
    /
    USER CODE BEGIN Error_Handler_Debug /
    /
    User can add his own implementation to report the HAL error return state /
    __disable_irq();
    while (1)
    {
    }
    /
    USER CODE END Error_Handler_Debug */
    }

#ifdef USE_FULL_ASSERT
/**

  • @brief Reports the name of the source file and the source line number
  •     where the assert_param error has occurred.
    
  • @param file: pointer to the source file name
  • @param line: assert_param error line source number
  • @RetVal None
    */
    void assert_failed(uint8_t file, uint32_t line)
    {
    /
    USER CODE BEGIN 6 /
    /
    User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) /
    /
    USER CODE END 6 /
    }
    #endif /
    USE_FULL_ASSERT */

I2C fails to initialise when using with nRF52 DK

The PM Sensor fails to initialise when using with nRF52 DK. I am using Segger Embedded Studio. The code keeps getting routed to NRF_BREAKPINT_COND in app_error_weak.c. This is the error I am getting.

Error 8: Initialization of I2C connection failed! <info> app: PM Sensor Initialization started. <error> app: ERROR 3735928559 [Unknown error code] at /home/dinesh/temp_data/nRF5_SDK_15.0.0_a53641a/modules/nrfx/drivers/src/nrfx_twim.c:250 PC at: 0x0002F8C3 <error> app: End of error report

Any help would be appreciated.

Getting almost same values for 2p5 to 10p

When measuring particles I receive almost the identical values for the sizes 2.5, 4 and 10.
Is this because there are no additional particles of those sizes, or is this a problem with the sensor?

The same issue has been discussed here, but there could not be found a good explanation: paulvha/sps30#3

Have you had similar experiences and/or do you have an explanation for the behaviour?

Thanks in advance!

outside usage?

Have you any recommendations for outside usage of your sensor?
Temperature range is nice, but what can happen if humidity is >95%?
What is the best protection method?

Thanks,
Michael

SPS30 sensor get serial fail

Hi,
I have some problems with your SPS30 sensor with the TI CC3220MODASF MCU.
Using your SDK (sps30_get_serial), and when I read the serial number, I only get "FF" bytes and the CRC check fails.
can you help me solve my problem?

Note: I am using your SCD30 sensor with the same MCU as TI CC3220MODASF and everything is working properly.

Thank yo

reset Data-Ready Flag

Hello,
I think after
0x0104 Stop Measurement Set Pointer
the Data Ready Flag should be resetted.
(for 0x0202 Read Data-Ready Flag Set Pointer & Read Data)

In this time the flag show a "ready" and wrong results (not clear for my why worng) will be received.
In my case these values were much higher as the values before the "stop" command.

Maybe it is "cosmetic" - but it could help to get right values in case of program error ;)

use of "stop measurement"

Hi,
not full clear from documentation for me - maybe you could explain?
What is the right sequence for reading data?

    1. 0x0010 Start Measurement Set Pointer & Write Data
    1. 0x0104 Stop Measurement Set Pointer
    1. 0x0202 Read Data-Ready Flag Set Pointer & Read Data
    1. 0x0300 Read Measured Values Set Pointer & Read Data

and for next loop again 1/2/3/4 ?

If I will skip the second step (stop measurement) will get other values.
I know that I must not check flag (step 3) if loop > 1 second (currently use about 5 seconds).
When the "Stop" should be used?

Thanks!
Michael

Arduino platform errors - please use https://github.com/sensirion/arduino-sps / 'sensirion-sps' from Library Manager

Hi
I am trying to read values of the sps30 via I2C with esp8266 and the arduino IDE.
i used library embedded-sps and embedded-common to programing, but when my code run at function

while (sps30_probe() != 0) { Serial.print("probe failed\n"); delay(1000); }
then serial shows this message: "probe failed"

I found the problem and i saw, inside function:

`int16_t sensirion_i2c_read_bytes(uint8_t address, uint8_t *data, int64_t num_words) {
int16_t ret;
int64_t i, j;
int64_t size = num_words * (SENSIRION_WORD_SIZE + CRC8_LEN);
int64_t word_buf[SENSIRION_MAX_BUFFER_WORDS];
uint8_t * const buf8 = (uint8_t *)word_buf;

ret = sensirion_i2c_read(address, buf8, size);
if (ret != STATUS_OK)
    return ret;
/* check the CRC for each word */
for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + CRC8_LEN) {

    ret = sensirion_common_check_crc(&buf8[i], SENSIRION_WORD_SIZE,
                                     buf8[i + SENSIRION_WORD_SIZE]);
    if (ret != STATUS_OK)
        return ret;

    data[j++] = buf8[i];
    data[j++] = buf8[i + 1];
}
return STATUS_OK;

}`

`uint8_t sensirion_common_generate_crc(uint8_t *data, int64_t count)
{
int64_t current_byte;
uint8_t crc = CRC8_INIT;
uint8_t crc_bit;

/* calculates 8-Bit checksum with given polynomial */
for (current_byte = 0; current_byte < count; ++current_byte) {
    crc ^= (data[current_byte]);
    for (crc_bit = 8; crc_bit > 0; --crc_bit) {
        if (crc & 0x80)
            crc = (crc << 1) ^ CRC8_POLYNOMIAL;
        else
            crc = (crc << 1);
    }
}
return crc;

}`
with buf8[30],buf8[31] and buf8[32] checksum value not corect
when use buf8[30] ,buf8[31] to calculate checksum value, the result don't match with buf8[32],
program cannot break out while loop.
I tried with 3 board: arduino, esp32, esp8266 inside arduino IDE but error still same.
I don't know how can i fix
Can i remove checksum function ?
Plz help me, thanks

understanding PM0.5 / PM1.0 / typical particle size

Hi,
if I get as sample 100 #/cm³ for PM0.5 and 105 #/cm³ for PM1.0 - why the "typical particle size" then can be 0.53μm ?
Is this not a middle value? How it will be calculated? (if most particles <= 0.5μm )

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.