I am an engineering student and I am doing a project using an STM32L476RG board with the Arduino IDE. My goal is to read four analog inputs from four vibration sensors attached on a surface and to detect the signals from an impact on the surface. Then i want to calculate the TDOA in order the estimate the impact position with a multilateration algorithm. To calculate the TDOA I need a very high clock frequency, this is why I am using a Nucleo-64 board rather than an Arduino UNO.
I am encountering many difficulties in reading all the signals using the ADC of the Nucleo-64 board so that i can maximize the sample rate, as i have not a sufficient knowledge regarding these things. Could someone please help me?
I also checked the library of STM32duino but i was not able to find anything that could solve my issue.
For now I did experiments using only two sensors in order to see if it works. I used this code:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
void loop() {
// read the input on analog pins A0 and A1
int sensorValueA0 = analogRead(A0);
int sensorValueA1 = analogRead(A1);
// print out the value you read:
Serial.print("Variable_A0:");
Serial.print(sensorValueA0);
Serial.println(",");
Serial.print("Variable_A1:");
Serial.println(sensorValueA1);
}
I sent the voltage signal of the piezoelectric sensors through an amplification circuit and a comparator, which i calibrated in order to give me high signal when there is a sufficient vibration of the sensors. From the code i get correctly on a serial plot high values of the two signals when i tap on the surface. However, i see that i get values every 1 ms (1 value for each sensor every 2 ms), therefore it is not sufficient to evaluate the TDOA of the signals.
Sometime ago I did look at STM32 for fast sampling with the ADC using DMA. I was attempting to get samples at arounf 310kHz. I used the Cube IDE and followed at least this tutorial: https://www.digikey.ch/en/maker/projects/getting-started-with-stm32-working-with-adc-and-dma/f5009db3a3ed4370acaf545a3370c30c . I seem to remember getting samples at the desired rate but failed to do square root calculations fast enough for my project which now has the status "Pending", that is it is effectively abandoned.
You may also find that other MCUs give you enough speed, even possibly a Uno. Instead of using analogRead() in the loop() you can drive the ADC using a timer and take samples on a conversion interrupt or let it free run and take samples with a timer ISR (Interrupt service routine).
There are techniques for avoiding the square roots like linearising or squaring the hyperbolic equations.
There are other techniques using iterative optimisations (older ones like Foy's Algorithm, Chan and Ho Algorithm) or gradient descent as found in many ML enabled environments (for TDOA, the cost function would represent the squared error between measured and predicted TDOA and The gradient descent algorithm minimises this error by adjusting the estimated source position in a given coordinate system)
I don't know if that would be fast enough for your needs though.
at 9600 bauds you get roughly 1 character every ms and print becomes blocking when you have filled the 64 bytes buffer, so you are throttling your code with those prints.
You could try to print at a much higher baud rate (or not at all and acquire enough data to perform the analysis and then only print the result).
Using DMA and possibly going down to 8 bit precision for the analogRead would give you faster reads as well
It is going back a bit but it was this to interpret a phase modulated radio time signal: http://www.marvellconsultants.co.uk/DCF/DCFRx_c.txt if you are curious. Search for 'Babylonian' method . The code, incidentally, is not mine. I was simply trying to duplicate the main functionality.
I later did some tests with an ESP32 and it was fast enough using its standard square root function for 16bit samples at 310kHz but then I shelved the project. I had no intention of simply copying the code. My goal was to build it from scratch, simplifying it by dropping the more desperate optimisation logic and trying to understand it as I went along.
I tried to use the Cube IDE but it didn't work, moreover it seems to me to be very complicated.
Actually, I do not have a precise required sample frequency, but the better the frequency the more precise the estimated positions. Ideally, I would use the maximum sample frequency of the Nucleo board but I accept also values that give me a reasonable error on the position estimation. As an order of magnitude I would have a sample frequency greater than 1 MHz.
Indeed. Once you have to go behind the basic facilities of the Arduino IDE to exploit the more performant features of the underlying MCU then the complexity is magnified many times. As you get closer to the hardware, you cut through abstraction layers until you achieve the best balance between performance an complexity.
Maybe a reasonable starting point would be to more precisely determine the hardware performance necessary to support your project.
Thank you.
As I am a beginner, i would ask a help in writing this code. I would be glad if you can give me a hand because this is my first project using these devices. How can I implement these changes?
Moreover, the baud rate is related to the speed communication of Arduino through the serial port, this doesn't mean that it is acquiring data faster, am I correct?
While the baud rate doesn't directly influence the speed of functions like analogRead(), in your specific case, it indirectly impacts the loop because of how the Serial.print calls are handled.
When you perform multiple analogRead() calls and then use Serial.print to send the values, the outgoing data is added to a small serial buffer (64 bytes on most Arduino boards). If you attempt to send data faster than the baud rate allows (e.g., 9600 bits per second, which equates to about 960 characters per second), the buffer will fill up. Once the buffer is full, Serial.printbecomes blocking, meaning it halts the loop until there is enough space in the buffer to continue writing all the bytes.
This blocking behaviour prevents the loop from running at its intended speed, which delays the next set of analogRead() calls and slows down the sampling rate.
➜ To avoid this, you can either increase the baud rate to transmit data faster or reduce the amount of data being sent to the serial port.
You may get a bit further by processing the incoming data immediately but storing the intermediate results in a data structure then, periodically, consolidate and print them. This will at least stop the slow printing activities interfering with the data collection and processing.
If that does not work, then you have to look at other options as mentioned before:
Driving the ADC by a hardware timer at the desired frequency and processing the results when the ADC signals these are ready or
Configuring the ADC is a free running mode and configure a hardware timer running at the desired frequency to execute your routine to collect and process the results from the ADC.
There is also this site https://www.stm32duino.com/ which specialised in using STM32 hardware with the Arduino IDE. However, you may find that your requirements exceed what is possible or what can be achieved via the Arduino IDE and the available libraries for your board.
The Cube IDE is different in concept to say the Arduino IDE. There you select the features you want by clicking through a graphical user interface. It then goes off and generates a huge amount of code which you have to comb through and fill in the blanks it leaves for your specific configuration.