There are a multiple questions intersperced throughout this post. Kindly read carefully and answer the pieces you can for an upvote.
Use Case
Create a time series csv file of pressure reads. These reads need to be of maximum frequency, but I only need it to last less than 10 seconds or so.
Material
- Arduino Uno Clone (unalterable)
- serial over USB 2.0 (alterable)
- pyserial (Python 3)
- SSD
Problem
Identify and fix the bottleneck preventing frequency from maximum reads/s.
The code
Arduino
void setup() {
//speed over the usb serial interface
Serial.begin(450000);
}
void loop() {
//get sensor data: read the input on analog pin 0:
int sensorValue = analogRead(A0); //returns value 0-1023
//send over serial
Serial.println(sensorValue);
}
Python
...
ser.serial()
ser.baudrate=450000
ser.open()
while True:
try:
serialData = float(ser.readline().decode().replace('\n', ''))
except(ValueError):
continue #I ran the code without this try/except and got 100 reads higher than when this is included in here. Caused no change in order of magnitude.
now = datetime.datetime.now()
serialData2 = serialData * voltageOffset #a couple of these happen
outfile.write(now+', '+str(serialData2)+'\n')
except(KeyboardInterrupt):
...
os.sys.exit(0)
Bandwidths
Some calculations to figure out where the bottleneck resides.
1 packet = 10 bits
serial wire speed: 450000/10 = 45000 packets / second
Arduino samples-into-serial/second rate:
100 µs * 1s/(10^-6 µs) = 1 x 10^4 samples / second
arduino silicon adapter: cheap cloned chip. Assumed to be underperforming as it is not an arduino-licensed product, but this is deemed acceptable loss as will be explained later. Assumed to have unlimited bandwidth, though this obviously will increase performance by an order of magnitude.
USB 2.0
packets/s = 480 mb/s * 10^6 b/mb * 1 packet/10 b = 48 x 10^6 packets / second
PC file write speed
400 MB/s = 320 x 10^6 packets/s
PC python script
Unknown. Presumed to be infinite? the datetime.now()
query is known to take ~ 8 µs.
Conclusion: The bottleneck is the serial baud rate.
Adjusting the baud rate confirms this. I'm finding that el-cheapo arduino uno only supports a maximum 450000 baud rate, when the arduino actually supports 2-7 million from what I can gather online. That being said, something is still wrong.
The linchpin question
Recall that at this baud rate, we should be seeing pressure reads generated at a theoretical 45000 packets / second.
baudrate-data length-no parity-1 end bit present: 450000-8-n-1
Let sensorValue = 1023
. Data looks like this going into println
: 1023\n
When I run serial.println(sensorValue)
in the arduino, how many packets are sent over the wire to the PC? How is this serialized into packets? How many packets will be sent over the wire? Which one of the following is true:
A. 16 bit int
requires 2 packets to be transmitted. The \n
exacts 2 more. Total = 4
B. sensorValue
is converted to a string and sent over the wire with separate packets: 1 for 1
, 1 for 0
, 1 for 2
, 1 for 3
, 1 for \
, and 1 for n
. Total = 6
C. Other (help me optimize??)
Tests
I'm not seeing results as I would expect.
Let baud = 450000
.
Changing value
in the arduino code serial.println(value)
produces different reads/second in my txt file:
value = readPin\n
>> 5417 pressure reads/s
value = 1
>> 10201 reads/s
value = 1023
>> 4279 reads/s
This tells me a couple things. There is overhead when reading the analogPin's value. I can also conclude the arduino also only sends over multiple packets of data when it has to (1 > 8 bit of data, 1023 > 16 bit of data). Wait, what? That doesn't make very much sense. How does that happen?
Adjusting the baud rate causes the number of reads to change until the arduino maxes out. But lets find the throughput I should be expecting.
450000 b/s * (1 packet / 10b) * [1 read / (4 or 6 packets)] = 11250 reads OR 7500 theoretical reads/s
Actual = 5417 reads/s
Where did 2,000 reads disappear to?