Increase the serial.read rate (using python program to read data from an arduino nano)

I try to use a python program to read the output data from an arduino nano. The data saved from the python program shows a sampling speed of about 17 ms/data point. I want to increase the speed to at least 1 ms/data point (1000/second). How should I change the code? Thank you very much.

Arduino code

const int voltagePin = A0;
const int HANDSHAKE = 0;
const int VOLTAGE_REQUEST = 1;

int inByte;


void printVoltage() {
  // Read value from analog pin
  float value = analogRead(voltagePin);


  // Get the time point
  unsigned long time_ms = millis();

  // Write the result
  if (Serial.availableForWrite()) {
    String outstr = String(String(time_ms, DEC) + "," + String(value, DEC));
    Serial.println(outstr);
  }
}


void setup() {
  // Initialize serial communication
  Serial.begin(1000000);
}


void loop() {
  // Check if data has been sent to Arduino and respond accordingly
  if (Serial.available() > 0) {
    // Read in request
    inByte = Serial.read();

    // If data is requested, fetch it and write it, or handshake
    switch (inByte) {
      case VOLTAGE_REQUEST:
        printVoltage();
        break;
        //case HANDSHAKE:
        //if (Serial.availableForWrite()) {
        //Serial.print(""); //Please input:
    }
    //break;
  }
}

Python code

    for n in range (0, 200):
        #names = ['0', 'A', 'B', 'C', 'D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', '1' ]

        
        #print (names)
        for name in names:
            # Alphabet dictionary
            alphabet_dict = {'A': '00', 'B': '01', 'C': '010', 'D': '011', 'E': '0110', 'F': '0111', 'G': '01110',
                             'H': '10', 'I': '11',
                             'J': '110', 'K': '111', 'L': '1110', 'M': '1111', 'N': '11110', 'O': '100', 'P': '101',
                             'Q': '1010',
                             'R': '1011', 'S': '10110', 'T': '10111', 'U': '1100', 'V': '1101', 'W': '11010',
                             'X': '11011', 'Y': '110110',
                             'Z': '110111'}

            board = pyfirmata.Arduino ('/dev/cu.usbserial-AR0JYQ1Q')

            HANDSHAKE = 0
            VOLTAGE_REQUEST = 1


            def find_arduino(port='/dev/cu.usbserial-AR0JYQ1Q'):
                """Get the name of the port that is connected to Arduino."""
                if port is None:
                    ports = serial.tools.list_ports.comports ()
                    for p in ports:
                        if p.manufacturer is not None and "Arduino" in p.manufacturer:
                            port = p.device
                return port


            

            port = find_arduino ()
            arduino = serial.Serial (port, baudrate=1000000)
            

            # Ask Arduino for data
            arduino.write (bytes ([VOLTAGE_REQUEST]))

            # Receive data
            raw = arduino.read_until ()


            def parse_raw(raw):
                """Parse bytes output from Arduino."""
                raw = raw.decode ()
                if raw [-1] != "\n":
                    raise ValueError (
                        "Input must end with newline, otherwise message is incomplete."
                    )

                t, V = raw.rstrip ().split (",")

                return int (t), float (V) * 5 / 1023


            def request_single_voltage(arduino):
                """Ask Arduino for a single data point"""
                # Ask Arduino for data
                arduino.write (bytes ([VOLTAGE_REQUEST]))

                # Read in the data
                raw = arduino.read_until ()

                # Parse and return
                return parse_raw (raw)


            time_ms = []
            voltage = []

            for i in range (200):
                # Request and append
                t, V = request_single_voltage (arduino)
                time_ms.append (t)
                voltage.append (V)

                # Wait 20 ms
                # time.sleep(0)

            time_ms = np.array (time_ms)
            voltage = np.array (voltage)
            voltage = gaussian_filter1d (voltage, sigma=2)
            df = pd.DataFrame (voltage, time_ms)
            df.drop(df.index[0])

            df.name = name

            df.to_csv (r'/Users/renzha/Documents/work/preparing papers/non-contact HCI/' + df.name + '.txt')

increase the bit-rate in both. but do i see that the current bit-rate is 1M.

if the bit-rate can't be increase, then the amount of data needs to be reduced

What is connected to pin A0 on the Arduino ?

I think the problem is that you are not requesting a new data point until you have fully processed the previous data point. Your PC should have plenty of room to buffer the 200 lines of data and THEN process them. Change the commands so that instead of requesting one sample you request a burst of 200. Then have the Arduino send 200 samples as fast as it can. When you receive all 200 samples, process the raw data.

You can also reduce bytes sent by sending millis() - startTime. That will generally be fewer digits than millis().

It might be faster to use:

  Serial.print(time_ms);
  Serial.print(',');
  Serial.println(value);

Rather than converting integers to String, concatenating the Strings together, and then printing.

Thanks johnwasser. Use serial.print() is actually better.
I tried to sending millis()-startTime, but it seems not working. Is there something wrong with my code (attached below)? The output is also attached. Another issue is that there is always a ' ,0' at the first row. How to get rid of it? Thank you very much.

Code:

const int voltagePin = A0;
const int HANDSHAKE = 0;
const int VOLTAGE_REQUEST = 1;

int inByte;
unsigned long time_ms;
unsigned long currentTime;
unsigned long startTime;

void printVoltage() {
  // Read value from analog pin
  float value = analogRead(voltagePin);


  // Get the time point

  // Write the result
  if (Serial.availableForWrite()) {
    time_ms = currentTime - startTime;
    //String outstr = String(String(time_ms, DEC) + "," + String(value, DEC));
    Serial.print(time_ms);
    Serial.print(',');
    Serial.println(value);
  }
}


void setup() {
  // Initialize serial communication
  Serial.begin(2000000);
  startTime = millis();
}


void loop() {
  currentTime = millis();
  // Check if data has been sent to Arduino and respond accordingly
  if (Serial.available() > 0) {
    // Read in request
    inByte = Serial.read();

    // If data is requested, fetch it and write it, or handshake
    switch (inByte) {
      case VOLTAGE_REQUEST:
        printVoltage();
        break;
        //case HANDSHAKE:
        //if (Serial.availableForWrite()) {
        //Serial.print(""); //Please input:
    }
    //break;
  }
}

output

,0
38924,1.8010351928268375
38939,1.7673310947296734
38955,1.726345081240634
38971,1.7047727111069237
38986,1.704557397007679
39003,1.706025341687453
39019,1.694857704278058
39034,1.6794792010031014
39051,1.6774535193372666
39067,1.6901716258099335
39084,1.700360155546886
39099,1.695448339473061
39115,1.6843788643262432
39131,1.6846311775276162
39147,1.6975992981040338
39163,1.7065983718438178
39179,1.7001630135559682
39195,1.6880536675469604
39211,1.687619683174833
39227,1.6997293579007215
39243,1.7079387260748993
39259,1.7018522349167382
39275,1.691631854934649
39290,1.693132943902862
39307,1.7049324895897155
39322,1.7099859142495382
39339,1.6996399753081077
39355,1.6861418356869227
39370,1.6860384009952136
39387,1.6976944066516753
39403,1.7041427727607088
39418,1.6970526132916772
39435,1.6881446469146528
39450,1.6919819859579832
39467,1.7046105354857817
39483,1.7085084098028394
39498,1.6970988914513327
39515,1.684299047542023
39530,1.6855829221254401
39546,1.696979390159147
39563,1.7010530906479349

I tried to make a voltage meter using A0 and the GND.

Hi,
What is your application?
Why do you need a high sample rate?

Thanks... Tom.. :smiley: :+1: :coffee: :australia:

Sending a burst of 200 samples runs in about:
99 milliseconds at 2M baud.
103 milliseconds at 1M baud,
111 milliseconds at 500k baud,
111 milliseconds at 250k baud,
You can even get over 1000 samples per second at 115200 baud.

Often, when you are analyzing data, it is important that the sample interval is a constant. Here is a way to get 1kHz samples:

unsigned long time_us;
unsigned long startTime;

void sendBurst()
{
  // Delay to get to an even millisecond
  delayMicroseconds(1000 - (micros() % 1000));
  startTime = micros();

  for (int i = 0; i < 200; i++)
  {
    // Delay to get to an even millisecond
    delayMicroseconds(1000 - (micros() % 1000));
    time_us = micros() - startTime;

    // Read value from analog pin
    int value = analogRead(voltagePin);

    // Write the result
    Serial.print(time_us);
    Serial.print(',');
    Serial.println(value);
  }
}

Thank you very much johnwasser. The speed is very fast now. However, time_us = micros() - startTime; does not return a good number (see below). Maybe it is due to the ,0 in the first row. is there a way to get rid of it? Thank you.

output

,0
38924,1.8010351928268375
38939,1.7673310947296734
38955,1.726345081240634
38971,1.7047727111069237
38986,1.704557397007679
39003,1.706025341687453
39019,1.694857704278058
39034,1.6794792010031014
39051,1.6774535193372666
39067,1.6901716258099335

It returns good numbers for me. Perhaps your Python program is mangling them. Here is the output of the Arduino sketch:

996,559
2032,558
3040,559
4040,563
5036,568
6040,573
7040,578
8040,582
9040,584
10040,585
11040,584
12040,579
13040,573
14040,567
15040,562
16048,556
17044,553
18048,550
19036,551
20036,554
21036,559
22036,564
23036,570
24036,574
25036,578
26036,579
27048,578
28048,576
29036,572
30036,565
31036,560
32036,554
33036,550
34036,546
35036,546
36028,546
37040,550
38028,555
39028,561
40028,565
41040,570
42028,572
43036,574
44032,572
45040,569
46032,565
47040,559
48032,553
49036,546
50040,543
51040,540
52040,540
53036,542
54040,546
55040,552
56040,556
57040,562
58040,565
59044,567
60036,569
61036,566
62036,563
63036,557
64036,552
65044,545
66036,541
67036,536
68036,535
69036,536
70036,539
71036,543
72036,548
73036,553
74036,558
75036,561
76036,563
77036,563
78028,560
79028,556
80040,550
81028,544
82028,539
83040,533
84032,532
85040,529
86032,531
87040,534
88032,539
89040,544
90032,549
91040,554
92032,557
93040,558
94040,557
95040,555
96040,549
97036,543
98040,537
99040,532
100040,527
101032,525
102028,525
103020,527
104036,531
105036,536
106036,541
107036,547
108028,550
109036,552
110036,553
111036,552
112028,548
113028,542
114036,536
115036,531
116036,525
117028,522
118036,520
119036,521
120036,523
121020,528
122032,533
123032,539
124032,543
125024,546
126032,549
127028,549
128028,546
129020,541
130032,536
131032,529
132032,524
133024,518
134024,517
135032,514
136028,516
137032,519
138024,525
139028,529
140032,535
141032,540
142024,542
143028,544
144028,543
145032,539
146024,535
147036,528
148036,522
149036,517
150036,512
151028,510
152040,509
153036,512
154036,516
155028,521
156036,527
157036,531
158036,536
159028,537
160040,538
161036,536
162036,533
163020,528
164032,522
165032,515
166028,511
167020,507
168028,504
169032,505
170032,508
171032,512
172024,518
173028,523
174032,528
175028,531
176020,533
177032,534
178032,532
179032,526
180020,521
181032,515
182032,509
183032,505
184020,502
185032,500
186032,501
187032,504
188032,509
189024,515
190040,521
191036,524
192036,528
193028,529
194036,527
195036,524
196036,520
197028,514
198036,507
199036,501
200036,498

The ',0' actually appears after df = pd.DataFrame (voltage, time_ms). I do not know why it cause that. I can of course do data processing afterward, but it is still a little bit annoying.