From Arduino Nano 33 IoT to CY8CKIT-062S2-43012
In my previous post Smart Air Quality Monitor #3 - Exploring Air Quality Sensor ZMOD4510 and Pmod Type 6A (Extended I2C) I've described my journey of connecting the air quality sensor with Arduino Nano 22 IoT. The sensor was connected using the I2C bus protocol to MCU and was able to measure air quality. Now I need to repeat it with the CY8CKIT-062S2-43012 kit.
I2C Master Project
Some of Cypress's boards have PMOD interfaces, but the CY8CKIT-062S2-43012 board doesn't have it. As I need to use the I2C interface to interact with ZMOD4510 ModusToolbox comes with a set of prebuilt projects and one of them was to showcase I2C interaction by controlling the built-in LED. I've used Project Creator to import this project. I've typed "I2C" to filter the projects. and then I've selected it.
I've connected the board to my PC using USB and then build and deployed this project.
After a few minutes, the initial build was completed. But the firmware was not uploaded due to the following warning:
It was the result I've my upgrade of ModusToolbox from 2.3.1 to 2.4. I've uploaded the new KitProg3 firmware to remove this blocker.
vlasov01@remote:~/ModusToolbox/tools_2.4/fw-loader/bin$ ./fw-loader --info Cypress Firmware Updater, Version: 3.3.0.1370 (C) Copyright 2018-2021 by Cypress Semiconductor Corporation (an Infineon company) All Rights Reserved Info: Start API initialization Info: Connected - KitProg3 CMSIS-DAP BULK-0C1416A4002A9400 Info: Hardware initialization complete 485 ms vlasov01@remote:~/ModusToolbox/tools_2.4/fw-loader/bin$ sudo ./fw-loader --update-kp3 [sudo] password for vlasov01: Cypress Firmware Updater, Version: 3.3.0.1370 (C) Copyright 2018-2021 by Cypress Semiconductor Corporation (an Infineon company) All Rights Reserved Info: Start API initialization Info: Connected - KitProg3 CMSIS-DAP BULK-0C1416A4002A9400 Info: Hardware initialization complete 486 ms Found KP firmware image: "/home/vlasov01/ModusToolbox/tools_2.4/kp-firmware/kitprog3.cyacd" Device 'KitProg3 CMSIS-DAP BULK-0C1416A4002A9400' opened successfully Info: Kit FW is 'KitProg3' ver. 2.30 b1155. Upgrade file is 'KitProg3' ver. 2.30 b1155. Info: Disconnected - KitProg3 CMSIS-DAP BULK-0C1416A4002A9400 Info: Connected - KitProg3 Bootloader-A4000216140C2A94 Info: Bootloader Version: Major 1, Minor 1, Build 60 Info: FW Upgrade to version: 2.30 b1155 Info: Bootloading of KitProg FW... Info: Verifying of KitProg FW... Info: Bootloading of DAPLink... Info: Verifying of DAPLink... Info: Upgrade completed Info: Disconnected - KitProg3 Bootloader-A4000216140C2A94 Info: Connected - KitProg3 CMSIS-DAP BULK-0C1416A4002A9400 FW update completed successfully
And then I've uploaded the project firmware to the board. This time it was successful.
Then I've switched the power off from the board and connected P6_0 (SCL) to P9_0 and P6_1 (SCA) to P9_1 as described in the project documentation.
SCL and SCA are defined in https://www.cypress.com/file/175671/download as
- Serial data (SDA) is the I2C data signal. It is a bidirectional data signal used to transmit or receive all bus data.
- Serial clock (SCL) is the master-generated I2C clock. Although the slave never generates the clock signal, it may hold the clock low, stalling the bus until it is ready to send data or ACK/NAK the latest data or address.
And the LED on the board started blinking as expected.
I2C Wrapper for ZMOD4510
I've followed the documentation provided with ZMOD4510 firmware. It asked to create a hardware-specific wrapper for the I2C bus, so the ZMOD4510 library can use it. Another requirement is to create a new main.c file with the right sequence of interacting with the ZMOD4510 library. I've completed both of these changes and added the following files to the project.
i2c_wrapper_psoc6.h
hal_psoc6.h
i2c_wrapper_psoc6.c
hal_psoc6.c
As well I've modified main.c as per ZMOD4510 documentation to initialize the sensor and added a loop to read data from the sensor.
#include "zmod4510_config_oaq2.h" #include "zmod4xxx.h" #include "zmod4xxx_hal.h" #include "oaq_2nd_gen.h" #include "i2c_wrapper_psoc6.h" #include "hal_psoc6.h" #include <stdlib.h> /******************************************************************************* * Function Name: main ******************************************************************************** * Summary: * This is the main function for CM4 CPU. * 1. I2C Master sends command packet to the ZMOD4510 slave * 2. I2C Master reads the response packet * * Parameters: * void * * Return: * int * *******************************************************************************/ /* Global variables */ zmod4xxx_dev_t dev; /* The ambient compensation needs humidity and temperature measurements! */ float humidity_pct; float temperature_degc; /* Sensor target variables */ uint8_t zmod4xxx_status; uint8_t adc_result[ZMOD4510_ADC_DATA_LEN]; uint8_t prod_data[ZMOD4510_PROD_DATA_LEN]; oaq_2nd_gen_handle_t algo_handle; oaq_2nd_gen_results_t algo_results; void error_handle() { printf("\r\n**************Fatal Error***********\r\n"); handle_error(); } int16_t measurements_counter = 0; void loop() { /* lib_ret -> Return of Library * api_ret -> Return of API */ int8_t lib_ret; zmod4xxx_err api_ret; uint8_t polling_counter = 0; api_ret = zmod4xxx_start_measurement(&dev); measurements_counter++; if (api_ret) { printf("ERROR %i during start of measurements, exiting program!\r\n\n", api_ret); error_handle(); } //psoc6_delay(15); //50); do { api_ret = zmod4xxx_read_status(&dev, &zmod4xxx_status); if (api_ret) { printf("ERROR %i during read of sensor status:%i, wait:%i\r\n", api_ret, zmod4xxx_status, polling_counter); zmod4xxx_status = api_ret; //printf("Error %i during read of sensor status, exiting program!", api_ret); //error_handle(); } printf("><"); psoc6_delay(15); //15); polling_counter++; } while ((zmod4xxx_status & STATUS_SEQUENCER_RUNNING_MASK) && (polling_counter <= ZMOD4510_OAQ2_COUNTER_LIMIT)); if (ZMOD4510_OAQ2_COUNTER_LIMIT <= polling_counter) { api_ret = zmod4xxx_check_error_event(&dev); if (api_ret) { printf("ERROR %i during check event, exiting program!", api_ret); error_handle(); } printf("ERROR ERROR_GAS_TIMEOUT during read of sensor status, exiting program!"); error_handle(); } else { polling_counter = 0; } api_ret = zmod4xxx_read_adc_result(&dev, adc_result); if (api_ret) { printf("ERROR %i during read of ADC results, exiting program!", api_ret); error_handle(); } printf("\r\n"); //first 901 measurements are for stabilization int z = 0; for (int i = 0; i < ZMOD4510_ADC_DATA_LEN; i++) { z += abs(adc_result[i]); printf("adc[%i]=%i ", i, adc_result[i]);//kOhm } if (z == 0) { printf(" WARN No ADC result, measurement[%i]\r\n", measurements_counter); } else { /* Humidity and temperature measurements are needed for ambient compensation. * It is highly recommended to have a real humidity and temperature sensor * for these values! */ humidity_pct = 50.0; // 50% RH temperature_degc = 20.0; // 20 degC // get sensor results with API lib_ret = calc_oaq_2nd_gen(&algo_handle, &dev, adc_result, humidity_pct, temperature_degc, &algo_results); if ((OAQ_2ND_GEN_STABILIZATION != lib_ret) && (OAQ_2ND_GEN_OK != lib_ret)) { printf("ERROR when calculating algorithm, exiting program!"); error_handle(); } else { //printf("\r\n*********** Measurements ***********\r\n"); //first 901 measurements are for stabilization printf("\r\n measurement[%i]", measurements_counter); for (int i = 0; i < 8; i++) { printf(" Rmox[%i]=%4.2fk ", i, algo_results.rmox[i] / 1e3); //kOhm } printf(" O3_ppb=%4.2f", algo_results.O3_conc_ppb); printf(" Fast AQI=%i", algo_results.FAST_AQI); printf(" EPA AQI=%i", algo_results.EPA_AQI); if (lib_ret == OAQ_2ND_GEN_STABILIZATION) { printf(" Warmup!\r\n"); } else { printf(" Valid!\r\n"); } } } /* Delay (1980ms). This delay is necessary to keep the right measurement timing and * call a measurement every 1.998 seconds with a maximum of 5% deviation.*/ psoc6_delay(1980); } void setup(void) { int8_t ret; zmod4xxx_err api_ret; api_ret = init_hardware(&dev); if (api_ret) { printf("ERROR %i during initialize hardware, exiting program!\r\n\n", api_ret); error_handle(); } /* \x1b[2J\x1b[;H - ANSI ESC sequence for clear screen */ //printf("\x1b[2J\x1b[;H"); printf("*************************\r\n"); printf("PSoC 6 MCU I2C ZMOD4510 \r\n"); printf("**************************\r\n\n"); dev.i2c_addr = ZMOD4510_I2C_ADDR; dev.pid = ZMOD4510_PID; dev.init_conf = &zmod_oaq_sensor_type[INIT]; dev.meas_conf = &zmod_oaq_sensor_type[MEASURE]; dev.prod_data = prod_data; api_ret = zmod4xxx_read_sensor_info(&dev); printf("*****ZMOD4510 read the sensor info************\r\n\n"); if (api_ret) { printf("ERROR %i ZMOD4510 during read the sensor info, exiting program!\r\n\n", api_ret); error_handle(); } printf("*****ZMOD4510 read the tracking number************\r\n"); uint8_t track_number[ZMOD4XXX_LEN_TRACKING]; api_ret = zmod4xxx_read_tracking_number(&dev, track_number); if (api_ret) { printf("ERROR %i during read the tracking number, exiting program!\r\n\n", api_ret); error_handle(); } printf("sensor tracking number: x"); for (uint8_t i=0;i<ZMOD4XXX_LEN_TRACKING;i++) printf("%x",track_number[i]); printf("\r\n"); printf("*****ZMOD4510 prepare the sensor************\r\n\n"); api_ret = zmod4xxx_prepare_sensor(&dev); if (api_ret) { printf("ERROR %i in the sensor preparation, exiting program!\r\n\n", api_ret); error_handle(); } printf("INFO *****ZMOD4510 the library initialization************\r\n\n"); ret = init_oaq_2nd_gen(&algo_handle, &dev); if (ret) { printf("ERROR %i during the library initialization, exiting program!\r\n\n", api_ret); error_handle(); } } int main(void) { setup(); printf("INFO *****ZMOD4510 measurements************\r\n\n"); for (;;) { loop(); //psoc6_i2c_write(i2c_addr, reg_addr, buf, len); /* Give delay between commands. */ //cyhal_system_delay_ms(CMD_TO_CMD_DELAY); /* Read adc value */ //psoc6_i2c_read(i2c_addr, reg_addr, buf, len); } }
But I was getting the I2C Bus error. After some investigation, I've found the issue. The I2C example was configured to act as a master (control) and a slave (LED) at the same time. It was defined in resource_map.h file by I2C_MODE define, which was set to I2C_MODE_BOTH. After I've changed it to I2C_MODE_MASTER it started working without the I2C Bus error.
#define I2C_MODE (I2C_MODE_MASTER)
Logic Analyzer
The sensor board was interacting over the I2C protocol, but I was not able to initialize it properly and I was not been able to get valid readings from the sensor, I've got a strong suggestion from the ZMOD4510 support to use Logic Analyzer. Fortunately, I have one. I've connected it to the board and configured for I2C protocol.
So I've collected and compared the results of interaction between the sensor board and Arduino Nano 33 IoT and CY8CKIT-062S2-43012 by exporting data to Excel file. I found one difference. The Arduino code was not reading the sensor tracking number. But it is a required call according to the support.
ZMOD4510 findings
Another suggestion was to power off/on the sensor. And it really helped. The ZMOD4510 sensor has its own MCU and state. So the reset of Arduino Nano 33 IoT or CY8CKIT-062S2-43012 is not propagating to the sensor with a simple I2C connection. The power cycling of ZMOD4510 allowed resetting its state and proper initializing sequence.
I've started getting raw data from the sensor. At first, the sensor requires to make 901 measurements during warmup after each power cycling. As I need to wait 2 seconds between measurements it takes 30 minutes to get it to the operational state. This finding has a very important consequence. It needs to be constantly powered on or it requires in advance planning to turn it on for 30 minutes before it can be used to measure air quality.
adc[0]=17 adc[1]=124 adc[2]=242 adc[3]=138 adc[4]=204 adc[5]=207 adc[6]=195 adc[7]=28 adc[8]=193 adc[9]=221 adc[10]=192 adc[11]=119 adc[12]=191 adc[13]=229 adc[14]=191 adc[15]=42 adc[16]=190 adc[17]=74 measurement[3] Rmox[0]=77282.53k Rmox[1]=1039.17k Rmox[2]=787.41k Rmox[3]=762.24k Rmox[4]=735.48k Rmox[5]=724.98k Rmox[6]=711.87k Rmox[7]=696.65k O3_ppb=0.00 Fast AQI=0 EPA AQI=0 Warmup! ... adc[0]=17 adc[1]=98 adc[2]=229 adc[3]=139 adc[4]=107 adc[5]=14 adc[6]=96 adc[7]=226 adc[8]=96 adc[9]=84 adc[10]=95 adc[11]=164 adc[12]=95 adc[13]=251 adc[14]=95 adc[15]=140 adc[16]=95 adc[17]=109 measurement[1146] Rmox[0]=3305.20k Rmox[1]=143.40k Rmox[2]=118.96k Rmox[3]=117.73k Rmox[4]=116.21k Rmox[5]=116.96k Rmox[6]=116.01k Rmox[7]=115.74k O3_ppb=0.00 Fast AQI=0 EPA AQI=0 Valid!
Another finding is that even I'm getting the readings from the sensor and they seem valid, I'm not getting the valid output from the ZMOD4510 algorithm. The key values are all equal to 0 ->
O3_ppb=0.00
Fast AQI=0
EPA AQI=0
O3_ppb=0.00 Fast AQI=0 EPA AQI=0
I may need to investigate further why it is the case. Maybe the FPU library is not configured properly or some other issues.
Update #1
I've switched to M4F arm-none-eabi-gcc library ( lib_oaq_2nd_gen.a ) and switched VFP_SELECT to hardfp in the Makefile. But I've got the same result from the algorithm output again.