Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for new I2C sensor SHT30 #150

Merged
merged 10 commits into from
Apr 10, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ list(APPEND available_tests
spi_4_line_devices_test
i2c_dht12_test
i2c_rtc8563_test
i2c_sht30_test
)

list(FIND available_tests ${selected_test_project} test_found)
Expand Down
1 change: 1 addition & 0 deletions lib/files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ set(SMOOTH_SOURCES
${smooth_dir}/application/io/i2c/DHT12.cpp
${smooth_dir}/application/io/i2c/AxpPMU.cpp
${smooth_dir}/application/io/i2c/PCF8563.cpp
${smooth_dir}/application/io/i2c/SHT30.cpp
${smooth_dir}/application/io/spi/BME280SPI.cpp
${smooth_dir}/application/io/spi/BME280Core.cpp
${smooth_dir}/application/io/wiegand/Wiegand.cpp
Expand Down
5 changes: 3 additions & 2 deletions lib/smooth/application/io/i2c/DHT12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
#include "smooth/application/io/i2c/DHT12.h"

using namespace smooth::core;
using namespace smooth::core::util;

namespace smooth::application::sensor
{
Expand All @@ -28,13 +29,13 @@ namespace smooth::application::sensor
}

// Read the raw data register from the DHT12, the last reagister is the checksum byte
bool DHT12::read_raw_registers(core::util::FixedBuffer<uint8_t, 5>& raw_data)
bool DHT12::read_raw_registers(FixedBuffer<uint8_t, 5>& raw_data)
{
return read(address, HumidityWholePart, raw_data);
}

// Is the checksum valid
bool DHT12::is_checksum_valid(core::util::FixedBuffer<uint8_t, 5>& raw_data)
bool DHT12::is_checksum_valid(FixedBuffer<uint8_t, 5>& raw_data)
{
uint8_t sum = static_cast<uint8_t>(raw_data[HumidityWholePart] + raw_data[HumidityFractionalPart]
+ raw_data[TemperatureWholePart] + raw_data[TemperatureFractionalPart]);
Expand Down
236 changes: 236 additions & 0 deletions lib/smooth/application/io/i2c/SHT30.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*
Smooth - A C++ framework for embedded programming on top of Espressif's ESP-IDF
Copyright 2019 Per Malmberg (https://gitbub.com/PerMalmberg)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <thread>
#include <vector>
#include <algorithm>
#include "smooth/application/io/i2c/SHT30.h"
#include <smooth/core/logging/log.h>

using namespace std::chrono;
using namespace smooth::core;
using namespace smooth::core::util;
using namespace smooth::core::logging;

namespace smooth::application::sensor
{
// Class constants
static const char* TAG = "SHT30";

constexpr const milliseconds delay_2ms(2);
constexpr const milliseconds delay_5ms(5);
constexpr const milliseconds delay_7ms(7);
constexpr const milliseconds delay_16ms(16);

constexpr const milliseconds repeatability_delay[] = { delay_16ms, delay_7ms, delay_5ms,
delay_16ms, delay_7ms, delay_5ms };

constexpr const uint16_t repeatability_value[] = { 0x2C06, 0x2C0D, 0x2C10, 0x2400, 0x240B, 0x2416 };

constexpr const char* repeatability_name[] = { "MeasureHighRepeatabilityWithClockStretchingEnabled",
"MeasureMediumRepeatabilityWithClockStretchingEnabled",
"MeasureLowRepeatabilityWithClockStretchingEnabled",
"MeasureHighRepeatabilityWithClockStretchingDisabled",
"MeasureMediumRepeatabilityWithClockStretchingDisabled",
"MeasureLowRepeatabilityWithClockStretchingDisabled" };

// Constructor
SHT30::SHT30(i2c_port_t port, uint8_t address, std::mutex& guard) : I2CMasterDevice(port, address, guard)
{
}

// Read humidity and temperature measurements
bool SHT30::read_measurements(float& humidity, float& temperature)
{
util::FixedBuffer<uint8_t, 6> raw_data{};
util::FixedBuffer<uint8_t, 3> temp_raw_data{};
util::FixedBuffer<uint8_t, 3> humd_raw_data{};

bool res = false;

if (execute_repeatability_command() & execute_read_block(raw_data))
{
std::copy(raw_data.begin(), raw_data.begin() + 3, temp_raw_data.begin());
std::copy(raw_data.begin() + 3, raw_data.end(), humd_raw_data.begin());

if (execute_crc_checking(temp_raw_data, 2) & execute_crc_checking(humd_raw_data, 2))
{
uint16_t raw_temp = static_cast<uint16_t>((raw_data[0] << 8) + raw_data[1]);
uint16_t raw_humd = static_cast<uint16_t>((raw_data[3] << 8) + raw_data[4]);

// convert raw values into real values
temperature = static_cast<float>(-45.0 + (175.0 * raw_temp / 65535.0));
humidity = static_cast<float>(100.0 * (raw_humd / 65535.0));

res = true;
}
}

return res;
}

// Read status register
bool SHT30::read_status(uint16_t& status)
{
bool res = false;
util::FixedBuffer<uint8_t, 3> raw_data{};

if (execute_write_command(Command::ReadStatus)
& execute_read_block(raw_data)
& execute_crc_checking(raw_data, 2))
{
status = static_cast<uint16_t>((raw_data[0] << 8) + raw_data[1]);
res = true;
}

return res;
}

// Clear the status register on the SHT30
bool SHT30::clear_status()
{
return execute_write_command(Command::ClearStatus);
}

// Perform a soft reset on the SHT30
bool SHT30::soft_reset()
{
return execute_write_command(Command::DoSoftReset);
}

// Turn heater ON
bool SHT30::turn_heater_on()
{
return execute_write_command(Command::EnableHeater);
}

// Turn heater OFF
bool SHT30::turn_heater_off()
{
return execute_write_command(Command::DisableHeater);
}

// Set repeatability mode
void SHT30::set_repeatability_mode(Repeatabilty mode)
{
repeatability_command = mode;
}

// Get repeatability mode
std::string SHT30::get_repeatability_mode()
{
return repeatability_name[repeatability_command];
}

// Write command is required before we can read the data from the SHT30
bool SHT30::write_command(uint16_t cmd)
{
std::vector<uint8_t> command;
command.push_back(static_cast<uint8_t>(cmd >> 8));
command.push_back(static_cast<uint8_t>(cmd & 0xFF));
bool res = write(address, command, true);

return res;
}

// Calculate the CRC
uint8_t SHT30::calculate_crc(FixedBuffer<uint8_t, 3>& raw_data, uint8_t length)
{
// Polynomial: 0x31 (x8 + x5 + x4 + 1)
// Initialization: 0xFF
// Final XOR: 0x00
// Example: CRC (0xBEEF) = 0x92

const uint8_t CRC8POLY = 0x31;
const uint8_t CRC8INIT = 0xFF;
uint8_t calc_crc = CRC8INIT;

for (uint8_t index = 0; index < length; index++)
{
calc_crc = static_cast<uint8_t>(calc_crc ^ raw_data[index]);

// for each bit postion in byte
for (uint8_t bits_left = 8; bits_left > 0; bits_left--)
{
calc_crc = (calc_crc & 0x80)
? CRC8POLY ^ static_cast<uint8_t>(calc_crc << 1)
: static_cast<uint8_t>(calc_crc << 1);
}
}

return calc_crc;
}

// Execute the write command to SHT30
bool SHT30::execute_write_command(Command cmd)
{
bool res = write_command(cmd);

// required delay between sending commands
std::this_thread::sleep_for(delay_2ms);

if (!res)
{
Log::error(TAG, "Execute write command failed");
}

return res;
}

// Execute the repeatability command to SHT30
bool SHT30::execute_repeatability_command()
{
bool res = write_command(repeatability_value[repeatability_command]);

// required delay after sending repeatability command
std::this_thread::sleep_for(repeatability_delay[repeatability_command]);

if (!res)
{
Log::error(TAG, "Execute repeatability command failed");
}

return res;
}

// Execute CRC checking
bool SHT30::execute_crc_checking(FixedBuffer<uint8_t, 3>& raw_data, uint8_t length)
{
// last byte in raw_data holds the crc value sent from SHT30
bool res = calculate_crc(raw_data, length) == raw_data[2];

if (!res)
{
Log::error(TAG, "Execute CRC check failed");
}

return res;
}

// Execute reading block of data from SHT30
bool SHT30::execute_read_block(FixedBufferBase<uint8_t>& raw_data)
{
bool res = read_block(address, raw_data);

if (!res)
{
Log::error(TAG, "Execute read block failed");
}

return res;
}
}
40 changes: 40 additions & 0 deletions lib/smooth/core/io/i2c/I2CMasterDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,46 @@ namespace smooth::core::io::i2c
return res == ESP_OK;
}

bool I2CMasterDevice::read_block(uint8_t address,
core::util::FixedBufferBase<uint8_t>& data,
bool end_with_nack)
{
I2CCommandLink link(*this);

// Set R/W bit to 1 for read.
auto read_address = static_cast<uint8_t>((address << 1) | 0x1);

// Generate start condition
auto res = i2c_master_start(link);

// Write the read address, then read the desired amount,
// ending the read with a NACK to signal the slave to stop sending data.
res |= i2c_master_write_byte(link, read_address, true);

if (data.size() > 1)
{
res |= i2c_master_read(link, data.data(), data.size() - 1, I2C_MASTER_ACK);
}

res |= i2c_master_read_byte(link, data.data() + data.size() - 1,
end_with_nack ? I2C_MASTER_NACK : I2C_MASTER_ACK);

// Complete the read with a stop condition.
res |= i2c_master_stop(link);
res |= i2c_master_cmd_begin(port, link, to_tick(timeout));

if (res != ESP_OK)
{
std::stringstream ss;
ss << "Error during read of address 0x" << std::hex << static_cast<int32_t>(address);
log_error(res, ss.str().c_str());
i2c_reset_tx_fifo(port);
i2c_reset_rx_fifo(port);
}

return res == ESP_OK;
}

bool I2CMasterDevice::is_present() const
{
std::vector<uint8_t> found;
Expand Down
Loading