diff --git a/.gitmodules b/.gitmodules
index 8b8a7fc..a3d5b53 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -16,3 +16,6 @@
[submodule "fw/Libs/rgbled"]
path = fw/Libs/rgbled
url = git@gitlab.com:VelesLabs/stm32libs/rgbled.git
+[submodule "fw/Libs/sps30"]
+ path = fw/Libs/sps30
+ url = git@gitlab.com:VelesLabs/stm32libs/sps30.git
diff --git a/fw/.cproject b/fw/.cproject
index f852c90..ef4b314 100644
--- a/fw/.cproject
+++ b/fw/.cproject
@@ -36,6 +36,7 @@
+
@@ -69,6 +70,7 @@
+
@@ -103,6 +105,7 @@
+
@@ -140,6 +143,7 @@
+
@@ -172,6 +176,7 @@
+
diff --git a/fw/Core/Inc/sps30.h b/fw/Core/Inc/sps30.h
deleted file mode 100644
index 9b1b943..0000000
--- a/fw/Core/Inc/sps30.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * sps30.h
- *
- * Created on: Jul 18, 2021
- * Author: mrs
- */
-
-#ifndef INC_SPS30_H_
-#define INC_SPS30_H_
-
-#include "stdint.h"
-#include "stm32l0xx_ll_i2c.h"
-#include "stm32l0xx_ll_utils.h"
-#include "i2c.h"
-#include "crc8.h"
-
-/*
- * Defines & macros
- */
-
-#define SPS30_I2C_ADDRESS 0x69
-#define SPS30_MEASURED_VALUES_COUNT 10
-
-/*
- * Return values
- */
-
-#define SPS30_OK 0
-#define SPS30_ERROR -1 // generic error
-#define SPS30_CRC8_ERROR -2 // checksum failed
-
-/*
- * Data types
- */
-
-typedef enum {
- SPS30_START_MEASUREMENT = 0x0010,
- SPS30_STOP_MEASUREMENT = 0x0104,
- SPS30_READ_DATA_READY_FLAG = 0x0202,
- SPS30_READ_MEASURED_VALUES = 0x0300,
- SPS30_SLEEP = 0x1001,
- SPS30_WAKE_UP = 0x1103,
- SPS30_START_FAN_CLEANING = 0x5607,
- SPS30_READ_AUTO_CLEANING_INTERVAL = 0x8004,
- SPS30_WRITE_AUTO_CLEANING_INTERVAL = 0x8004,
- SPS30_READ_PRODUCT_TYPE = 0xD002,
- SPS30_READ_SERIAL_NUMBER = 0xD033,
- SPS30_READ_VERSION = 0xD100,
- SPS30_READ_DEVICE_STATUS_REGISTER = 0xD206,
- SPS30_CLEAR_DEVICE_STATUS_REGISTER = 0xD210,
- SPS30_RESET = 0xD304
-} sps30_cmd_t;
-
-typedef enum {
- SPS30_FLOAT_FORMAT = 0x03,
- SPS30_UINT16_FORMAT = 0x05
-} sps30_data_format_t;
-
-typedef enum {
- PM0_5 = 0, /* this category is used only for number concentration */
- PM1_0,
- PM2_5,
- PM4_0,
- PM10_0,
- SPS30_PM_CATEGORIES_COUNT
-} sps30_pm_categories_t;
-
-typedef struct {
- /* PM0.5 is skipped for mass concentration */
- uint16_t mass_concentration[SPS30_PM_CATEGORIES_COUNT]; /* ug / m^3 */
- uint16_t number_concentration[SPS30_PM_CATEGORIES_COUNT]; /* 1 / cm^3 */
- uint16_t typical_particle_size; /* nm */
-} sps30_data_t;
-
-int8_t sps30_send_cmd(sps30_cmd_t cmd);
-
-int8_t sps30_start_measurement( void );
-int8_t sps30_stop_measurement( void );
-int8_t sps30_read_measured_values(sps30_data_t *measured_data);
-
-int8_t sps30_sleep( void );
-int8_t sps30_wake_up( void );
-
-int8_t sps30_start_fan_cleaning( void );
-
-int8_t sps30_reset( void );
-
-int8_t sps30_read_status_register ( void );
-
-int8_t sps30_read_firmware_version ( uint8_t * fw_ver_hi, uint8_t * fw_ver_lo );
-
-#endif /* INC_SPS30_H_ */
diff --git a/fw/Core/Src/sps30.c b/fw/Core/Src/sps30.c
deleted file mode 100644
index 884395d..0000000
--- a/fw/Core/Src/sps30.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * sps30.c
- *
- * Created on: Jul 18, 2021
- * Author: david
- */
-
-#include "sps30.h"
-#include "main.h" /* for uart_disable_interrupts() */
-
-int8_t sps30_send_cmd(sps30_cmd_t cmd)
-{
- uint8_t buffer[32];
- uint8_t result;
-
- // start measurement
- buffer[0] = cmd >> 8;
- buffer[1] = cmd & 0x00ff;
- uart_disable_interrupts();
- result = i2c_transmit(SPS30_I2C_ADDRESS<<1, buffer, 2);
- uart_enable_interrupts();
- // TODO: Proc to vraci NACK? Vyresit.
- if (result != I2C_OK) {
- return SPS30_ERROR;
- }
-
- return SPS30_OK;
-}
-
-int8_t sps30_start_measurement( void )
-{
- uint8_t buffer[5];
- uint8_t result;
-
- buffer[0] = SPS30_START_MEASUREMENT >> 8;
- buffer[1] = SPS30_START_MEASUREMENT & 0x00ff;
- buffer[2] = SPS30_UINT16_FORMAT;
- buffer[3] = 0x00;
- buffer[4] = crc8_calculate(buffer + 2, 2);
-
- uart_disable_interrupts();
- result = i2c_transmit(SPS30_I2C_ADDRESS<<1, buffer, 5);
- uart_enable_interrupts();
-
- if (result != I2C_OK) {
- return SPS30_ERROR;
- }
- return SPS30_OK;
-}
-
-int8_t sps30_stop_measurement( void )
-{
- return sps30_send_cmd(SPS30_STOP_MEASUREMENT);
-}
-
-int8_t sps30_read_measured_values(sps30_data_t *measured_data)
-{
- uint8_t buffer[32];
-
- uint8_t result;
-
- // start measurement
- buffer[0] = SPS30_READ_MEASURED_VALUES >> 8;
- buffer[1] = SPS30_READ_MEASURED_VALUES & 0xFF;
- uart_disable_interrupts();
- result = i2c_transmit(SPS30_I2C_ADDRESS<<1, buffer, 2);
- uart_enable_interrupts();
- if (result != I2C_OK) {
- return SPS30_ERROR;
- }
- LL_mDelay(10); // 10 ms should be enough
- // read out
- uart_disable_interrupts();
- result = i2c_receive(SPS30_I2C_ADDRESS<<1, buffer, 3 * SPS30_MEASURED_VALUES_COUNT);
- uart_enable_interrupts();
- if (result != I2C_OK)
- {
- return SPS30_ERROR;
- }
- /* check data integrity */
- for (uint8_t i = 0; i < SPS30_MEASURED_VALUES_COUNT; i++)
- {
- uint8_t checksum_calculated = crc8_calculate(buffer + 3*i, 2);
- uint8_t checksum_received = buffer[3*i + 2];
- if (checksum_calculated != checksum_received) {
- return SPS30_CRC8_ERROR;
- }
- }
- /* copy to output struct */
- /* mass concencration [ug / m^3] */
- int pos = 0;
- for (int i = 0; i < 4; i++) {
- /* i + 1 because mass concentration starts at PM1.0 (there is no PM0.5) */
- measured_data->mass_concentration[i + 1] = (buffer[pos] << 8) + buffer[pos + 1];
- pos += 3; /* 2 B data, 1 B crc */
- }
- /* number concentration [1 / cm^3] */
- for (int i = 0; i < 5; i++) {
- measured_data->number_concentration[i] = (buffer[pos] << 8) + buffer[pos + 1];
- pos += 3;
- }
- /* typical particle size [nm] */
- measured_data->typical_particle_size = (buffer[pos] << 8) + buffer[pos + 1];
-
- return SPS30_OK;
-}
-
-int8_t sps30_sleep( void )
-{
- return sps30_send_cmd(SPS30_SLEEP);
-}
-
-int8_t sps30_wake_up( void )
-{
- return sps30_send_cmd(SPS30_WAKE_UP);
-}
-
-int8_t sps30_start_fan_cleaning( void )
-{
- return sps30_send_cmd(SPS30_START_FAN_CLEANING);
-}
-
-int8_t sps30_reset( void )
-{
- return sps30_send_cmd(SPS30_RESET);
-}
-
-int8_t sps30_read_status_register ( void )
-{
- uint8_t buffer[6];
-
- uint8_t result;
-
- // start measurement
- buffer[0] = SPS30_READ_DEVICE_STATUS_REGISTER >> 8;
- buffer[1] = SPS30_READ_DEVICE_STATUS_REGISTER & 0x00ff;
- uart_disable_interrupts();
- result = i2c_transmit(SPS30_I2C_ADDRESS<<1, buffer, 2);
- uart_enable_interrupts();
- if (result != I2C_OK) {
- return SPS30_ERROR;
- }
- LL_mDelay(10); // 10 ms should be enough
- // read out
- uart_disable_interrupts();
- result = i2c_receive(SPS30_I2C_ADDRESS<<1, buffer, 6);
- uart_enable_interrupts();
- // TODO
-
- return SPS30_OK;
-}
-
-int8_t sps30_read_firmware_version ( uint8_t * fw_ver_hi, uint8_t * fw_ver_lo )
-{
- uint8_t i2c_tx_buffer[2];
- uint8_t i2c_rx_buffer[3];
-
- uint8_t result;
-
- // start measurement
- i2c_tx_buffer[0] = SPS30_READ_VERSION >> 8;
- i2c_tx_buffer[1] = SPS30_READ_VERSION & 0x00ff;
- uart_disable_interrupts();
- result = i2c_transmit(SPS30_I2C_ADDRESS<<1, i2c_tx_buffer, 2);
- uart_enable_interrupts();
-
- // TODO: Proc to vraci NACK? Vyresit.
- /*if (result != I2C_OK) {
- return SPS30_ERROR;
- }
- return SPS30_OK;*/
-
- LL_mDelay(1); // 10 ms should be enough
- // read out
- uart_disable_interrupts();
- result = i2c_receive(SPS30_I2C_ADDRESS<<1, i2c_rx_buffer, 3);
- uart_enable_interrupts();
- /*if (result != I2C_OK)
- {
- return SPS30_ERROR;
- }*/
-
- *fw_ver_hi = i2c_rx_buffer[0];
- *fw_ver_lo = i2c_rx_buffer[1];
-
- return SPS30_OK;
-}
diff --git a/fw/Libs/sps30 b/fw/Libs/sps30
new file mode 160000
index 0000000..35a281c
--- /dev/null
+++ b/fw/Libs/sps30
@@ -0,0 +1 @@
+Subproject commit 35a281c505effbc1db8c37ed2ff681c04bdba269