#include "mcp3550.h" #include "mcp23018.h" #include "driver/gpio.h" #include #define TAG_MICS "MICS_ADC" #define MCP3550_SPS 3.75f #define MCP3550_CONVERSION_MS ((int)(1000.0f / MCP3550_SPS)) // ~267ms #define MCP3550_TIMEOUT_MS 400 #define ADC_COUNT 4 const uint8_t adc_cs_pins[ADC_COUNT] = { MCP_CS_ADC_NH3, MCP_CS_ADC_CO, MCP_CS_ADC_NO2, MCP_CS_ADC_UVC }; spi_device_handle_t mcp3550_handle; void mcp3550_spi_init() { // spi_device_interface_config_t devcfg = { // .clock_speed_hz = 100000, // .mode = 0, // .spics_io_num = -1, // We handle CS manually // .queue_size = 1, // }; // ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_add_device(SPI2_HOST, &devcfg, &mcp3550_handle)); // Set MISO pin for input (needed for polling) gpio_set_direction(MCP3550_MISO_GPIO, GPIO_MODE_INPUT); } // int32_t mcp3550_read(uint8_t cs_pin) // { // uint8_t rx_buf[4] = {0}; // 25 bits fits in 4 bytes // int64_t start = esp_timer_get_time(); // in microseconds // while (true) { // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW // vTaskDelay(pdMS_TO_TICKS(30)); // Wait before retrying // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH // vTaskDelay(pdMS_TO_TICKS(200)); // Wait before retrying // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW // esp_rom_delay_us(1000); // Wait before retrying // spi_transaction_t trans = { // .length = 25, // .rx_buffer = rx_buf, // }; // esp_err_t err = spi_device_transmit(mcp3550_handle, &trans); // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH // if (err != ESP_OK) { // ESP_LOGE(TAG_MICS, "SPI transmit failed on CS pin %u", cs_pin); // return INT32_MIN; // } // bool dr = (rx_buf[0] & 0x80) != 0; // if (!dr) break; // if ((esp_timer_get_time() - start) > (MCP3550_TIMEOUT_MS * 1000)) { // ESP_LOGW(TAG_MICS, "Timeout waiting for DR=0 on CS pin %u", cs_pin); // return INT32_MIN; // } // vTaskDelay(pdMS_TO_TICKS(10)); // Wait before retrying // } // // Combine 22-bit result (drop DR + status bits) // uint32_t raw = ((rx_buf[0] & 0x3F) << 16) | (rx_buf[1] << 8) | rx_buf[2]; // // Sign-extend 22-bit value // int32_t value = raw; // if (value & (1 << 21)) value |= 0xFFC00000; // return value; // } // int32_t mcp3550_read(uint8_t cs_pin) // { // uint8_t rx_buf[4] = {0}; // uint32_t timeout_us = MCP3550_TIMEOUT_MS * 1000; // int64_t start = esp_timer_get_time(); // // Start conversion // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW // // Wait until MISO/SDO goes LOW = DR ready // while (gpio_get_level(MCP3550_MISO_GPIO)) { // if ((esp_timer_get_time() - start) > timeout_us) { // ESP_LOGW(TAG_MICS, "Timeout waiting for MISO=0 on CS %u", cs_pin); // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH // return INT32_MIN; // } // esp_rom_delay_us(10); // micro delay // } // // Data is ready, do full SPI read // spi_transaction_t trans = { // .length = 25, // 25 bits // .rx_buffer = rx_buf, // }; // esp_err_t err = spi_device_transmit(mcp3550_handle, &trans); // mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH to start next conversion // if (err != ESP_OK) { // ESP_LOGE(TAG_MICS, "SPI transmit failed"); // return INT32_MIN; // } // // Extract 22-bit result // uint32_t raw = ((rx_buf[0] & 0x3F) << 16) | (rx_buf[1] << 8) | rx_buf[2]; // // Sign-extend 22-bit value // int32_t value = raw; // if (value & (1 << 21)) value |= 0xFFC00000; // return value; // } int32_t mcp3550_read(uint8_t cs_pin) { uint32_t data = 0; int64_t start = esp_timer_get_time(); uint32_t timeout_us = MCP3550_TIMEOUT_MS * 1000; ESP_LOGI(TAG_MCP, "Starting read from ADC CS %d", cs_pin); // CS LOW mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); vTaskDelay(pdMS_TO_TICKS(10)); // Wait for DR (MISO LOW) while (gpio_get_level(MCP3550_MISO_GPIO)) { if ((esp_timer_get_time() - start) > timeout_us) { ESP_LOGW(TAG_MICS, "Timeout waiting for MISO=0 on CS %u", cs_pin); mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH return INT32_MIN; } vTaskDelay(pdMS_TO_TICKS(10)); // Wait 1 tick (e.g., 1ms) } // Clock out 25 bits for (int i = 0; i < 25; i++) { gpio_set_level(MCP3550_SCK_GPIO, 1); esp_rom_delay_us(5); // small delay to simulate clock high data = (data << 1) | gpio_get_level(MCP3550_MISO_GPIO); gpio_set_level(MCP3550_SCK_GPIO, 0); esp_rom_delay_us(5); // small delay to simulate clock low } mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH // Extract 22-bit value (bits [23:2]) uint32_t raw = (data >> 2) & 0x3FFFFF; // Sign-extend 22-bit value int32_t value = raw; if (value & (1 << 21)) value |= 0xFFC00000; return value; } float mcp3550_to_voltage(int32_t value, float vref) { return ((float)value / (1 << 21)) * vref; } mics_adc_data_t mcp3550_read_all(float vref) { mics_adc_data_t data; int32_t raw[ADC_COUNT]; float volts[ADC_COUNT]; for (int i = 0; i < ADC_COUNT; i++) { raw[i] = mcp3550_read(adc_cs_pins[i]); vTaskDelay(pdMS_TO_TICKS(10)); // Wait before retrying volts[i] = mcp3550_to_voltage(raw[i], vref); } data.raw_nh3 = raw[0]; data.volts_nh3 = volts[0]; data.raw_co = raw[1]; data.volts_co = volts[1]; data.raw_no2 = raw[2]; data.volts_no2 = volts[2]; data.raw_uvc = raw[3]; data.volts_uvc = volts[3]; return data; } void log_mics_adc_values(mics_adc_data_t *data) { ESP_LOGI(TAG_MICS, "ADC Readings:\n" " NH3 -> Raw: %ld, Voltage: %f V\n" " CO -> Raw: %ld, Voltage: %f V\n" " NO2 -> Raw: %ld, Voltage: %f V\n" " UVC -> Raw: %ld, Voltage: %f V", data->raw_nh3, data->volts_nh3, data->raw_co, data->volts_co, data->raw_no2, data->volts_no2, data->raw_uvc, data->volts_uvc); }