configs/components/ads131m08/ads131m08.cpp

2044 lines
66 KiB
C++

#include "ads131m08.h"
//#include <chrono>
//#include <thread>
//#include "esphome/components/spi/spi.h"
#include "esphome/core/log.h"
#include <string>
#include <charconv>
#include <format>
#include "esp_system.h"
#include "spi_flash_mmap.h"
//using namespace esphome::spi;
namespace esphome {
namespace ads131m08 {
static const char *const TAG = "ads131m08";
ADS131M08Hub::ADS131M08Hub()
: base_frame(40, 0)
{
}
void ADS131M08Hub::setup()
{
for(int i = 0; i < ADC_CHANNELS; i++) {
//last_publish_time_[i] = 0;
num_samples_[i] = 0;
sample_sum_[i] = 0;
sample_squared_sum_[i] = 0;
}
// 1. Create a binary semaphore
this->data_ready_semhandle = xSemaphoreCreateBinary();
sync_reset_pin_->setup();
sync_reset_pin_->pin_mode(esphome::gpio::FLAG_OUTPUT); // Set SYNC/RESET pin as output
sync_reset_pin_->digital_write(true);
// setup DRDY pin interrupt
this->drdy_pin_->setup();
this->drdy_pin_->pin_mode(esphome::gpio::FLAG_INPUT | esphome::gpio::FLAG_PULLUP); // external pull-up
this->spi_setup();
// Check if setup failed
if (this->is_failed()) {
ESP_LOGE(TAG, "SPI setup failed!");
this->mark_failed(LOG_STR("SPI setup failed." ));
return;
}
// Send RESET command
bool reset_ok = adc_reset_retry();
//ESP_LOGW(TAG, "adc word length: %d", adc_word_length_);
if(!reset_ok) {
ESP_LOGE(TAG, "ADC reset failed!");
this->mark_failed(LOG_STR("Reset failed." ));
this->adc_init_ = 0;
return;
}
if(!this->adc_initialize(32)) {
ESP_LOGE(TAG, "ADC reset failed!");
this->mark_failed(LOG_STR("Initialisation failed." ));
this->adc_init_ = 0;
return;
}
set_reg_osr();
if(!adc_lock(true)) {
ESP_LOGE(TAG, "ADC lock failed!");
this->mark_failed(LOG_STR("ADC lock failed." ));
this->adc_init_ = 0;
return;
}
if(!adc_lock(false)) {
ESP_LOGE(TAG, "ADC unlock failed!");
this->mark_failed(LOG_STR("ADC unlock failed." ));
this->adc_init_ = 0;
return;
}
this->drdy_pin_->attach_interrupt(&ADS131M08Hub::isr_handler, this, gpio::INTERRUPT_FALLING_EDGE);
//cs_ctr_=1000;
//SPIDevice::enable(); // leave CS low
}
void ADS131M08Hub::dump_config()
{
ESP_LOGCONFIG(TAG, "ADS131M08:");
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DRDY Pin: ", this->drdy_pin_);
if(this->sync_reset_pin_ != nullptr) {
LOG_PIN(" SYNC/RESET Pin: ", this->sync_reset_pin_);
}
if (this->data_rate_ < 1000000) {
ESP_LOGCONFIG(TAG, " Data rate: %" PRId32 "kHz", this->data_rate_ / 1000);
} else {
ESP_LOGCONFIG(TAG, " Data rate: %" PRId32 "MHz", this->data_rate_ / 1000000);
}
if (this->clock_frequency_ < 1000000) {
ESP_LOGCONFIG(TAG, " Clock frequency: %.3fkHz", this->clock_frequency_ / 1000);
} else {
ESP_LOGCONFIG(TAG, " Clock frequency: %.3fMHz", this->clock_frequency_ / 1000000);
}
uint16_t osr = 1 << (7 + this->osr_);
if (osr == 16384) {
osr = 16256;
}
ESP_LOGCONFIG(TAG, " Oversampling ratio: %" PRId32, osr);
ESP_LOGCONFIG(TAG, " Reference Voltage: %.2fV", this->reference_voltage_);
ESP_LOGCONFIG(TAG, " SPI Mode: %d", this->mode_);
ESP_LOGCONFIG(TAG, " Bit Order: %s", this->bit_order_ == 1 ? "msb_first" : this->bit_order_ == 0 ? "lsb_first" : "unknown");
}
void ADS131M08Hub::set_channel_gain(uint8_t channel, ADS131M08_PGA_GAIN gain)
{
if(channel < ADC_CHANNELS) {
set_channel_pga(channel, gain);
}
}
uint8_t ADS131M08Hub::update_adc_word_length()
{
int word_nbytes = this->adc_word_length_ >> 3;
spiframe frame(2 * word_nbytes, 0); // prepare with CMD_NULL; always cater for crc
write_array(frame); // write CMD_NULL to ADC to retrieve status
read_array(frame);
uint16_t status = frame[0] << 8 | frame[1];
//print_command_response_to_string(CMD_NULL, frame).c_str();
switch(status & MASK_STATUS_WLENGTH) {
case WLENGTH_16_BITS:
this->adc_word_length_ = 16;
this->conversion_factor_ = this->reference_voltage_/32768.0; // TODO: must double check this with a test
break;
case WLENGTH_24_BITS:
this->adc_word_length_ = 24;
this->conversion_factor_ = this->reference_voltage_/8388608.0;
break;
default:
this->adc_word_length_ = 32;
this->conversion_factor_ = this->reference_voltage_/8388608.0;
break;
}
return this->adc_word_length_;
}
bool ADS131M08Hub::adc_initialize(uint8_t word_length)
{
this->adc_init_++;
update_adc_word_length();
if (!adc_register_write(REG_CLOCK, reg_clock_allch_off)) {
ESP_LOGE(TAG, "CLOCK register write / read to turn off all channels failed");
return false;
}
ESP_LOGV(TAG, "Turned off all channels so short frames can be written during config");
if(!adc_register_write(REG_MODE, MODE_MASK_RESET_HAPPENED & reg_mode_cfg24)) {
ESP_LOGE(TAG, "MODE register write / read to set word size to 24bits failed");
return false;
}
//update_adc_word_length();
ESP_LOGV(TAG, "Written MODE register; Cleared the RESET flag, made DRDY active low pulse");
if(!adc_register_write(REG_THRSHLD_LSB, 0x09)) {
ESP_LOGE(TAG, "THRSHLD_LSB register write / read to set DBLOCK filter failed");
return false;
}
ESP_LOGV(TAG, "Written THRSHLD_LSB register; Set DCBLOCK filter to have a corner frequency of 622 mHz");
if(!adc_set_word_length(word_length))
return false;
// we leave should channels off, as the individual sensors should turn it on
if(!adc_register_write(REG_CLOCK, reg_clock_allch_off)) {
ESP_LOGE(TAG, "CLOCK register write / read to turn-off all channels failed");
return false;
}
//ESP_LOGV(TAG, "Turned on all ADC channels; Re-wrote defaults for other bits in CLOCK register");
if(!drdy_pin_->digital_read()) {
ESP_LOGE(TAG, "DRDY pin is low after initialization!");
return false;
}
this->adc_init_--;
return true;
}
bool ADS131M08Hub::adc_set_word_length(uint8_t word_length)
{
uint16_t setting;
switch(word_length) {
case 32:
setting = reg_mode_cfg32;
break;
case 16:
setting = reg_mode_cfg16;
break;
default:
setting = reg_mode_cfg24;
break;
}
if(!adc_register_write_masked(REG_MODE, setting, reg_mode_cfg_mask, __LINE__)) {
ESP_LOGE(TAG, "MODE register write / read to set word size to %ubits failed", word_length);
return false;
}
ESP_LOGV(TAG, "Written MODE register; Made ADC word size %u bits. Re-wrote other set bits in MODE register", word_length);
//update_adc_word_length();
return true;
}
// Implementing nested CS enable/disable handling in ADS131M08Hub to allow multiple transfers in a row without toggling CS between them, which is required for proper communication with the ADC according to the datasheet recommendations.
void ADS131M08Hub::enable(const char *txt)
{
cs_ctr_++; // Increment counter on each enable call
//esph_log_i(TAG, "cs_ctr_e: %s %d", txt, cs_ctr_.load());
if(cs_ctr_ == 1) { // Only set CS low on the first enable call
SPIDevice::enable(); // Call the base class enable to set CS low
if(txt != nullptr)
esph_log_i(TAG, "enable: %s", txt);
}
}
void ADS131M08Hub::disable(const char *txt)
{
//esph_log_i(TAG, "cs_ctr_d: %s %d", txt, cs_ctr.load());
cs_ctr_--; // Decrement counter on each disable call
if(cs_ctr_ == 0) { // Only set CS high when counter returns to 0
SPIDevice::disable(); // Call the base class disable to set CS high
if(txt != nullptr)
esph_log_i(TAG, "disable: %s", txt);
}
else if (cs_ctr_ < 0) { // Sanity check to prevent counter from going negative
cs_ctr_ = 0;
}
}
// ISR function to handle interrupt from drdy pin
// MUST be fast and non-blocking
void ADS131M08Hub::isr_handler(ADS131M08Hub *arg)
{
{
InterruptLock lock;
if(arg != nullptr) {
arg->isr_ctr_++;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(arg->data_ready_semhandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR(); // Switch to the waiting task immediately
}
}
}
}
void ADS131M08Hub::loop()
{
// Check the semaphore (0 timeout means non-blocking)
if (xSemaphoreTake(data_ready_semhandle, 0) == pdTRUE) {
if(this->adc_init_ == 0) {
uint32_t num_samples = 0;
uint32_t crc_errors = 0;
{
CHIP_SELECTx
// Perform the read here safely outside of ISR
uint64_t start = micros();
//this->txf_init(); // implementing datasheet recommended TXF init in ISR
this->adc_sync();
if(rms_calc_req_) {
std::pair<uint32_t, uint32_t> result = read_multi();
num_samples = result.first;
crc_errors = result.second;
uint64_t end = micros();
if(this->sensors_ac[SAMPLE_TIME_SENSOR] != nullptr)
this->sensors_ac[SAMPLE_TIME_SENSOR]->publish_state(static_cast<float>(end - start)/1000.0);
if(this->sensors_ac[MAX_SAMPLES_SENSOR] != nullptr)
this->sensors_ac[MAX_SAMPLES_SENSOR]->publish_state(static_cast<float>(num_samples));
if(this->sensors_ac[CRC_ERRORS_SENSOR] != nullptr)
this->sensors_ac[CRC_ERRORS_SENSOR]->publish_state(static_cast<float>(crc_errors));
uint16_t ctr = isr_ctr_;
ESP_LOGI(TAG, "WL: %u isr_ctr_: %u", this->adc_word_length_, ctr); }
else {
read_single();
}
isr_ctr_ = 0;
}
if (crc_errors > 30) {
ESP_LOGW(TAG, "High CRC error rate.");
adc_set_word_length(this->adc_word_length_); // for some reason the adc occasionally reverts to 24bits; this will reset the word length to what it should be
//update_adc_word_length();
spiframe recoverframe(40, 0);
read_array(recoverframe);
ESP_LOGI(TAG, "WL: %u Frame: %s", this->adc_word_length_, frame_to_string(recoverframe).c_str());
}
//ESP_LOGW(TAG, "%llu ms (%llu us), max samples: %u", (end - start)/1000, (end - start), num_samples);
}
}
}
/// @brief Set 16 bit frameword
/// @param frame
/// @param w_index
/// @param data uint16_t denotes command or CRC
/// @return
bool ADS131M08Hub::set_frame_word(spiframe& frame, int w_index, uint16_t data)
{
size_t word_nbytes = adc_word_length_ >> 3;
size_t frame_words = frame.size() / word_nbytes;
int b_index = w_index * word_nbytes;
//ESP_LOGD(TAG, "b_index: %d, word_nbytes: %d, frame words: %d, w_index: %d, frame size: %d", b_index, word_nbytes, frame_words, w_index, frame.size());
if(w_index >= frame_words) {
if(w_index > MAX_FRAME_SIZE) {
ESP_LOGE(TAG, "Word index of %d out of bounds", w_index);
//esp_crosscore_int_send_print_backtrace(xPortGetCoreID());
return false; // invalid - something has gone wrong
}
//int oldsize = frame.size();
frame.resize(b_index + word_nbytes);
//ESP_LOGD(TAG, "Resized frame from %d, to %d.", oldsize, frame.size());
}
switch(adc_word_length_) {
// deliberate no breaks after first two cases
case 32:
frame[b_index + 3] = 0;
case 24:
frame[b_index + 2] = 0;
case 16:
frame[b_index + 1] = data & 0xFF;
frame[b_index] = (data >> 8) & 0xFF;
break;
default:
ESP_LOGW(TAG, "Invalid adc word length of %d", adc_word_length_);
break;
}
return true;
}
bool ADS131M08Hub::set_frame_word(spiframe& frame, int w_index, uint32_t data)
{
size_t word_nbytes = adc_word_length_ >> 3;
size_t frame_words = frame.size() / word_nbytes;
int b_index = w_index * word_nbytes;
if(w_index >= frame_words) {
if(w_index > MAX_FRAME_SIZE) {
ESP_LOGE(TAG, "Word index of %d out of bounds", w_index);
//esp_crosscore_int_send_print_backtrace(xPortGetCoreID());
return false; // invalid - something has gone wrong
}
frame.resize(b_index + word_nbytes);
}
switch(adc_word_length_) {
// deliberate no breaks after first two cases
case 32:
frame[b_index++] = (data >> 24) & 0xFF;
case 24:
frame[b_index++] = (data >> 16) & 0xFF;
case 16:
frame[b_index++] = (data >> 8) & 0xFF;
frame[b_index] = data & 0xFF;
break;
default:
ESP_LOGW(TAG, "Invalid adc word length of %d", adc_word_length_);
break;
}
return true;
}
// index(i) = zero-reference, returns i'th word, LSB aligned
uint32_t ADS131M08Hub::get_unsigned_frame_word(const spiframe& frame, int w_index, bool force_16bits)
{
uint32_t result = 0;
size_t word_nbytes = adc_word_length_ >> 3;
int b_index = w_index * word_nbytes;
if(b_index > (frame.size() - word_nbytes)) {
//ESP_LOGE(TAG, "Word index of %d out of bounds. b_index: %d, frame_size: %d, Caller: %s Line: %d", w_index, b_index, frame.size(), caller.c_str(), line);
//esp_crosscore_int_send_print_backtrace(xPortGetCoreID());
return result; // illegal
}
switch(force_16bits ? 16 : adc_word_length_) {
// deliberate no breaks after first two cases
case 32:
result += frame[b_index++] << 24;
case 24:
result += frame[b_index++] << 16;
case 16:
result += frame[b_index++] << 8;
result += frame[b_index];
break;
default:
ESP_LOGW(TAG, "Invalid adc word length of %d", adc_word_length_);
break;
}
return result;
}
int32_t ADS131M08Hub::get_sign_ext_frame_word(const spiframe& frame, int w_index)
{
int32_t result = 0;
size_t word_nbytes = adc_word_length_ >> 3;
int b_index = w_index * word_nbytes;
if(b_index > (frame.size() - word_nbytes)) {
ESP_LOGW(TAG, "Word index of %d out of bounds", w_index);
return result; // invalid
}
uint32_t raw = 0;
switch(adc_word_length_) {
case 32:
raw = (frame[b_index] << 24) | (frame[b_index + 1] << 16) | (frame[b_index + 2]) << 8 | frame[b_index + 3];
result = static_cast<int32_t>(raw);
break;
case 24:
raw = (frame[b_index] << 16) | (frame[b_index + 1] << 8) | frame[b_index + 2];
if (raw & 0x800000)
result = static_cast<int32_t>(raw | 0xFF000000); // Sign extend
else
result = static_cast<int32_t>(raw);
break;
case 16:
raw = ((frame[b_index] << 8) | frame[b_index + 1]);
result = static_cast<int16_t>(raw);
break;
default:
ESP_LOGW(TAG, "Invalid adc word length of %d", adc_word_length_);
break;
}
return result;
}
//{
// // Read STATUS register to check if data is ready for the specified channel
// // STATUS register is the first word in the SPI frame, so we can read it directly
// uint16_t status;
// this->enable();
// this->transfer_byte(0x00); // Send dummy byte to read STATUS register
// status = this->transfer_byte(0x00); // Read upper byte of STATUS
// status <<= 8;
// status |= this->transfer_byte(0x00); // Read lower byte of STATUS
// this->disable();
// // Check the corresponding bit in the STATUS register for the specified channel
// // Assuming each channel corresponds to a specific bit in the STATUS register, e.g. bit 0 for channel 0, bit 1 for channel 1, etc.
// return (status & (1 << channel)) != 0; // Return true if data is ready for the specified channel
//}
void ADS131M08Hub::adc_hard_reset()
{
float one_cycle = 1000000 / this->clock_frequency_; // one cycle duration in micro seconds
uint32_t reset_pulse_duration = static_cast<uint32_t>(1.05 * 2048 * one_cycle); // 2048 clock cycles converted to microseconds, multiplied by 1.05 to ensure we meet the minimum pulse duration requirement
if(reset_pulse_duration < 1) {
reset_pulse_duration = 1; // set it to 1 microsecond
}
sync_reset_pin_->digital_write(false); // Pull SYNC/RESET pin low to reset the ADC
delay_microseconds_safe(reset_pulse_duration); // Hold reset for the calculated duration
sync_reset_pin_->digital_write(true); // Set SYNC/RESET pin high to allow normal operation
}
void ADS131M08Hub::adc_sync()
{
float one_cycle = 1000000 / this->clock_frequency_; // one cycle duration in micro seconds
uint32_t sync_pulse_duration = static_cast<uint32_t>(std::round(50 * one_cycle)); // minimum is 1/clock freq, max = 2047/clock freq
if(sync_pulse_duration < 1) {
sync_pulse_duration = 1; // set it to 1 microsecond
}
sync_reset_pin_->digital_write(false); // Pull SYNC/RESET pin low to sync the ADC
delay_microseconds_safe(sync_pulse_duration); // Hold sync for the calculated duration
sync_reset_pin_->digital_write(true); // Set SYNC/RESET pin high to allow normal operation
}
// ********************** from datasheet ************************
void ADS131M08Hub::txf_init()
{
CHIP_SELECT
if (firstRead) {
// Clear the ADC's 2-deep FIFO on the first read
for(int i = 0; i < numFrameWords * adc_word_length_; i++) {
this->write_byte(0);
}
for(int i = 0; i < numFrameWords; i++) {
this->read_byte(); // should perhaps read dwords instead of bytes, but we just want to clear the FIFO so it doesn't matter much?
}
firstRead = false; // Clear the flag
// we will do this later, after sorting out basic communication
// DMA.enable(); // Let the DMA start sending ADC data to memory
}
// Send the dummy data to the ADC to get the ADC data
for(int i = 0; i < numFrameWords * adc_word_length_; i++) {
this->write_byte(0);
}
}
bool ADS131M08Hub::adc_reset_retry()
{
adc_word_length_ = 24;
bool reset_ok = adc_soft_reset();
uint8_t retry_wordlengths[] = { 32, 32, 24, 24, 16, 16 } ;
int i = 0;
while( i < sizeof(retry_wordlengths) && !reset_ok) {
adc_word_length_ = retry_wordlengths[i];
//ESP_LOGW(TAG, "ADC word length: %d", adc_word_length_);
reset_ok = adc_soft_reset();
i++;
}
return reset_ok;
}
//int ADS131M08Hub::build_frame()
bool ADS131M08Hub::adc_soft_reset()
{
int index = 0;
uint16_t reset_response;
uint16_t cmd = 0;
// we assume 24 bit wordlength if adc_word_length_is illegal
//if(adc_word_length_ != 32 && adc_word_length_ != 24 && adc_word_length_ != 16) {
// adc_word_length_ = 24;
//}
update_adc_word_length();
size_t word_nbytes = adc_word_length_ >> 3;
int framelength = numFrameWords * word_nbytes;
spiframe frame(framelength, 0);
size_t frame_len;
// curly braces confine chip select scope
{
CHIP_SELECTx
cmd = CMD_RESET;
set_frame_word(frame, 0, cmd);
index += 2;
for(; index < framelength; index++) {
frame[index] = 0;
}
frame_len = add_crc(frame, crc_pos::crc_after_firstword);
write_array(frame);
ESP_LOGVV(TAG, "Sent Frame: %s", frame_to_string(frame).c_str());
}
delay_microseconds_safe(T_REGACQ);
{
CHIP_SELECTx
read_array(frame);
reset_response = get_unsigned_frame_word(frame, 0, true);
ESP_LOGVV(TAG, "Read Frame: %s", frame_to_string(frame).c_str());
}
ESP_LOGVV(TAG, "reset_response: 0x%04X", reset_response);
if (reset_response == RSP_RESET_OK) {
return true;
}
return false;
}
// overwrites last (or second) frameword with CRC, therefore frame should be large enough for payload and crc
size_t ADS131M08Hub::add_crc(spiframe& frame, crc_pos crcpos)
{
size_t effective_frame_length;
size_t frame_length = frame.size();
if (frame_length == 0 || adc_word_length_ == 0)
return 0;
size_t word_nbytes = adc_word_length_ >> 3;
if (crcpos == crc_pos::crc_after_firstword) {
effective_frame_length = word_nbytes * 2;
}
else {
effective_frame_length = frame_length;
}
if (effective_frame_length > frame_length) {
if (frame.capacity() >= effective_frame_length)
{
frame.resize(effective_frame_length);
if (frame.size() < effective_frame_length) {
return 0;
}
frame_length = effective_frame_length;
}
else {
return 0;
}
}
size_t frame_words = effective_frame_length / word_nbytes; // integer division, we discard remainder if any
size_t payload_len = word_nbytes * (frame_words - 1);
if (payload_len < word_nbytes)
return false;
uint16_t crc = get_crc(frame);
set_frame_word(frame, frame_words - 1, crc);
return frame_words * word_nbytes;
}
// CRC should be 16bits, MSB aligned in last word of frame
bool ADS131M08Hub::check_crc(const spiframe& frame)
{
if (adc_word_length_ == 0)
return false;
size_t word_nbytes = adc_word_length_ >> 3;
int frame_length = frame.size();
size_t frame_words = frame_length / word_nbytes; // integer division, we discard remainder if any
size_t payload_len = word_nbytes * (frame_words - 1);
if (payload_len < word_nbytes)
return false;
uint16_t frame_crc = (frame[payload_len] << 8) + frame[payload_len + 1]; // Frames are MSB aligned, so is CRC
auto crc = get_crc(frame);
bool crc_ok = crc == frame_crc;
return crc_ok;
}
uint16_t ADS131M08Hub::read_frame_crc(const spiframe& frame)
{
if (adc_word_length_ == 0)
return 0;
size_t word_nbytes = adc_word_length_ >> 3;
int frame_length = frame.size();
size_t frame_words = frame_length / word_nbytes; // integer division, we discard remainder if any
size_t payload_len = word_nbytes * (frame_words - 1);
if (payload_len < word_nbytes)
return 0;
return (frame[payload_len] << 8) + frame[payload_len + 1]; // Frames are MSB aligned, so is CRC
}
/*
CRC Parameters for ADS131M08
Polynomial: 0x1021 (x^16 + x^12 + x^5 + 1).
Initial Value (Seed): 0xFFFF.
Input Data: The calculation is performed over all bits in the SPI frame preceding the CRC word.
Output Format: The resulting 16-bit CRC is placed in the most significant 16 bits of the final 24-bit (or 32-bit) SPI word; the remaining bits are typically padded with zeros.
*/
uint16_t ADS131M08Hub::get_crc(const spiframe& frame)
{
size_t word_nbytes = adc_word_length_ >> 3;
size_t frame_words = frame.size() / word_nbytes; // integer division, we discard remainder if any
size_t payload_len = word_nbytes * (frame_words - 1);
uint16_t crc_result = crc(0xFFFF, frame[0]);
for (int j = 1; j < payload_len; j++) {
crc_result = crc(crc_result, frame[j]);
}
return crc_result;
}
uint16_t ADS131M08Hub::crc(uint16_t crc_register, uint8_t data)
{
uint16_t xor_result, input_bit, crc_result;
crc_result = crc_register;
for (int i = 7; i >= 0; i--) {
input_bit = (data >> i) & 0x01;
xor_result = input_bit ^ (crc_result & 0x8000) >> 15;
crc_result = crc_result << 1;
if(xor_result)
crc_result = crc_result ^ 0x1021;
}
return crc_result;
}
void ADS131M08Hub::write_byte(uint8_t byte)
{
CHIP_SELECT
this->transfer_byte(byte);
}
uint8_t ADS131M08Hub::read_byte()
{
CHIP_SELECT
uint8_t result = this->transfer_byte(0x00); // Send dummy byte to read data
return result;
}
void ADS131M08Hub::read_array(spiframe& frame)
{
CHIP_SELECT
for (size_t i = 0; i < frame.size(); i++) {
frame[i] = this->transfer_byte(0x00); // Send dummy byte to read data
}
}
void ADS131M08Hub::write_array(const spiframe& frame)
{
CHIP_SELECT
for (size_t i = 0; i < frame.size(); i++) {
this->transfer_byte(frame[i]);
}
}
void ADS131M08Hub::transfer_array(const spiframe& tx_frame, spiframe& rx_frame)
{
CHIP_SELECT
auto frame_length = tx_frame.size();
if(rx_frame.size() < frame_length)
rx_frame.resize(frame_length);
for (size_t i = 0; i < frame_length; i++) {
rx_frame[i] = this->transfer_byte(tx_frame[i]);
}
}
// ********************** end of from datasheet ************************
void ADS131M08Hub::set_measure_rms(uint8_t channel, bool enable)
{
if(channel < ADC_CHANNELS) {
ESP_LOGW(TAG, "set_measure_rms[%d] = %s", channel, enable ? "enabled" : "disabled");
this->rms_enabled_[channel] = enable;
this->rms_calc_req_ = true;
}
}
void ADS131M08Hub::read_single()
{
int word_nbytes = adc_word_length_ >> 3;
int frame_length = numFrameWords * word_nbytes;
spiframe frame(frame_length, 0);
this->read_array(frame);
bool crc_ok = check_crc(frame);
uint16_t drdy_status = get_unsigned_frame_word(frame, 0, true) & MASK_STATUS_DRDY;
//data_ready = crc_ok && (drdy_status != 0);
if(crc_ok) {
// sample channels
for (int i = 0; i < ADC_CHANNELS; i++) {
bool channel_ready = drdy_status & 1;
drdy_status = drdy_status >> 1;
bool ac_sensor_exist = this->sensors_ac[i] != nullptr;
bool dc_sensor_exist = this->sensors_dc[i] != nullptr;
if (channel_ready && (ac_sensor_exist || dc_sensor_exist)) {
int32_t raw = get_sign_ext_frame_word(frame, i + 1);
this->sampled_values_[i] = this->conversion_factor_ * raw;
float value = this->conversion_factor_ * raw;
if(ac_sensor_exist)
this->sensors_ac[i]->publish_state(value);
if(dc_sensor_exist)
this->sensors_dc[i]->publish_state(value);
}
}
}
}
std::pair<uint32_t, uint32_t> ADS131M08Hub::read_multi()
{
int32_t raw;
float value;
std::pair<uint32_t, uint32_t> result;
result.first = 0;
result.second = 0;
bool crc_ok, data_ready, ac_sensor_exist, dc_sensor_exist;
uint16_t drdy_status, status;
int i, max_iters = 250; // going above 255 will result in overflows
uint32_t elapsed_time = 0;
uint32_t loop_start_time = micros();
int word_nbytes = adc_word_length_ >> 3;
int frame_length = numFrameWords * word_nbytes;
spiframe frame(frame_length, 0);
// integrate for rms values over #iterations or sample_time depending on what comes first
for(int i = 0; i < ADC_CHANNELS; i++) {
//last_publish_time_[i] = 0;
num_samples_[i] = 0;
sample_sum_[i] = 0;
sample_squared_sum_[i] = 0;
}
while (elapsed_time < this->sample_time_ && max_iters-- > 0) {
this->read_array(frame);
//ESP_LOGD(TAG, "max_iters: %d, frame: %s", max_iters, frame_to_string(frame).c_str());
crc_ok = check_crc(frame);
if (crc_ok) {
status = get_unsigned_frame_word(frame, 0, true);
// skip frame immediately afte resync
if ((status & MASK_STATUS_RESYNC) == 0) {
drdy_status = status & MASK_STATUS_DRDY;
// sample channels
for (i = 0; i < ADC_CHANNELS && drdy_status != 0; i++) {
data_ready = drdy_status & 1;
drdy_status = drdy_status >> 1;
ac_sensor_exist = this->sensors_ac[i] != nullptr;
dc_sensor_exist = this->sensors_dc[i] != nullptr;
if (data_ready && (ac_sensor_exist || dc_sensor_exist)) {
raw = get_sign_ext_frame_word(frame, i + 1);
value = this->conversion_factor_ * raw;
this->sampled_values_[i] = value;
if (this->rms_enabled_[i]) {
(this->num_samples_[i])++;
(this->sample_sum_[i]) += static_cast<int64_t>(raw);
(this->sample_squared_sum_[i]) += value * value;
}
else {
if(ac_sensor_exist)
this->sensors_ac[i]->publish_state(value);
if(dc_sensor_exist)
this->sensors_dc[i]->publish_state(value);
}
}
}
}
}
else {
result.second++;
//ESP_LOGW(TAG, "max_iters: %d, frame: %s CRC error", max_iters, frame_to_string(frame).c_str());
}
elapsed_time = micros() - loop_start_time;
}
// publish integrated values
for (i = 0; i < ADC_CHANNELS; i++) {
if (this->rms_enabled_[i]) {
ac_sensor_exist = this->sensors_ac[i] != nullptr;
dc_sensor_exist = this->sensors_dc[i] != nullptr;
auto num_samples = this->num_samples_[i];
if(num_samples > result.first) {
result.first = num_samples;
}
if (num_samples == 0) {
// should never happen, but let's play safe and avoid dividing by zero
if(ac_sensor_exist)
this->sensors_ac[i]->publish_state(NAN);
if(dc_sensor_exist)
this->sensors_dc[i]->publish_state(NAN);
}
else {
int64_t sample_sum = sample_sum_[i];
float rms_dc = this->conversion_factor_ * static_cast<float>(sample_sum) / num_samples;
float rms_ac_squared = sample_squared_sum_[i] / num_samples - rms_dc * rms_dc;
float rms_ac = 0;
if (rms_ac_squared > 0) {
rms_ac = std::sqrtf(rms_ac_squared);
}
this->sps_ = (1e6 * (float)num_samples) / elapsed_time;
if(ac_sensor_exist)
this->sensors_ac[i]->publish_state(rms_ac);
if(dc_sensor_exist)
this->sensors_dc[i]->publish_state(rms_dc);
}
this->num_samples_[i] = 0;
this->sample_sum_[i] = 0.0f;
this->sample_squared_sum_[i] = 0.0f;
}
}
return result;
}
bool ADS131M08Hub::adc_lock(bool enable)
{
int word_nbytes = adc_word_length_ >> 3;
spiframe frame(word_nbytes * 2, 0);
uint16_t command = CMD_UNLOCK;
if (enable) {
command = CMD_LOCK;
}
set_frame_word(frame, 0, command);
add_crc(frame, crc_after_frame);
write_array(frame);
delay_microseconds_safe(T_REGACQ);
read_array(frame);
auto ack = get_unsigned_frame_word(frame, 0, true);
return ack == command;
}
/// @brief
/// @param address - register address
/// @param value - mask is used to remove unwanted bits before adding to value read from register
/// @param mask - register contents read from adc is masked with inverse of this
/// @param line - temporary / for debugging
/// @return success flag
bool ADS131M08Hub::adc_register_write_masked(uint8_t address, uint16_t value, uint16_t mask, int line)
{
uint16_str reg_contents = adc_register_read(address, 1);
if(reg_contents.empty()) {
return false;
}
ESP_LOGW(TAG, "1: reg contents: 0x%04X, address: 0x%02X, value: 0x%04X, mask: 0x%04X, caller: %d", reg_contents[0],address, value,mask, line );
reg_contents[0] = (reg_contents[0] & ~mask) | (value & mask);
ESP_LOGW(TAG, "After applying mask - reg contents: 0x%04X, caller: %d", reg_contents[0], line );
return adc_register_write(address, reg_contents);
}
/// @brief
/// @param start_address - first register address
/// @param values - mask is used to remove unwanted bits before adding to value read from register
/// @param masks - register contents read from adc is masked with inverse of this
/// @param line - temporary / for debugging
/// @return success flag
bool ADS131M08Hub::adc_register_write_masked(uint8_t start_address, const uint16_str& values, const uint16_str& masks, int line)
{
uint8_t nregs = values.size();
uint8_t nmasks = masks.size();
if(nregs != nmasks) {
ESP_LOGW(TAG, "Number register values to write (%d) are different than the corresponding number of masks (%d). Register write aborted.", nregs, nmasks);
return false;
}
uint16_str reg_contents = adc_register_read(start_address, nregs);
if(reg_contents.empty()) {
ESP_LOGW(TAG, "No contents read from adc. Expected %d values. Register write aborted.", nregs);
return false;
}
for (int i = 0; i < nregs; i++) {
//ESP_LOGW(TAG, "Before change %d: reg contents: 0x%04X, address: 0x%02X, value: 0x%04X, mask: 0x%04X, caller: %d", i, reg_contents[i], start_address + i, values[i], masks[i], line);
reg_contents[i] = (reg_contents[i] & ~masks[i]) | (values[i] & masks[i]);
//ESP_LOGW(TAG, "After applying mask - reg contents: 0x%04X, caller: %d", reg_contents[i], line);
}
return adc_register_write(start_address, reg_contents);
}
bool ADS131M08Hub::adc_register_write(uint16_t address, uint16_t data)
{
uint16_str arg;
arg += data;
return adc_register_write(address, arg);
}
bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str& data)
{
int nregs = data.size();
if(nregs == 0) {
return false; // invalid
}
int word_nbytes = adc_word_length_ >> 3;
int wreg_framelength = word_nbytes * (2 + nregs); // ensure room for crc
int rreg_resp_framelength = word_nbytes * (2 + (nregs == 1 ? -1 :nregs)); // add room for CRC if nregs > 1
spiframe frame(wreg_framelength, 0);
spiframe rxframe(wreg_framelength, 0);
uint16_t addr_regcnt_mask = (start_address << 7) | ((nregs - 1 ) & MASK_CMD_RW_REG_COUNT);
uint16_t wreg_addr_opcode = CMD_WREG | addr_regcnt_mask; // Combine WREG command, start_address and register count
uint16_t rreg_addr_opcode = CMD_RREG | addr_regcnt_mask; // Combine RREG command, start_address and register count
bool has_mode_reg = (start_address == REG_MODE) || ((start_address < REG_MODE) && ((start_address + nregs) > REG_MODE));
{
CHIP_SELECT
set_frame_word(frame, 0, wreg_addr_opcode);
for(int i = 0; i < nregs; i++) {
set_frame_word(frame, i + 1, data[i]);
}
add_crc(frame, crc_after_frame);
ESP_LOGVV(TAG, "%s\n", rwreg_command_frame_to_string(frame).c_str());
transfer_array(frame, rxframe); // WREG
}
ESP_LOGVV(TAG, "Sent Frame: %s", frame_to_string(frame).c_str());
if (has_mode_reg) {
update_adc_word_length();
}
delay_microseconds_safe(T_REGACQ);
auto rxdata = adc_register_read(start_address, nregs);
bool verified = !rxdata.empty();
for(int i = 0; i < rxdata.size() && verified; i++) {
verified = rxdata[i] == data[i];
if(!verified) {
ESP_LOGE(TAG, "Write register failed: tx data= 0x%04X, rx data= 0x%04X", data[i] ,rxdata[i]);
}
}
delay_microseconds_safe(T_REGACQ);
return verified;
}
// recommended not to read more than 20 registers at a time
uint16_str ADS131M08Hub::adc_register_read(uint8_t address, uint8_t nregs)
{
uint16_str result;
if(nregs == 0) {
return result; // invalid
}
if(nregs > 31) {
// We are limited to 31 registers, otherwise we will ran into DMA / SPI buffer problems
ESP_LOGE(TAG, "Trying to read %d registers. This exceeds maximum register read count of 31!", nregs);
return result; // invalid
}
int word_nbytes = adc_word_length_ >> 3;
int request_framelength = 2 * word_nbytes; // ensure room for crc
int response_framelength = word_nbytes * (2 + (nregs == 1 ? -1 : nregs)); // add room for CRC if nregs > 1
spiframe frame(response_framelength, 0);
uint16_t rreg_addr_opcode = CMD_RREG | (address << 7) | ((nregs - 1) & MASK_CMD_RW_REG_COUNT); // Combine RREG command, address and register count
{
CHIP_SELECT
frame.resize(request_framelength);
set_frame_word(frame, 0, rreg_addr_opcode);
add_crc(frame, crc_after_frame);
write_array(frame); // send RREG
}
ESP_LOGVV(TAG, "%s\n", rwreg_command_frame_to_string(frame).c_str());
ESP_LOGVV(TAG, "Sent Frame: %s", frame_to_string(frame).c_str());
delay_microseconds_safe(T_REGACQ);
{
CHIP_SELECT
frame.resize(response_framelength);
read_array(frame);
}
ESP_LOGVV(TAG, "Recv Frame: %s", frame_to_string(frame).c_str());
if(nregs > 1) {
//auto crc = get_crc(frame);
//auto frame_crc = read_frame_crc(frame);
//ESP_LOGW(TAG, "CRC calc: 0x%04X, CRC recv: 0x%04X", crc, frame_crc);
bool crc_ok = check_crc(frame);
if(!crc_ok) {
ESP_LOGW(TAG, "CRC failed reading ADC registers!");
result.clear();
return result; // invalid
}
}
result.resize(nregs);
int offset = 0;
uint16_t rreg_ack = rreg_addr_opcode; // not really and ack, but since we don't get an ack when reading 1 register we pretend this is the ack
if(nregs > 1) {
offset = 1;
rreg_ack = get_unsigned_frame_word(frame, 0, true);;
}
//uint16_t rreg_ack = (nregs == 1) ? rreg_addr_opcode : get_unsigned_frame_word(frame, 0, true);
nregs = (rreg_ack & MASK_CMD_RW_REG_COUNT) + 1;
uint16_t start_addr = (rreg_ack & MASK_CMD_RW_REG_ADDRESS) >> 7;
for(int i = 0; i < nregs; i++) {
result[i] = get_unsigned_frame_word(frame, i + offset, true);
}
print_command_response_to_string(rreg_addr_opcode, frame);
delay_microseconds_safe(1);
return result;
}
int8_t ADS131M08Hub::is_data_ready_soft(uint8_t channel)
{
switch (channel) {
case 0:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY0);
case 1:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY1);
case 2:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY2);
case 3:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY3);
case 4:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY4);
case 5:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY5);
case 6:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY6);
case 7:
return (readRegister(REG_STATUS) & MASK_STATUS_DRDY7);
default:
return -1; // Invalid channel
}
}
bool ADS131M08Hub::is_reset_status()
{
return (readRegister(REG_STATUS) & MASK_STATUS_RESET);
}
bool ADS131M08Hub::is_lock_spi()
{
return (readRegister(REG_STATUS) & MASK_STATUS_LOCK);
}
bool ADS131M08Hub::set_drdy_format(uint8_t drdy_format)
{
if (drdy_format > 1)
{
return false;
}
return adc_register_write_masked(REG_MODE, drdy_format, MASK_MODE_DRDY_FMT, __LINE__);
}
bool ADS131M08Hub::set_drdy_idle_state(uint8_t drdy_state)
{
if (drdy_state > 1)
{
return false;
}
return adc_register_write_masked(REG_MODE, drdy_state < 1, MASK_MODE_DRDY_HiZ, __LINE__);
}
bool ADS131M08Hub::set_power_mode(uint8_t power_mode)
{
if (power_mode > 3)
{
return false;
}
return adc_register_write_masked(REG_CLOCK, power_mode, MASK_CLOCK_PWR, __LINE__);
}
bool ADS131M08Hub::set_reg_osr()
{
uint16_t osr = 3; // default
if (this->osr_ <= 16256 && this->osr_ >= 128) {
if (this->osr_ == 16256)
osr = 7;
else {
osr = 0;
auto tmp = this->osr_/128;
while (tmp >>= 1)
osr++;
}
}
return adc_register_write_masked(REG_CLOCK, osr << 2, MASK_CLOCK_OSR, __LINE__);
}
//void ADS131M08Hub::set_full_scale(uint8_t channel, float scale)
//{
// if (channel > 7) {
// return;
// }
//
// this->full_scale.ch[channel].f = scale;
//
//}
//
//float ADS131M08Hub::get_full_scale(uint8_t channel)
//{
// if (channel > 7) {
// return 0.0;
// }
//
// return this->full_scale.ch[channel].f;
//
//}
bool ADS131M08Hub::set_channel_enable(uint8_t channel, bool enable)
{
if (channel > 7)
return false;
uint16_t enable_mask = MASK_CLOCK_CH0 << channel;
uint16_t reg_value = (enable) ? enable_mask : 0;
//ESP_LOGD(TAG, "adc_register_write_masked(REG_CLOCK=%02X, reg_value=%04X, enable_mask=%04X, __LINE__)", REG_CLOCK, reg_value, enable_mask);
return adc_register_write_masked(REG_CLOCK, reg_value, enable_mask, __LINE__);
//return true;
}
bool ADS131M08Hub::set_channel_pga(uint8_t channel, ADS131M08_PGA_GAIN pga)
{
uint16_t pgaCode = (uint16_t) pga;
switch (channel) {
case 0:
if(!adc_register_write_masked(REG_GAIN1, pgaCode, MASK_GAIN_PGAGAIN0, __LINE__))
return false;
this->pgaGain[0] = pga;
return true;
case 1:
if(!adc_register_write_masked(REG_GAIN1, pgaCode << 4, MASK_GAIN_PGAGAIN1, __LINE__))
return false;
this->pgaGain[1] = pga;
return true;
case 2:
if(!adc_register_write_masked(REG_GAIN1, pgaCode << 8, MASK_GAIN_PGAGAIN2, __LINE__))
return false;
this->pgaGain[2] = pga;
return true;
case 3:
if(!adc_register_write_masked(REG_GAIN1, pgaCode << 12, MASK_GAIN_PGAGAIN3, __LINE__))
return false;
this->pgaGain[3] = pga;
return true;
case 4:
if(!adc_register_write_masked(REG_GAIN2, pgaCode, MASK_GAIN_PGAGAIN4, __LINE__))
return false;
this->pgaGain[4] = pga;
return true;
case 5:
if(!adc_register_write_masked(REG_GAIN2, pgaCode << 4, MASK_GAIN_PGAGAIN5, __LINE__))
return false;
this->pgaGain[5] = pga;
return true;
case 6:
if(!adc_register_write_masked(REG_GAIN2, pgaCode << 8, MASK_GAIN_PGAGAIN6, __LINE__))
return false;
this->pgaGain[6] = pga;
return true;
case 7:
if(!adc_register_write_masked(REG_GAIN2, pgaCode << 12, MASK_GAIN_PGAGAIN7, __LINE__))
return false;
this->pgaGain[7] = pga;
return true;
default:
return false; // Invalid channel
}
return false;
}
ADS131M08_PGA_GAIN ADS131M08Hub::get_channel_pga(uint8_t channel)
{
if(channel > 7)
{
return ADS131M08_PGA_GAIN::PGA_INVALID;
}
return pgaGain[channel];
}
void ADS131M08Hub::set_global_chop(uint16_t global_chop)
{
adc_register_write_masked(REG_CFG, global_chop << 8, MASK_CFG_GC_EN, __LINE__);
}
void ADS131M08Hub::set_global_chop_delay(uint16_t delay)
{
adc_register_write_masked(REG_CFG, delay << 9, MASK_CFG_GC_DLY, __LINE__);
}
bool ADS131M08Hub::set_channel_phase_calibration(uint8_t channel, int16_t phase_offset)
{
if(channel > 7)
return false;
uint16_t reg_addr = 5 * channel + REG_CH0_CFG;
if(phase_offset < -512 || phase_offset > 511)
return false;
return adc_register_write_masked(reg_addr, (phase_offset << 6), MASK_CHX_CFG_PHASE, __LINE__);
}
bool ADS131M08Hub::set_dcblock_filter_disable(uint8_t channel, bool disable)
{
if (channel > 7)
return false;
uint16_t reg_addr = 5 * channel + REG_CH0_CFG;
uint16_t reg_value = (disable) ? MASK_CHX_CFG_DCBLKX_DIS : 0;
return adc_register_write_masked(reg_addr, reg_value, MASK_CHX_CFG_DCBLKX_DIS, __LINE__);
}
bool ADS131M08Hub::set_channel_input_selection(uint8_t channel, ADS131M08_INPUT_CHANNEL_MUX input)
{
if(channel > 7)
return false;
uint16_t reg_addr = 5 * channel + REG_CH0_CFG;
return adc_register_write_masked(reg_addr, input, MASK_CHX_CFG_MUX, __LINE__);
}
bool ADS131M08Hub::set_channel_offset_calibration(uint8_t channel, int32_t offset)
{
if(channel > 7)
return false;
if(offset < -8388608 || offset > 8388607)
return false;
uint16_t MSB = offset >> 8;
uint16_t LSB = offset << 8;
uint16_t reg_addr = 5 * channel + REG_CH0_OCAL_MSB;
return adc_register_write_masked(reg_addr, { MSB, LSB }, { MASK_CHX_OCAL_MSB, MASK_CHX_OCAL_LSB }, __LINE__);
}
bool ADS131M08Hub::set_channel_gain_calibration(uint8_t channel, uint32_t gain)
{
if(channel > 7)
return false;
if(gain > 16777215)
return false;
uint16_t MSB = gain >> 8;
uint8_t LSB = gain;
uint16_t reg_addr = 5 * channel + REG_CH0_GCAL_MSB;
return adc_register_write_masked(reg_addr, { MSB, LSB }, { MASK_CHX_GCAL_MSB, MASK_CHX_GCAL_LSB }, __LINE__);
}
//bool ADS131M08Hub::is_data_ready()
//{
// if (digitalRead(drdyPin) == HIGH)
// {
// return false;
// }
// return true;
//}
uint16_t ADS131M08Hub::get_id()
{
return readRegister(REG_ID);
}
uint16_t ADS131M08Hub::get_mode_reg()
{
return readRegister(REG_MODE);
}
uint16_t ADS131M08Hub::get_clock_reg()
{
return readRegister(REG_CLOCK);
}
uint16_t ADS131M08Hub::get_cfg_reg()
{
return readRegister(REG_CFG);
}
//AdcOutput ADS131M08Hub::read_adc_raw()
//{
// uint8_t x = 0;
// uint8_t x2 = 0;
// uint8_t x3 = 0;
// int32_t aux;
// AdcOutput res;
//
// digitalWrite(csPin, LOW);
// delayMicroseconds(1);
//
// x = spi.transfer(0x00);
// x2 = spi.transfer(0x00);
// spi.transfer(0x00);
//
// this->resultRaw.status = ((x << 8) | x2);
//
// for(int i = 0; i<8; i++)
// {
// x = spi.transfer(0x00);
// x2 = spi.transfer(0x00);
// x3 = spi.transfer(0x00);
//
// aux = (((x << 16) | (x2 << 8) | x3) & 0x00FFFFFF);
// if (aux > 0x7FFFFF)
// {
// this->resultRaw.ch[i].i = ((~(aux)&0x00FFFFFF) + 1) * -1;
// }
// else
// {
// this->resultRaw.ch[i].i = aux;
// }
// }
//
// delayMicroseconds(1);
// digitalWrite(csPin, HIGH);
//
// return this->resultRaw;
//}
//float ADS131M08Hub::scale_result(uint8_t num)
//{
// if( num >= 8) {
// return 0.0;
// }
//
// return this->resultFloat.ch[num].f = (float)(this->resultRaw.ch[num].i * rawToVolts * this->full_scale.ch[num].f);
//}
//AdcOutput ADS131M08Hub::scale_result()
//{
// // update status
// this->resultFloat.status = this->resultRaw.status;
// // Scale all channels
// for(int i = 0; i<8; i++)
// {
// this->scaleResult(i);
// }
//
// return this->resultFloat;
//}
//
//AdcOutput ADS131M08Hub::read_adc_float()
//{
// this->readAdcRaw();
// return this->scaleResult();
//}
// end of from tpcorrea
// for debug only - remove references, declarations and definitions before submitting for production
std::string ADS131M08Hub::frame_to_string(const spiframe& frame)
{
std::string str;
char buffer[20];
const std::string::size_type new_cap(768);
str.reserve(new_cap);
for (int i = 0; i < frame.size(); i++) {
snprintf(buffer, sizeof(buffer), " %02X", frame[i]);
str += buffer;
}
return str;
}
std::string ADS131M08Hub::conversion_frame_to_string(const spiframe& frame)
{
std::string str;
char buffer[20];
const std::string::size_type new_cap(768);
str.reserve(new_cap);
int word_nbytes = adc_word_length_ >> 3;
int frame_nwords = frame.size() / word_nbytes;
uint16_t drdy_status = get_unsigned_frame_word(frame, 0, true) & MASK_STATUS_DRDY;
// init i = 0 to also print status word
for (int i = 1; i < frame_nwords - 1; i++) {
if(i == 0) {
for (int j = 0; j < word_nbytes; j++) {
snprintf(buffer, sizeof(buffer), "%02X", frame[j]);
str += buffer;
}
}
else {
if (drdy_status & 1) {
str += std::format(" CH{}:", i - 1);
for (int j = 0; j < word_nbytes; j++) {
snprintf(buffer, sizeof(buffer), "%02X", frame[i * word_nbytes + j]);
str += buffer;
}
}
drdy_status = drdy_status >> 1;
}
}
return str;
}
std::string ADS131M08Hub::rwreg_command_frame_to_string(const spiframe& frame)
{
char buffer[100];
std::string str;
str.reserve(512);
uint16_t cmdadr = get_unsigned_frame_word(frame, 0, true);
uint16_t address = (cmdadr & MASK_CMD_RW_REG_ADDRESS) >> 7;
int nregs = 1 + (cmdadr & MASK_CMD_RW_REG_COUNT);
str = command_to_string(cmdadr);
uint16_t command = (cmdadr & MASK_CMD_RW_REG) ? (cmdadr & MASK_CMD_RW_REG) : cmdadr;
const char* ws = " \t\n\r\f\v"; // Defines whitespace
if (command == CMD_RREG) {
str += " : regs ";
for (int i = 0; i < nregs; i++) {
str += reg_addr_to_string(address + i);
str.erase(str.find_last_not_of(ws) + 1);
str += (i < (nregs - 1) ? ", " : "");
}
}
if (command == CMD_WREG) {
str += ", data:";
for (int i = 0; i < nregs; i++) {
uint16_t data = get_unsigned_frame_word(frame, i + 1, true);
snprintf(buffer, sizeof(buffer), " 0x%04X", data);
str += buffer;
}
for (int i = 0; i < nregs; i++) {
uint16_t data = get_unsigned_frame_word(frame, i + 1, true);
str += "\n ";
str += reg_data_to_string(address + i, data);
str.erase(str.find_last_not_of(ws) + 1);
str += (i < (nregs - 1) ? ", " : "");
}
}
return str;
}
std::string ADS131M08Hub::reg_addr_to_string(int address)
{
return reg_data_to_string(address, 0, true);
}
std::string ADS131M08Hub::reg_data_to_string(int address, uint16_t data, bool nameonly)
{
std::string str;
char buffer[100];
str.reserve(120);
switch (address) {
case REG_ID:
str += std::format("{:<10}", "ID");
if (nameonly)
break;
snprintf(buffer, sizeof(buffer), ": %d ", (data >> 8) & 0x0F);
str += buffer;
break;
case REG_STATUS:
str += std::format("{:<10}", "STATUS");
if (nameonly)
break;
str += ":";
str += status_to_string(data);
break;
case REG_MODE:
str += std::format("{:<10}", "MODE");
if (nameonly)
break;
str += ":";
if ((data & MASK_MODE_REG_CRC_EN) != 0) str += " REGMAP_CRC_EN /";
if ((data & MASK_MODE_RX_CRC_EN) != 0) str += " SPI_IN_CRC_EN /";
if ((data & MASK_MODE_TIMEOUT) != 0) str += " SPI_TIMEOUT_EN /";
str += " CRC=";
str += ((data & MASK_MODE_CRC_TYPE) != 0) ? "ANSI /" : "CCITT /";
if ((data & MASK_MODE_RESET) != 0) str += " RESET /";
str += " WL=";
if ((data & MASK_MODE_WLENGTH) == WLENGTH_16_BITS) str += "16";
if ((data & MASK_MODE_WLENGTH) == WLENGTH_24_BITS) str += "24";
if ((data & MASK_MODE_WLENGTH) == WLENGTH_32_BITS_LSB_ZERO_PADDING) str += "32zp";
if ((data & MASK_MODE_WLENGTH) == WLENGTH_32_BITS_MSB_SIGN_EXTEND) str += "32se";
str += " / DRDY SEL=";
if ((data & MASK_MODE_DRDY_SEL) == DRDY_SEL_MOST_LAGGING) str += "MOST_LAGGING";
if ((data & MASK_MODE_DRDY_SEL) == DRDY_SEL_LOGICAL_OR) str += "OR_OF_CHANLS";
if ((data & MASK_MODE_DRDY_SEL) == DRDY_SEL_MOST_LEADING_CHAN) str += "MOST_LEADING";
if ((data & MASK_MODE_DRDY_SEL) == DRDY_SEL_MOST_LEADING_CHAN2) str += "MOST_LEADING";
str += " / DRDY IDLE=";
str += ((data & MASK_MODE_DRDY_HiZ) != 0) ? "Hi_IMP" : "LOGIC_HI";
str += " / DRDY ACTV=";
str += ((data & MASK_MODE_DRDY_FMT) != 0) ? "LOW_FIX_DUR" : "LOGIC_LOW";
break;
case REG_CLOCK:
str += std::format("{:<10}", "CLOCK");
if (nameonly)
break;
str += ": CH ENABLED=";
str += ((data & MASK_CLOCK_CH7) != 0) ? "7" : "_";
str += ((data & MASK_CLOCK_CH6) != 0) ? "6" : "_";
str += ((data & MASK_CLOCK_CH5) != 0) ? "5" : "_";
str += ((data & MASK_CLOCK_CH4) != 0) ? "4" : "_";
str += ((data & MASK_CLOCK_CH3) != 0) ? "3" : "_";
str += ((data & MASK_CLOCK_CH2) != 0) ? "2" : "_";
str += ((data & MASK_CLOCK_CH1) != 0) ? "1" : "_";
str += ((data & MASK_CLOCK_CH0) != 0) ? "0" : "_";
str += " / XTAL OSC=";
str += ((data & MASK_CLOCK_XTAL_DIS) != 0) ? "DIS" : "EN";
str += " / EXT VREF=";
str += ((data & MASK_CLOCK_EXTREF_EN) != 0) ? "EN" : "DIS";
{
uint16_t osr = 1 << (7 + ((data & MASK_CLOCK_OSR) >> 2));
if (osr == 16384) {
osr = 16256;
}
snprintf(buffer, sizeof(buffer), " / OSR=%d", osr);
str += buffer;
}
str += " / POWER=";
if ((data & MASK_CLOCK_PWR) == PM_VERY_LOW_POWER) str += "VERY_LOW";
if ((data & MASK_CLOCK_PWR) == PM_LOW_POWER) str += "VERY_LOW";
if ((data & MASK_CLOCK_PWR) == PM_HIGH_RESOLUTION) str += "HIGH_RES";
if ((data & MASK_CLOCK_PWR) == PM_HIGH_RESOLUTION2) str += "HIGH_RES";
break;
case REG_GAIN1:
{
str += std::format("{:<10}", "GAIN1");
if (nameonly)
break;
auto gain = 1 << ((data & MASK_GAIN_PGAGAIN3) >> 24);
snprintf(buffer, sizeof(buffer), ": Ch3=%d /", gain);
str += buffer;
gain = 1 << ((data & MASK_GAIN_PGAGAIN2) >> 16);
snprintf(buffer, sizeof(buffer), " Ch2=%d /", gain);
str += buffer;
gain = 1 << ((data & MASK_GAIN_PGAGAIN1) >> 8);
snprintf(buffer, sizeof(buffer), " Ch1=%d /", gain);
str += buffer;
gain = 1 << (data & MASK_GAIN_PGAGAIN0);
snprintf(buffer, sizeof(buffer), " Ch0=%d", gain);
str += buffer;
break;
}
case REG_GAIN2:
{
str += std::format("{:<10}", "GAIN2");
if (nameonly)
break;
auto gain = 1 << ((data & MASK_GAIN_PGAGAIN7) >> 24);
snprintf(buffer, sizeof(buffer), ": Ch7=%d /", gain);
str += buffer;
gain = 1 << ((data & MASK_GAIN_PGAGAIN6) >> 16);
snprintf(buffer, sizeof(buffer), " Ch6=%d /", gain);
str += buffer;
gain = 1 << ((data & MASK_GAIN_PGAGAIN5) >> 8);
snprintf(buffer, sizeof(buffer), " Ch5=%d /", gain);
str += buffer;
gain = 1 << (data & MASK_GAIN_PGAGAIN4);
snprintf(buffer, sizeof(buffer), " Ch4=%d", gain);
str += buffer;
break;
}
case REG_CFG:
{
str += std::format("{:<10}", "CFG");
if (nameonly)
break;
str += ": GC_EN=";
str += ((data & MASK_CFG_GC_EN) != 0) ? "Y /" : "N /";
auto gc_dly = 1 << (1 + ((data & MASK_CFG_GC_DLY) >> 9));
snprintf(buffer, sizeof(buffer), " GC DELAY= %d", gc_dly);
str += buffer;
str += " / CUR DET= [";
str += ((data & MASK_CFG_CD_EN) != 0) ? "Y] " : "N] ";
str += ((data & MASK_CFG_CD_ALLCH) != 0) ? "ALL_CH" : "ANY_CH";
auto cd_num = 1 << ((data & MASK_CFG_CD_NUM) >> 4);
snprintf(buffer, sizeof(buffer), " / CD_NUM= %d", cd_num);
str += buffer;
auto cd_len = (data & MASK_CFG_CD_LEN) >> 1;
switch (cd_len) {
case 0:
cd_len = 128;
break;
case 1:
cd_len = 256;
break;
case 2:
cd_len = 512;
break;
case 3:
cd_len = 768;
break;
case 4:
cd_len = 1280;
break;
case 5:
cd_len = 1792;
break;
case 6:
cd_len = 2560;
break;
case 7:
cd_len = 3584;
break;
default:
cd_len = 0; // illegal case
break;
}
snprintf(buffer, sizeof(buffer), " / CD_LEN= %d", cd_len);
str += buffer;
break;
}
case REG_THRSHLD_MSB:
str += std::format("{:<10}", "TH_MSB");
if (nameonly)
break;
snprintf(buffer, sizeof(buffer), ": 0x%04X", data);
str += buffer;
break;
case REG_THRSHLD_LSB:
str += std::format("{:<10}", "TH_LSB");
if (nameonly)
break;
snprintf(buffer, sizeof(buffer), ": 0x%04X / ", (data & MASK_THRSHLD_LSB_CD_TH_LSB) >> 8);
str += buffer;
snprintf(buffer, sizeof(buffer), " DCBLOCK: 0x%04X", data & MASK_THRSHLD_LSB_DCBLOCK);
str += buffer;
break;
case REG_CH0_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH0_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH0_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH0_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH0_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH1_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH1_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH1_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH1_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH1_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH2_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH2_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH2_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH2_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH2_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH3_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH3_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH3_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH3_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH3_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH4_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH4_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH4_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH4_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH4_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH5_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH5_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH5_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH5_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH5_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH6_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH6_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH6_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH6_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH6_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH7_CFG:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH7_OCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH7_OCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH7_GCAL_MSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REG_CH7_GCAL_LSB:
str += reg_config_to_string(address, data, nameonly);
break;
case REGMAP_CRC:
str += std::format("{:<10}", std::format("REGMAP_CRC[{:#02}]", address));
if (nameonly)
break;
snprintf(buffer, sizeof(buffer), " value: 0x%04X", data);
str += buffer;
}
return str;
}
std::string ADS131M08Hub::reg_config_to_string(int address, uint16_t data, bool nameonly)
{
std::string str;
str.reserve(256);
int ch_num = (address - REG_CH0_CFG) / 5;
int gen_addr = (address - REG_CH0_CFG) % 5;
std::string ch_str = std::format("CH{}_", ch_num);
switch (gen_addr)
{
case REG_CHX_CFG:
str += std::format("{:<10}", std::format("{}CFG", ch_str));
if (nameonly)
break;
str += std::format(": PH_DEL={0:}", (data & MASK_CHX_CFG_PHASE) >> 6);
str += " DCBLOCK_EN=";
str += ((data & MASK_CHX_CFG_DCBLKX_DIS) != 0) ? "N /" : "Y /";
str += std::format("{}INPUT=", ch_str);
if ((data & MASK_CHX_CFG_MUX) == ICM_AIN0P_AIN0N) str += "AIN0P and AIN0N";
if ((data & MASK_CHX_CFG_MUX) == ICM_INPUT_SHORTED) str += "ADC inputs shorted";
if ((data & MASK_CHX_CFG_MUX) == ICM_POSITIVE_DC_TEST_SIGNAL) str += "Positive DC test signal";
if ((data & MASK_CHX_CFG_MUX) == ICM_NEGATIVE_DC_TEST_SIGNAL) str += "Negative DC test signal";
break;
case REG_CHX_OCAL_MSB:
str += std::format("{:<13}", std::format("{}OCAL_MSB", ch_str));
if (nameonly)
break;
str += std::format(": {:#04x}", data);
break;
case REG_CHX_OCAL_LSB:
str += std::format("{:<13}", std::format("{}OCAL_LSB", ch_str));
if (nameonly)
break;
str += std::format(": {:#04x}", (data & MASK_CHX_OCAL_LSB) >> 8);
break;
case REG_CHX_GCAL_MSB:
str += std::format("{:<13}", std::format("{}GCAL_MSB", ch_str));
if (nameonly)
break;
str += std::format(": {:#04x}", data);
break;
case REG_CHX_GCAL_LSB:
str += std::format("{:<13}", std::format("{}GCAL_LSB", ch_str));
if (nameonly)
break;
str += std::format(": {:#04x}", (data & MASK_CHX_GCAL_LSB) >> 8);
break;
}
return str;
}
std::string ADS131M08Hub::command_to_string(uint16_t cmdadr)
{
std::string str;
uint16_t reg_addr, nregs;
char buffer[40];
const std::string::size_type new_cap(256);
str.reserve(new_cap);
snprintf(buffer, sizeof(buffer), "0x%04X", cmdadr);
uint16_t cmd_sw = (cmdadr & MASK_CMD_RW_REG) ? (cmdadr & MASK_CMD_RW_REG) : cmdadr;
switch (cmd_sw) {
case CMD_NULL:
str = "NULL [";
str += buffer;
str += "]";
break;
case CMD_RESET:
str = "RESET [";
str += buffer;
str += "]";
break;
case CMD_STANDBY:
str = "STANDBY [";
str += buffer;
str += "]";
break;
case CMD_WAKEUP:
str = "WAKEUP [";
str += buffer;
str += "]";
break;
case CMD_LOCK:
str = "LOCK [";
str += buffer;
str += "]";
break;
case CMD_UNLOCK:
str = "UNLOCK [";
str += buffer;
str += "]";
break;
case CMD_RREG:
str = "RREG [";
str += buffer;
str += "]: ";
nregs = cmdadr & MASK_CMD_RW_REG_COUNT;
nregs++;
reg_addr = (cmdadr & MASK_CMD_RW_REG_ADDRESS) >> 7;
snprintf(buffer, sizeof(buffer), "#regs:%d, start_addr:%d ", nregs, reg_addr);
str += buffer;
break;
case CMD_WREG:
str = "WREG [";
str += buffer;
str += "]: ";
nregs = cmdadr & MASK_CMD_RW_REG_COUNT;
nregs++;
reg_addr = (cmdadr & MASK_CMD_RW_REG_ADDRESS) >> 7;
snprintf(buffer, sizeof(buffer), "#regs:%d, start_addr:%d ", nregs, reg_addr);
str += buffer;
break;
default:
str = "UNKNOWN command [";
str += buffer;
str += "]";
break;
}
return str;
}
// we do not return strings from this function as its length could exceed maximum length that log print can handle
void ADS131M08Hub::print_command_response_to_string(uint16_t cmdadr_sent, const spiframe& frame)
{
std::string str;
uint16_t nregs, start_addr, respcmd;
char buffer[40];
str.reserve(256);
uint16_t response = frame[0] << 8 | frame[1];
snprintf(buffer, sizeof(buffer), " [0x%04X]: ", response);
std::string resp_txt(buffer);
uint16_t command = (cmdadr_sent & MASK_CMD_RW_REG) ? (cmdadr_sent & MASK_CMD_RW_REG) : cmdadr_sent;
switch (command) {
case CMD_NULL:
str = "STATUS" + resp_txt;
str += status_to_string(response);
ESP_LOGD(TAG, "%s", str.c_str());
break;
case CMD_RESET:
str = "RESET RESPONSE" + resp_txt;
switch (response) {
case RSP_RESET_OK:
str += "RESET SUCCESS";
break;
case RSP_RESET_NOK:
str += "RESET NOT COMPLETE";
break;
default:
str += "UNKNOWN RESET RESPONSE";
break;
}
ESP_LOGD(TAG, "%s", str.c_str());
break;
case CMD_STANDBY:
str = "STANDBY RESPONSE" + resp_txt;
if (response == CMD_STANDBY) {
str += "IN STANDBY";
}
else {
str += "UNKNOWN";
}
ESP_LOGD(TAG, "%s", str.c_str());
break;
case CMD_WAKEUP:
str = "WAKEUP RESPONSE" + resp_txt;
if (response == CMD_WAKEUP) {
str += "EXITED STANDBY";
}
else {
str += "UNKNOWN";
}
ESP_LOGD(TAG, "%s", str.c_str());
break;
case CMD_LOCK:
str = "LOCK RESPONSE " + resp_txt;
if (response == CMD_LOCK) {
str += "LOCKED";
}
else {
str += "UNKNOWN";
}
ESP_LOGD(TAG, "%s", str.c_str());
break;
case CMD_UNLOCK:
str = "UNLOCK RESPONSE" + resp_txt;
if (response == CMD_UNLOCK) {
str += "UNLOCKED";
}
else {
str += "UNKNOWN";
}
ESP_LOGD(TAG, "%s", str.c_str());
break;
case CMD_RREG:
{
int offset = 0;
str = "RREG RESPONSE" + resp_txt;
nregs = (cmdadr_sent & MASK_CMD_RW_REG_COUNT) + 1;
uint16_t rreg_ack = cmdadr_sent; // not really and ack, but since we don't get an ack when reading 1 register we pretend this is the ack
if (nregs > 1) {
offset = 1;
rreg_ack = get_unsigned_frame_word(frame, 0, true);;
nregs = (rreg_ack & MASK_CMD_RW_REG_COUNT) + 1; // should not change here, but just in case adc decided to sent different number of registers
}
start_addr = (rreg_ack & MASK_CMD_RW_REG_ADDRESS) >> 7;
snprintf(buffer, sizeof(buffer), "#regs:%d, start_addr:%d ", nregs, start_addr);
ESP_LOGVV(TAG, "%s", buffer);
if (nregs == 1) {
start_addr = (cmdadr_sent & MASK_CMD_RW_REG_ADDRESS) >> 7;
str = reg_data_to_string(start_addr, response);
ESP_LOGVV(TAG, "%s", str.c_str());
}
else {
respcmd = response & MASK_CMD_RW_REG_RESP;
if (respcmd == RREG_RESP) {
for (int i = 0; i < nregs; i++) {
uint16_t rxdata = get_unsigned_frame_word(frame, i + offset, true);
str = reg_data_to_string(start_addr + i, rxdata);
ESP_LOGVV(TAG, "%s", str.c_str());
}
}
else {
str = "WARNING: invalid response to RREG. Should be Exxxx or Fxxxx";
ESP_LOGW(TAG, "%s", str.c_str());
}
}
}
break;
case CMD_WREG:
str = "WREG RESPONSE" + resp_txt;
respcmd = response & MASK_CMD_RW_REG_RESP;
if (respcmd == WREG_RESP) {
nregs = response & MASK_CMD_RW_REG_COUNT;
start_addr = (response & MASK_CMD_RW_REG_ADDRESS) >> 7;
snprintf(buffer, sizeof(buffer), "#regs:%d, start-addr:%d ", nregs + 1, start_addr);
str += buffer;
ESP_LOGVV(TAG, "%s", str.c_str());
}
else {
str += "WARNING: invalid response to WREG. Should be 4xxxx or 5xxxx";
ESP_LOGW(TAG, "%s", str.c_str());
}
break;
default:
str = "Response to UNKNOWN command [";
str += buffer;
str += "]";
ESP_LOGW(TAG, "%s", str.c_str());
break;
}
}
std::string ADS131M08Hub::status_to_string(uint16_t response)
{
std::string str;
str.reserve(90);
if ((response & MASK_STATUS_LOCK) != 0) str += " LOCK /";
if ((response & MASK_STATUS_RESYNC) != 0) str += " RESYNC /";
if ((response & MASK_STATUS_REGMAP) != 0) str += " REGMAP /";
if ((response & MASK_STATUS_CRC_ERR) != 0) str += " CRC_ERR /";
str += " CRC=";
str += ((response & MASK_STATUS_CRC_TYPE) != 0) ? "ANSI /" : "CCITT /";
if ((response & MASK_STATUS_RESET) != 0) str += " RESET /";
str += " WL=";
if ((response & MASK_STATUS_WLENGTH) == WLENGTH_16_BITS) str += "16";
if ((response & MASK_STATUS_WLENGTH) == WLENGTH_24_BITS) str += "24";
if ((response & MASK_STATUS_WLENGTH) == WLENGTH_32_BITS_LSB_ZERO_PADDING) str += "32zp";
if ((response & MASK_STATUS_WLENGTH) == WLENGTH_32_BITS_MSB_SIGN_EXTEND) str += "32se";
str += " / DRDY=";
str += ((response & MASK_STATUS_DRDY7) != 0) ? "7" : "_";
str += ((response & MASK_STATUS_DRDY6) != 0) ? "6" : "_";
str += ((response & MASK_STATUS_DRDY5) != 0) ? "5" : "_";
str += ((response & MASK_STATUS_DRDY4) != 0) ? "4" : "_";
str += ((response & MASK_STATUS_DRDY3) != 0) ? "3" : "_";
str += ((response & MASK_STATUS_DRDY2) != 0) ? "2" : "_";
str += ((response & MASK_STATUS_DRDY1) != 0) ? "1" : "_";
str += ((response & MASK_STATUS_DRDY0) != 0) ? "0" : "_";
return str;
}
// end of for debug only section
} // namespace ads131m08
} // namespace esphome