#include "ads131m08.h" namespace esphome { namespace ads131m08 { static const char *const TAG = "ads131m08"; void ADS131M08Hub::setup() { // Initialize SPI with correct settings for ADS131M08 // SPI mode 1: CPOL=0, CPHA=1 this->spi_setup(); // not sure if next few lines are needed here or is it handled in spi_setup() SPI.begin(); SPI.setDataMode(SPI_MODE1); SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV8); // Adjust clock speed as needed this->drdy_pin_->setup(); this->drdy_pin_->pin_mode(esphome::gpio::FLAG_INPUT | esphome::gpio::FLAG_PULLUP); this->drdy_pin_->attach_interrupt(&ADS131M08Hub::isr, this, gpio::INTERRUPT_FALLING_EDGE); this->initialize_ads131m08(); } void ADS131M08Hub::initialize_ads131m08() { // Send RESET command SPI.transfer(0x06); delay(1); // Send UNLOCK command SPI.transfer(0x55); SPI.transfer(0x55); // Configure registers as needed (example: set gain, etc.) // Write to MODE register for continuous conversion, disable internal reference (use external MAX6070AAUT12+T at 1.25V) write_register(0x02, 0x0000); // MODE register, continuous mode, INTREF_EN = 0 // Wait for external reference to settle delay(100); // Start conversions SPI.transfer(0x08); // START command } void ADS131M08Hub::write_register(uint8_t reg, uint16_t value) { SPI.transfer(0x40 | reg); // WREG command SPI.transfer(0x00); // Number of registers - 1 (0 for one register) SPI.transfer((value >> 8) & 0xFF); SPI.transfer(value & 0xFF); } void IRAM_ATTR ADS131M08Hub::isr(ADS131M08Hub *arg) { arg->txf_init(); // implementing datasheet recommended TXF init in ISR arg->data_ready_ = true; } void ADS131M08Hub::loop() { if (this->data_ready_) { this->data_ready_ = false; this->read_data_(); } } // ********************** from datasheet ************************ void ADS131M08Hub::txf_init() { if (firstRead) { // Clear the ADC's 2-deep FIFO on the first read for (int i = 0; i sensors_[ch] != nullptr) { int offset = ch * 3; int32_t raw = (data_buffer_[offset] << 16) | (data_buffer_[offset + 1] << 8) | data_buffer_[offset + 2]; if (raw & 0x800000) raw |= 0xFF000000; // Sign extend float v = (raw / 8388608.0) * this->reference_voltage_; // 2^23 = 8388608, Vref = 1.25V (MAX6070AAUT12+T) this->sensors_[ch]->publish_state(v); } } } */ void ADS131M08Hub::read_data_() { this->enable(); // ADS131M08 Frame: 1 Status Word + 8 Channel Words + 1 CRC Word (24-bit words) uint8_t frame[30]; // 10 words * 3 bytes this->read_array(frame, 30); this->disable(); for (int i = 0; i < 8; i++) { if (this->sensors_[i] != nullptr) { // Convert 24-bit two's complement to float (Simplified) int32_t raw = (frame[3+(i*3)] << 16) | (frame[4+(i*3)] << 8) | frame[5+(i*3)]; if (raw & 0x800000) raw |= 0xFF000000; // Sign extend this->sensors_[i]->publish_state(raw * (this->reference_voltage_ / 8388608.0)); // 2^23 = 8388608, Vref = 1.25V ( for MAX6070AAUT12+T ) } } } // =============== from tpcorrea ================= ADS131M08Hub::ADS131M08Hub() : csPin(0), drdyPin(0), clkPin(0), misoPin(0), mosiPin(0), resetPin(0) { for( uint16_t i = 0U; i < 8; i++){ fullScale.ch[i].f = 1.2; // +-1.2V pgaGain[i] = ADS131M08_PgaGain::PGA_1; resultFloat.ch[i].f = 0.0; resultRaw.ch[i].u[0] = 0U; resultRaw.ch[i].u[1] = 0U; } } uint8_t ADS131M08Hub::writeRegister(uint8_t address, uint16_t value) { uint16_t res; uint8_t addressRcv; uint8_t bytesRcv; uint16_t cmd = 0; digitalWrite(csPin, LOW); delayMicroseconds(1); cmd = (CMD_WRITE_REG) | (address << 7) | 0; //res = spi.transfer16(cmd); spi.transfer16(cmd); spi.transfer(0x00); spi.transfer16(value); spi.transfer(0x00); for(int i = 0; i < 8; i++) { spi.transfer16(0x0000); spi.transfer(0x00); } res = spi.transfer16(0x0000); spi.transfer(0x00); for(int i = 0; i < 9; i++) { spi.transfer16(0x0000); spi.transfer(0x00); } delayMicroseconds(1); digitalWrite(csPin, HIGH); addressRcv = (res & REGMASK_CMD_READ_REG_ADDRESS) >> 7; bytesRcv = (res & REGMASK_CMD_READ_REG_BYTES); if (addressRcv == address) { return bytesRcv + 1; } return 0; } void ADS131M08Hub::writeRegisterMasked(uint8_t address, uint16_t value, uint16_t mask) { // Escribe un valor en el registro, aplicando la mascara para tocar unicamente los bits necesarios. // No realiza el corrimiento de bits (shift), hay que pasarle ya el valor corrido a la posicion correcta // Leo el contenido actual del registro uint16_t register_contents = readRegister(address); // Cambio bit aa bit la mascara (queda 1 en los bits que no hay que tocar y 0 en los bits a modificar) // Se realiza un AND co el contenido actual del registro. Quedan "0" en la parte a modificar register_contents = register_contents & ~mask; // se realiza un OR con el valor a cargar en el registro. Ojo, valor debe estar en el posicion (shitf) correcta register_contents = register_contents | value; // Escribo nuevamente el registro writeRegister(address, register_contents); } uint16_t ADS131M08Hub::readRegister(uint8_t address) { uint16_t cmd; uint16_t data; cmd = CMD_READ_REG | (address << 7 | 0); digitalWrite(csPin, LOW); delayMicroseconds(1); //data = spi.transfer16(cmd); spi.transfer16(cmd); spi.transfer(0x00); for(int i = 0; i < 9; i++) { spi.transfer16(0x0000); spi.transfer(0x00); } data = spi.transfer16(0x0000); spi.transfer(0x00); for(int i = 0; i < 9; i++) { spi.transfer16(0x0000); spi.transfer(0x00); } delayMicroseconds(1); digitalWrite(csPin, HIGH); return data; } void ADS131M08Hub::begin(uint8_t clk_pin, uint8_t miso_pin, uint8_t mosi_pin, uint8_t cs_pin, uint8_t drdy_pin, uint8_t reset_pin) { // Set pins up csPin = cs_pin; drdyPin = drdy_pin; clkPin = clk_pin; misoPin = miso_pin; mosiPin = mosi_pin; resetPin = reset_pin; spi = SPIClass(mosi_pin, miso_pin, clk_pin, cs_pin); spi.begin(); spi.beginTransaction(settings); // Configure chip select as an output pinMode(csPin, OUTPUT); pinMode(resetPin, OUTPUT); // Configure DRDY as an input pinMode(drdyPin, INPUT); } int8_t ADS131M08Hub::isDataReadySoft(byte channel) { if (channel == 0) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY0); } else if (channel == 1) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY1); } else if (channel == 2) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY2); } else if (channel == 3) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY3); } else if (channel == 4) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY4); } else if (channel == 5) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY5); } else if (channel == 6) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY6); } else if (channel == 7) { return (readRegister(REG_STATUS) & REGMASK_STATUS_DRDY7); } else { return -1; } } bool ADS131M08Hub::isResetStatus(void) { return (readRegister(REG_STATUS) & REGMASK_STATUS_RESET); } bool ADS131M08Hub::isLockSPI(void) { return (readRegister(REG_STATUS) & REGMASK_STATUS_LOCK); } bool ADS131M08Hub::setDrdyFormat(uint8_t drdyFormat) { if (drdyFormat > 1) { return false; } else { writeRegisterMasked(REG_MODE, drdyFormat, REGMASK_MODE_DRDY_FMT); return true; } } bool ADS131M08Hub::setDrdyStateWhenUnavailable(uint8_t drdyState) { if (drdyState > 1) { return false; } else { writeRegisterMasked(REG_MODE, drdyState < 1, REGMASK_MODE_DRDY_HiZ); return true; } } bool ADS131M08Hub::setPowerMode(uint8_t powerMode) { if (powerMode > 3) { return false; } else { writeRegisterMasked(REG_CLOCK, powerMode, REGMASK_CLOCK_PWR); return true; } } bool ADS131M08Hub::setOsr(uint16_t osr) { if (osr > 7) { return false; } else { writeRegisterMasked(REG_CLOCK, osr << 2 , REGMASK_CLOCK_OSR); return true; } } void ADS131M08Hub::setFullScale(uint8_t channel, float scale) { if (channel > 7) { return; } this->fullScale.ch[channel].f = scale; } float ADS131M08Hub::getFullScale(uint8_t channel) { if (channel > 7) { return 0.0; } return this->fullScale.ch[channel].f; } void ADS131M08Hub::reset() { digitalWrite(this->resetPin, LOW); delay(10); digitalWrite(this->resetPin, HIGH); } bool ADS131M08Hub::setChannelEnable(uint8_t channel, uint16_t enable) { if (channel > 7) { return false; } if (channel == 0) { writeRegisterMasked(REG_CLOCK, enable << 8, REGMASK_CLOCK_CH0_EN); return true; } else if (channel == 1) { writeRegisterMasked(REG_CLOCK, enable << 9, REGMASK_CLOCK_CH1_EN); return true; } else if (channel == 2) { writeRegisterMasked(REG_CLOCK, enable << 10, REGMASK_CLOCK_CH2_EN); return true; } else if (channel == 3) { writeRegisterMasked(REG_CLOCK, enable << 11, REGMASK_CLOCK_CH3_EN); return true; } else if (channel == 4) { writeRegisterMasked(REG_CLOCK, enable << 11, REGMASK_CLOCK_CH4_EN); return true; } else if (channel == 5) { writeRegisterMasked(REG_CLOCK, enable << 11, REGMASK_CLOCK_CH5_EN); return true; } else if (channel == 6) { writeRegisterMasked(REG_CLOCK, enable << 11, REGMASK_CLOCK_CH6_EN); return true; } else if (channel == 7) { writeRegisterMasked(REG_CLOCK, enable << 11, REGMASK_CLOCK_CH7_EN); return true; } return false; } bool ADS131M08Hub::setChannelPGA(uint8_t channel, ADS131M08_PgaGain pga) { uint16_t pgaCode = (uint16_t) pga; if (channel > 7) { return false; } if (channel == 0) { writeRegisterMasked(REG_GAIN1, pgaCode, REGMASK_GAIN_PGAGAIN0); this->pgaGain[0] = pga; return true; } else if (channel == 1) { writeRegisterMasked(REG_GAIN1, pgaCode << 4, REGMASK_GAIN_PGAGAIN1); this->pgaGain[1] = pga; return true; } else if (channel == 2) { writeRegisterMasked(REG_GAIN1, pgaCode << 8, REGMASK_GAIN_PGAGAIN2); this->pgaGain[2] = pga; return true; } else if (channel == 3) { writeRegisterMasked(REG_GAIN1, pgaCode << 12, REGMASK_GAIN_PGAGAIN3); this->pgaGain[3] = pga; return true; } if (channel == 4) { writeRegisterMasked(REG_GAIN2, pgaCode, REGMASK_GAIN_PGAGAIN4); this->pgaGain[4] = pga; return true; } else if (channel == 5) { writeRegisterMasked(REG_GAIN2, pgaCode << 4, REGMASK_GAIN_PGAGAIN5); this->pgaGain[5] = pga; return true; } else if (channel == 6) { writeRegisterMasked(REG_GAIN2, pgaCode << 8, REGMASK_GAIN_PGAGAIN6); this->pgaGain[6] = pga; return true; } else if (channel == 7) { writeRegisterMasked(REG_GAIN2, pgaCode << 12, REGMASK_GAIN_PGAGAIN7); this->pgaGain[7] = pga; return true; } return false; } ADS131M08_PgaGain ADS131M08Hub::getChannelPGA(uint8_t channel) { if(channel > 7) { return ADS131M08_PgaGain::PGA_INVALID; } return this->pgaGain[channel]; } void ADS131M08Hub::setGlobalChop(uint16_t global_chop) { writeRegisterMasked(REG_CFG, global_chop << 8, REGMASK_CFG_GC_EN); } void ADS131M08Hub::setGlobalChopDelay(uint16_t delay) { writeRegisterMasked(REG_CFG, delay << 9, REGMASK_CFG_GC_DLY); } bool ADS131M08Hub::setInputChannelSelection(uint8_t channel, uint8_t input) { if (channel > 3) { return false; } if (channel == 0) { writeRegisterMasked(REG_CH0_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 1) { writeRegisterMasked(REG_CH1_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 2) { writeRegisterMasked(REG_CH2_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 3) { writeRegisterMasked(REG_CH3_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 4) { writeRegisterMasked(REG_CH4_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 5) { writeRegisterMasked(REG_CH5_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 6) { writeRegisterMasked(REG_CH6_CFG, input, REGMASK_CHX_CFG_MUX); return true; } else if (channel == 7) { writeRegisterMasked(REG_CH7_CFG, input, REGMASK_CHX_CFG_MUX); return true; } return false; } bool ADS131M08Hub::setChannelOffsetCalibration(uint8_t channel, int32_t offset) { uint16_t MSB = offset >> 8; uint8_t LSB = offset; if (channel > 7) { return false; } if (channel == 0) { writeRegisterMasked(REG_CH0_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH0_OCAL_LSB, LSB << 8, REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 1) { writeRegisterMasked(REG_CH1_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH1_OCAL_LSB, LSB << 8, REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 2) { writeRegisterMasked(REG_CH2_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH2_OCAL_LSB, LSB << 8, REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 3) { writeRegisterMasked(REG_CH3_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH3_OCAL_LSB, LSB << 8 , REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 4) { writeRegisterMasked(REG_CH4_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH4_OCAL_LSB, LSB << 8 , REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 5) { writeRegisterMasked(REG_CH5_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH5_OCAL_LSB, LSB << 8 , REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 6) { writeRegisterMasked(REG_CH6_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH6_OCAL_LSB, LSB << 8 , REGMASK_CHX_OCAL0_LSB); return true; } else if (channel == 7) { writeRegisterMasked(REG_CH7_OCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH7_OCAL_LSB, LSB << 8 , REGMASK_CHX_OCAL0_LSB); return true; } return false; } bool ADS131M08Hub::setChannelGainCalibration(uint8_t channel, uint32_t gain) { uint16_t MSB = gain >> 8; uint8_t LSB = gain; if (channel > 7) { return false; } if (channel == 0) { writeRegisterMasked(REG_CH0_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH0_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 1) { writeRegisterMasked(REG_CH1_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH1_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 2) { writeRegisterMasked(REG_CH2_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH2_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 3) { writeRegisterMasked(REG_CH3_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH3_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 4) { writeRegisterMasked(REG_CH4_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH4_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 5) { writeRegisterMasked(REG_CH5_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH5_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 6) { writeRegisterMasked(REG_CH6_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH6_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } else if (channel == 7) { writeRegisterMasked(REG_CH7_GCAL_MSB, MSB, 0xFFFF); writeRegisterMasked(REG_CH7_GCAL_LSB, LSB << 8, REGMASK_CHX_GCAL0_LSB); return true; } return false; } bool ADS131M08Hub::isDataReady() { if (digitalRead(drdyPin) == HIGH) { return false; } return true; } uint16_t ADS131M08Hub::getId() { return readRegister(REG_ID); } uint16_t ADS131M08Hub::getModeReg() { return readRegister(REG_MODE); } uint16_t ADS131M08Hub::getClockReg() { return readRegister(REG_CLOCK); } uint16_t ADS131M08Hub::getCfgReg() { return readRegister(REG_CFG); } AdcOutput ADS131M08Hub::readAdcRaw(void) { 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::scaleResult(uint8_t num) { if( num >= 8) { return 0.0; } return this->resultFloat.ch[num].f = (float)(this->resultRaw.ch[num].i * rawToVolts * this->fullScale.ch[num].f); } AdcOutput ADS131M08Hub::scaleResult(void) { // 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::readAdcFloat(void) { this->readAdcRaw(); return this->scaleResult(); } // end of from tpcorrea void ADS131M08Hub::dump_config() { ESP_LOGCONFIG(TAG, "ADS131M08:"); LOG_PIN(" CS Pin:", this->cs_); LOG_PIN(" DRDY Pin:", this->drdy_pin_); ESP_LOGCONFIG(TAG, " Reference Voltage: %.2fV", this->reference_voltage_); } } // namespace ads131m08 } // namespace esphome