/** * @file adxl355.c * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg) * @brief ADXL355 3-axis accelerometer driver implementation * @version 1.0 * @date 2025-08-23 * @copyright Copyright (c) 2025 * */#include"node_acc_adxl355.h"#include"node_spi.h"#include"esp_log.h"#include"esp_check.h"#include"freertos/FreeRTOS.h"#include"freertos/task.h"//==============================================================================staticconstchar*TAG="ADXL355";//==============================================================================/* Private function declarations */staticesp_err_tadxl355_configure_spi_device(spi_device_handle_t*device_handle);staticesp_err_tadxl355_read_register(adxl355_handle_t*handle,uint8_treg,uint8_t*value);staticesp_err_tadxl355_read_registers(adxl355_handle_t*handle,uint8_treg,uint8_t*data,uint8_tlen);staticesp_err_tadxl355_write_register(adxl355_handle_t*handle,uint8_treg,uint8_tvalue);staticesp_err_tadxl355_write_registers(adxl355_handle_t*handle,uint8_treg,constuint8_t*data,uint8_tlen);staticconstchar*adxl355_get_range_string(adxl355_range_trange);staticconstchar*adxl355_get_odr_string(adxl355_output_data_rate_todr);//==============================================================================esp_err_tadxl355_init(adxl355_handle_t*handle,adxl355_range_trange,adxl355_output_data_rate_todr){esp_err_tret;ESP_LOGI(TAG,"=== ADXL355 INITIALIZATION START ===");ESP_LOGI(TAG,"Initialization parameters: range=%s, ODR=%s",adxl355_get_range_string(range),adxl355_get_odr_string(odr));if(handle==NULL){ESP_LOGE(TAG,"Invalid argument: handle=%p",(void*)handle);returnESP_ERR_INVALID_ARG;}/* Step 1: Configure SPI device for ADXL355 */ESP_LOGI(TAG,"Configuring SPI device for ADXL355...");ret=adxl355_configure_spi_device(&handle->spi_handle);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to configure SPI device: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"SPI device configured successfully");/* Initialize handle */handle->range=range;handle->odr=odr;/* Set initial scale factor based on range */ESP_LOGI(TAG,"Setting scale factor for range %s...",adxl355_get_range_string(range));switch(range){caseADXL355_RANGE_2G:handle->scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_2G;ESP_LOGI(TAG,"Range %s selected, scale factor: %.2e g/LSB",adxl355_get_range_string(range),handle->scale_factor);break;caseADXL355_RANGE_4G:handle->scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_4G;ESP_LOGI(TAG,"Range %s selected, scale factor: %.2e g/LSB",adxl355_get_range_string(range),handle->scale_factor);break;caseADXL355_RANGE_8G:handle->scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_8G;ESP_LOGI(TAG,"Range %s selected, scale factor: %.2e g/LSB",adxl355_get_range_string(range),handle->scale_factor);break;default:ESP_LOGE(TAG,"Invalid range setting: %d",range);returnESP_ERR_INVALID_ARG;}/* Reset device */ESP_LOGI(TAG,"Resetting ADXL355 device...");ret=adxl355_reset(handle);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to reset device: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"Device reset completed");/* Wait for reset to complete */vTaskDelay(pdMS_TO_TICKS(100));/* Read device info to verify communication */ESP_LOGI(TAG,"Reading device information...");adxl355_device_info_tdevice_info;ret=adxl355_read_device_info(handle,&device_info);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read device info: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"Device ID: 0x%02X, Vendor ID: 0x%02X",device_info.device_id,device_info.vendor_id);/* Set range and ODR */ESP_LOGI(TAG,"Setting range to %s...",adxl355_get_range_string(range));ret=adxl355_set_range(handle,range);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to set range: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"Range set successfully");ESP_LOGI(TAG,"Setting ODR to %s...",adxl355_get_odr_string(odr));ret=adxl355_set_odr(handle,odr);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to set ODR: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"ODR set successfully");/* Enable measurement mode */ESP_LOGI(TAG,"Enabling measurement mode...");ret=adxl355_enable_measurement(handle);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to enable measurement: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"Measurement mode enabled");/* Enable temperature processing */ESP_LOGI(TAG,"Enabling temperature processing...");ret=adxl355_enable_temperature(handle);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to enable temperature: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"Temperature processing enabled");ESP_LOGI(TAG,"=== ADXL355 INITIALIZATION COMPLETED SUCCESSFULLY ===");ESP_LOGI(TAG,"Final configuration: range=%s, ODR=%s, scale_factor=%.2e g/LSB",adxl355_get_range_string(handle->range),adxl355_get_odr_string(handle->odr),handle->scale_factor);returnESP_OK;}//==============================================================================esp_err_tadxl355_deinit(adxl355_handle_t*handle){if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Disable measurement mode */esp_err_tret=adxl355_disable_measurement(handle);if(ret!=ESP_OK){ESP_LOGW(TAG,"Failed to disable measurement during deinit");}/* Clear handle */handle->spi_handle=NULL;handle->range=0;handle->odr=0;handle->scale_factor=0.0f;returnESP_OK;}//==============================================================================esp_err_tadxl355_reset(adxl355_handle_t*handle){if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Send reset command */esp_err_tret=adxl355_write_register(handle,ADXL355_REG_RESET,ADXL355_REG_RESET_RESET_CODE);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to send reset command");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_read_device_info(adxl355_handle_t*handle,adxl355_device_info_t*device_info){esp_err_tret;uint8_tdata[4];if(handle==NULL||device_info==NULL){ESP_LOGE(TAG,"Invalid arguments: handle=%p, device_info=%p",(void*)handle,(void*)device_info);returnESP_ERR_INVALID_ARG;}/* Read 4 bytes starting from DEVID_AD register */ret=adxl355_read_registers(handle,ADXL355_REG_DEVID_AD,data,4);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read device info registers: %s",esp_err_to_name(ret));returnret;}/* Extract device information (exactly like plasmapper) */device_info->vendor_id=data[0];device_info->family_id=data[1];device_info->device_id=data[2];device_info->revision_id=data[3];returnESP_OK;}//==============================================================================esp_err_tadxl355_read_status(adxl355_handle_t*handle,adxl355_status_t*status){if(handle==NULL||status==NULL){returnESP_ERR_INVALID_ARG;}uint8_tstatus_reg;esp_err_tret=adxl355_read_register(handle,ADXL355_REG_STATUS,&status_reg);if(ret!=ESP_OK){returnret;}*status=(adxl355_status_t)status_reg;returnESP_OK;}//==============================================================================esp_err_tadxl355_read_raw_accelerations(adxl355_handle_t*handle,adxl355_raw_accelerations_t*raw_accel){esp_err_tret;uint8_tdata[9];if(handle==NULL||raw_accel==NULL){returnESP_ERR_INVALID_ARG;}/* Read 9 bytes starting from XDATA3 register */ret=adxl355_read_registers(handle,ADXL355_REG_XDATA3,data,9);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read acceleration data");returnret;}/* Initialize to 0 (exactly like plasmapper) */raw_accel->x=0;raw_accel->y=0;raw_accel->z=0;/* Reconstruct 20-bit values (exactly like plasmapper) *//* X-axis: data[2], data[1], data[0] */((uint8_t*)&raw_accel->x)[1]=data[2];((uint8_t*)&raw_accel->x)[2]=data[1];((uint8_t*)&raw_accel->x)[3]=data[0];/* Y-axis: data[5], data[4], data[3] */((uint8_t*)&raw_accel->y)[1]=data[5];((uint8_t*)&raw_accel->y)[2]=data[4];((uint8_t*)&raw_accel->y)[3]=data[3];/* Z-axis: data[8], data[7], data[6] */((uint8_t*)&raw_accel->z)[1]=data[8];((uint8_t*)&raw_accel->z)[2]=data[7];((uint8_t*)&raw_accel->z)[3]=data[6];/* Convert to 20-bit signed values (divide by 4096) */raw_accel->x/=4096;raw_accel->y/=4096;raw_accel->z/=4096;returnESP_OK;}//==============================================================================esp_err_tadxl355_read_accelerations(adxl355_handle_t*handle,adxl355_accelerations_t*accel){esp_err_tret;adxl355_raw_accelerations_traw_accel;floatscale_factor;if(handle==NULL||accel==NULL){returnESP_ERR_INVALID_ARG;}/* Read raw accelerations */ret=adxl355_read_raw_accelerations(handle,&raw_accel);if(ret!=ESP_OK){returnret;}/* Get current scale factor */ret=adxl355_read_acceleration_scale_factor(handle,&scale_factor);if(ret!=ESP_OK){returnret;}/* Convert to g units */accel->x=raw_accel.x*scale_factor;accel->y=raw_accel.y*scale_factor;accel->z=raw_accel.z*scale_factor;returnESP_OK;}//==============================================================================esp_err_tadxl355_read_raw_temperature(adxl355_handle_t*handle,uint16_t*raw_temp){esp_err_tret;uint8_tdata[2];if(handle==NULL||raw_temp==NULL){returnESP_ERR_INVALID_ARG;}/* Read 2 bytes starting from TEMP2 register */ret=adxl355_read_registers(handle,ADXL355_REG_TEMP2,data,2);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read temperature data");returnret;}/* Reconstruct 16-bit value (exactly like plasmapper) */((uint8_t*)raw_temp)[0]=data[1];((uint8_t*)raw_temp)[1]=data[0];returnESP_OK;}//==============================================================================esp_err_tadxl355_read_temperature(adxl355_handle_t*handle,float*temp){esp_err_tret;uint16_traw_temp;if(handle==NULL||temp==NULL){returnESP_ERR_INVALID_ARG;}/* Read raw temperature */ret=adxl355_read_raw_temperature(handle,&raw_temp);if(ret!=ESP_OK){returnret;}/* Convert to °C using plasmapper's formula */*temp=(((int16_t)raw_temp)-((int16_t)ADXL355_TEMPERATURE_INTERCEPT_LSB))/ADXL355_TEMPERATURE_SLOPE+ADXL355_TEMPERATURE_INTERCEPT_DEG_C;returnESP_OK;}//==============================================================================esp_err_tadxl355_set_range(adxl355_handle_t*handle,adxl355_range_trange){esp_err_tret;uint8_trange_register;if(handle==NULL){ESP_LOGE(TAG,"Invalid handle: NULL");returnESP_ERR_INVALID_ARG;}ESP_LOGI(TAG,"Setting ADXL355 range to %s...",adxl355_get_range_string(range));/* Read current range register value like plasmapper */ret=adxl355_read_register(handle,ADXL355_REG_RANGE,&range_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read range register: %s",esp_err_to_name(ret));returnret;}/* Clear range bits (bits 0-1) like plasmapper */range_register&=~ADXL355_REG_RANGE_RANGE_MASK;/* Set new range value */uint8_tnew_range_value=range&ADXL355_REG_RANGE_RANGE_MASK;range_register|=new_range_value;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_RANGE,range_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to write range register: %s",esp_err_to_name(ret));returnret;}/* Verify the write by reading back */uint8_tverify_register;ret=adxl355_read_register(handle,ADXL355_REG_RANGE,&verify_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to verify range register: %s",esp_err_to_name(ret));returnret;}if(verify_register!=range_register){ESP_LOGE(TAG,"Range register verification failed! Expected: 0x%02X, Got: 0x%02X",range_register,verify_register);returnESP_ERR_INVALID_STATE;}/* Update scale factor */floatold_scale_factor=handle->scale_factor;switch(range){caseADXL355_RANGE_2G:handle->scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_2G;break;caseADXL355_RANGE_4G:handle->scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_4G;break;caseADXL355_RANGE_8G:handle->scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_8G;break;default:ESP_LOGE(TAG,"Invalid range value: %d",range);returnESP_ERR_INVALID_ARG;}handle->range=range;ESP_LOGI(TAG,"Range changed from %.2e to %s, scale factor: %.2e g/LSB",old_scale_factor,adxl355_get_range_string(range),handle->scale_factor);returnESP_OK;}//==============================================================================esp_err_tadxl355_read_range(adxl355_handle_t*handle,adxl355_range_t*range){esp_err_tret;uint8_trange_register;if(handle==NULL||range==NULL){returnESP_ERR_INVALID_ARG;}ret=adxl355_read_register(handle,ADXL355_REG_RANGE,&range_register);if(ret!=ESP_OK){returnret;}*range=(adxl355_range_t)(range_register&ADXL355_REG_RANGE_RANGE_MASK);returnESP_OK;}//==============================================================================esp_err_tadxl355_set_odr(adxl355_handle_t*handle,adxl355_output_data_rate_todr){esp_err_tret;uint8_tfilter_reg;if(handle==NULL){ESP_LOGE(TAG,"Invalid handle: NULL");returnESP_ERR_INVALID_ARG;}ESP_LOGI(TAG,"Setting ADXL355 ODR to %s...",adxl355_get_odr_string(odr));/* Read current filter register value */ret=adxl355_read_register(handle,ADXL355_REG_FILTER,&filter_reg);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read filter register: %s",esp_err_to_name(ret));returnret;}/* Clear ODR bits and set new value (like plasmapper) */uint8_told_odr=filter_reg&ADXL355_REG_FILTER_ODR_MASK;filter_reg&=~ADXL355_REG_FILTER_ODR_MASK;uint8_tnew_odr_value=odr&ADXL355_REG_FILTER_ODR_MASK;filter_reg|=new_odr_value;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_FILTER,filter_reg);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to write filter register: %s",esp_err_to_name(ret));returnret;}/* Verify the write by reading back */uint8_tverify_register;ret=adxl355_read_register(handle,ADXL355_REG_FILTER,&verify_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to verify filter register: %s",esp_err_to_name(ret));returnret;}if(verify_register!=filter_reg){ESP_LOGE(TAG,"Filter register verification failed! Expected: 0x%02X, Got: 0x%02X",filter_reg,verify_register);returnESP_ERR_INVALID_STATE;}handle->odr=odr;ESP_LOGI(TAG,"ODR changed from %s to %s",adxl355_get_odr_string(old_odr),adxl355_get_odr_string(odr));returnESP_OK;}//==============================================================================esp_err_tadxl355_read_odr(adxl355_handle_t*handle,adxl355_output_data_rate_t*odr){esp_err_tret;uint8_tfilter_reg;if(handle==NULL||odr==NULL){returnESP_ERR_INVALID_ARG;}ret=adxl355_read_register(handle,ADXL355_REG_FILTER,&filter_reg);if(ret!=ESP_OK){returnret;}*odr=(adxl355_output_data_rate_t)(filter_reg&ADXL355_REG_FILTER_ODR_MASK);returnESP_OK;}//==============================================================================esp_err_tadxl355_enable_measurement(adxl355_handle_t*handle){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Read current power control register value */ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read power control register");returnret;}/* Clear standby bit to enable measurement */power_ctl_register&=~ADXL355_REG_POWER_CTL_STANDBY;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_POWER_CTL,power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to enable measurement");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_disable_measurement(adxl355_handle_t*handle){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Read current power control register value */ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read power control register");returnret;}/* Set standby bit to disable measurement */power_ctl_register|=ADXL355_REG_POWER_CTL_STANDBY;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_POWER_CTL,power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to disable measurement");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_is_measurement_enabled(adxl355_handle_t*handle,bool*is_enabled){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL||is_enabled==NULL){returnESP_ERR_INVALID_ARG;}ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){returnret;}*is_enabled=!(power_ctl_register&ADXL355_REG_POWER_CTL_STANDBY);returnESP_OK;}//==============================================================================esp_err_tadxl355_enable_temperature(adxl355_handle_t*handle){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Read current power control register value */ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read power control register");returnret;}/* Clear temperature off bit to enable temperature processing */power_ctl_register&=~ADXL355_REG_POWER_CTL_TEMP_OFF;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_POWER_CTL,power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to enable temperature");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_disable_temperature(adxl355_handle_t*handle){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Read current power control register value */ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read power control register");returnret;}/* Set temperature off bit to disable temperature processing */power_ctl_register|=ADXL355_REG_POWER_CTL_TEMP_OFF;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_POWER_CTL,power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to disable temperature");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_is_temperature_enabled(adxl355_handle_t*handle,bool*is_enabled){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL||is_enabled==NULL){returnESP_ERR_INVALID_ARG;}ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){returnret;}*is_enabled=!(power_ctl_register&ADXL355_REG_POWER_CTL_TEMP_OFF);returnESP_OK;}//==============================================================================esp_err_tadxl355_enable_data_ready(adxl355_handle_t*handle){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Read current power control register value */ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read power control register");returnret;}/* Clear data ready off bit to enable data ready output */power_ctl_register&=~ADXL355_REG_POWER_CTL_DRDY_OFF;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_POWER_CTL,power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to enable data ready");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_disable_data_ready(adxl355_handle_t*handle){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL){returnESP_ERR_INVALID_ARG;}/* Read current power control register value */ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read power control register");returnret;}/* Set data ready off bit to disable data ready output */power_ctl_register|=ADXL355_REG_POWER_CTL_DRDY_OFF;/* Write updated register value */ret=adxl355_write_register(handle,ADXL355_REG_POWER_CTL,power_ctl_register);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to disable data ready");returnret;}returnESP_OK;}//==============================================================================esp_err_tadxl355_is_data_ready_enabled(adxl355_handle_t*handle,bool*is_enabled){esp_err_tret;uint8_tpower_ctl_register;if(handle==NULL||is_enabled==NULL){returnESP_ERR_INVALID_ARG;}ret=adxl355_read_register(handle,ADXL355_REG_POWER_CTL,&power_ctl_register);if(ret!=ESP_OK){returnret;}*is_enabled=!(power_ctl_register&ADXL355_REG_POWER_CTL_DRDY_OFF);returnESP_OK;}//==============================================================================esp_err_tadxl355_read_acceleration_scale_factor(adxl355_handle_t*handle,float*scale_factor){esp_err_tret;adxl355_range_trange;if(handle==NULL||scale_factor==NULL){returnESP_ERR_INVALID_ARG;}ret=adxl355_read_range(handle,&range);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to read range");returnret;}switch(range){caseADXL355_RANGE_2G:*scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_2G;break;caseADXL355_RANGE_4G:*scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_4G;break;caseADXL355_RANGE_8G:*scale_factor=ADXL355_ACCELERATION_SCALE_FACTOR_RANGE_8G;break;default:*scale_factor=1.0f;break;}returnESP_OK;}//==============================================================================// Private Functions//==============================================================================/** * @brief Configure and add ADXL355 SPI device to SPI3 bus * @param device_handle Output parameter, returns SPI device handle * @return ESP_OK on success, error code on failure */staticesp_err_tadxl355_configure_spi_device(spi_device_handle_t*device_handle){esp_err_tret;spi_device_interface_config_tdev_config={0};/* Configure ADXL355-specific SPI device parameters */dev_config.clock_speed_hz=10000000;// 10 MHz (ADXL355 maximum supported)dev_config.mode=0;// SPI Mode 0 (CPOL=0, CPHA=0)dev_config.spics_io_num=ADXL355_CS_GPIO_PIN;// CS pindev_config.queue_size=1;// Single transaction queuedev_config.command_bits=0;// No command bitsdev_config.address_bits=8;// 8-bit addressdev_config.input_delay_ns=30;// 30ns delaydev_config.flags=0;// Default flags (full-duplex)/* Add SPI device to bus */ret=spi_bus_add_device(SPI3_HOST,&dev_config,device_handle);if(ret!=ESP_OK){ESP_LOGE(TAG,"Failed to add ADXL355 device to SPI3 bus: %s",esp_err_to_name(ret));returnret;}ESP_LOGI(TAG,"ADXL355 SPI device configured successfully");returnESP_OK;}//==============================================================================/** * @brief Read single register * @param handle ADXL355 handle * @param reg Register address * @param value Output value * @return ESP_OK on success, error code on failure */staticesp_err_tadxl355_read_register(adxl355_handle_t*handle,uint8_treg,uint8_t*value){ESP_LOGD(TAG,"READ_REG: reg=0x%02X, handle=%p, value_ptr=%p",reg,(void*)handle,(void*)value);if(handle==NULL||value==NULL){ESP_LOGE(TAG,"READ_REG: Invalid arguments - handle=%p, value=%p",(void*)handle,(void*)value);returnESP_ERR_INVALID_ARG;}/* Left shift address by 1 and set LSB to 1 for read (exactly like plasmapper) */uint8_tread_cmd=(reg<<1)|0x01;ESP_LOGD(TAG,"READ_REG: reg=0x%02X, shifted=0x%02X, read_cmd=0x%02X",reg,reg<<1,read_cmd);/* Use SPI transaction: write command then read data */spi_transaction_tt={0};t.cmd=0;/* No command bits */t.addr=read_cmd;/* Address with read bit set */t.length=8;/* 1 byte = 8 bits */t.tx_buffer=NULL;/* No write data */t.rx_buffer=value;/* Read data buffer */t.flags=0;/* Use default flags */ESP_LOGD(TAG,"READ_REG: SPI transaction - addr=0x%02X, length=%d bits",t.addr,t.length);esp_err_tret=spi_device_polling_transmit(handle->spi_handle,&t);if(ret!=ESP_OK){ESP_LOGE(TAG,"READ_REG: SPI read transaction failed: %s",esp_err_to_name(ret));returnret;}ESP_LOGD(TAG,"READ_REG: Success - reg=0x%02X, value=0x%02X",reg,*value);returnESP_OK;}//==============================================================================/** * @brief Read multiple registers * @param handle ADXL355 handle * @param reg Starting register address * @param data Output data buffer * @param len Number of bytes to read * @return ESP_OK on success, error code on failure */staticesp_err_tadxl355_read_registers(adxl355_handle_t*handle,uint8_treg,uint8_t*data,uint8_tlen){if(handle==NULL||data==NULL||len==0){returnESP_ERR_INVALID_ARG;}/* Left shift address by 1 and set LSB to 1 for read (exactly like plasmapper) */uint8_tread_cmd=(reg<<1)|0x01;/* Use the new ADXL355-specific SPI read function */returnadxl355_spi_read(handle,read_cmd,data,len);}//==============================================================================/** * @brief Write single register * @param handle ADXL355 handle * @param reg Register address * @param value Value to write * @return ESP_OK on success, error code on failure */staticesp_err_tadxl355_write_register(adxl355_handle_t*handle,uint8_treg,uint8_tvalue){ESP_LOGD(TAG,"WRITE_REG: reg=0x%02X, value=0x%02X, handle=%p",reg,value,(void*)handle);if(handle==NULL){ESP_LOGE(TAG,"WRITE_REG: Invalid handle - NULL");returnESP_ERR_INVALID_ARG;}/* Left shift address by 1 (exactly like plasmapper) */uint8_twrite_cmd=reg<<1;ESP_LOGD(TAG,"WRITE_REG: reg=0x%02X, shifted=0x%02X, write_cmd=0x%02X",reg,reg<<1,write_cmd);/* Use SPI transaction: write command and data */spi_transaction_tt={0};t.cmd=0;/* No command bits */t.addr=write_cmd;/* Address with write bit clear */t.length=8;/* 1 byte = 8 bits */t.tx_buffer=&value;/* Write data */t.rx_buffer=NULL;/* No read data */t.flags=0;/* Use default flags */ESP_LOGD(TAG,"WRITE_REG: SPI transaction - addr=0x%02X, length=%d bits, data=0x%02X",t.addr,t.length,value);esp_err_tret=spi_device_polling_transmit(handle->spi_handle,&t);if(ret!=ESP_OK){ESP_LOGE(TAG,"WRITE_REG: SPI write transaction failed: %s",esp_err_to_name(ret));returnret;}ESP_LOGD(TAG,"WRITE_REG: Success - reg=0x%02X, value=0x%02X",reg,value);returnESP_OK;}//==============================================================================/** * @brief Write multiple registers * @param handle ADXL355 handle * @param reg Starting register address * @param data Data to write * @param len Number of bytes to write * @return ESP_OK on success, error code on failure */staticesp_err_tadxl355_write_registers(adxl355_handle_t*handle,uint8_treg,constuint8_t*data,uint8_tlen){if(handle==NULL||data==NULL||len==0){returnESP_ERR_INVALID_ARG;}/* Left shift address by 1 (exactly like plasmapper) */uint8_twrite_cmd=reg<<1;/* Use SPI transaction: write command and data */spi_transaction_tt={0};t.cmd=0;/* No command bits */t.addr=write_cmd;/* Address with write bit clear */t.length=len*8;/* Data size in bits */t.tx_buffer=data;/* Write data */t.rx_buffer=NULL;/* No read data */t.flags=0;/* Use default flags */esp_err_tret=spi_device_polling_transmit(handle->spi_handle,&t);if(ret!=ESP_OK){ESP_LOGE(TAG,"SPI write registers transaction failed: %s",esp_err_to_name(ret));returnret;}returnESP_OK;}//==============================================================================/** * @brief ADXL355 specific: Write command then read data * @param handle SPI handle * @param cmd Command byte to send * @param data Buffer to store received data * @param len Length of data to read * @return ESP_OK on success, error code on failure */esp_err_tadxl355_spi_read(adxl355_handle_t*handle,uint8_tcmd,uint8_t*data,intlen){if(handle==NULL||data==NULL||len<=0){ESP_LOGE(TAG,"Invalid arguments for ADXL355 SPI read");returnESP_ERR_INVALID_ARG;}/* Configure transaction exactly like plasmapper *//* command = 0, address = cmd, writeData = NULL, readData = data, dataSize = len * 8 */spi_transaction_tt={0};t.cmd=0;/* No command bits */t.addr=cmd;/* Address (already formatted) */t.length=len*8;/* Data size in bits */t.tx_buffer=NULL;/* No write data */t.rx_buffer=data;/* Read data buffer */t.flags=0;/* Use default flags *//* Use polling transmit like plasmapper */esp_err_tret=spi_device_polling_transmit(handle->spi_handle,&t);if(ret!=ESP_OK){ESP_LOGE(TAG,"ADXL355 SPI read failed: %s",esp_err_to_name(ret));returnret;}returnESP_OK;}/** * @brief Get string representation of range setting * @param range Range setting * @return String representation */staticconstchar*adxl355_get_range_string(adxl355_range_trange){switch(range){caseADXL355_RANGE_2G:return"±2G";caseADXL355_RANGE_4G:return"±4G";caseADXL355_RANGE_8G:return"±8G";default:return"Unknown";}}/** * @brief Get string representation of ODR setting * @param odr ODR setting * @return String representation */staticconstchar*adxl355_get_odr_string(adxl355_output_data_rate_todr){switch(odr){caseADXL355_ODR_4000:return"4000 Hz";caseADXL355_ODR_2000:return"2000 Hz";caseADXL355_ODR_1000:return"1000 Hz";caseADXL355_ODR_500:return"500 Hz";caseADXL355_ODR_250:return"250 Hz";caseADXL355_ODR_125:return"125 Hz";caseADXL355_ODR_62_5:return"62.5 Hz";caseADXL355_ODR_31_25:return"31.25 Hz";caseADXL355_ODR_15_625:return"15.625 Hz";caseADXL355_ODR_7_813:return"7.813 Hz";caseADXL355_ODR_3_906:return"3.906 Hz";default:return"Unknown";}}
/** * @file main.c * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg) * @brief * @version 1.0 * @date 2024-11-17 * * @copyright Copyright (c) 2024 * *//* DEPENDENCIES */// ESP#include"esp_system.h" // ESP32 System#include"nvs_flash.h" // ESP32 NVS#include"esp_chip_info.h" // ESP32 Chip Info#include"esp_psram.h" // ESP32 PSRAM#include"esp_flash.h" // ESP32 Flash#include"esp_log.h" // ESP32 Logging// BSP#include"node_led.h"#include"node_exit.h"#include"node_spi.h"#include"node_lcd.h"#include"node_timer.h"#include"node_rtc.h"#include"node_sdcard.h"#include"node_wifi.h"#include"node_mqtt.h"#include"node_acc_adxl355.h"/* Variables */constchar*TAG="AIoTNode";/* ADXL355 handle */adxl355_handle_tadxl355_handle;/** * @brief Entry point of the program * @param None * @retval None */voidapp_main(void){esp_err_tret;uint32_tflash_size;esp_chip_info_tchip_info;charmqtt_pub_buff[64];// int count = 0;/* ADXL355 variables */adxl355_accelerations_taccel;// adxl355_raw_accelerations_t raw_accel; // Unused variable, commented outfloattemperature;// SPI3 device handle is now managed internally by ADXL355// Initialize NVSret=nvs_flash_init();if(ret==ESP_ERR_NVS_NO_FREE_PAGES||ret==ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());// Erase if neededret=nvs_flash_init();}// Get FLASH sizeesp_flash_get_size(NULL,&flash_size);esp_chip_info(&chip_info);// Display CPU core countprintf("CPU Cores: %d\n",chip_info.cores);// Display FLASH sizeprintf("Flash size: %ld MB flash\n",flash_size/(1024*1024));// Display PSRAM sizeprintf("PSRAM size: %d bytes\n",esp_psram_get_size());// BSP Initializationled_init();exit_init();spi2_init();// spi3_init(); // Refactored, now called during ADXL355 initializationlcd_init();// spiffs_test(); /* Run SPIFFS test */while(sd_card_init())/* SD card not detected */{lcd_show_string(0,0,200,16,16,"SD Card Error!",RED);vTaskDelay(500);lcd_show_string(0,20,200,16,16,"Please Check!",RED);vTaskDelay(500);}// clean the screenlcd_clear(WHITE);lcd_show_string(0,0,200,16,16,"SD Initialized!",RED);sd_card_test_filesystem();/* Run SD card test */lcd_show_string(0,0,200,16,16,"SD Tested CSW! ",RED);// sd_card_unmount();vTaskDelay(3000);// Initialize WiFi and MQTT firstlcd_show_string(0,0,lcd_self.width,16,16,"WiFi STA Test ",RED);ret=wifi_sta_wpa2_init();if(ret==ESP_OK){ESP_LOGI(TAG_WIFI,"WiFi STA Init OK");lcd_show_string(0,0,lcd_self.width,16,16,"WiFi STA Test OK",RED);}else{ESP_LOGE(TAG_WIFI,"WiFi STA Init Failed");}// only when the ip is obtained, start mqttEventBits_tev=0;ev=xEventGroupWaitBits(wifi_event_group,CONNECTED_BIT,pdTRUE,pdFALSE,portMAX_DELAY);if(ev&CONNECTED_BIT){mqtt_app_start();}// Initialize and test ADXL355 after WiFi and MQTTlcd_clear(WHITE);lcd_show_string(0,0,200,16,16,"Testing ADXL355...",BLUE);ESP_LOGI(TAG,"=== MAIN: Starting ADXL355 test ===");// Step 1: Initialize SPI3 bus (general purpose)spi3_init();// Step 2: Initialize ADXL355 sensor (includes SPI device configuration)ret=adxl355_init(&adxl355_handle,ADXL355_RANGE_2G,ADXL355_ODR_1000);if(ret==ESP_OK){ESP_LOGI(TAG,"✓ ADXL355 initialization successful!");lcd_show_string(0,20,200,16,16,"ADXL355 OK!",GREEN);// Read device infoadxl355_device_info_tdevice_info;esp_err_tinfo_ret=adxl355_read_device_info(&adxl355_handle,&device_info);if(info_ret==ESP_OK){ESP_LOGI(TAG,"Device ID: 0x%02X, Vendor ID: 0x%02X",device_info.device_id,device_info.vendor_id);charinfo_str[64];snprintf(info_str,sizeof(info_str),"ID: 0x%02X, 0x%02X, 0x%02X",device_info.vendor_id,device_info.family_id,device_info.device_id);lcd_show_string(0,40,200,16,16,info_str,BLACK);// Test temperature readingfloattest_temp;esp_err_ttemp_test_ret=adxl355_read_temperature(&adxl355_handle,&test_temp);if(temp_test_ret==ESP_OK){ESP_LOGI(TAG,"Temperature test: %.2f°C",test_temp);chartemp_str[64];snprintf(temp_str,sizeof(temp_str),"Temp: %.1f°C",test_temp);lcd_show_string(0,60,200,16,16,temp_str,BLUE);}else{ESP_LOGE(TAG,"Temperature test failed: %s",esp_err_to_name(temp_test_ret));lcd_show_string(0,60,200,16,16,"Temp: Failed!",RED);}}else{ESP_LOGE(TAG,"✗ Failed to read device info: %s",esp_err_to_name(info_ret));}}else{ESP_LOGE(TAG,"✗ ADXL355 initialization failed: %s",esp_err_to_name(ret));lcd_show_string(0,20,200,16,16,"ADXL355 Failed!",RED);}vTaskDelay(3000);ESP_LOGI(TAG,"ADXL355 initialized and ready for MQTT publishing");ESP_LOGI(TAG,"=== MAIN: Entering main loop ===");while(1){if(s_is_mqtt_connected){// Read both acceleration and temperature dataesp_err_taccel_ret=adxl355_read_accelerations(&adxl355_handle,&accel);esp_err_ttemp_ret=adxl355_read_temperature(&adxl355_handle,&temperature);if(accel_ret==ESP_OK&&temp_ret==ESP_OK){// Format MQTT payload with both acceleration and temperaturesnprintf(mqtt_pub_buff,64,"{\"x\":%.4f,\"y\":%.4f,\"z\":%.4f,\"temp\":%.2f}",accel.x,accel.y,accel.z,temperature);intmqtt_ret=esp_mqtt_client_publish(s_mqtt_client,MQTT_PUBLISH_TOPIC,mqtt_pub_buff,strlen(mqtt_pub_buff),1,0);if(mqtt_ret<0){ESP_LOGE(TAG,"✗ MQTT publish failed: %d",mqtt_ret);}// Output both acceleration and temperature to consoleprintf("Accelerations: X: %f g, Y: %f g, Z: %f g | Temperature: %.2f°C\n",accel.x,accel.y,accel.z,temperature);// Also log to ESP log systemESP_LOGI(TAG,"Data: X=%.4f, Y=%.4f, Z=%.4f g, Temp=%.2f°C",accel.x,accel.y,accel.z,temperature);}else{if(accel_ret!=ESP_OK){ESP_LOGE(TAG,"✗ Failed to read ADXL355 acceleration data: %s",esp_err_to_name(accel_ret));}if(temp_ret!=ESP_OK){ESP_LOGE(TAG,"✗ Failed to read ADXL355 temperature data: %s",esp_err_to_name(temp_ret));}}}else{ESP_LOGW(TAG,"MQTT not connected, skipping data read");}led_toggle();vTaskDelay(2000/portTICK_PERIOD_MS);}}
/** * @file main.c * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg) * @brief * @version 1.0 * @date 2024-11-17 * * @copyright Copyright (c) 2024 * *//* DEPENDENCIES */// ESP#include"esp_system.h" // ESP32 System#include"nvs_flash.h" // ESP32 NVS#include"esp_chip_info.h" // ESP32 Chip Info#include"esp_psram.h" // ESP32 PSRAM#include"esp_flash.h" // ESP32 Flash#include"esp_log.h" // ESP32 Logging// BSP#include"node_led.h"#include"node_exit.h"#include"node_spi.h"#include"node_lcd.h"#include"node_timer.h"#include"node_rtc.h"#include"node_sdcard.h"#include"node_wifi.h"#include"node_mqtt.h"#include"node_acc_adxl355.h"/* Variables */constchar*TAG="AIoTNode";/* ADXL355 handle */adxl355_handle_tadxl355_handle;extern"C"voidapp_main();/** * @brief Entry point of the program * @param None * @retval None */voidapp_main(){esp_err_tret;uint32_tflash_size;esp_chip_info_tchip_info;charmqtt_pub_buff[64];// int count = 0;/* ADXL355 variables */adxl355_accelerations_taccel;// adxl355_raw_accelerations_t raw_accel; // Unused variable, commented outfloattemperature;// SPI3 device handle is now managed internally by ADXL355// Initialize NVSret=nvs_flash_init();if(ret==ESP_ERR_NVS_NO_FREE_PAGES||ret==ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());// Erase if neededret=nvs_flash_init();}// Get FLASH sizeesp_flash_get_size(NULL,&flash_size);esp_chip_info(&chip_info);// Display CPU core countprintf("CPU Cores: %d\n",chip_info.cores);// Display FLASH sizeprintf("Flash size: %ld MB flash\n",flash_size/(1024*1024));// Display PSRAM sizeprintf("PSRAM size: %d bytes\n",esp_psram_get_size());// BSP Initializationled_init();exit_init();spi2_init();// spi3_init(); // Refactored, now called during ADXL355 initializationlcd_init();// spiffs_test(); /* Run SPIFFS test */while(sd_card_init())/* SD card not detected */{lcd_show_string(0,0,200,16,16,(char*)"SD Card Error!",RED);vTaskDelay(500);lcd_show_string(0,20,200,16,16,(char*)"Please Check!",RED);vTaskDelay(500);}// clean the screenlcd_clear(WHITE);lcd_show_string(0,0,200,16,16,(char*)"SD Initialized!",RED);sd_card_test_filesystem();/* Run SD card test */lcd_show_string(0,0,200,16,16,(char*)"SD Tested CSW! ",RED);// sd_card_unmount();vTaskDelay(3000);// Initialize WiFi and MQTT firstlcd_show_string(0,0,lcd_self.width,16,16,(char*)"WiFi STA Test ",RED);ret=wifi_sta_wpa2_init();if(ret==ESP_OK){ESP_LOGI(TAG_WIFI,"WiFi STA Init OK");lcd_show_string(0,0,lcd_self.width,16,16,(char*)"WiFi STA Test OK",RED);}else{ESP_LOGE(TAG_WIFI,"WiFi STA Init Failed");}// only when the ip is obtained, start mqttEventBits_tev=0;ev=xEventGroupWaitBits(wifi_event_group,CONNECTED_BIT,pdTRUE,pdFALSE,portMAX_DELAY);if(ev&CONNECTED_BIT){mqtt_app_start();}// Initialize and test ADXL355 after WiFi and MQTTlcd_clear(WHITE);lcd_show_string(0,0,200,16,16,(char*)"Testing ADXL355...",BLUE);ESP_LOGI(TAG,"=== MAIN: Starting ADXL355 test ===");// Step 1: Initialize SPI3 bus (general purpose)spi3_init();// Step 2: Initialize ADXL355 sensor (includes SPI device configuration)ret=adxl355_init(&adxl355_handle,ADXL355_RANGE_2G,ADXL355_ODR_1000);if(ret==ESP_OK){ESP_LOGI(TAG,"✓ ADXL355 initialization successful!");lcd_show_string(0,20,200,16,16,(char*)"ADXL355 OK!",GREEN);// Read device infoadxl355_device_info_tdevice_info;esp_err_tinfo_ret=adxl355_read_device_info(&adxl355_handle,&device_info);if(info_ret==ESP_OK){ESP_LOGI(TAG,"Device ID: 0x%02X, Vendor ID: 0x%02X",device_info.device_id,device_info.vendor_id);charinfo_str[64];snprintf(info_str,sizeof(info_str),"ID: 0x%02X, 0x%02X, 0x%02X",device_info.vendor_id,device_info.family_id,device_info.device_id);lcd_show_string(0,40,200,16,16,(char*)info_str,BLACK);// Test temperature readingfloattest_temp;esp_err_ttemp_test_ret=adxl355_read_temperature(&adxl355_handle,&test_temp);if(temp_test_ret==ESP_OK){ESP_LOGI(TAG,"Temperature test: %.2f°C",test_temp);chartemp_str[64];snprintf(temp_str,sizeof(temp_str),"Temp: %.1f°C",test_temp);lcd_show_string(0,60,200,16,16,(char*)temp_str,BLUE);}else{ESP_LOGE(TAG,"Temperature test failed: %s",esp_err_to_name(temp_test_ret));lcd_show_string(0,60,200,16,16,(char*)"Temp: Failed!",RED);}}else{ESP_LOGE(TAG,"✗ Failed to read device info: %s",esp_err_to_name(info_ret));}}else{ESP_LOGE(TAG,"✗ ADXL355 initialization failed: %s",esp_err_to_name(ret));lcd_show_string(0,20,200,16,16,(char*)"ADXL355 Failed!",RED);}vTaskDelay(3000);ESP_LOGI(TAG,"ADXL355 initialized and ready for MQTT publishing");ESP_LOGI(TAG,"=== MAIN: Entering main loop ===");while(1){if(s_is_mqtt_connected){// Read both acceleration and temperature dataesp_err_taccel_ret=adxl355_read_accelerations(&adxl355_handle,&accel);esp_err_ttemp_ret=adxl355_read_temperature(&adxl355_handle,&temperature);if(accel_ret==ESP_OK&&temp_ret==ESP_OK){// Format MQTT payload with both acceleration and temperaturesnprintf(mqtt_pub_buff,64,"{\"x\":%.4f,\"y\":%.4f,\"z\":%.4f,\"temp\":%.2f}",accel.x,accel.y,accel.z,temperature);intmqtt_ret=esp_mqtt_client_publish(s_mqtt_client,MQTT_PUBLISH_TOPIC,mqtt_pub_buff,strlen(mqtt_pub_buff),1,0);if(mqtt_ret<0){ESP_LOGE(TAG,"✗ MQTT publish failed: %d",mqtt_ret);}// Output both acceleration and temperature to consoleprintf("Accelerations: X: %f g, Y: %f g, Z: %f g | Temperature: %.2f°C\n",accel.x,accel.y,accel.z,temperature);// Also log to ESP log systemESP_LOGI(TAG,"Data: X=%.4f, Y=%.4f, Z=%.4f g, Temp=%.2f°C",accel.x,accel.y,accel.z,temperature);}else{if(accel_ret!=ESP_OK){ESP_LOGE(TAG,"✗ Failed to read ADXL355 acceleration data: %s",esp_err_to_name(accel_ret));}if(temp_ret!=ESP_OK){ESP_LOGE(TAG,"✗ Failed to read ADXL355 temperature data: %s",esp_err_to_name(temp_ret));}}}else{ESP_LOGW(TAG,"MQTT not connected, skipping data read");}led_toggle();vTaskDelay(2000/portTICK_PERIOD_MS);}}