Sunday, 31 May 2020

STM32 Blue Pill with MPXV7002DP Differential Air Pressure Sensor and Venturi Tube

The previous post dealt with the mathematics behind a venturi tube.  This post will cover how to use the tube I designed with an MPXV7002 sensor via a ADS1115 16 bit ADC.

I will be honest I haven't had any experience with the STM32 Blue Pill or the ADS1115 ADC but I'll get there and I will blog about my adventures along the way.  Hopefully someone else will find what I have learned and achieved (or not achieved) useful...

I'm going to first get a the STM32 to work with the Arduino IDE - I then may move across to visual studio and platformIO.  I'm not a huge fan of the Arduino IDE but it gets things done when one is in a rush...we are definitely in a rush here...

Update after several hours....It was incredibly difficult to get the STM32 Blue Pill to work with the Arduino IDE.  I followed all of the instructions given on these sites:



Disappointingly it didn't make a single difference.  The STM32 would not reprogram via the Arduino IDE.  

I'm using a genuine RS232 Serial programming cable and I've checked the connections several times but there has been no response from the board...

I watched the following youtube videos:


But nothing seemed to work....

Finally I watched this video:


...and downloaded all of the files mentioned in the description, followed the instructions and managed to load BLINK onto the STM32 Blue Pill using the microUSB connector.  A bootloader compatible is flashed onto the STM32 which then enumerates the board as a USB device.

I'm happy I got things working but this was a painful experience and took far longer than it should have...hey ho.

Now that I have the STM32 Blue Pill working and programming via the USB port it is time to get connecting the ADS1115 breakout board and MPXY7002DP Pressure sensor connected up.

The ADS1115 is a 16 bit ADC (analogue to digital converter) and a programmable gain amplifier or PGA.  It is connected to the microcontroller via I2C connections and requires a 3.3 Vdc supply and ground.

The MPX7002DP is an analogue sensor and requires a 5 Vdc supply and a ground connection.  The signal output will connect to the A0 input on the ADS1115 ADC.

The pin connections for the STM32 Blue Pill will be 3.3 Vdc, 5 Vdc, Ground, SDA and SCL (I2C).  I've decided to use SCL1 and SDA1 for I2C connections.

Here is the pin connection information for the STM32 Blue Pill:

Image Credit: https://components101.com/


Here is a connection diagram for those that like images.  I used Fritzing to create it and to be honest that program has come a long way.  I really like it for quick diagrams...it's very useful.

Now is the time for my least favourite part...writing firmware.  I'm actually not particularly good at this - I just muddle through.

The idea is to reuse the code from my previous post accounting for the new Venturi tube dimensions and the 16 bit ADC.

The first thing to do is download the following libraries and install them for use with the Arduino IDE:



Then read the example code for the Adafruit library and apply it!  I may have failed to set the I2C address correctly when testing this...

Here is the test code...it does work however I think the Venturi tube needs more work to improve sensitivity...

1:  // Venturi Mark 3 Test Code    
2:  // Using the MPX7002DP Differential Air Pressure Sensor  
3:  // Converts the sensor data received to volumetric flow   
4:  // rate and velocity of flow   
5:  //   
6:  // A.Lang - ©2020   
7:  //   
8:  // This code exercises the MPX7002DP   
9:  // Pressure sensor connected to ADS1115 16 bit ADC  
10:  // Gain of ADC set to 4096...  
11:  // Averaging added to reduce noise and remove offset.  
12:    
13:  //Libraries  
14:  #include <Adafruit_ADS1015.h>  
15:    
16:  #include <Average.h>  
17:    
18:  //Set Average sample rate  
19:  Average < float > averageValue(200);  
20:    
21:  // Initialise ADC object  
22:  Adafruit_ADS1115 ads1115(0x48); /* Use this for the 16-bit version */  
23:    
24:  //variables   
25:    
26:  int sampleNumber = 0; // variable to store the sample number    
27:  int16_t sensorValue = 0; // variable to store the Raw Data value coming from the sensor   
28:    
29:  float averageInitialValue = 0; // variable to store the average inital value   
30:    
31:  float diffPressure = 0; // variable to store converted kPa value    
32:  float volumetricFlow = 0; // variable to store volumetric flow rate value    
33:  float velocityFlow = 0; // variable to store velocity of flow value    
34:  float offset = 0; // variable to store offset differential pressure   
35:    
36:  //constants - these will not change   
37:  const float tubeArea1 = 0.01455; // area of Large Part of Venturi Tube   
38:  const float tubeArea2 = 0.0044; // area of Smaller Part of Venturi Tube   
39:  const float airDensity = 1.206; // The density of air at 20 °C    
40:    
41:  void setup() {  
42:   // start serial port at 9600 bps and wait for port to open:   
43:   Serial.begin(115200);  
44:    
45:   Serial.flush(); //Clear the Serial Terminal buffer  
46:   Serial.println(); //Insert a blank line  
47:    
48:   ads1115.setGain(GAIN_ONE); // 1x gain  +/- 4.096V 1 bit = 2mV  
49:   ads1115.begin(); // Start the ADC running  
50:    
51:   //Header for CSV data   
52:    
53:   Serial.print("Sample Number, Raw Sensor Value, Differential Pressure, Volumetric Flow Rate, Velocity of Flow,");  
54:   Serial.println();  
55:   Serial.print("    ,   bits  ,   Pa   ,  m^3/second  ,   m/s  ,");  
56:   Serial.println();  
57:    
58:   // get initial sensor value   
59:   for (int i = 0; i < 200; i++) {  
60:    
61:    // read the value from the sensor:    
62:    sensorValue = ads1115.readADC_SingleEnded(0);  
63:    delay(25);  
64:    //push sensor values to averageValue object   
65:    averageValue.push(sensorValue);  
66:   }  
67:    
68:   for (int i = 0; i < 200; i++) {  
69:    // get average Sensor values   
70:    averageValue.get(i);  
71:    
72:   }  
73:    
74:   //calculate mean average sensor and store it   
75:   averageInitialValue = averageValue.mean();  
76:    
77:   Serial.print("Average Initial Value :");  
78:   Serial.print(averageInitialValue);  
79:   Serial.println();  
80:    
81:  }  
82:    
83:  void loop() {  
84:    
85:   // read the value from the sensor via ADC 0 pin:    
86:   sensorValue = ads1115.readADC_SingleEnded(0);  
87:    
88:   // initial value    
89:   sensorValue = sensorValue - (int) averageInitialValue;  
90:    
91:   // increment sample counter    
92:   sampleNumber++;  
93:    
94:   // map the Raw data to kPa   
95:   diffPressure = map(sensorValue, 0, 32768, 0, 4000);  
96:    
97:   if (sensorValue >= 0) {  
98:    //calculate volumetric flow rate for Exhalation   
99:    volumetricFlow = tubeArea1 * (sqrt((2 / airDensity) * (diffPressure / (sq(tubeArea1 / tubeArea2) - 1))));  
100:    
101:    //calculate velocity of flow    
102:    velocityFlow = volumetricFlow / tubeArea1;  
103:   }  
104:   // convert reading to a positive value   
105:   else if (sensorValue <= 0) {  
106:    diffPressure = diffPressure * -1;  
107:    
108:    //calculate volumetric flow rate for Inhalation   
109:    volumetricFlow = tubeArea2 * (sqrt((2 / airDensity) * (diffPressure / (1 - sq(tubeArea2 / tubeArea1)))));  
110:    
111:    //calculate velocity of flow    
112:    velocityFlow = volumetricFlow / tubeArea2;  
113:   }  
114:    
115:   // Print the results as comma separated values for easier processing   
116:   // in a spreadsheet program   
117:    
118:   Serial.print(sampleNumber);  
119:   Serial.print(",");  
120:   Serial.print(sensorValue);  
121:   Serial.print(",");  
122:   Serial.print(diffPressure);  
123:   Serial.print(",");  
124:   Serial.print(volumetricFlow);  
125:   Serial.print(",");  
126:   Serial.print(velocityFlow);  
127:   Serial.print(",");  
128:   Serial.println();  
129:    
130:   // wait 25 milliseconds before the next loop   
131:   // for the analog-to-digital converter and   
132:   // pressure sensor to settle after the last reading:   
133:   delay(25);  
134:    
135:  }  
I suppose I should explain what the code does and how it works:

The code makes use of two external libraries - Adafruit_ADS1015.h and Average.h.  These libraries are used to control the 16 bit analogue to digital converter (ADC) and to calculate a mean average on the data received.

Several variables are then initialised to store and process the data we are going to receive from the MPX7002 sensor via the ADC.

Several constants are initialised.  These are values which will never change.

The setup function initialises serial communications from the STM32 Blue pill to the external computer at 9600 baud.  That means that text data from the STM32 will be received at 9600 bits per second.  Each character is 8 bits long so that means that in theory 1200 characters can be received in one second.  We are not sending 1200 characters so there should be more than enough speed to receive all of the data we need.

The next part of the setup function sets the ADC gain to receive signals between zero volts and 4.096 volts.  This is so that we don't waste time trying to measure signals that won't be present.  The output from the MPX7002 sensor is from 0.5 Vdc to 4.5 Vdc which overlaps most closely with the programmable gain setting chosen.  I may choose to change this later after some more testing.

Next the setup function sets the ADC into operation and performs two measurment loops to measure the average of the data received.  This is so that the offset can be removed later and prevent issues with displaying the data.  The average calculated is sent to the serial terminal so it can be viewed and recorded.

The serial terminal is then sent two strings to prepare the data for comma separated values.  This is so that if we want to graph the data externally we can copy and paste the serial data and import it into a spreadsheet program.  The data separated by commas is automatically placed in columns ready for tabling and graphing.

The next function is the main loop which runs continuously...

The data is read from the sensor via the ADC. 

The offset previously calculated in the setup function is removed.

The sample number is incremented.

The raw data from the ADC is converted to kPA

If the data is greater than zero (exhalation) the volumetric flow is calculated and then the velocity of flow is calculated.

If the data is less than zero (inhalation) then the data is converted to positive and then the volumetric flow is calculated and then the velocity of flow is calculated.

The data calculated is sent via the serial terminal to the external computer.

A delay of 25 ms is present to ensure the sensor is given time to settle before the loop starts again taking the next measurement.

----

The results from the serial plotter within the Arduino IDE are quite good.  It is also possible to copy and paste the contents of the serial terminal and graph with a suitable spreadsheet program.

Here are some of the results:




Here is a photo of the test setup just so everyone can see what is going on: